Compare commits

...

4 Commits

Author SHA1 Message Date
赵云
4c083cc698 Fix Bug #464: [目录管理-诊疗目录] 新增项目时"零售价"未与"诊疗子项"合计总价自动同步
根因:calculateTotalPrice中form.value.retailPrice赋值被nextTick包裹,
在多调用方(watcher/selectRow/addItem)并发时产生竞态,导致零售价更新丢失
修复:移除nextTick,改为同步赋值确保零售价实时同步总价

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 13:03:25 +08:00
Ranyunqiao
6367654ada 476 住院医生工作-检查申请单界面缺失核心临床字段(紧急程度、过敏史、检查目的等) 2026-05-14 12:56:04 +08:00
关羽
360256e589 Fix Bug #465: [住院医生工作站-检验申请] 检验项目选择列表被限制为500项,导致医生无法检索并开立其余800多项
问题根因:
- 前端使用 pageSize=500 分页拉取数据,el-transfer 组件客户端过滤在 1400+ 条数据下存在渲染和搜索性能问题
- 数据库实际有 1400 项已启用的检验类诊疗项目,但仅加载了 500 项

修复方案:
1. 改用 pageSize=9999 一次性拉取全部数据,消除分页导致的 500 项截断
2. 新增顶部搜索框,支持按项目名称/拼音首拼/业务编号实时过滤
3. 使用 computed 属性动态生成 transfer 组件数据,搜索时自动过滤
4. 显示总数统计(未搜索时显示总数,搜索时显示匹配数/总数)
5. 移除不再需要的 applicationList 变量引用和 onBeforeMount 空调用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 12:23:13 +08:00
荀彧
feb033b857 Fix Bug #462: [目录管理-诊疗目录] 编辑弹窗中"所需标本"下拉框数据加载失败,显示为"无数据" Fix: selectDictDataByType方法移除Redis缓存读取逻辑,直接查询数据库避免缓存为空数据导致前端下拉框无数据 2026-05-14 12:15:47 +08:00
4 changed files with 369 additions and 397 deletions

View File

