fix: 问题修复

This commit is contained in:
huxuejian 2026-08-06 16:33:15 +08:00
parent 3a6e88bea6
commit 635d0a8a0f
6 changed files with 307 additions and 125 deletions

BIN
src/assets/seal_custom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -1,5 +1,5 @@
<template> <template>
<el-drawer :model-value="visible" title="处方订单详情" size="70%" style="--el-drawer-padding-primary: 0" @close="close()"> <el-drawer :model-value="visible" title="处方详情" size="70%" style="--el-drawer-padding-primary: 0" @close="close()">
<my-layout> <my-layout>
<layout-main> <layout-main>
<div v-if="data" ref="paperRef" class="prescription-container rx-print-area"> <div v-if="data" ref="paperRef" class="prescription-container rx-print-area">
@ -11,21 +11,21 @@
<div>处方</div> <div>处方</div>
</div> </div>
</div> </div>
<div class="hospital">海宁康华互联网医院</div> <div class="hospital">海宁康华医院互联网医院 <br />处方笺</div>
<div class="sub-header"> <div class="sub-header">
<span class="no">NO:{{ no }}</span> <span class="no">NO:{{ no }}</span>
</div> </div>
</div> </div>
<div class="patient-info"> <div class="patient-info">
<div class="info-row"> <div class="info-row">
<div class="info-item" style="width: 28%">姓名{{ data.name }}</div> <div class="info-item w-1/4">姓名{{ data.name }}</div>
<div class="info-item" style="width: 20%">性别{{ info.sex }}</div> <div class="info-item w-1/4">性别{{ info.sex }}</div>
<div class="info-item" style="width: 28%">费别{{ data.feeType === 'YB' ? '医保' : '自费' }}</div> <div class="info-item w-1/4">年龄{{ info.age }}</div>
<div class="info-item" style="width: 24%">年龄{{ info.age }}</div> <div class="info-item w-1/4">费别{{ data.feeType === 'YB' ? '医保' : '自费' }}</div>
</div> </div>
<div class="info-row"> <div class="info-row">
<div class="info-item" style="width: 60%">门诊病历号{{ data.blhno }}</div> <div class="info-item" style="width: 50%">门诊病历号{{ data.blhno }}</div>
<div class="info-item" style="width: 40%">科别{{ data.deptName }}</div> <div class="info-item" style="width: 50%">科别{{ data.deptName }}</div>
</div> </div>
<div class="info-row"> <div class="info-row">
<div class="info-item full-width">地址 {{ data.address }}</div> <div class="info-item full-width">地址 {{ data.address }}</div>
@ -37,25 +37,27 @@
<!-- 诊断信息 --> <!-- 诊断信息 -->
<div class="diagnosis-section"> <div class="diagnosis-section">
<div class="diagnosis-header"> <div class="diagnosis-header">
<span>诊断</span> <div class="w-0 flex-grow flex">
<span class="date">开方日期{{ info.time }}</span> <div class="flex-shrink-0">诊断</div>
<div class="w-0 flex-grow break-all">{{ diagnosisStr }}</div>
</div> </div>
<div class="diagnosis-items"> <div class="min-w-1/4 flex-shrink-0 white-space-nowrap">开方日期{{ info.time }}</div>
<div v-for="(i, idx) in diagnosis" :key="i.code">{{ idx + 1 }}. {{ i.name }}</div>
</div> </div>
</div> </div>
<!-- 药品处方区域 --> <!-- 药品处方区域 -->
<div class="medicine-section"> <div class="medicine-section">
<div class="rp-symbol">Rp</div> <div class="flex items-center mb-2 relative">
<div v-if="data.prescriptionType === 'medicalWinePurchase'" class="medicine-list"> <div class="w-3 border-t border-gray-500 border-dashed"></div>
<div v-for="i in data.wines" :key="i._id" class="medicine-item"> <div class="rp-symbol mx-2">Rp</div>
<div class="medicine-name">{{ i.name }} x {{ i.quantity }} {{ i.unit }}</div> <div class="flex-grow border-t border-gray-500 border-dashed"></div>
<div class="medicine-spec">{{ i.details }}</div> <div class="absolute right-0 -top-2">
<div class="border-2 border-red-500 px-2 py-1 text-red-500 transform rotate-15">处方外购专用章</div>
<img class="w-30 h-30 mt-3" :src="stampPng" alt="">
</div> </div>
</div> </div>
<div v-else class="medicine-list"> <div class="medicine-list">
<div v-for="i in drugList" :key="i.insurance_code" class="medicine-item"> <div v-for="(i, idx) in drugList" :key="i.insurance_code" class="medicine-item">
<div class="medicine-name">{{ i.drugName }} x {{ i.quantity }} {{ i.unit }}</div> <div class="medicine-name">{{ idx + 1 }}. {{ i.drugName }} x {{ i.quantity }} {{ i.unit }}</div>
<div class="medicine-spec">规格{{ i.specification }}</div> <div class="medicine-spec">规格{{ i.specification }}</div>
<div class="medicine-usage">用法{{ i.memo }}</div> <div class="medicine-usage">用法{{ i.memo }}</div>
</div> </div>
@ -63,9 +65,8 @@
</div> </div>
<!-- 底部签章区域 --> <!-- 底部签章区域 -->
<div class="footer"> <div class="footer">
<div class="stamp-area"> <div class="flex pt-3">
<div class="signatures"> <div class="flex-shrink-0 w-1/3">
<div class="signature-item" style="width: 33%">
<div>医生 <div>医生
<img v-if="data.doctorSignImg" class="signImage" :src="data.doctorSignImg" /> <img v-if="data.doctorSignImg" class="signImage" :src="data.doctorSignImg" />
<text v-else>{{ data.doctorName }}</text> <text v-else>{{ data.doctorName }}</text>
@ -74,19 +75,21 @@
<!-- <image class="stamp-img" src="/static/stamp.png"></image> --> <!-- <image class="stamp-img" src="/static/stamp.png"></image> -->
</div> </div>
</div> </div>
<div class="signature-item" style="width: 33%"> <div class="flex-shrink-0 w-1/3">
<div>审方<img v-if="data.pharmacistSignImg" class="signImage" :src="data.pharmacistSignImg" /> <div>审方<img v-if="data.pharmacistSignImg" class="signImage" :src="data.pharmacistSignImg" />
<text v-else>{{ data.pharmacist }}</text> <text v-else>{{ data.pharmacist }}</text>
</div> </div>
<div>发药</div>
</div> </div>
<div class="signature-item" style="width: 34%">
<div class="stamp">收费盖章
<!-- <image class="stamp-img" src="/static/stamp.png"></image> -->
</div> </div>
<div>配方</div> <div class="flex pt-3">
<div>复核</div> <div class="flex-shrink-0 w-1/3">
调配
</div> </div>
<div class="flex-shrink-0 w-1/3">
复核发药
</div>
<div class="flex-shrink-0 w-1/3">
发药
</div> </div>
</div> </div>
<div class="notice">注意请勿丢失处方当日有效因特殊情况该处方有效为天签名</div> <div class="notice">注意请勿丢失处方当日有效因特殊情况该处方有效为天签名</div>
@ -107,6 +110,8 @@ import { getSealImage } from '@/api/hlw';
import validate from "@/utils/validate"; import validate from "@/utils/validate";
import dayjs from 'dayjs'; import dayjs from 'dayjs';
const stampPng = new URL("@/assets/seal_custom.png", import.meta.url).href; // logo
const emits = defineEmits(['close', 'printed']) const emits = defineEmits(['close', 'printed'])
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -126,6 +131,7 @@ const paperRef = ref(null)
const doctorCode = ref('') const doctorCode = ref('')
const pharmacistNo = ref('') const pharmacistNo = ref('')
const diagnosis = computed(() => props.data && Array.isArray(props.data.diagnosisList) ? props.data.diagnosisList : []) const diagnosis = computed(() => props.data && Array.isArray(props.data.diagnosisList) ? props.data.diagnosisList : [])
const diagnosisStr = computed(() => diagnosis.value.map((item) => item.name).join('+'))
const drugList = computed(() => { const drugList = computed(() => {
const drugs = props.data && Array.isArray(props.data.drugs) ? props.data.drugs : []; const drugs = props.data && Array.isArray(props.data.drugs) ? props.data.drugs : [];
return drugs.map((item) => { return drugs.map((item) => {
@ -218,7 +224,7 @@ defineExpose({ print });
/* 头部样式 */ /* 头部样式 */
.header { .header {
border-bottom: 1px solid #000; border-bottom: 1px solid #000;
padding-bottom: 16px; padding-bottom: 8px;
margin-bottom: 12px; margin-bottom: 12px;
} }
@ -253,13 +259,12 @@ defineExpose({ print });
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
font-size: 14px; font-size: 14px;
margin-top: 10px;
flex-direction: column; flex-direction: column;
} }
/* 患者信息表格 */ /* 患者信息表格 */
.patient-info { .patient-info {
margin: 12px 0; margin: 12px 0 0;
} }
.info-row { .info-row {
@ -282,32 +287,24 @@ defineExpose({ print });
/* 诊断区域 */ /* 诊断区域 */
.diagnosis-section { .diagnosis-section {
margin: 15px 0; margin: 12px 0 0;
} }
.diagnosis-header { .diagnosis-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-bottom: 8px; padding: 0 6px;
}
.date {
font-size: 13px;
color: #666;
} }
/* 药品处方 */ /* 药品处方 */
.medicine-section { .medicine-section {
position: relative; position: relative;
padding-left: 30px; margin: 12px 0 0;
margin: 18px 0;
} }
.rp-symbol { .rp-symbol {
position: absolute;
left: 0;
top: 0;
font-weight: bold; font-weight: bold;
font-size: 20px;
} }
.medicine-item { .medicine-item {
@ -326,8 +323,8 @@ defineExpose({ print });
/* 底部签章 */ /* 底部签章 */
.footer { .footer {
border-top: 1px dashed #999; border-top: 1px dashed #999;
padding-top: 15px; // padding-top: 15px;
margin-top: 20px; margin-top: 12px;
} }
.stamp-area { .stamp-area {
@ -379,8 +376,8 @@ defineExpose({ print });
@media print { @media print {
@page { @page {
size: A4; size: A5 portrait;
margin: 12mm; margin: 8mm;
} }
body.printing-rx> :not(.rx-print-root) { body.printing-rx> :not(.rx-print-root) {
@ -399,5 +396,11 @@ defineExpose({ print });
padding: 0 !important; padding: 0 !important;
box-shadow: none !important; box-shadow: none !important;
} }
body.printing-rx>.rx-print-root .medicine-item,
body.printing-rx>.rx-print-root .footer {
break-inside: avoid;
page-break-inside: avoid;
}
} }
</style> </style>

View File

@ -16,7 +16,7 @@
<el-input :model-value="form.mobile" placeholder="请输入" maxlength="50" :disabled="disableMobile" <el-input :model-value="form.mobile" placeholder="请输入" maxlength="50" :disabled="disableMobile"
@update:model-value="change($event,'mobile')" /> @update:model-value="change($event,'mobile')" />
</el-form-item> </el-form-item>
<el-form-item class="is-required" label="手机号" prop="phone"> <el-form-item label="手机号" prop="phone">
<el-input :model-value="form.phone" placeholder="请输入手机号" maxlength="20" <el-input :model-value="form.phone" placeholder="请输入手机号" maxlength="20"
@update:model-value="change($event,'phone')" /> @update:model-value="change($event,'phone')" />
</el-form-item> </el-form-item>
@ -95,6 +95,7 @@ const rules = [
} }
}, },
{ key: 'phone', test: val => { { key: 'phone', test: val => {
if (!val) return [true, ''];
// / // /
const [pass, v] = verifyString(val, '手机号', 1, 20); const [pass, v] = verifyString(val, '手机号', 1, 20);
if (!pass) return [false, v]; if (!pass) return [false, v];

View File

@ -13,6 +13,15 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<div active-title-bar class="my-2.5">适应诊断</div>
<el-row>
<el-col :span="24">
<el-form-item class="mb-5px" label="适应诊断">
<span v-if="diagnosisLoading">加载中...</span>
<span v-else>{{ diagnosisText || '-' }}</span>
</el-form-item>
</el-col>
</el-row>
<div active-title-bar class="my-2.5">药品标准用法用量</div> <div active-title-bar class="my-2.5">药品标准用法用量</div>
<el-row> <el-row>
<el-col :span="12" v-for="item in usageInfo" :key="item.prop"> <el-col :span="12" v-for="item in usageInfo" :key="item.prop">
@ -75,7 +84,7 @@ import { computed, ref, watch } from 'vue';
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { setHlwDrugOnSale, setOnlineDrugOnSale } from "@/api/hlw"; import { setHlwDrugOnSale, setOnlineDrugOnSale, getHlwDiagnosisList } from "@/api/hlw";
import { getDrugFieldDict } from './drug-dict.js' import { getDrugFieldDict } from './drug-dict.js'
import { memberStore } from "@/store/member"; import { memberStore } from "@/store/member";
@ -179,6 +188,33 @@ const showForm = computed(() => {
}) })
const form = computed(() => ({ ...props.data, ...showForm.value })) const form = computed(() => ({ ...props.data, ...showForm.value }))
const loading = ref(false) const loading = ref(false)
const diagnosisLoading = ref(false)
const diagnosisList = ref([])
let diagnosisRequestId = 0
const diagnosisText = computed(() => diagnosisList.value
.map(item => item.code ? `${item.name}${item.code}` : item.name)
.filter(Boolean)
.join('、'))
async function loadDiagnoses() {
const requestId = ++diagnosisRequestId;
const ids = Array.isArray(props.data.applicableDiagnosisIds)
? props.data.applicableDiagnosisIds.map(id => String(id))
: [];
diagnosisList.value = [];
if (!ids.length) return;
diagnosisLoading.value = true;
const res = await getHlwDiagnosisList({ ids, page: 1, pageSize: Math.max(30, ids.length) });
if (requestId !== diagnosisRequestId) return;
diagnosisLoading.value = false;
if (res.success) {
const list = res.data && Array.isArray(res.data.data) ? res.data.data : [];
const diagnosisMap = new Map(list.map(item => [String(item._id), item]));
diagnosisList.value = ids.map(id => diagnosisMap.get(id)).filter(Boolean);
} else {
ElMessage.error(res.message || '适应诊断加载失败');
}
}
function close() { function close() {
@ -221,4 +257,13 @@ async function setOnSale() {
} }
} }
watch(() => props.visible, (visible) => {
if (visible) {
loadDiagnoses();
} else {
diagnosisRequestId += 1;
diagnosisLoading.value = false;
}
});
</script> </script>

View File

@ -31,50 +31,33 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<div active-title-bar class="mb-2.5">药品规则配置</div> <div active-title-bar class="mb-2.5">适应诊断</div>
<el-row> <el-row>
<template v-if="libType === 'online'"> <el-col :span="24">
<el-col :span="12" v-for="item in suitableInfo" :key="item.prop"> <el-form-item label="适应诊断" prop="applicableDiagnosisIds">
<el-form-item :label="item.label" :prop="item.prop" <div class="w-full">
:class="requiredMap[item.prop] ? 'is-required' : ''"> <el-popover v-model:visible="diagnosisPopoverVisible" placement="bottom-start" trigger="click"
<el-select filterable clearable :model-value="form[item.prop]" :placeholder="`请选择${item.label}`" :width="600">
class="w-full" @update:model-value="changeValue($event, item.prop)"> <template #reference>
<el-option v-for="option in item.options" :key="option" :label="option.label" <el-input v-model="diagnosisKeyword" clearable placeholder="请输入诊断名称、编码或拼音码搜索"
:value="option.value" /> @input="searchDiagnoses" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" :key="repeatTag">
<el-form-item label="重复用药标识" prop="repeatTag">
<!-- <el-select-v2 filterable clearable :multiple="true" :model-value="form.repeatTag" placeholder="请选择重复用药标识"
class="w-full" @update:model-value="changeValue($event, 'repeatTag')">
<el-option v-for="option in repeatTagList" :key="option.value" :label="option.label"
:value="option.value" />
</el-select-v2> -->
<el-select-v2 :model-value="form.repeatTag || []" :options="repeatTagList" placeholder="请选择重复用药标识"
class="w-full" multiple filterable clearable
@update:model-value="changeValue($event, 'repeatTag')" />
</el-form-item>
</el-col>
</template> </template>
<el-col v-for="item in otherInfo" :key="item.prop"> <div v-loading="diagnosisLoading" class="diagnosis-dropdown">
<el-form-item :label="item.label" :prop="item.prop" :class="requiredMap[item.prop] ? 'is-required' : ''"> <div v-for="item in diagnosisOptions" :key="item._id" class="diagnosis-option"
<div class="other-info-row"> :class="{ 'diagnosis-option--selected': isDiagnosisSelected(item._id) }"
<el-select class="other-info-select" placeholder="请选择" :model-value="form[item.prop]" @click="toggleDiagnosis(item)">
@change="val => selectChange(item.prop, val)"> <span class="diagnosis-option-label">{{ diagnosisLabel(item) }}</span>
<el-option v-for="option in item.options" :key="option.value" :label="option.label" <span v-if="isDiagnosisSelected(item._id)" class="diagnosis-option-check"></span>
:value="option.value" />
</el-select>
<!-- 当选择为 true 时才渲染输入框同时把输入框拉伸占满剩余空间 -->
<div v-if="item.prop === 'need_medication_proof' && form.need_medication_proof"
class="other-info-desc">
<el-input clearable :model-value="form.medication_proof_desc" placeholder="描述需上传的用药证明文件类型,用于患者端显示"
style="width:100%" @update:model-value="val => changeValue(val, 'medication_proof_desc')" />
</div> </div>
<div v-else-if="item.prop === 'has_usage_restriction' && form.has_usage_restriction" <el-empty v-if="!diagnosisLoading && !diagnosisOptions.length" :image-size="60"
class="other-info-desc"> description="暂无诊断" />
<el-input clearable :model-value="form.usage_restriction_desc" placeholder="描述该药品的限制使用范围,用于医生、药师端展示" </div>
style="width:100%" @update:model-value="val => changeValue(val, 'usage_restriction_desc')" /> </el-popover>
<div v-if="selectedDiagnosisOptions.length" class="selected-diagnoses">
<el-tag v-for="item in selectedDiagnosisOptions" :key="item._id" closable
@close="toggleDiagnosis(item)">
{{ diagnosisLabel(item) }}
</el-tag>
</div> </div>
</div> </div>
</el-form-item> </el-form-item>
@ -95,7 +78,7 @@
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { addHlwDrugInfo, editHlwDrugInfo, addOnlineDrugInfo, editOnlineDrugInfo, getOnlineDrugRepeatTags } from "@/api/hlw"; import { addHlwDrugInfo, editHlwDrugInfo, addOnlineDrugInfo, editOnlineDrugInfo, getOnlineDrugRepeatTags, getHlwDiagnosisList } from "@/api/hlw";
import categoryList from '@/baseData/drug-category'; import categoryList from '@/baseData/drug-category';
import dosageUnitList from '@/baseData/dosageUnit.js'; import dosageUnitList from '@/baseData/dosageUnit.js';
import medicineInfoStore from '@/store/medicine-info'; import medicineInfoStore from '@/store/medicine-info';
@ -132,6 +115,13 @@ const frequencyOptions = computed(() => getOptionNames(medicineUseInfo.value.fre
const usageOptions = computed(() => getOptionNames(medicineUseInfo.value.usageList)) const usageOptions = computed(() => getOptionNames(medicineUseInfo.value.usageList))
const repeatTagList = ref([]) const repeatTagList = ref([])
const diagnosisOptions = ref([])
const diagnosisDefaultOptions = ref([])
const diagnosisLoading = ref(false)
const diagnosisKeyword = ref('')
const diagnosisPopoverVisible = ref(false)
let diagnosisRequestId = 0
let diagnosisSearchTimer = null
const basicInfo = computed(() => [ const basicInfo = computed(() => [
{ label: '药品名称', prop: "name", maxlength: 100 }, { label: '药品名称', prop: "name", maxlength: 100 },
{ label: '药品规格', prop: "specification", maxlength: 100 }, { label: '药品规格', prop: "specification", maxlength: 100 },
@ -176,11 +166,6 @@ const suitableInfo = [
{ label: '是否儿童用药', prop: 'suitableChild', options: [{ label: '是', value: true }, { label: '否', value: false }] }, { label: '是否儿童用药', prop: 'suitableChild', options: [{ label: '是', value: true }, { label: '否', value: false }] },
// { label: '', prop: 'repeatTag', multiple: true, options: new Array(5).fill(1).map((_, index) => ({ label: index + 1, value: index + 1 })) }, // { label: '', prop: 'repeatTag', multiple: true, options: new Array(5).fill(1).map((_, index) => ({ label: index + 1, value: index + 1 })) },
] ]
// /使
const otherInfo = [
{ label: '是否上传用药证明', prop: 'need_medication_proof', type: 'select', options: [{ label: '是', value: true }, { label: '否', value: false }] },
{ label: '是否限制使用范围', prop: 'has_usage_restriction', type: 'select', options: [{ label: '是', value: true }, { label: '否', value: false }] }
]
const requiredMap = computed(() => { const requiredMap = computed(() => {
const m = getDrugFieldDict(props.libType).reduce((acc, cur) => { const m = getDrugFieldDict(props.libType).reduce((acc, cur) => {
@ -204,6 +189,96 @@ function changeValue(val, prop) {
editForm.value[prop] = val editForm.value[prop] = val
} }
function diagnosisLabel(item) {
return item.code ? `${item.name}${item.code}` : item.name;
}
const selectedDiagnosisOptions = computed(() => {
const optionMap = new Map(diagnosisOptions.value.map(item => [String(item._id), item]));
const defaultOptionMap = new Map(diagnosisDefaultOptions.value.map(item => [String(item._id), item]));
return (form.value.applicableDiagnosisIds || [])
.map(id => optionMap.get(String(id)) || defaultOptionMap.get(String(id)))
.filter(Boolean);
})
function isDiagnosisSelected(id) {
return (form.value.applicableDiagnosisIds || []).some(item => String(item) === String(id));
}
function toggleDiagnosis(item) {
const id = String(item._id);
const selectedIds = (form.value.applicableDiagnosisIds || []).map(itemId => String(itemId));
const nextIds = selectedIds.includes(id)
? selectedIds.filter(itemId => itemId !== id)
: [...selectedIds, id];
changeValue(nextIds, 'applicableDiagnosisIds');
}
function mergeDiagnosisOptions(items = []) {
const optionMap = new Map(diagnosisOptions.value.map(item => [String(item._id), item]));
items.forEach(item => optionMap.set(String(item._id), item));
diagnosisOptions.value = [...optionMap.values()];
}
function replaceDiagnosisOptions(items = []) {
const selectedIds = new Set((form.value.applicableDiagnosisIds || []).map(id => String(id)));
const selectedOptions = diagnosisOptions.value.filter(item => selectedIds.has(String(item._id)));
const optionMap = new Map(selectedOptions.map(item => [String(item._id), item]));
items.forEach(item => optionMap.set(String(item._id), item));
diagnosisOptions.value = [...optionMap.values()];
}
async function fetchDiagnoses(params, requestId, replace = false) {
const res = await getHlwDiagnosisList({ page: 1, pageSize: 30, ...params });
if (requestId !== diagnosisRequestId) return;
if (!res.success) {
ElMessage.error(res.message || '适应诊断加载失败');
return;
}
const list = res.data && Array.isArray(res.data.data) ? res.data.data : [];
if (replace) {
replaceDiagnosisOptions(list);
} else {
mergeDiagnosisOptions(list);
}
}
function searchDiagnoses(keyword = '') {
if (diagnosisSearchTimer) {
clearTimeout(diagnosisSearchTimer);
diagnosisSearchTimer = null;
}
const normalizedKeyword = typeof keyword === 'string' ? keyword.trim() : '';
const requestId = ++diagnosisRequestId;
if (!normalizedKeyword) {
diagnosisLoading.value = false;
replaceDiagnosisOptions(diagnosisDefaultOptions.value);
return;
}
diagnosisLoading.value = true;
diagnosisSearchTimer = setTimeout(async () => {
await fetchDiagnoses({ keyword: normalizedKeyword }, requestId, true);
if (requestId === diagnosisRequestId) diagnosisLoading.value = false;
diagnosisSearchTimer = null;
}, 300);
}
async function initializeDiagnoses() {
diagnosisOptions.value = [];
const selectedIds = Array.isArray(props.data.applicableDiagnosisIds)
? props.data.applicableDiagnosisIds.map(id => String(id))
: [];
const requestId = ++diagnosisRequestId;
diagnosisLoading.value = true;
const requests = [fetchDiagnoses({}, requestId)];
if (selectedIds.length) {
requests.push(fetchDiagnoses({ ids: selectedIds, pageSize: Math.max(30, selectedIds.length) }, requestId));
}
await Promise.all(requests);
diagnosisDefaultOptions.value = [...diagnosisOptions.value];
if (requestId === diagnosisRequestId) diagnosisLoading.value = false;
}
function close() { function close() {
if (loading.value) return; if (loading.value) return;
@ -241,7 +316,6 @@ async function save() {
if (!props.data._id) { if (!props.data._id) {
payload.params.onSale = true; payload.params.onSale = true;
} }
loading.value = false;
const { success, message } = await api(payload); const { success, message } = await api(payload);
loading.value = false; loading.value = false;
if (success) { if (success) {
@ -264,7 +338,8 @@ watch(() => props.visible, async (val) => {
if (val) { if (val) {
// reset local editForm when opening drawer // reset local editForm when opening drawer
editForm.value = {} editForm.value = {}
await attachMedicineConfig() diagnosisKeyword.value = ''
await Promise.all([attachMedicineConfig(), initializeDiagnoses()])
if (props.libType === 'online') { if (props.libType === 'online') {
getRepeatTags() getRepeatTags()
} }
@ -286,6 +361,14 @@ watch(() => props.visible, async (val) => {
editForm.value.repeatTag = [props.data.repeatTag] editForm.value.repeatTag = [props.data.repeatTag]
} }
} }
} else {
diagnosisPopoverVisible.value = false
diagnosisRequestId += 1
diagnosisLoading.value = false
if (diagnosisSearchTimer) {
clearTimeout(diagnosisSearchTimer)
diagnosisSearchTimer = null
}
} }
}) })
@ -321,4 +404,52 @@ function selectChange(prop, val) {
min-width: 0; min-width: 0;
/* allow input to shrink properly inside flex */ /* allow input to shrink properly inside flex */
} }
.diagnosis-dropdown {
min-height: 60px;
max-height: 320px;
overflow-y: auto;
}
.diagnosis-option {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 36px;
padding: 0 12px;
border-radius: 4px;
cursor: pointer;
color: var(--el-text-color-regular);
}
.diagnosis-option:hover,
.diagnosis-option--selected {
background: var(--el-fill-color-light);
}
.diagnosis-option--selected {
color: var(--el-color-primary);
}
.diagnosis-option-label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.diagnosis-option-check {
flex-shrink: 0;
margin-left: 16px;
font-size: 18px;
font-weight: 600;
color: var(--el-color-primary);
}
.selected-diagnoses {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
</style> </style>

View File

@ -25,7 +25,8 @@
</el-form> </el-form>
<div class="mt-6 flex justify-end text-sm"> <div class="mt-6 flex justify-end text-sm">
<span class="cursor-pointer text-gray-600 hover:text-blue-500 transition-colors" @click.prevent="editPassword">修改密码</span> <span class="cursor-pointer text-gray-600 hover:text-blue-500 transition-colors"
@click.prevent="editPassword">修改密码</span>
</div> </div>
</div> </div>
<ModifyPasswordDialog :showModifyDialog="showModifyDialog" @close="closeModifyDialog" <ModifyPasswordDialog :showModifyDialog="showModifyDialog" @close="closeModifyDialog"
@ -59,6 +60,7 @@ const loginFormRef = ref();
const loading = ref(false); const loading = ref(false);
const loginForm = reactive({ const loginForm = reactive({
username: "", username: "",
// password: "",
password: "zytzyy6789", password: "zytzyy6789",
}); });
const loginRules = { const loginRules = {