feat(basicmanage): 新增医嘱组套对话框组件和相关API
- 实现 MedicalOrderSetDialog.vue 组件,支持医嘱组套的新增、编辑功能 - 添加中药医嘱基础列表组件 tcmMedicineList.vue,支持键盘导航选择 - 创建医嘱组套相关API接口文件,包含个人、科室、全院组套的增删改查功能 - 实现医嘱组套的组合拆组功能,支持批量操作 - 集成分页、搜索、缓存等优化功能提升用户体验 - 添加表单验证和数据校验机制确保数据完整性
This commit is contained in:
@@ -0,0 +1,885 @@
|
||||
<template>
|
||||
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="1300px" @close="handleDialogClose">
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
||||
<div class="dialog-top-row">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入名称" style="width: 220px" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="使用范围" v-if="showRangeSelector && !isEdit">
|
||||
<el-select
|
||||
v-model="rangeSelectValue"
|
||||
placeholder="个人/科室/全院"
|
||||
style="width: 220px"
|
||||
@change="handleRangeChange"
|
||||
>
|
||||
<el-option label="个人" value="personal" />
|
||||
<el-option label="科室" value="department" />
|
||||
<el-option label="全院" value="hospital" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
label="参与者"
|
||||
prop="practitionerId"
|
||||
v-if="currentTab === 'personal' && !isDoctorStation && !showRangeSelector"
|
||||
>
|
||||
<el-select
|
||||
v-model="formData.practitionerId"
|
||||
placeholder="请选择参与者"
|
||||
clearable
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in participantListOptions"
|
||||
:key="item.practitionerId"
|
||||
:label="item.practitionerName"
|
||||
:value="item.practitionerId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="科室" prop="organizationId" v-if="currentTab === 'department'">
|
||||
<el-tree-select
|
||||
clearable
|
||||
v-model="formData.organizationId"
|
||||
:data="organization"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
default-expand-all
|
||||
placeholder="请选择科室"
|
||||
style="width: 220px"
|
||||
:render-after-expand="false"
|
||||
@change="handleOrgChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-button type="primary" @click="handleAddRow">新增</el-button>
|
||||
<el-button @click="handleCombineGroup">组合</el-button>
|
||||
<el-button @click="handleSplitGroup">拆组</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
max-height="650"
|
||||
ref="prescriptionRef"
|
||||
:data="prescriptionList"
|
||||
row-key="uniqueKey"
|
||||
border
|
||||
@cell-click="clickRow"
|
||||
@selection-change="handleSelectionChange"
|
||||
:expand-row-keys="expandOrder"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :selectable="isRowSelectable" />
|
||||
<el-table-column label="组" align="center" width="60">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.groupId">{{ getGroupIcon(scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-radio-group v-model="scope.row.therapyEnum" size="small">
|
||||
<el-radio-button label="1">长期</el-radio-button>
|
||||
<el-radio-button label="2">临时</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<span v-else>
|
||||
{{
|
||||
scope.row.therapyEnum == '1' ? '长期' : scope.row.therapyEnum == '2' ? '临时' : '-'
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="医嘱" align="center" prop="productName" width="300">
|
||||
<template #default="scope">
|
||||
<template v-if="getRowDisabled(scope.row)">
|
||||
<el-popover
|
||||
:popper-style="{ padding: '0' }"
|
||||
placement="bottom-start"
|
||||
:visible="scope.row.showPopover"
|
||||
:width="1200"
|
||||
>
|
||||
<adviceBaseList
|
||||
ref="adviceTableRef"
|
||||
:popoverVisible="scope.row.showPopover"
|
||||
:adviceQueryParams="adviceQueryParams"
|
||||
@selectAdviceBase="(row) => selectAdviceBase(scope.row.uniqueKey, row)"
|
||||
/>
|
||||
<template #reference>
|
||||
<el-input
|
||||
:ref="'adviceRef' + scope.$index"
|
||||
style="width: 100%"
|
||||
v-model="scope.row.adviceName"
|
||||
placeholder="请选择项目"
|
||||
@input="(value) => handleInput(value, scope.row, scope.$index)"
|
||||
@click="handleFocus(scope.row, scope.$index)"
|
||||
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
|
||||
@keydown="
|
||||
(e) => {
|
||||
if (!scope.row.showPopover) return;
|
||||
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) {
|
||||
e.preventDefault();
|
||||
adviceTableRef.handleKeyDown(e);
|
||||
}
|
||||
}
|
||||
"
|
||||
@blur="handleBlur(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.adviceName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="单次剂量" align="center" width="250" prop="sortNumber">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.adviceType == 1">
|
||||
<!-- 新增/未保存行:统一按“数量 + 单位 = 剂量 + 单位”展示 -->
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-input
|
||||
style="width: 70px; margin-right: 10px"
|
||||
v-model="scope.row.doseQuantity"
|
||||
@input="
|
||||
(value) => {
|
||||
scope.row.dose = value * scope.row.unitConversionRatio;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
{{
|
||||
scope.row.minUnitCode_dictText ||
|
||||
scope.row.unitCodeName ||
|
||||
scope.row.unitCode_dictText ||
|
||||
''
|
||||
}}
|
||||
</span>
|
||||
<span>{{ ' = ' }}</span>
|
||||
<el-input
|
||||
style="width: 70px; margin-right: 10px"
|
||||
v-model="scope.row.dose"
|
||||
@input="
|
||||
(value) => {
|
||||
scope.row.doseQuantity = value / scope.row.unitConversionRatio;
|
||||
}
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
{{
|
||||
scope.row.doseUnitCode_dictText ||
|
||||
scope.row.unitCodeName ||
|
||||
scope.row.unitCode_dictText ||
|
||||
''
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.dose }}</span>
|
||||
</template>
|
||||
<span v-else>{{ '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="给药途径" align="center" width="150" prop="sortNumber">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.adviceType == 1">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-select v-model="scope.row.methodCode" placeholder="给药途径" clearable filterable>
|
||||
<el-option
|
||||
v-for="dict in method_code"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.methodCode }}</span>
|
||||
</template>
|
||||
<span v-else>{{ '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="用药频次" align="center" width="150" prop="sortNumber">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.adviceType == 1">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-select
|
||||
v-model="scope.row.rateCode"
|
||||
placeholder="频次"
|
||||
style="width: 120px"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getRateOptions(scope.row)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.rateCode_dictText }}</span>
|
||||
</template>
|
||||
<span v-else>{{ '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="用药天数" align="center" width="100" prop="sortNumber">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.adviceType == 1">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-input
|
||||
v-model="scope.row.dispensePerDuration"
|
||||
@change="handleQuantityChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.dispensePerDuration }}</span>
|
||||
</template>
|
||||
<span v-else>{{ '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="总量/执行次数" align="center" width="150" prop="sortNumber">
|
||||
<template #default="scope">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-input
|
||||
v-model="scope.row.sortNumber"
|
||||
type="number"
|
||||
min="1"
|
||||
@change="handleQuantityChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
<span v-else>{{ scope.row.sortNumber }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="单位" align="center" width="120" prop="unitCode">
|
||||
<template #default="scope">
|
||||
<template v-if="!scope.row.groupPackageId">
|
||||
<el-select
|
||||
v-model="scope.row.selectUnitCode"
|
||||
placeholder="请选择单位"
|
||||
@change="handleUnitChange(scope.row)"
|
||||
>
|
||||
<el-option
|
||||
v-if="scope.row.minUnitCode"
|
||||
:key="scope.row.minUnitCode"
|
||||
:label="scope.row.minUnitCode_dictText || scope.row.minUnitCode"
|
||||
:value="scope.row.minUnitCode"
|
||||
/>
|
||||
<el-option
|
||||
v-if="scope.row.unitCode"
|
||||
:key="scope.row.unitCode"
|
||||
:label="scope.row.unitCode_dictText || scope.row.unitCode"
|
||||
:value="scope.row.unitCode"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<span>{{ scope.row.unitCodeName }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
circle
|
||||
size="small"
|
||||
@click="handleDeleteRow(scope.$index, scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm" :loading="submitLoading">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue';
|
||||
import adviceBaseList from './adviceBaseList';
|
||||
import {
|
||||
queryParticipantList,
|
||||
queryGroupDetail,
|
||||
getOrgTree,
|
||||
} from './api.js';
|
||||
import { saveOrderGroup } from '@/views/doctorstation/components/api.js';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
// 是否在“医生站/住院医生站”场景复用:个人不选人(practitionerId 置空),科室可选科室
|
||||
isDoctorStation: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
method_code: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
rate_code: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
// 顶部是否显示“个人/科室/全院”切换(用于住院医生站顶部【组套】按钮场景)
|
||||
showRangeSelector: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['saved']);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('');
|
||||
const currentTab = ref('personal'); // personal | department | hospital
|
||||
const isEdit = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const rangeSelectValue = ref('personal');
|
||||
|
||||
const participantListOptions = ref([]);
|
||||
const organization = ref([]);
|
||||
const adviceQueryParams = reactive({});
|
||||
|
||||
const formData = reactive({
|
||||
groupPackageId: undefined,
|
||||
name: '',
|
||||
practitionerId: undefined,
|
||||
organizationId: undefined,
|
||||
});
|
||||
|
||||
const prescriptionList = ref([]);
|
||||
const expandOrder = ref([]);
|
||||
const nextId = ref(1);
|
||||
const rowIndex = ref(0);
|
||||
const selectedRows = ref([]);
|
||||
const groupIndex = ref(1);
|
||||
|
||||
const formRef = ref();
|
||||
const prescriptionRef = ref();
|
||||
const adviceTableRef = ref();
|
||||
|
||||
const formRules = computed(() => {
|
||||
const rules = {
|
||||
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
||||
};
|
||||
// 独立页面:个人必须选参与者;科室必须选科室
|
||||
if (!props.isDoctorStation && currentTab.value === 'personal') {
|
||||
rules.practitionerId = [{ required: true, message: '请选择参与者', trigger: 'change' }];
|
||||
}
|
||||
if (currentTab.value === 'department') {
|
||||
rules.organizationId = [{ required: true, message: '请选择科室', trigger: 'change' }];
|
||||
}
|
||||
return rules;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
queryParticipantList().then((res) => {
|
||||
participantListOptions.value = res.data || [];
|
||||
});
|
||||
getOrgTree().then((res) => {
|
||||
organization.value = res.data?.records || [];
|
||||
});
|
||||
});
|
||||
|
||||
function openAdd(tab) {
|
||||
currentTab.value = tab;
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '新增医嘱';
|
||||
dialogVisible.value = true;
|
||||
|
||||
if (props.showRangeSelector) rangeSelectValue.value = tab;
|
||||
|
||||
prescriptionList.value = [];
|
||||
expandOrder.value = [];
|
||||
nextId.value = 1;
|
||||
rowIndex.value = 0;
|
||||
selectedRows.value = [];
|
||||
groupIndex.value = 1;
|
||||
|
||||
formData.groupPackageId = undefined;
|
||||
formData.name = '';
|
||||
formData.practitionerId = undefined;
|
||||
formData.organizationId = undefined;
|
||||
|
||||
Object.keys(adviceQueryParams).forEach((key) => {
|
||||
delete adviceQueryParams[key];
|
||||
});
|
||||
|
||||
prescriptionRef.value?.clearSelection?.();
|
||||
|
||||
if (tab === 'personal') {
|
||||
// 医生站场景:个人组套需要关联当前医生才能查询到
|
||||
formData.practitionerId = userStore.practitionerId;
|
||||
} else if (tab === 'department') {
|
||||
formData.organizationId = userStore.orgId;
|
||||
}
|
||||
|
||||
addEmptyRow();
|
||||
}
|
||||
|
||||
function handleRangeChange(tab) {
|
||||
openAdd(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从外部选中的医嘱直接生成组套明细
|
||||
* @param tab 使用范围:personal / department / hospital
|
||||
* @param rows 选中的处方行列表
|
||||
*/
|
||||
function openFromSelection(tab, rows = []) {
|
||||
currentTab.value = tab;
|
||||
isEdit.value = false;
|
||||
dialogTitle.value = '另存组套';
|
||||
dialogVisible.value = true;
|
||||
|
||||
if (props.showRangeSelector) rangeSelectValue.value = tab;
|
||||
|
||||
// 重置表单
|
||||
formData.groupPackageId = undefined;
|
||||
formData.name = '';
|
||||
formData.practitionerId = undefined;
|
||||
formData.organizationId = undefined;
|
||||
|
||||
if (tab === 'personal') {
|
||||
// 医生站场景:个人组套需要关联当前医生才能查询到
|
||||
formData.practitionerId = userStore.practitionerId;
|
||||
} else if (tab === 'department') {
|
||||
formData.organizationId = userStore.orgId;
|
||||
}
|
||||
|
||||
const validRows = (rows || []).filter((i) => i.adviceDefinitionId);
|
||||
if (validRows.length === 0) {
|
||||
proxy.$modal.msgWarning('所选医嘱中没有有效的医嘱项,请先选择医嘱后再另存组套');
|
||||
dialogVisible.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
prescriptionList.value = validRows.map((row, index) => {
|
||||
let therapyEnum = row.therapyEnum;
|
||||
if (!therapyEnum && row.contentJson) {
|
||||
try {
|
||||
const content = JSON.parse(row.contentJson);
|
||||
therapyEnum = content.therapyEnum;
|
||||
} catch (e) {}
|
||||
}
|
||||
therapyEnum = therapyEnum != null ? String(therapyEnum) : '1';
|
||||
|
||||
return {
|
||||
uniqueKey: index + 1,
|
||||
showPopover: false,
|
||||
check: false,
|
||||
isEdit: false,
|
||||
statusEnum: 1,
|
||||
adviceDefinitionId: row.adviceDefinitionId,
|
||||
adviceTableName: row.adviceTableName || 'advice_definition',
|
||||
sortNumber: row.quantity ?? row.sortNumber ?? 1,
|
||||
selectUnitCode: row.selectUnitCode || row.unitCode,
|
||||
adviceName: row.adviceName || row.productName,
|
||||
unitCodeName: row.unitCodeName || row.unitCode_dictText || '',
|
||||
methodCode: row.methodCode,
|
||||
rateCode: row.rateCode,
|
||||
dispensePerDuration: row.dispensePerDuration,
|
||||
dose: row.dose,
|
||||
doseQuantity: row.doseQuantity,
|
||||
minUnitCode: row.minUnitCode,
|
||||
minUnitCode_dictText: row.minUnitCode_dictText,
|
||||
unitCode: row.unitCode,
|
||||
unitCode_dictText: row.unitCode_dictText,
|
||||
groupId: row.groupId,
|
||||
groupOrder: row.groupOrder,
|
||||
therapyEnum: therapyEnum,
|
||||
adviceType: row.adviceType,
|
||||
};
|
||||
});
|
||||
|
||||
expandOrder.value = [];
|
||||
nextId.value = prescriptionList.value.length + 1;
|
||||
}
|
||||
|
||||
function openEdit(tab, row) {
|
||||
currentTab.value = tab;
|
||||
isEdit.value = true;
|
||||
dialogTitle.value = '编辑医嘱';
|
||||
dialogVisible.value = true;
|
||||
|
||||
formData.groupPackageId = row.groupPackageId;
|
||||
formData.name = row.name;
|
||||
formData.practitionerId = row.practitionerId;
|
||||
formData.organizationId = row.organizationId;
|
||||
|
||||
queryGroupDetail({ groupPackageId: row.groupPackageId }).then((res) => {
|
||||
const detailList = res.data || [];
|
||||
prescriptionList.value = detailList.map((item, index) => {
|
||||
const therapyEnum = item.therapyEnum != null ? String(item.therapyEnum) : '1'; // 统一转成字符串
|
||||
|
||||
return {
|
||||
uniqueKey: index + 1,
|
||||
showPopover: false,
|
||||
check: false,
|
||||
isEdit: false,
|
||||
statusEnum: 1,
|
||||
groupPackageId: item.groupPackageId,
|
||||
adviceDefinitionId: item.orderDefinitionId,
|
||||
adviceTableName: item.orderDefinitionTable,
|
||||
sortNumber: item.quantity,
|
||||
selectUnitCode: item.unitCode,
|
||||
adviceName: item.orderDefinitionName,
|
||||
unitCodeName: item.unitCodeName,
|
||||
groupId: item.groupId,
|
||||
groupOrder: item.groupOrder,
|
||||
therapyEnum, // 长期/临时类型:1-长期,2-临时
|
||||
// 回显单次剂量/给药途径/用药频次/天数等字段
|
||||
dispensePerDuration: item.dispensePerDuration,
|
||||
dose: item.dose,
|
||||
doseQuantity: item.doseQuantity,
|
||||
methodCode: item.methodCode,
|
||||
rateCode: item.rateCode,
|
||||
// 医嘱类型(药品=1):没有则按表名推断(药品表 -> 药品)
|
||||
adviceType:
|
||||
item.adviceType !== undefined
|
||||
? item.adviceType
|
||||
: item.orderDefinitionTable === 'med_medication_definition'
|
||||
? 1
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
nextId.value = prescriptionList.value.length + 1;
|
||||
addEmptyRow();
|
||||
});
|
||||
}
|
||||
|
||||
function addEmptyRow() {
|
||||
prescriptionList.value.unshift({
|
||||
uniqueKey: nextId.value++,
|
||||
showPopover: false,
|
||||
check: false,
|
||||
isEdit: true,
|
||||
statusEnum: 1,
|
||||
therapyEnum: '1', // 默认长期
|
||||
});
|
||||
}
|
||||
|
||||
function handleAddRow() {
|
||||
addEmptyRow();
|
||||
}
|
||||
|
||||
function handleSelectionChange(selection) {
|
||||
selectedRows.value = selection || [];
|
||||
}
|
||||
|
||||
function handleCombineGroup() {
|
||||
const rows = selectedRows.value || [];
|
||||
if (rows.length <= 1) {
|
||||
proxy.$modal.msgWarning('至少选择两项');
|
||||
return;
|
||||
}
|
||||
|
||||
// 必须都已选择医嘱
|
||||
if (rows.some((r) => !r.adviceDefinitionId)) {
|
||||
proxy.$modal.msgWarning('请先完成医嘱选择');
|
||||
return;
|
||||
}
|
||||
|
||||
// 相同分组用法需要相同
|
||||
const methodSet = new Set();
|
||||
rows.forEach((r) => {
|
||||
if (r.methodCode != null) methodSet.add(r.methodCode);
|
||||
});
|
||||
if (methodSet.size > 1) {
|
||||
proxy.$modal.msgWarning('同一分组药品用法必须相同');
|
||||
return;
|
||||
}
|
||||
|
||||
// 相同分组频次需要相同
|
||||
const rateSet = new Set();
|
||||
rows.forEach((r) => {
|
||||
if (r.rateCode != null) rateSet.add(r.rateCode);
|
||||
});
|
||||
if (rateSet.size > 1) {
|
||||
proxy.$modal.msgWarning('同一分组药品频次必须相同');
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = Date.now().toString();
|
||||
const gid = timestamp + groupIndex.value;
|
||||
|
||||
const newList = [...prescriptionList.value];
|
||||
rows.forEach((item, selectIndex) => {
|
||||
const idx = newList.findIndex((r) => r.uniqueKey === item.uniqueKey);
|
||||
if (idx !== -1) {
|
||||
newList[idx].groupId = gid;
|
||||
newList[idx].groupOrder = selectIndex + 1;
|
||||
}
|
||||
});
|
||||
|
||||
groupIndex.value += 1;
|
||||
prescriptionList.value = newList;
|
||||
prescriptionRef.value?.clearSelection?.();
|
||||
proxy.$modal.msgSuccess('组套成功');
|
||||
}
|
||||
|
||||
function isRowSelectable(row) {
|
||||
return !!row.adviceDefinitionId;
|
||||
}
|
||||
|
||||
function getGroupIcon(row) {
|
||||
if (!row.groupId) return '';
|
||||
const sameGroup = prescriptionList.value
|
||||
.filter((item) => item.groupId === row.groupId)
|
||||
.sort((a, b) => {
|
||||
const aOrder = a.groupOrder ?? 0;
|
||||
const bOrder = b.groupOrder ?? 0;
|
||||
return aOrder - bOrder;
|
||||
});
|
||||
if (sameGroup.length === 0) return '';
|
||||
const index = sameGroup.findIndex((item) => item.uniqueKey === row.uniqueKey);
|
||||
if (index === 0) {
|
||||
return '┏';
|
||||
}
|
||||
if (index === sameGroup.length - 1) {
|
||||
return '┗';
|
||||
}
|
||||
return '┃';
|
||||
}
|
||||
|
||||
function getRateOptions(row) {
|
||||
const all = props.rate_code || [];
|
||||
if (row?.therapyEnum == '2') {
|
||||
return all.filter(
|
||||
(dict) => dict.value === 'ST' || (dict.label && dict.label.indexOf('立即') !== -1)
|
||||
);
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
function handleSplitGroup() {
|
||||
const rows = selectedRows.value || [];
|
||||
if (rows.length < 1) {
|
||||
proxy.$modal.msgWarning('至少选择一项');
|
||||
return;
|
||||
}
|
||||
if (rows.some((r) => !r.groupId)) {
|
||||
proxy.$modal.msgWarning('包含非组合数据无法拆组');
|
||||
return;
|
||||
}
|
||||
|
||||
const newList = [...prescriptionList.value];
|
||||
rows.forEach((item) => {
|
||||
const idx = newList.findIndex((r) => r.uniqueKey === item.uniqueKey);
|
||||
if (idx !== -1) {
|
||||
newList[idx].groupId = undefined;
|
||||
newList[idx].groupOrder = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
prescriptionList.value = newList;
|
||||
prescriptionRef.value?.clearSelection?.();
|
||||
proxy.$modal.msgSuccess('拆组成功');
|
||||
}
|
||||
|
||||
function submitForm() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
|
||||
// 验证至少选择了一条医嘱
|
||||
const detailList = prescriptionList.value.filter((item) => item.adviceDefinitionId);
|
||||
if (detailList.length === 0) {
|
||||
proxy?.$modal?.msgWarning?.('请至少选择一条医嘱');
|
||||
return;
|
||||
}
|
||||
|
||||
submitLoading.value = true;
|
||||
setTimeout(() => {
|
||||
console.log('[submitForm] 当前tab:', currentTab.value);
|
||||
const params = { ...formData };
|
||||
console.log('[submitForm] formData:', formData);
|
||||
// 添加 rangeCode 字段,用于后端判断组套类型
|
||||
if (currentTab.value === 'personal') {
|
||||
params.rangeCode = 1;
|
||||
} else if (currentTab.value === 'department') {
|
||||
params.rangeCode = 2;
|
||||
} else {
|
||||
params.rangeCode = 3;
|
||||
}
|
||||
console.log('[submitForm] 设置后的rangeCode:', params.rangeCode);
|
||||
// 医生站场景:个人组套需要设置 practitionerId 为当前医生,否则查询时查不到
|
||||
console.log('[submitForm] isDoctorStation:', props.isDoctorStation, 'currentTab:', currentTab.value);
|
||||
console.log('[submitForm] userStore.practitionerId:', userStore.practitionerId);
|
||||
if (props.isDoctorStation && currentTab.value === 'personal') {
|
||||
params.practitionerId = userStore.practitionerId;
|
||||
console.log('[submitForm] 已设置 practitionerId:', params.practitionerId);
|
||||
} else {
|
||||
console.log('[submitForm] 未设置 practitionerId, 当前值:', params.practitionerId);
|
||||
}
|
||||
|
||||
params.detailList = detailList.map((item) => ({
|
||||
orderDefinitionId: item.adviceDefinitionId,
|
||||
orderDefinitionTable: item.adviceTableName,
|
||||
quantity: item.sortNumber,
|
||||
unitCode: item.selectUnitCode,
|
||||
methodCode: item.methodCode,
|
||||
rateCode: item.rateCode,
|
||||
dispensePerDuration: item.dispensePerDuration,
|
||||
dose: item.dose,
|
||||
doseQuantity: item.doseQuantity,
|
||||
groupId: item.groupId,
|
||||
groupOrder: item.groupOrder,
|
||||
therapyEnum: item.therapyEnum || '1',
|
||||
}));
|
||||
|
||||
console.log('[submitForm] 准备保存组套, params:', params);
|
||||
// 使用 saveOrderGroup,它会根据 rangeCode 自动选择正确的API
|
||||
saveOrderGroup(params)
|
||||
.then((res) => {
|
||||
console.log('[submitForm] 保存API返回:', res);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(res.message || '保存成功');
|
||||
console.log('[submitForm] 准备触发 saved 事件');
|
||||
emit('saved');
|
||||
console.log('[submitForm] saved 事件已触发');
|
||||
dialogVisible.value = false;
|
||||
prescriptionList.value = [];
|
||||
} else {
|
||||
proxy?.$modal?.msgError?.(res.message || '保存失败');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('保存失败:', err);
|
||||
proxy?.$modal?.msgError?.(err?.message || '保存失败,请检查网络或联系管理员');
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
function selectAdviceBase(key, row) {
|
||||
const currentRow = prescriptionList.value[rowIndex.value];
|
||||
const preservedTherapyEnum = currentRow?.therapyEnum || row.therapyEnum || '1';
|
||||
|
||||
prescriptionList.value[rowIndex.value] = {
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
...JSON.parse(JSON.stringify(row)),
|
||||
};
|
||||
prescriptionList.value[rowIndex.value].dose = undefined;
|
||||
prescriptionList.value[rowIndex.value].doseQuantity = undefined;
|
||||
prescriptionList.value[rowIndex.value].doseUnitCode = row.doseUnitCode;
|
||||
prescriptionList.value[rowIndex.value].minUnitCode = row.minUnitCode;
|
||||
prescriptionList.value[rowIndex.value].unitCode =
|
||||
row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode;
|
||||
prescriptionList.value[rowIndex.value].categoryEnum = row.categoryCode;
|
||||
prescriptionList.value[rowIndex.value].isEdit = false;
|
||||
prescriptionList.value[rowIndex.value].definitionId = JSON.parse(
|
||||
JSON.stringify(row)
|
||||
).chargeItemDefinitionId;
|
||||
prescriptionList.value[rowIndex.value].therapyEnum = preservedTherapyEnum;
|
||||
addEmptyRow();
|
||||
expandOrder.value = [key];
|
||||
}
|
||||
|
||||
function handleFocus(row, index) {
|
||||
rowIndex.value = index;
|
||||
row.showPopover = true;
|
||||
adviceQueryParams.searchKey = row.adviceName || '';
|
||||
// 设置 organizationId,否则 adviceBaseList 会跳过 API 调用
|
||||
adviceQueryParams.organizationId = userStore.orgId;
|
||||
}
|
||||
|
||||
function handleInput(value, row, index) {
|
||||
adviceQueryParams.searchKey = value || '';
|
||||
handleFocus(row, index);
|
||||
}
|
||||
|
||||
function handleBlur(row) {
|
||||
row.showPopover = false;
|
||||
}
|
||||
|
||||
function clickRow() {}
|
||||
|
||||
function getRowDisabled(row) {
|
||||
return row.isEdit;
|
||||
}
|
||||
|
||||
function handleQuantityChange(row) {
|
||||
if (row.sortNumber && row.sortNumber > 0) row.sortNumber = parseInt(row.sortNumber);
|
||||
else row.sortNumber = 1;
|
||||
prescriptionList.value = [...prescriptionList.value];
|
||||
}
|
||||
|
||||
function handleUnitChange(row) {
|
||||
prescriptionList.value = [...prescriptionList.value];
|
||||
}
|
||||
|
||||
function handleDeleteRow(index) {
|
||||
if (prescriptionList.value.length <= 1) {
|
||||
proxy.$modal.msgWarning('至少保留一行');
|
||||
return;
|
||||
}
|
||||
proxy.$modal
|
||||
.confirm('确定要删除该行吗?')
|
||||
.then(() => {
|
||||
prescriptionList.value.splice(index, 1);
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function isLeafNode(node) {
|
||||
return !node.children || node.children.length === 0;
|
||||
}
|
||||
|
||||
function handleDialogClose() {
|
||||
formRef.value?.resetFields?.();
|
||||
prescriptionList.value = [];
|
||||
}
|
||||
|
||||
function handleOrgChange(value) {
|
||||
if (!value) return;
|
||||
const findNode = (nodes) => {
|
||||
for (let node of nodes) {
|
||||
if (node.id === value) return node;
|
||||
if (node.children && node.children.length > 0) {
|
||||
const found = findNode(node.children);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const selectedNode = findNode(organization.value);
|
||||
if (selectedNode && !isLeafNode(selectedNode)) {
|
||||
proxy.$modal.msgWarning('只能选择末级科室');
|
||||
formData.organizationId = null;
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openAdd,
|
||||
openEdit,
|
||||
openFromSelection,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dialog-top-row :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,129 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取个人组套
|
||||
* @param {*} queryParams
|
||||
*/
|
||||
export function getPersonalList(queryParams) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/get-personal',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取科室组套
|
||||
* @param {*} queryParams
|
||||
*/
|
||||
export function getDeptList(queryParams) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/get-organization',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全院组套
|
||||
* @param {*} queryParams
|
||||
*/
|
||||
export function getAllList(queryParams) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/get-hospital',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存个人组套
|
||||
* @param {*} data
|
||||
*/
|
||||
export function savePersonal(data) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/save-personal',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存科室组套
|
||||
* @param {*} data
|
||||
*/
|
||||
export function saveDepartment(data) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/save-organization',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存全院组套
|
||||
* @param {*} data
|
||||
*/
|
||||
export function saveAll(data) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/save-hospital',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组套明细
|
||||
* @param {*} data
|
||||
*/
|
||||
export function queryGroupDetail(params) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/get-group-package-detail',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组套
|
||||
* @param {*} data
|
||||
*/
|
||||
export function deleteGroup(data) {
|
||||
return request({
|
||||
url: '/personalization/orders-group-package/group-package-detail?groupPackageId=' + data.groupPackageId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询参与者下拉列表
|
||||
* @param {*} data
|
||||
*/
|
||||
export function queryParticipantList(params) {
|
||||
return request({
|
||||
url: '/app-common/practitioner-list',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取科室列表
|
||||
*/
|
||||
export function getOrgTree() {
|
||||
return request({
|
||||
url: '/base-data-manage/organization/organization',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取中药列表
|
||||
*/
|
||||
export function getTcmMedicine(params) {
|
||||
return request({
|
||||
url: '/doctor-station/chinese-medical/tcm-advice-base-info',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<div @keyup="handleKeyDown" tabindex="0" ref="tableWrapper" class="table-container">
|
||||
<el-table
|
||||
ref="adviceBaseRef"
|
||||
height="350"
|
||||
:data="adviceBaseList"
|
||||
highlight-current-row
|
||||
@current-change="handleCurrentChange"
|
||||
row-key="adviceCode"
|
||||
v-loading="loading"
|
||||
@cell-click="clickRow"
|
||||
>
|
||||
<el-table-column label="名称" align="center" prop="adviceName" />
|
||||
<el-table-column label="类型" align="center" prop="categoryCodeText" />
|
||||
<el-table-column label="医保等级" align="center" prop="chrgitmLv_dictText" />
|
||||
<el-table-column label="包装单位" align="center" prop="unitCode_dictText" />
|
||||
<el-table-column label="最小单位" align="center" prop="minUnitCode_dictText" />
|
||||
<el-table-column label="库存数量" align="center">
|
||||
<template #default="scope">{{ handleQuantity(scope.row) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页组件 -->
|
||||
<div class="pagination-wrapper">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNum"
|
||||
:page-size="20"
|
||||
:total="total"
|
||||
layout="prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick } from 'vue';
|
||||
import { getTcmMedicine } from './api';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
const props = defineProps({
|
||||
searchKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
organizationId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['selectMedicine']);
|
||||
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const adviceBaseRef = ref();
|
||||
const tableWrapper = ref();
|
||||
const currentIndex = ref(0);
|
||||
const currentSelectRow = ref({});
|
||||
const queryParams = ref({
|
||||
pageSize: 20,
|
||||
pageNum: 1,
|
||||
searchKey: '',
|
||||
organizationId: props.organizationId,
|
||||
});
|
||||
const adviceBaseList = ref([]);
|
||||
|
||||
// 前端缓存 - 使用 sessionStorage 持久化缓存
|
||||
const TCM_CACHE_PREFIX = 'tcm_advice_cache_';
|
||||
const TCM_CACHE_EXPIRE = 5 * 60 * 1000; // 缓存5分钟
|
||||
|
||||
function getTcmCacheKey(orgId, pageNum, searchKey) {
|
||||
return TCM_CACHE_PREFIX + orgId + '_' + pageNum + '_' + (searchKey || 'none');
|
||||
}
|
||||
|
||||
function getFromTcmCache(orgId, pageNum, searchKey) {
|
||||
try {
|
||||
const key = getTcmCacheKey(orgId, pageNum, searchKey);
|
||||
const cachedData = sessionStorage.getItem(key);
|
||||
if (!cachedData) return null;
|
||||
|
||||
const cacheData = JSON.parse(cachedData);
|
||||
if (Date.now() - cacheData.timestamp > TCM_CACHE_EXPIRE) {
|
||||
sessionStorage.removeItem(key);
|
||||
return null;
|
||||
}
|
||||
console.log('从TCM前端缓存获取:', key);
|
||||
return cacheData;
|
||||
} catch (e) {
|
||||
console.error('读取TCM缓存失败:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function setToTcmCache(orgId, pageNum, searchKey, data, total) {
|
||||
try {
|
||||
const key = getTcmCacheKey(orgId, pageNum, searchKey);
|
||||
const cacheData = {
|
||||
data: data,
|
||||
total: total,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
sessionStorage.setItem(key, JSON.stringify(cacheData));
|
||||
console.log('写入TCM前端缓存:', key);
|
||||
} catch (e) {
|
||||
console.error('写入TCM缓存失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 防抖函数 - 避免重复请求
|
||||
const debouncedGetList = debounce(
|
||||
() => {
|
||||
// 只有当 organizationId 有效时才请求
|
||||
if (!queryParams.value.organizationId) {
|
||||
console.log('organizationId 无效,跳过请求');
|
||||
return;
|
||||
}
|
||||
getList();
|
||||
},
|
||||
300,
|
||||
{ leading: false, trailing: true }
|
||||
);
|
||||
|
||||
// 监听搜索关键字变化
|
||||
watch(
|
||||
() => props.searchKey,
|
||||
(newValue) => {
|
||||
queryParams.value.searchKey = newValue || '';
|
||||
debouncedGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 监听 organizationId 变化
|
||||
watch(
|
||||
() => props.organizationId,
|
||||
(newValue) => {
|
||||
queryParams.value.organizationId = newValue;
|
||||
// organizationId 变化时重新加载
|
||||
if (newValue) {
|
||||
debouncedGetList();
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 不再页面加载时立即请求,等待用户点击时再请求
|
||||
|
||||
function getList() {
|
||||
const orgId = queryParams.value.organizationId;
|
||||
const pageNum = queryParams.value.pageNum;
|
||||
const searchKey = queryParams.value.searchKey;
|
||||
|
||||
// 尝试从本地缓存获取(只有第一页且无搜索关键字时使用缓存)
|
||||
if (pageNum === 1 && !searchKey) {
|
||||
const cached = getFromTcmCache(orgId, pageNum, searchKey);
|
||||
if (cached) {
|
||||
adviceBaseList.value = cached.data;
|
||||
total.value = cached.total;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 显示 loading
|
||||
loading.value = true;
|
||||
|
||||
getTcmMedicine(queryParams.value).then((res) => {
|
||||
adviceBaseList.value = res.data.records || [];
|
||||
total.value = res.data.total || 0;
|
||||
|
||||
// 缓存第一页数据
|
||||
if (pageNum === 1 && !searchKey) {
|
||||
setToTcmCache(orgId, pageNum, searchKey, adviceBaseList.value, total.value);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
currentIndex.value = 0;
|
||||
if (adviceBaseList.value.length > 0 && adviceBaseRef.value) {
|
||||
adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]);
|
||||
}
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 当前页改变
|
||||
function handlePageChange(page) {
|
||||
queryParams.value.pageNum = page;
|
||||
getList();
|
||||
}
|
||||
|
||||
// 处理键盘事件
|
||||
const handleKeyDown = (event) => {
|
||||
const key = event.key;
|
||||
const data = adviceBaseList.value;
|
||||
|
||||
switch (key) {
|
||||
case 'ArrowUp':
|
||||
event.preventDefault();
|
||||
if (currentIndex.value > 0) {
|
||||
currentIndex.value--;
|
||||
setCurrentRow(data[currentIndex.value]);
|
||||
}
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
event.preventDefault();
|
||||
if (currentIndex.value < data.length - 1) {
|
||||
currentIndex.value++;
|
||||
setCurrentRow(data[currentIndex.value]);
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
event.preventDefault();
|
||||
if (currentSelectRow.value) {
|
||||
emit('selectMedicine', currentSelectRow.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function handleQuantity(row) {
|
||||
if (row.inventoryList) {
|
||||
const totalQuantity = row.inventoryList.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
||||
return totalQuantity.toString() + (row.minUnitCode_dictText || '');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 设置选中行(带滚动)
|
||||
const setCurrentRow = (row) => {
|
||||
adviceBaseRef.value.setCurrentRow(row);
|
||||
const tableBody = adviceBaseRef.value.$el.querySelector('.el-table__body-wrapper');
|
||||
const currentRowEl = adviceBaseRef.value.$el.querySelector('.current-row');
|
||||
if (tableBody && currentRowEl) {
|
||||
currentRowEl.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
};
|
||||
|
||||
// 当前行变化时更新索引
|
||||
const handleCurrentChange = (currentRow) => {
|
||||
currentIndex.value = adviceBaseList.value.findIndex((item) => item === currentRow);
|
||||
currentSelectRow.value = currentRow;
|
||||
};
|
||||
|
||||
function clickRow(row) {
|
||||
emit('selectMedicine', row);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
handleKeyDown,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
background: #fff;
|
||||
}
|
||||
.popover-table-wrapper:focus {
|
||||
outline: 2px solid #409eff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,820 @@
|
||||
<template>
|
||||
<div class="app-container tcm-medical-order-set">
|
||||
<el-row :gutter="20" class="full-height">
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="5" class="left-panel">
|
||||
<el-card shadow="never" class="tree-card panel-shadow">
|
||||
<template #header>
|
||||
<span>中医组套</span>
|
||||
</template>
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
:props="treeProps"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
@node-click="(data, node) => handleTreeNodeClick(data, node)"
|
||||
/>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧操作区域 -->
|
||||
<el-col :span="19" class="right-panel">
|
||||
<!-- 上方表单:组套公共字段 -->
|
||||
<el-card shadow="never" class="form-card panel-shadow">
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px">
|
||||
<el-button type="primary" plain @click="clearForm">清空 </el-button>
|
||||
<el-button type="primary" @click="createNew">新建 </el-button>
|
||||
<el-button type="primary" @click="saveCommonForm">保存 </el-button>
|
||||
</div>
|
||||
<div class="common-bar">
|
||||
<el-form :model="commonForm" :inline="true" class="demo-form-inline">
|
||||
<el-form-item label="组套名称">
|
||||
<el-input v-model="commonForm.name" placeholder="组套名称" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="使用范围">
|
||||
<el-select
|
||||
v-model="commonForm.useRange"
|
||||
placeholder="个人/科室/全院"
|
||||
clearable
|
||||
@change="handleRangeChange"
|
||||
>
|
||||
<el-option label="个人" value="personal" />
|
||||
<el-option label="科室" value="department" />
|
||||
<el-option label="全院" value="hospital" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用人" v-if="commonForm.useRange === 'personal'">
|
||||
<el-select
|
||||
v-model="commonForm.practitionerId"
|
||||
placeholder="请选择使用人"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in participantListOptions"
|
||||
:key="item.practitionerId"
|
||||
:label="item.practitionerName"
|
||||
:value="item.practitionerId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="科室" v-if="commonForm.useRange === 'department'">
|
||||
<el-tree-select
|
||||
clearable
|
||||
v-model="commonForm.organizationId"
|
||||
:data="organization"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
default-expand-all
|
||||
placeholder="请选择科室"
|
||||
style="width: 200px"
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用法">
|
||||
<el-select v-model="commonForm.method" placeholder="用法" clearable>
|
||||
<el-option
|
||||
v-for="item in method_code"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="频次">
|
||||
<el-select v-model="commonForm.frequency" placeholder="频次" clearable>
|
||||
<el-option
|
||||
v-for="item in rate_code"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 下方:中药明细卡片列表 -->
|
||||
<el-card shadow="never" class="detail-card panel-shadow" style="height: 100%">
|
||||
<div class="drug-card-list">
|
||||
<!-- 已有药品卡片 -->
|
||||
<el-card
|
||||
v-for="(item, index) in drugList"
|
||||
:key="item.id"
|
||||
class="drug-card"
|
||||
shadow="never"
|
||||
>
|
||||
<template v-if="!item.isEditing">
|
||||
<div class="drug-card-body">
|
||||
<div class="drug-top">
|
||||
<div class="drug-view-name">{{ item.drugName || '-' }}</div>
|
||||
</div>
|
||||
<div class="drug-divider"></div>
|
||||
<div class="drug-bottom" style="margin-top: 10px">
|
||||
<div class="drug-bottom-left">
|
||||
<span class="drug-view-qty">{{ item.quantity }} {{ item.unit }}</span>
|
||||
</div>
|
||||
<div class="drug-view-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
:icon="Edit"
|
||||
@click.stop="editDrug(item)"
|
||||
title="编辑"
|
||||
/>
|
||||
<el-button
|
||||
v-if="drugList.length > 1"
|
||||
type="danger"
|
||||
text
|
||||
:icon="Delete"
|
||||
@click.stop="removeDrug(index)"
|
||||
title="删除"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div class="drug-card-body">
|
||||
<div class="drug-top">
|
||||
<el-form :model="item" class="drug-card-form">
|
||||
<el-form-item>
|
||||
<el-popover
|
||||
:popper-style="{ padding: '0' }"
|
||||
placement="bottom-start"
|
||||
:visible="item.showPopover"
|
||||
:width="800"
|
||||
>
|
||||
<tcmMedicineList
|
||||
:ref="(el) => setMedicineTableRef(el, item)"
|
||||
:searchKey="item.searchKey"
|
||||
:organizationId="commonForm.organizationId"
|
||||
@selectMedicine="(row) => handleSelectMedicine(row, item)"
|
||||
/>
|
||||
<template #reference>
|
||||
<el-input
|
||||
v-model="item.drugName"
|
||||
placeholder="请选择中药"
|
||||
clearable
|
||||
@input="(val) => handleSearchKeyChange(val, item)"
|
||||
@click="() => handleClick(item)"
|
||||
@keydown="(e) => handleKeyDown(e, item)"
|
||||
@blur="() => handleBlur(item)"
|
||||
/>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="drug-bottom">
|
||||
<div class="drug-bottom-left">
|
||||
<div class="quantity-wrap">
|
||||
<el-input v-model="item.quantity" />
|
||||
<el-input value="g" style="width: 80px" disabled />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drug-edit-actions">
|
||||
<el-button type="primary" link @click.stop="saveDrug(item)"> 保存 </el-button>
|
||||
<el-button type="danger" link @click.stop="removeDrug(index)"> 删除 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 始终存在的新增卡片 -->
|
||||
<el-card class="drug-card add-card" shadow="never" @click="addDrug">
|
||||
<div class="add-card-content">
|
||||
<el-icon class="add-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
<span>新增</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="TcmMedicalOrderSet">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import {
|
||||
getPersonalList,
|
||||
getDeptList,
|
||||
getAllList,
|
||||
queryGroupDetail,
|
||||
queryParticipantList,
|
||||
getOrgTree,
|
||||
savePersonal,
|
||||
saveDepartment,
|
||||
saveAll,
|
||||
} from './components/api';
|
||||
import TcmMedicineList from './components/tcmMedicineList.vue';
|
||||
import { Edit, Delete, Plus } from '@element-plus/icons-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 字典数据
|
||||
const { method_code, rate_code } = proxy.useDict('method_code', 'rate_code');
|
||||
|
||||
// 左侧树形结构数据
|
||||
const treeData = ref([]);
|
||||
|
||||
const treeProps = {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
};
|
||||
|
||||
// 加载左侧树数据
|
||||
function loadTreeData() {
|
||||
Promise.all([
|
||||
getPersonalList({ tcmFlag: 1 }),
|
||||
getDeptList({ tcmFlag: 1 }),
|
||||
getAllList({ tcmFlag: 1 }),
|
||||
])
|
||||
.then(([personalRes, deptRes, allRes]) => {
|
||||
const personalList = personalRes?.data || [];
|
||||
const deptList = deptRes?.data || [];
|
||||
const allList = allRes?.data || [];
|
||||
|
||||
treeData.value = [
|
||||
{
|
||||
id: 'personal',
|
||||
label: '个人',
|
||||
children: personalList.map((item) => ({
|
||||
id: item.groupPackageId,
|
||||
value: item.groupPackageId,
|
||||
label: item.name,
|
||||
practitionerId: item.practitionerId || '',
|
||||
organizationId: '',
|
||||
})),
|
||||
},
|
||||
{
|
||||
id: 'department',
|
||||
label: '科室',
|
||||
children: deptList.map((item) => ({
|
||||
id: item.groupPackageId,
|
||||
value: item.groupPackageId,
|
||||
label: item.name,
|
||||
practitionerId: '',
|
||||
organizationId: item.organizationId || '',
|
||||
})),
|
||||
},
|
||||
{
|
||||
id: 'hospital',
|
||||
label: '全院',
|
||||
children: allList.map((item) => ({
|
||||
id: item.groupPackageId,
|
||||
value: item.groupPackageId,
|
||||
label: item.name,
|
||||
practitionerId: '',
|
||||
organizationId: '',
|
||||
})),
|
||||
},
|
||||
];
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('加载组套树失败');
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadTreeData();
|
||||
loadParticipantList();
|
||||
loadOrganization();
|
||||
});
|
||||
|
||||
// 使用人列表
|
||||
const participantListOptions = ref([]);
|
||||
|
||||
// 科室树形数据
|
||||
const organization = ref([]);
|
||||
|
||||
// 加载使用人列表
|
||||
function loadParticipantList() {
|
||||
queryParticipantList().then((res) => {
|
||||
participantListOptions.value = res.data || [];
|
||||
});
|
||||
}
|
||||
|
||||
// 加载科室树
|
||||
function loadOrganization() {
|
||||
getOrgTree().then((res) => {
|
||||
organization.value = res.data.records || [];
|
||||
});
|
||||
}
|
||||
|
||||
// 使用范围变更
|
||||
function handleRangeChange() {
|
||||
commonForm.practitionerId = '';
|
||||
commonForm.organizationId = '';
|
||||
}
|
||||
|
||||
// 上方公共字段表单
|
||||
const commonForm = reactive({
|
||||
name: '',
|
||||
useRange: '',
|
||||
practitionerId: '',
|
||||
organizationId: '',
|
||||
method: '',
|
||||
frequency: '',
|
||||
});
|
||||
|
||||
// 下方药品卡片列表
|
||||
const drugList = ref([]);
|
||||
const nextDrugId = ref(1);
|
||||
const currentSetName = ref('');
|
||||
|
||||
// 动态设置表格组件引用
|
||||
function setMedicineTableRef(el, item) {
|
||||
if (el) {
|
||||
item.tableRef = el;
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyDrug() {
|
||||
return {
|
||||
id: nextDrugId.value++,
|
||||
drugCode: '',
|
||||
drugName: '',
|
||||
orderDefinitionId: '',
|
||||
quantity: 1,
|
||||
unitCode: '',
|
||||
unit: 'g',
|
||||
isEditing: true,
|
||||
showPopover: false,
|
||||
searchKey: '',
|
||||
tableRef: null,
|
||||
rateCode: '',
|
||||
methodCode: '',
|
||||
};
|
||||
}
|
||||
|
||||
// 点击树节点
|
||||
function handleTreeNodeClick(data, node) {
|
||||
if (data.children != undefined) {
|
||||
return;
|
||||
}
|
||||
// 从父节点获取使用范围:personal/department/hospital
|
||||
const useRange = node.parent?.data?.id || '';
|
||||
// 组套名称从树节点获取
|
||||
commonForm.name = data.label || '';
|
||||
commonForm.useRange = useRange;
|
||||
// 从树节点数据中获取使用人/科室
|
||||
commonForm.practitionerId = data.practitionerId || '';
|
||||
commonForm.organizationId = data.organizationId || '';
|
||||
|
||||
// 调用查询接口回显数据
|
||||
queryGroupDetail({ groupPackageId: data.value })
|
||||
.then((res) => {
|
||||
const detailList = res.data || [];
|
||||
if (detailList.length === 0) {
|
||||
drugList.value = [createEmptyDrug()];
|
||||
return;
|
||||
}
|
||||
|
||||
// 取第一个药品的频次和用法赋值到表单
|
||||
const firstDrug = detailList[0];
|
||||
commonForm.method = firstDrug.methodCode || '';
|
||||
commonForm.frequency = firstDrug.rateCode || '';
|
||||
|
||||
// 回显药品列表
|
||||
drugList.value = detailList.map((detail, index) => ({
|
||||
id: index,
|
||||
drugCode: '',
|
||||
drugName: detail.orderDefinitionName || '',
|
||||
orderDefinitionId: detail.orderDefinitionId,
|
||||
quantity: detail.quantity,
|
||||
unitCode: detail.unitCode,
|
||||
unit: detail.unitCode_dictText || 'g',
|
||||
isEditing: false,
|
||||
showPopover: false,
|
||||
searchKey: '',
|
||||
tableRef: null,
|
||||
rateCode: detail.rateCode,
|
||||
methodCode: detail.methodCode,
|
||||
}));
|
||||
currentSetName.value = data.label;
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('加载组套详情失败');
|
||||
});
|
||||
}
|
||||
|
||||
// 新增药品
|
||||
function addDrug() {
|
||||
// 如果有药品列表,检查上一个药品是否未保存
|
||||
if (drugList.value.length > 0) {
|
||||
const last = drugList.value[drugList.value.length - 1];
|
||||
// 如果上一个药品未选择药品,不允许添加下一个
|
||||
if (!last.orderDefinitionId) {
|
||||
ElMessage.warning('请先选择上一个药品');
|
||||
return;
|
||||
}
|
||||
// 如果上一个药品已有药品ID且处于编辑状态,自动保存
|
||||
if (last.isEditing) {
|
||||
const msg = validateDrug(last);
|
||||
if (!msg) {
|
||||
last.isEditing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
drugList.value.push(createEmptyDrug());
|
||||
}
|
||||
|
||||
// 删除药品
|
||||
function removeDrug(index) {
|
||||
if (drugList.value.length <= 1) {
|
||||
ElMessage.warning('至少保留一条药品信息');
|
||||
return;
|
||||
}
|
||||
drugList.value.splice(index, 1);
|
||||
}
|
||||
|
||||
function validateDrug(item) {
|
||||
if (!item.orderDefinitionId) return '请选择中药';
|
||||
if (!item.quantity || Number(item.quantity) <= 0) return '请填写数量';
|
||||
return '';
|
||||
}
|
||||
|
||||
function saveDrug(item) {
|
||||
const msg = validateDrug(item);
|
||||
if (msg) {
|
||||
ElMessage.warning(msg);
|
||||
return false;
|
||||
}
|
||||
item.isEditing = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function editDrug(item) {
|
||||
item.isEditing = true;
|
||||
}
|
||||
|
||||
// 处理搜索关键字变化
|
||||
function handleSearchKeyChange(value, item) {
|
||||
item.searchKey = value;
|
||||
}
|
||||
|
||||
// 处理点击输入框
|
||||
function handleClick(item) {
|
||||
item.showPopover = true;
|
||||
}
|
||||
|
||||
// 处理失焦
|
||||
function handleBlur(item) {
|
||||
item.showPopover = false;
|
||||
item.searchKey = '';
|
||||
}
|
||||
|
||||
// 处理键盘事件(上下键和回车)
|
||||
function handleKeyDown(event, item) {
|
||||
if (!item.showPopover) return;
|
||||
const key = event.key;
|
||||
if (['ArrowUp', 'ArrowDown', 'Enter'].includes(key)) {
|
||||
event.preventDefault();
|
||||
if (item.tableRef) {
|
||||
item.tableRef.handleKeyDown(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 选择中药时,回填信息
|
||||
function handleSelectMedicine(row, item) {
|
||||
item.drugCode = row.adviceCode;
|
||||
item.drugName = row.adviceName;
|
||||
item.orderDefinitionId = row.adviceDefinitionId;
|
||||
item.unitCode = row.unitCode;
|
||||
item.unit = row.unitCode_dictText || 'g';
|
||||
item.showPopover = false;
|
||||
item.searchKey = '';
|
||||
}
|
||||
|
||||
// 清空表单
|
||||
function clearForm() {
|
||||
commonForm.name = '';
|
||||
commonForm.useRange = '';
|
||||
commonForm.practitionerId = '';
|
||||
commonForm.organizationId = '';
|
||||
commonForm.method = '';
|
||||
commonForm.frequency = '';
|
||||
drugList.value = [];
|
||||
}
|
||||
|
||||
// 新建组套
|
||||
function createNew() {
|
||||
clearForm();
|
||||
commonForm.useRange = 'personal';
|
||||
drugList.value = [createEmptyDrug()];
|
||||
}
|
||||
|
||||
// 保存组套
|
||||
function saveCommonForm() {
|
||||
// 校验组套名称
|
||||
if (!commonForm.name) {
|
||||
ElMessage.warning('请填写组套名称');
|
||||
return;
|
||||
}
|
||||
// 校验使用范围
|
||||
if (!commonForm.useRange) {
|
||||
ElMessage.warning('请选择使用范围');
|
||||
return;
|
||||
}
|
||||
// 校验个人时选择使用人
|
||||
if (commonForm.useRange === 'personal' && !commonForm.practitionerId) {
|
||||
ElMessage.warning('请选择使用人');
|
||||
return;
|
||||
}
|
||||
// 校验科室时选择科室
|
||||
if (commonForm.useRange === 'department' && !commonForm.organizationId) {
|
||||
ElMessage.warning('请选择科室');
|
||||
return;
|
||||
}
|
||||
// 校验药品列表
|
||||
if (drugList.value.length === 0) {
|
||||
ElMessage.warning('请添加药品');
|
||||
return;
|
||||
}
|
||||
// 校验每个药品
|
||||
for (let i = 0; i < drugList.value.length; i++) {
|
||||
const item = drugList.value[i];
|
||||
if (!item.orderDefinitionId) {
|
||||
ElMessage.warning(`请选择第${i + 1}个药品`);
|
||||
return;
|
||||
}
|
||||
if (!item.quantity || Number(item.quantity) <= 0) {
|
||||
ElMessage.warning(`请填写第${i + 1}个药品的数量`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成组套ID(时间戳)
|
||||
const groupId = Date.now();
|
||||
|
||||
// 构建详情列表
|
||||
const detailList = drugList.value.map((item) => ({
|
||||
orderDefinitionId: item.orderDefinitionId,
|
||||
orderDefinitionTable: 'order_definition',
|
||||
quantity: item.quantity,
|
||||
unitCode: item.unitCode,
|
||||
dose: item.quantity,
|
||||
rateCode: commonForm.frequency,
|
||||
dispensePerDuration: 7,
|
||||
methodCode: commonForm.method,
|
||||
doseQuantity: item.quantity,
|
||||
groupId: groupId,
|
||||
}));
|
||||
|
||||
// 构建请求数据
|
||||
const data = {
|
||||
name: commonForm.name,
|
||||
tcmFlag: 1,
|
||||
detailList: detailList,
|
||||
};
|
||||
|
||||
// 根据使用范围添加不同字段
|
||||
if (commonForm.useRange === 'personal') {
|
||||
data.practitionerId = commonForm.practitionerId;
|
||||
} else if (commonForm.useRange === 'department') {
|
||||
data.organizationId = commonForm.organizationId;
|
||||
}
|
||||
// 全院不传 practitionerId 和 organizationId
|
||||
|
||||
// 调用对应接口
|
||||
let saveApi;
|
||||
if (commonForm.useRange === 'personal') {
|
||||
saveApi = savePersonal;
|
||||
} else if (commonForm.useRange === 'department') {
|
||||
saveApi = saveDepartment;
|
||||
} else {
|
||||
saveApi = saveAll;
|
||||
}
|
||||
|
||||
saveApi(data)
|
||||
.then(() => {
|
||||
ElMessage.success('保存成功');
|
||||
loadTreeData();
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error('保存失败:' + (err.message || '未知错误'));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tcm-medical-order-set {
|
||||
height: 90vh;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.left-panel,
|
||||
.right-panel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tree-card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.form-card,
|
||||
.detail-card {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.panel-shadow {
|
||||
border: 1px solid var(--el-border-color);
|
||||
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.common-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 14px;
|
||||
}
|
||||
|
||||
.common-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.common-item {
|
||||
width: 300px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.common-item :deep(.el-input__wrapper),
|
||||
.common-item :deep(.el-select__wrapper) {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.common-item :deep(.el-input__inner) {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.drug-card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.drug-card {
|
||||
width: 220px;
|
||||
border: 1px solid var(--el-border-color);
|
||||
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.12);
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.drug-card :deep(.el-card__body) {
|
||||
padding: 10px 10px 8px;
|
||||
}
|
||||
|
||||
.drug-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.drug-top {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.drug-divider {
|
||||
margin: 6px 0;
|
||||
border-top: 1px dashed var(--el-border-color);
|
||||
}
|
||||
|
||||
.drug-bottom {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.drug-bottom-left {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.drug-card-form {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.compact-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.compact-item :deep(.el-form-item__label) {
|
||||
padding: 0 0 4px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.compact-item :deep(.el-form-item__content) {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.quantity-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.unit-select {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.drug-select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.drug-edit-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0px;
|
||||
padding-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.drug-view-name {
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.drug-view-qty {
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-regular);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.drug-view-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 6px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.drug-view-actions :deep(.el-button),
|
||||
.drug-edit-actions :deep(.el-button) {
|
||||
padding: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.add-card {
|
||||
border-style: dashed;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-color-primary);
|
||||
box-shadow: 0 3px 14px rgba(64, 158, 255, 0.18);
|
||||
}
|
||||
|
||||
.add-card-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
.demo-form-inline .el-input {
|
||||
--el-input-width: 200px;
|
||||
}
|
||||
|
||||
.demo-form-inline .el-select {
|
||||
--el-select-width: 200px;
|
||||
}
|
||||
.el-form-item--default {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user