@@ -85,18 +85,13 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService {
String trimmedKey = searchKey.trim();
return dictDataMapper.selectDictDataByTypeWithSearch(dictType, trimmedKey);
}
// 否则使用原有方法(带缓存)
List<SysDictData> dictDatas = DictUtils.getDictCache(dictType);
if (StringUtils.isNotEmpty(dictDatas)) {
return dictDatas;
}
dictDatas = dictDataMapper.selectDictDataByType(dictType);
// 直接查询数据库,避免缓存中为空数据导致前端下拉框显示"无数据"
List<SysDictData> dictDatas = dictDataMapper.selectDictDataByType(dictType);
if (StringUtils.isNotEmpty(dictDatas)) {
DictUtils.setDictCache(dictType, dictDatas);
return dictDatas;
}
return null;
return dictDatas;
}
/**

View File

@@ -473,15 +473,12 @@ function calculateTotalPrice() {
}
});
totalPrice.value = sum.toFixed(2);
// Bug #464: 零售价与诊疗子项合计总价实时同步
// Bug #464: 零售价与诊疗子项合计总价实时同步直接赋值不使用nextTick避免多调用方竞争
const hasValidItem = treatmentItems.value.some(
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
);
if (hasValidItem) {
// 使用 nextTick 确保总价更新后零售价才更新,避免 Vue 响应式时序问题
nextTick(() => {
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
});
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
} else {
form.value.retailPrice = undefined;
}
@@ -763,10 +760,7 @@ function selectRow(row, index) {
treatmentItems.value[index].adviceDefinitionId = row.id;
treatmentItems.value[index].retailPrice = row.retailPrice || 0;
medicineSearchKey.value = '';
// 使用 nextTick 确保 DOM 更新后再计算总价
nextTick(() => {
calculateTotalPrice();
});
calculateTotalPrice();
}
// 清空诊疗子项

View File

@@ -6,9 +6,26 @@
<template>
<div class="LaboratoryTests-container">
<div v-loading="loading" class="transfer-wrapper">
<!-- 远程搜索框 -->
<div class="search-bar">
<el-input
v-model="searchKey"
placeholder="输入项目代码/名称搜索"
clearable
@keyup.enter="handleSearch"
@clear="handleSearch"
style="width: 300px; margin-bottom: 10px"
>
<template #append>
<el-button @click="handleSearch">搜索</el-button>
</template>
</el-input>
<span v-if="!searchKey" class="total-count"> {{ totalCount }} </span>
<span v-else class="total-count">搜索到 {{ filteredCount }} / {{ totalCount }} </span>
</div>
<el-transfer
v-model="transferValue"
:data="applicationList"
:data="transferData"
filter-placeholder="项目代码/名称"
filterable
:titles="['未选择', '已选择']"
@@ -117,7 +134,7 @@
</div>
</template>
<script setup name="LaboratoryTests">
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
import {getCurrentInstance, onMounted, reactive, ref, watch, computed} from 'vue';
import {patientInfo} from '../../../store/patient.js';
import {getApplicationList, saveInspection} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js';
@@ -140,61 +157,85 @@ const findTreeItem = (list, id) => {
const emits = defineEmits(['submitOk']);
const props = defineProps({});
const state = reactive({});
const applicationListAll = ref();
const applicationList = ref();
const applicationListAll = ref([]);
const loading = ref(false);
const orgOptions = ref([]); // 科室选项
const getList = async () => {
const orgOptions = ref([]);
const searchKey = ref('');
const totalCount = ref(0);
// 将已加载的全部数据转为 transfer 组件所需的格式
const buildTransferData = (records) => {
return records.map((item) => {
const priceInfo = item.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = item.unitCode_dictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
});
};
// 加载全部数据(不分页,一次性拉取)
const loadAllData = async () => {
if (!patientInfo.value?.inHospitalOrgId) {
applicationList.value = [];
applicationListAll.value = [];
return;
}
loading.value = true;
try {
const allRecords = [];
let currentPage = 1;
const pageSize = 500;
// 分页拉取全部数据后端单页最多500条
while (true) {
const res = await getApplicationList({
pageSize,
pageNo: currentPage,
categoryCode: '22',
organizationId: patientInfo.value.inHospitalOrgId,
adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
});
if (res.code !== 200) {
proxy.$message.error(res.message);
applicationList.value = [];
return;
}
const records = res.data?.records || [];
allRecords.push(...records);
// 当前页不足 pageSize 或已无数据,说明已全部拉取
if (records.length < pageSize) break;
currentPage++;
}
applicationListAll.value = allRecords;
applicationList.value = allRecords.map((item) => {
const priceInfo = item.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = item.unitCode_dictText || item.unitCode || '';
return {
adviceDefinitionId: item.adviceDefinitionId,
orgId: item.orgId,
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
key: item.adviceDefinitionId,
};
// 使用大 pageSize 一次性拉取所有启用状态的检验类诊疗项目
const res = await getApplicationList({
pageSize: 9999,
pageNo: 1,
categoryCode: '22',
organizationId: patientInfo.value.inHospitalOrgId,
adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
});
if (res.code !== 200) {
proxy.$message.error(res.message);
applicationListAll.value = [];
return;
}
applicationListAll.value = res.data?.records || [];
totalCount.value = res.data?.total || 0;
} catch (e) {
proxy.$message.error('获取检验项目列表失败');
applicationList.value = [];
applicationListAll.value = [];
} finally {
loading.value = false;
}
};
// 根据搜索关键词过滤数据
const filterData = (key) => {
if (!key || key.trim() === '') {
return applicationListAll.value;
}
const lowerKey = key.toLowerCase().trim();
return applicationListAll.value.filter((item) => {
return (
item.adviceName?.toLowerCase().includes(lowerKey) ||
item.pyStr?.toLowerCase().includes(lowerKey) ||
item.adviceBusNo?.toLowerCase().includes(lowerKey)
);
});
};
// transfer 组件实际显示的数据(受搜索词影响)
const transferData = computed(() => buildTransferData(filterData(searchKey.value)));
// 当前显示的条数
const filteredCount = computed(() => filterData(searchKey.value).length);
const getList = async () => {
await loadAllData();
};
const handleSearch = () => {
// 搜索时保持已选中的项目不受影响
};
const transferValue = ref([]);
const form = reactive({
// categoryType: '', // 项目类别
@@ -212,7 +253,6 @@ const form = reactive({
otherDiagnosisList: [], //其他断目录
});
const rules = reactive({});
onBeforeMount(() => {});
onMounted(() => {
getList();
});
@@ -225,12 +265,22 @@ const projectWithDepartment = (selectProjectIds, type) => {
let isRelease = true;
// 选中项目的数组
const arr = [];
// 根据选中的项目id查找对应的项目
// 根据选中的项目id查找对应的项目(从全部原始数据中查找)
selectProjectIds.forEach((element) => {
const searchData = applicationList.value.find((item) => {
const searchData = applicationListAll.value.find((item) => {
return element == item.adviceDefinitionId;
});
arr.push(searchData);
if (searchData) {
const priceInfo = searchData.priceList?.[0] || {};
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
const unit = searchData.unitCode_dictText || searchData.unitCode || '';
arr.push({
adviceDefinitionId: searchData.adviceDefinitionId,
orgId: searchData.orgId,
label: searchData.adviceName + ' (¥' + price + '/' + unit + ')',
key: searchData.adviceDefinitionId,
});
}
});
// 保存用户手动选择的发往科室(提交时需要保留)
const manualDept = type === 2 ? form.targetDepartment : '';
@@ -321,7 +371,7 @@ const submit = () => {
saveInspection(params).then((res) => {
if (res.code === 200) {
proxy.$message.success(res.msg);
applicationList.value = [];
transferValue.value = [];
emits('submitOk');
} else {
proxy.$message.error(res.message);
@@ -378,6 +428,19 @@ defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
.transfer-wrapper {
position: relative;
min-height: 300px;
.search-bar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
.total-count {
font-size: 13px;
color: #909399;
white-space: nowrap;
}
}
}
.el-transfer {

View File

@@ -5,36 +5,25 @@
-->
<template>
<div class="medicalExaminations-container">
<!-- 顶部标题栏 -->
<div class="form-header">
<div class="header-left">
<el-icon class="header-icon"><Files /></el-icon>
<span class="header-title">检查申请单</span>
</div>
<div class="header-right">
<span class="urgency-label">紧急程度</span>
<el-radio-group v-model="form.urgencyLevel" @change="handleUrgencyChange" class="urgency-radio-group">
<!-- 主体内容 -->
<div class="form-body">
<!-- 右上角紧急程度 -->
<div class="urgency-bar">
<span class="urgency-bar-label">紧急程度</span>
<el-radio-group v-model="form.urgencyLevel" @change="handleUrgencyChange" size="small">
<el-radio-button label="routine">普通</el-radio-button>
<el-radio-button label="emergency">急诊</el-radio-button>
</el-radio-group>
<transition name="el-fade-in-linear">
<span v-if="form.urgencyLevel === 'emergency'" class="emergency-tip">
<span v-if="form.urgencyLevel === 'emergency'" class="emergency-tip-inline">
<el-icon><WarningFilled /></el-icon>
急诊单将进入绿色通道
绿色通道
</span>
</transition>
</div>
</div>
<!-- 主体内容区 -->
<div class="form-body">
<!-- 选择检查项目 -->
<div class="section-card">
<div class="section-header">
<el-icon><Document /></el-icon>
<span>选择检查项目</span>
</div>
<div v-loading="loading" class="transfer-wrapper">
<div class="transfer-wrapper">
<el-transfer
v-model="transferValue"
:data="applicationList"
@@ -45,165 +34,150 @@
</div>
</div>
<!-- 申请信息 -->
<div class="section-card">
<div class="section-header">
<el-icon><EditPen /></el-icon>
<span>申请信息</span>
</div>
<el-form :model="form" :rules="rules" ref="formRef" label-position="top" class="info-form">
<!-- 第一行发往科室 + 紧急程度 + 期望检查时间 -->
<el-row :gutter="16">
<el-col :span="8">
<el-form-item label="发往科室" prop="targetDepartment">
<el-tree-select
clearable
style="width: 100%"
v-model="form.targetDepartment"
filterable
:data="orgOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
placeholder="请选择执行科室"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="期望检查时间">
<el-date-picker
v-model="form.expectedExaminationTime"
type="datetime"
placeholder="默认当前时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm"
:disabled-date="disabledFutureDate"
:default-value="new Date()"
/>
</el-form-item>
</el-col>
</el-row>
<el-form :model="form" :rules="rules" ref="formRef" label-position="top" class="info-form">
<!-- 第一行发往科室 + 期望检查时间 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="发往科室" prop="targetDepartment">
<el-tree-select
clearable
style="width: 100%"
v-model="form.targetDepartment"
filterable
:data="orgOptions"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
placeholder="请选择执行科室"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="期望检查时间">
<el-date-picker
v-model="form.expectedExaminationTime"
type="datetime"
placeholder="默认当前时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm"
:disabled-date="disabledFutureDate"
:default-value="new Date()"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行症状 + 体征 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="症状">
<el-input v-model="form.symptom" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者症状" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体征">
<el-input v-model="form.sign" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者体征" />
</el-form-item>
</el-col>
</el-row>
<!-- 症状 + 体征 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="症状">
<el-input v-model="form.symptom" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者症状" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="体征">
<el-input v-model="form.sign" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者体征" />
</el-form-item>
</el-col>
</el-row>
<!-- 临床诊断 + 其他诊断 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="临床诊断">
<el-input disabled v-model="form.clinicalDiagnosis" placeholder="自动带入主诊断" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他诊断">
<el-input disabled v-model="form.otherDiagnosis" placeholder="自动带入其他诊断" />
</el-form-item>
</el-col>
</el-row>
<!-- 临床诊断 + 其他诊断 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="临床诊断">
<el-input disabled v-model="form.clinicalDiagnosis" placeholder="自动带入主诊断" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="其他诊断">
<el-input disabled v-model="form.otherDiagnosis" placeholder="自动带入其他诊断" />
</el-form-item>
</el-col>
</el-row>
<!-- 相关结果 + 注意事项 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="相关结果">
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" :rows="2" placeholder="请输入相关检验结果" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="注意事项">
<el-input v-model="form.attention" autocomplete="off" type="textarea" :rows="2" placeholder="请输入检查注意事项" />
</el-form-item>
</el-col>
</el-row>
<!-- 相关结果 + 注意事项 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="相关结果">
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" :rows="2" placeholder="请输入相关检验结果" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="注意事项">
<el-input v-model="form.attention" autocomplete="off" type="textarea" :rows="2" placeholder="请输入检查注意事项" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 过敏史卡片 -->
<div class="section-card allergy-card">
<div class="section-header">
<el-icon><Warning /></el-icon>
<span>过敏史</span>
<span v-if="form.allergyHistory" class="header-count">{{ form.allergyHistory.length }}</span>
</div>
<div class="allergy-content">
<div class="allergy-input-row">
<!-- 检查目的 + 病史摘要 -->
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="检查目的" prop="examinationPurpose">
<el-input
v-model="form.allergyHistory"
v-model="form.examinationPurpose"
autocomplete="off"
type="textarea"
:rows="2"
:class="{ 'allergy-danger': isSevereAllergy }"
placeholder="如:造影剂过敏史等(系统将自动从患者档案带入)"
maxlength="200"
show-word-limit
placeholder="请输入检查目的,如:明确诊断、术后复查、疗效评估等"
/>
<span v-if="isSevereAllergy" class="allergy-severe-tag">
<el-icon><WarningFilled /></el-icon>
严重过敏
</span>
</div>
<div class="allergy-confirm">
<el-checkbox v-model="form.allergyConfirmed" size="small">
已通过口头询问确认无过敏史
</el-checkbox>
</div>
</div>
</div>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="病史摘要" prop="medicalHistorySummary">
<div class="history-field-wrapper">
<el-input
v-model="form.medicalHistorySummary"
autocomplete="off"
type="textarea"
:rows="2"
placeholder="请简要描述患者病史摘要"
/>
<el-button
type="primary"
plain
size="small"
class="history-sync-btn"
@click="handleSyncHistory"
:loading="syncingHistory"
>
<el-icon><Refresh /></el-icon>
同步
</el-button>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- 检查目的卡片 -->
<div class="section-card purpose-card">
<div class="section-header">
<el-icon><Aim /></el-icon>
<span>检查目的</span>
<span class="required-mark">必填</span>
</div>
<el-input
v-model="form.examinationPurpose"
autocomplete="off"
type="textarea"
:rows="2"
maxlength="200"
show-word-limit
placeholder="请输入检查目的,如:明确诊断、术后复查、疗效评估等"
/>
</div>
<!-- 病史摘要卡片 -->
<div class="section-card history-card">
<div class="section-header">
<el-icon><DocumentCopy /></el-icon>
<span>病史摘要</span>
<span class="required-mark">必填</span>
<el-button
type="primary"
plain
size="small"
class="sync-btn"
@click="handleSyncHistory"
:loading="syncingHistory"
>
<el-icon><Refresh /></el-icon>
同步现病史/体征
</el-button>
</div>
<el-input
v-model="form.medicalHistorySummary"
autocomplete="off"
type="textarea"
:rows="3"
placeholder="请简要描述患者病史摘要"
/>
</div>
</div>
<!-- 第六行过敏史 -->
<el-row :gutter="16">
<el-col :span="24">
<el-form-item label="过敏史">
<div class="allergy-wrapper">
<el-input
v-model="form.allergyHistory"
autocomplete="off"
type="textarea"
:rows="1"
:class="{ 'allergy-danger': isSevereAllergy }"
placeholder="如:造影剂过敏史等(系统将自动从患者档案带入)"
/>
<div class="allergy-actions">
<span v-if="isSevereAllergy" class="allergy-severe-tag-inline">
<el-icon><WarningFilled /></el-icon>
严重过敏
</span>
<el-checkbox v-model="form.allergyConfirmed" size="small">
已通过口头询问确认无过敏史
</el-checkbox>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<!-- 急诊确认弹窗 -->
@@ -228,6 +202,7 @@
<script setup name="MedicalExaminations">
import {getCurrentInstance, onMounted, reactive, ref, watch, computed, nextTick} from 'vue';
import dayjs from 'dayjs';
import {patientInfo} from '../../../store/patient.js';
import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js';
@@ -355,7 +330,7 @@ const form = reactive({
allergyHistory: '',
examinationPurpose: '',
medicalHistorySummary: '',
expectedExaminationTime: '',
expectedExaminationTime: dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'),
symptom: '',
sign: '',
clinicalDiagnosis: '',
@@ -622,7 +597,7 @@ const resetForm = () => {
form.allergyHistory = '';
form.examinationPurpose = '';
form.medicalHistorySummary = '';
form.expectedExaminationTime = '';
form.expectedExaminationTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
form.symptom = '';
form.sign = '';
form.clinicalDiagnosis = '';
@@ -705,81 +680,13 @@ $bg-color: #f5f7fa;
background: $bg-color;
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
// 顶部标题栏
.form-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 14px 20px;
background: linear-gradient(135deg, #fff 0%, #f0f7ff 100%);
border-bottom: 1px solid $border-color;
.header-left {
display: flex;
align-items: center;
gap: 10px;
.header-icon {
font-size: 24px;
color: $primary-color;
}
.header-title {
font-size: 18px;
font-weight: 600;
color: $text-primary;
letter-spacing: 1px;
}
}
.header-right {
display: flex;
align-items: center;
gap: 12px;
.urgency-label {
font-size: 13px;
color: $text-secondary;
font-weight: 500;
}
.urgency-radio-group {
:deep(.el-radio-button__inner) {
border-radius: 4px;
margin: 0;
}
:deep(.el-radio-button:first-child .el-radio-button__inner) {
border-radius: 4px;
}
:deep(.el-radio-button:last-child .el-radio-button__inner) {
border-radius: 4px;
}
}
.emergency-tip {
display: flex;
align-items: center;
gap: 4px;
color: $danger-color;
font-size: 13px;
font-weight: 500;
background: #fef0f0;
padding: 4px 10px;
border-radius: 4px;
border: 1px solid #fde2e2;
}
}
}
// 主体内容区
// 主体内容区 - 紧凑布局
.form-body {
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
gap: 8px;
padding: 8px 12px;
overflow-y: auto;
&::-webkit-scrollbar {
@@ -796,47 +703,30 @@ $bg-color: #f5f7fa;
}
}
// 卡片通用样式
// 紧急程度栏 - 右上角
.urgency-bar {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 8px;
padding: 4px 0;
margin-bottom: 4px;
}
.urgency-bar-label {
font-size: 13px;
font-weight: 500;
color: $text-regular;
white-space: nowrap;
}
// 卡片通用样式 - 紧凑
.section-card {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
border: 1px solid rgba(0, 0, 0, 0.04);
.section-header {
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 12px;
margin-bottom: 12px;
border-bottom: 1px dashed $border-color;
font-size: 14px;
font-weight: 600;
color: $text-primary;
> i {
font-size: 16px;
color: $primary-color;
}
.header-count {
margin-left: auto;
font-size: 12px;
font-weight: 400;
color: $text-secondary;
}
.required-mark {
font-size: 12px;
font-weight: 500;
color: #fff;
background: $danger-color;
padding: 2px 8px;
border-radius: 10px;
margin-left: 4px;
}
}
border-radius: 6px;
padding: 8px;
border: 1px solid #e4e7ed;
margin-bottom: 4px;
}
.transfer-wrapper {
@@ -850,10 +740,23 @@ $bg-color: #f5f7fa;
display: flex !important;
flex-direction: row !important;
}
// 信息表单
// 穿梭框按钮垂直居中
:deep(.el-transfer__buttons) {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 4px;
}
:deep(.el-transfer__button) {
margin: 4px 0;
}
// 信息表单 - 紧凑
.info-form {
:deep(.el-form-item) {
margin-bottom: 14px;
margin-bottom: 6px;
.el-form-item__label {
font-size: 13px;
@@ -883,53 +786,10 @@ $bg-color: #f5f7fa;
}
}
// 过敏史卡片
.allergy-card {
.allergy-content {
.allergy-input-row {
position: relative;
:deep(.el-textarea) {
.el-textarea__inner.allergy-danger {
border-color: $danger-color !important;
background-color: #fef0f0;
}
}
}
.allergy-severe-tag {
position: absolute;
right: 12px;
top: 8px;
display: flex;
align-items: center;
gap: 4px;
color: $danger-color;
font-size: 13px;
font-weight: 600;
background: #fef0f0;
padding: 3px 10px;
border-radius: 12px;
border: 1px solid #fde2e2;
}
.allergy-confirm {
margin-top: 10px;
padding-left: 4px;
}
}
}
// 病史摘要卡片
.history-card {
.section-header {
.sync-btn {
margin-left: auto;
font-size: 12px;
padding: 6px 12px;
border-radius: 16px;
}
}
// 过敏史危险输入样式
:deep(.el-textarea__inner.allergy-danger) {
border-color: $danger-color !important;
background-color: #fef0f0;
}
// 急诊确认弹窗
@@ -968,4 +828,64 @@ $bg-color: #f5f7fa;
.fade-in-linear-leave-to {
opacity: 0;
}
/* 紧急程度行内布局 */
.urgency-inline {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.emergency-tip-inline {
display: inline-flex;
align-items: center;
gap: 2px;
color: $danger-color;
font-size: 11px;
font-weight: 500;
background: #fef0f0;
padding: 2px 6px;
border-radius: 3px;
white-space: nowrap;
}
/* 过敏史包装 */
.allergy-wrapper {
width: 100%;
}
.allergy-actions {
display: flex;
align-items: center;
gap: 12px;
margin-top: 4px;
}
.allergy-severe-tag-inline {
display: inline-flex;
align-items: center;
gap: 2px;
color: $danger-color;
font-size: 11px;
font-weight: 600;
background: #fef0f0;
padding: 2px 8px;
border-radius: 3px;
}
/* 病史摘要同步按钮 */
.history-field-wrapper {
position: relative;
width: 100%;
}
.history-sync-btn {
position: absolute;
right: 4px;
top: -28px;
font-size: 11px;
padding: 2px 8px;
height: 24px;
}
</style>