2026-07-27 11:32:24 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<el-drawer v-loading="loading" :close-on-click-modal="false" :model-value="visible" title="药品详情" size="70%"
|
|
|
|
|
|
style="--el-drawer-padding-primary: 0" @close="close">
|
|
|
|
|
|
<my-layout>
|
|
|
|
|
|
<layout-main>
|
2026-08-07 11:11:58 +08:00
|
|
|
|
<el-form :label-width="140" label-suffix=":" class="p-15px pr-30px"
|
|
|
|
|
|
:class="loading ? 'pointer-events-none' : ''">
|
2026-07-27 11:32:24 +08:00
|
|
|
|
<div active-title-bar class="my-2.5">药品基本信息</div>
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12" v-for="item in basicInfoList" :key="item.prop">
|
|
|
|
|
|
<el-form-item class="mb-5px" :label="item.label" :prop="item.prop"
|
|
|
|
|
|
:class="requiredMap[item.prop] ? 'is-required' : ''">
|
2026-08-12 15:20:29 +08:00
|
|
|
|
{{ item.format ? item.format(form[item.prop]) : form[item.prop] }}
|
2026-07-27 11:32:24 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<div active-title-bar class="my-2.5">药品标准用法用量</div>
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12" v-for="item in usageInfo" :key="item.prop">
|
|
|
|
|
|
<el-form-item class="mb-5px" :label="item.label" :prop="item.prop"
|
|
|
|
|
|
:class="requiredMap[item.prop] ? 'is-required' : ''">
|
|
|
|
|
|
{{ form[item.prop] }}
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<div active-title-bar class="my-2.5">药品规则配置</div>
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="24">
|
2026-08-07 11:11:58 +08:00
|
|
|
|
<el-form-item class="mb-5px" label="适应诊断">
|
|
|
|
|
|
<span v-if="diagnosisLoading">加载中...</span>
|
|
|
|
|
|
<span v-else>{{ diagnosisText || '-' }}</span>
|
2026-07-27 11:32:24 +08:00
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
<div active-title-bar class="my-2.5">药品维护记录</div>
|
|
|
|
|
|
<el-row>
|
|
|
|
|
|
<el-col :span="12" v-for="item in maintenanceInfo" :key="item.prop">
|
|
|
|
|
|
<el-form-item class="mb-5px" :label="item.label" :prop="item.prop"
|
|
|
|
|
|
:class="requiredMap[item.prop] ? 'is-required' : ''">
|
|
|
|
|
|
{{ form[item.prop] }}
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</layout-main>
|
|
|
|
|
|
<layout-item>
|
|
|
|
|
|
<div common-shadow--r class="text-center py-10px">
|
|
|
|
|
|
<el-button plain type="primary" class="w-100px" @click="close()">取消</el-button>
|
|
|
|
|
|
<el-button :type="data.onSale ? 'info' : 'success'" class="w-100px" @click="setOnSale()">
|
|
|
|
|
|
{{ data.onSale ? '下架' : '上架' }}
|
|
|
|
|
|
</el-button>
|
2026-09-20 17:32:57 +08:00
|
|
|
|
<el-button v-if="libType === 'hlw'" type="warning" class="w-100px" :loading="syncingHis" @click="syncHis">
|
|
|
|
|
|
同步 HIS
|
|
|
|
|
|
</el-button>
|
2026-07-27 11:32:24 +08:00
|
|
|
|
<el-button type="primary" class="w-100px" @click="edit()">编辑</el-button>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- <el-button class="w-100px" :loading="loading" type="primary" @click="save()">保存</el-button> -->
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</layout-item>
|
|
|
|
|
|
</my-layout>
|
|
|
|
|
|
</el-drawer>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { computed, ref, watch } from 'vue';
|
|
|
|
|
|
import { storeToRefs } from "pinia";
|
|
|
|
|
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
|
|
|
import dayjs from "dayjs";
|
2026-09-20 17:32:57 +08:00
|
|
|
|
import { setHlwDrugOnSale, setOnlineDrugOnSale, getHlwDiagnosisList, updateHisDrugInfo } from "@/api/hlw";
|
2026-07-30 16:49:15 +08:00
|
|
|
|
import { getDrugFieldDict } from './drug-dict.js'
|
2026-07-27 11:32:24 +08:00
|
|
|
|
import { memberStore } from "@/store/member";
|
|
|
|
|
|
|
|
|
|
|
|
import MyLayout, { LayoutMain, LayoutItem } from "@/components/layout";
|
|
|
|
|
|
|
2026-08-12 15:20:29 +08:00
|
|
|
|
const antibioticLevelMap = { 0: '0 (无)', 1: '1 (非限制使用级)', 2: '2 (限制使用级)', 3: '3 (特殊使用级)' };
|
2026-08-13 13:53:17 +08:00
|
|
|
|
const tumorUseMap = { 0: '0 (无)', 1: '1 (免疫)', 2: '2 (普通)', 3: '3 (限制)' };
|
2026-08-12 15:20:29 +08:00
|
|
|
|
|
2026-07-27 11:32:24 +08:00
|
|
|
|
const emits = defineEmits(['close', 'edit', 'changeSale'])
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
visible: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
},
|
|
|
|
|
|
// 库类型:hlw = 互联网医院药品库(drug-info),online = 配送/线上药品库(online-drug-info)
|
|
|
|
|
|
libType: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: 'hlw'
|
|
|
|
|
|
},
|
|
|
|
|
|
data: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () => ({})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
const { memberInfo } = storeToRefs(memberStore());
|
|
|
|
|
|
|
|
|
|
|
|
const basicInfo = [
|
|
|
|
|
|
{ label: '药品名称', prop: "name" },
|
|
|
|
|
|
{ label: '药品规格', prop: "specification" },
|
|
|
|
|
|
{ label: '包装单位', prop: "unit" },
|
|
|
|
|
|
{ label: '包装量', prop: "package_amount" },
|
|
|
|
|
|
{ label: '生产厂家', prop: "manufacturer" },
|
|
|
|
|
|
{ label: '商品条形码', prop: "barcode" },
|
|
|
|
|
|
{ label: '医保编码', prop: "insurance_code" },
|
|
|
|
|
|
{ label: '药品类型', prop: "category" },
|
|
|
|
|
|
{ label: '药品拼音码', prop: "pinyin_code" },
|
|
|
|
|
|
{ label: '通用名', prop: "generic_name" },
|
|
|
|
|
|
{ label: '通用名拼音码', prop: "generic_pinyin" },
|
2026-08-12 15:20:29 +08:00
|
|
|
|
{ label: '抗生素级别', prop: "antibiotic_level", format: value => antibioticLevelMap[value] ?? '0 (无)' },
|
|
|
|
|
|
{ label: '肿瘤使用', prop: "tumor_use", format: value => tumorUseMap[value] ?? '0 (无)' },
|
2026-07-27 11:32:24 +08:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 在线药品库扩展展示字段:管理分类 + 剂型
|
|
|
|
|
|
const onlineBasicInfoExtra = [
|
|
|
|
|
|
{ label: '管理分类', prop: "manage_category" },
|
|
|
|
|
|
{ label: '剂型', prop: "dosage_form" },
|
|
|
|
|
|
{ label: 'hisID', prop: "his_drug_code" },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const basicInfoList = computed(() => {
|
2026-07-30 16:49:15 +08:00
|
|
|
|
const idField = props.libType === 'online'
|
|
|
|
|
|
? { label: '药库货品ID', prop: "product_id" }
|
|
|
|
|
|
: { label: 'ERP ID', prop: "erpId" };
|
|
|
|
|
|
const fields = [...basicInfo.slice(0, 9), idField, ...basicInfo.slice(9)];
|
|
|
|
|
|
return props.libType === 'online' ? [...fields, ...onlineBasicInfoExtra] : fields;
|
2026-07-27 11:32:24 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const usageInfo = [
|
|
|
|
|
|
{ label: '单次标准剂量', prop: "dosage" },
|
|
|
|
|
|
{ label: '剂量单位', prop: "dosage_unit" },
|
|
|
|
|
|
{ label: '给药标准频次', prop: "frequency" },
|
|
|
|
|
|
{ label: '给药标准方式', prop: "administration_method" },
|
|
|
|
|
|
{ label: '用药标准天数', prop: "days" },
|
|
|
|
|
|
{ label: '发药标准数量', prop: "recommended_quantity" },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const maintenanceInfo = [
|
|
|
|
|
|
{ label: '平台ID', prop: "_id" },
|
|
|
|
|
|
{ label: '当前状态', prop: "onSale" },
|
|
|
|
|
|
{ label: '创建时间', prop: "createTime" },
|
|
|
|
|
|
{ label: '创建人', prop: "creator" },
|
|
|
|
|
|
{ label: '最近更新时间', prop: "updateTime" },
|
|
|
|
|
|
{ label: '最近更新人', prop: "updater" },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const otherInfo = [
|
|
|
|
|
|
{ label: '适用性别', prop: 'suitableGender' },
|
|
|
|
|
|
{ label: '是否儿童用药', prop: 'suitableChild' },
|
|
|
|
|
|
{ label: '重复用药标识', prop: 'repeatTag' },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
2026-07-30 16:49:15 +08:00
|
|
|
|
const requiredMap = computed(() => getDrugFieldDict(props.libType).reduce((m, cur) => {
|
2026-07-27 11:32:24 +08:00
|
|
|
|
m[cur.field] = typeof cur.required === 'boolean' ? cur.required : false
|
|
|
|
|
|
return m
|
2026-07-30 16:49:15 +08:00
|
|
|
|
}, {}))
|
2026-07-27 11:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
const showForm = computed(() => {
|
|
|
|
|
|
const genderMap = { common: '通用', male: '男', female: '女' };
|
|
|
|
|
|
|
|
|
|
|
|
let repeatTag = props.data.repeatTag;
|
|
|
|
|
|
if (Array.isArray(repeatTag)) {
|
|
|
|
|
|
repeatTag = repeatTag.join(',');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
onSale: props.data.onSale ? '已上架' : '已下架',
|
|
|
|
|
|
createTime: props.data.createTime && dayjs(props.data.createTime).isValid() ? dayjs(props.data.createTime).format('YYYY-MM-DD HH:mm:ss') : '',
|
|
|
|
|
|
updateTime: props.data.updateTime && dayjs(props.data.updateTime).isValid() ? dayjs(props.data.updateTime).format('YYYY-MM-DD HH:mm:ss') : '',
|
|
|
|
|
|
// 如果存在描述文本,则认为为“是”,并展示描述;否则为“否”。保持与编辑抽屉的语义一致。
|
|
|
|
|
|
need_medication_proof: props.data.medication_proof_desc ? `是(${props.data.medication_proof_desc})` : '否',
|
|
|
|
|
|
has_usage_restriction: props.data.usage_restriction_desc ? `是(${props.data.usage_restriction_desc})` : '否',
|
|
|
|
|
|
suitableGender: genderMap[props.data.suitableGender] || props.data.suitableGender,
|
|
|
|
|
|
suitableChild: props.data.suitableChild ? '是' : '否',
|
|
|
|
|
|
repeatTag,
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
const form = computed(() => ({ ...props.data, ...showForm.value }))
|
|
|
|
|
|
const loading = ref(false)
|
2026-09-20 17:32:57 +08:00
|
|
|
|
const syncingHis = ref(false)
|
2026-08-06 16:33:15 +08:00
|
|
|
|
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;
|
2026-08-25 13:38:54 +08:00
|
|
|
|
const codes = Array.isArray(props.data.applicableDiagnosisCodes)
|
|
|
|
|
|
? props.data.applicableDiagnosisCodes.map(code => String(code))
|
2026-08-06 16:33:15 +08:00
|
|
|
|
: [];
|
|
|
|
|
|
diagnosisList.value = [];
|
2026-08-25 13:38:54 +08:00
|
|
|
|
if (!codes.length) return;
|
2026-08-06 16:33:15 +08:00
|
|
|
|
diagnosisLoading.value = true;
|
2026-08-25 13:38:54 +08:00
|
|
|
|
const res = await getHlwDiagnosisList({ codes, page: 1, pageSize: Math.max(30, codes.length) });
|
2026-08-06 16:33:15 +08:00
|
|
|
|
if (requestId !== diagnosisRequestId) return;
|
|
|
|
|
|
diagnosisLoading.value = false;
|
|
|
|
|
|
if (res.success) {
|
|
|
|
|
|
const list = res.data && Array.isArray(res.data.data) ? res.data.data : [];
|
2026-08-25 13:38:54 +08:00
|
|
|
|
const diagnosisMap = new Map(list.map(item => [String(item.code), item]));
|
|
|
|
|
|
diagnosisList.value = codes.map(code => diagnosisMap.get(code)).filter(Boolean);
|
2026-08-06 16:33:15 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
ElMessage.error(res.message || '适应诊断加载失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-27 11:32:24 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function close() {
|
|
|
|
|
|
if (loading.value) return;
|
|
|
|
|
|
emits('close', false)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function edit() {
|
|
|
|
|
|
if (loading.value) return;
|
|
|
|
|
|
close()
|
|
|
|
|
|
emits('edit')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function setOnSale() {
|
|
|
|
|
|
await ElMessageBox.confirm(`确定${props.data.onSale ? '下架' : '上架'}药品吗?`, '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning',
|
|
|
|
|
|
});
|
|
|
|
|
|
const onSale = !props.data.onSale;
|
|
|
|
|
|
const isOnline = props.libType === 'online';
|
|
|
|
|
|
const { success, message } = await (isOnline
|
|
|
|
|
|
? setOnlineDrugOnSale({
|
|
|
|
|
|
ids: [props.data._id],
|
|
|
|
|
|
onSale,
|
|
|
|
|
|
updater: memberInfo.value.anotherName,
|
|
|
|
|
|
updaterId: memberInfo.value._id,
|
|
|
|
|
|
})
|
|
|
|
|
|
: setHlwDrugOnSale({
|
|
|
|
|
|
ids: [props.data._id],
|
|
|
|
|
|
onSale,
|
|
|
|
|
|
updater: memberInfo.value.anotherName,
|
|
|
|
|
|
updaterId: memberInfo.value._id,
|
|
|
|
|
|
}));
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
ElMessage.success(message);
|
|
|
|
|
|
emits('changeSale', onSale)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ElMessage.error(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-20 17:32:57 +08:00
|
|
|
|
async function syncHis() {
|
|
|
|
|
|
if (!props.data._id || syncingHis.value) return;
|
|
|
|
|
|
syncingHis.value = true;
|
|
|
|
|
|
try {
|
|
|
|
|
|
const { success, message } = await updateHisDrugInfo({ id: props.data._id });
|
|
|
|
|
|
if (success) {
|
|
|
|
|
|
ElMessage.success(message || '同步 HIS 成功');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
ElMessage.error(message || '同步 HIS 失败');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
ElMessage.error(error?.message || '同步 HIS 失败');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
syncingHis.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-06 16:33:15 +08:00
|
|
|
|
watch(() => props.visible, (visible) => {
|
|
|
|
|
|
if (visible) {
|
|
|
|
|
|
loadDiagnoses();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
diagnosisRequestId += 1;
|
|
|
|
|
|
diagnosisLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-27 11:32:24 +08:00
|
|
|
|
</script>
|