fix:问题修复
This commit is contained in:
parent
5b316c61b0
commit
8138458b3b
@ -350,6 +350,14 @@ export async function importHlwDrugInfo(data) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getHlwSchedule(data) {
|
||||||
|
return await useHlw({ type: 'getHlwSchedule', ...data });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveHlwSchedule(data) {
|
||||||
|
return await useHlw({ type: 'saveHlwSchedule', ...data });
|
||||||
|
}
|
||||||
|
|
||||||
// 将互联网医院药品信息同步至 HIS
|
// 将互联网医院药品信息同步至 HIS
|
||||||
export async function updateHisDrugInfo(data) {
|
export async function updateHisDrugInfo(data) {
|
||||||
return await useHlw({
|
return await useHlw({
|
||||||
|
|||||||
@ -47,10 +47,10 @@
|
|||||||
<div class="w-3 border-t border-gray-500 border-dashed"></div>
|
<div class="w-3 border-t border-gray-500 border-dashed"></div>
|
||||||
<div class="rp-symbol mx-2">Rp</div>
|
<div class="rp-symbol mx-2">Rp</div>
|
||||||
<div class="flex-grow border-t border-gray-500 border-dashed"></div>
|
<div class="flex-grow border-t border-gray-500 border-dashed"></div>
|
||||||
<!-- <div class="absolute right-0 -top-2">
|
<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>
|
<!-- <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="">
|
<img class="w-30 h-30 mt-3" :src="stampPng" alt="">
|
||||||
</div> -->
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="medicine-list">
|
<div class="medicine-list">
|
||||||
<div v-for="(i, idx) in drugList" :key="i.insurance_code" class="medicine-item">
|
<div v-for="(i, idx) in drugList" :key="i.insurance_code" class="medicine-item">
|
||||||
@ -76,17 +76,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex pt-3">
|
<div class="flex pt-3">
|
||||||
|
<div class="flex-shrink-0 w-1/3">
|
||||||
|
审核:
|
||||||
|
</div>
|
||||||
<div class="flex-shrink-0 w-1/3">
|
<div class="flex-shrink-0 w-1/3">
|
||||||
调配:
|
调配:
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-shrink-0 w-1/3">
|
<div class="flex-shrink-0 w-1/3">
|
||||||
核对:
|
核对/发药:
|
||||||
</div>
|
|
||||||
<div class="flex-shrink-0 w-1/3">
|
|
||||||
发药:
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="notice">注意:请勿丢失,处方当日有效,因特殊情况,该处方有效为<span class="write_line"></span> 天(签名:<span class="write_line"></span> )</div>
|
<div class="footer-notice footer-notice--first leading-normal text-sm">1.根据《处方管理办法》规定,本处方开具三天有效;</div>
|
||||||
|
<div class="footer-notice leading-normal text-sm">2.本处方仅供患者{{ order?.drugStoreName||'' }}使用,本处方金额以实际药店成交金额为准;</div>
|
||||||
|
<div class="footer-notice leading-normal text-sm">3.请仔细阅读药品使用说明,服药谨遵医嘱,如有不适请立即停药并前往医院就诊。</div>
|
||||||
|
<!-- <div class="notice">注意:请勿丢失,处方当日有效,因特殊情况,该处方有效为<span class="write_line"></span> 天(签名:<span class="write_line"></span> )</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</layout-main>
|
</layout-main>
|
||||||
@ -114,7 +117,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, watch } from 'vue';
|
import { computed, nextTick, ref, watch } from 'vue';
|
||||||
import { getSealImage } from '@/api/hlw';
|
import { getConsultOrder, getSealImage } from '@/api/hlw';
|
||||||
import validate from "@/utils/validate";
|
import validate from "@/utils/validate";
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@ -141,6 +144,9 @@ const printPaperSize = ref('A5')
|
|||||||
const printing = ref(false)
|
const printing = ref(false)
|
||||||
const doctorCode = ref('')
|
const doctorCode = ref('')
|
||||||
const pharmacistNo = ref('')
|
const pharmacistNo = ref('')
|
||||||
|
const order = ref(null);
|
||||||
|
let orderLoadingId = '';
|
||||||
|
let orderLoadingPromise = null;
|
||||||
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 diagnosisStr = computed(() => diagnosis.value.map((item) => item.name).join('+'))
|
||||||
const drugList = computed(() => {
|
const drugList = computed(() => {
|
||||||
@ -211,8 +217,65 @@ function createPrintPageStyle(paperSize) {
|
|||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function currentPrescriptionOrderId() {
|
||||||
|
return typeof props.data?.orderId === 'string' ? props.data.orderId.trim() : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureOrder() {
|
||||||
|
const prescriptionOrderId = currentPrescriptionOrderId();
|
||||||
|
if (!prescriptionOrderId) {
|
||||||
|
order.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 避免复用上一张处方的订单信息,导致打印出错误的门店名称。
|
||||||
|
if (order.value?.orderId === prescriptionOrderId) return;
|
||||||
|
|
||||||
|
// 抽屉打开后立即查询;若紧接着点击打印,则复用同一次请求。
|
||||||
|
if (orderLoadingId === prescriptionOrderId && orderLoadingPromise) {
|
||||||
|
return orderLoadingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
order.value = null;
|
||||||
|
orderLoadingId = prescriptionOrderId;
|
||||||
|
orderLoadingPromise = getConsultOrder({ orderId: prescriptionOrderId, page: 1, pageSize: 1 })
|
||||||
|
.then((res) => {
|
||||||
|
const payload = res?.data?.data || res?.data || [];
|
||||||
|
const orders = Array.isArray(payload) ? payload : [];
|
||||||
|
// 请求期间若已切换处方,不允许旧请求覆盖当前处方的店铺名称。
|
||||||
|
if (props.visible && currentPrescriptionOrderId() === prescriptionOrderId) {
|
||||||
|
order.value = orders.find((item) => item?.orderId === prescriptionOrderId) || null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// 查询失败时保留空名称,展示与打印流程均不被阻断。
|
||||||
|
if (props.visible && currentPrescriptionOrderId() === prescriptionOrderId) order.value = null;
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
if (orderLoadingId === prescriptionOrderId) {
|
||||||
|
orderLoadingId = '';
|
||||||
|
orderLoadingPromise = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return orderLoadingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.visible, props.data?.orderId],
|
||||||
|
([visible]) => {
|
||||||
|
if (visible) {
|
||||||
|
void ensureOrder();
|
||||||
|
} else {
|
||||||
|
order.value = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
|
||||||
async function print(paperSize = 'A5') {
|
async function print(paperSize = 'A5') {
|
||||||
if (!paperRef.value) return;
|
if (!paperRef.value) return;
|
||||||
|
await ensureOrder();
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
|
||||||
// Element Plus 抽屉带有定位、变换和裁剪样式,直接打印抽屉内节点会得到空白页。
|
// Element Plus 抽屉带有定位、变换和裁剪样式,直接打印抽屉内节点会得到空白页。
|
||||||
@ -412,15 +475,18 @@ defineExpose({ print: openPrintDialog });
|
|||||||
.no {
|
.no {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.write_line {
|
.write_line {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.write_line+.write_line {
|
.write_line+.write_line {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.write_line::after {
|
.write_line::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -429,7 +495,6 @@ defineExpose({ print: openPrintDialog });
|
|||||||
bottom: -4px;
|
bottom: -4px;
|
||||||
border-bottom: 1px solid currentColor;
|
border-bottom: 1px solid currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.rx-print-root {
|
.rx-print-root {
|
||||||
@ -456,17 +521,20 @@ defineExpose({ print: openPrintDialog });
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-direction: column !important;
|
flex-direction: column !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
min-height: 281mm !important;
|
height: 281mm !important;
|
||||||
|
max-height: 281mm !important;
|
||||||
box-sizing: border-box !important;
|
box-sizing: border-box !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
overflow: hidden !important;
|
||||||
/* 打印时提高可读性,同时收紧行距,避免放大字体后版面变松散。 */
|
/* 打印时提高可读性,同时收紧行距,避免放大字体后版面变松散。 */
|
||||||
font-size: 15px !important;
|
font-size: 14px !important;
|
||||||
line-height: 1.28 !important;
|
line-height: 1.28 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root--a5 .rx-print-area {
|
body.printing-rx>.rx-print-root--a5 .rx-print-area {
|
||||||
min-height: 194mm !important;
|
height: 194mm !important;
|
||||||
|
max-height: 194mm !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .header {
|
body.printing-rx>.rx-print-root .header {
|
||||||
@ -480,7 +548,7 @@ defineExpose({ print: openPrintDialog });
|
|||||||
|
|
||||||
body.printing-rx>.rx-print-root .hospital {
|
body.printing-rx>.rx-print-root .hospital {
|
||||||
margin: 3px 0 !important;
|
margin: 3px 0 !important;
|
||||||
font-size: 18px !important;
|
font-size: 17px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .patient-info,
|
body.printing-rx>.rx-print-root .patient-info,
|
||||||
@ -498,7 +566,7 @@ defineExpose({ print: openPrintDialog });
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .rp-symbol {
|
body.printing-rx>.rx-print-root .rp-symbol {
|
||||||
font-size: 21px !important;
|
font-size: 20px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .medicine-item {
|
body.printing-rx>.rx-print-root .medicine-item {
|
||||||
@ -510,8 +578,8 @@ defineExpose({ print: openPrintDialog });
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .medicine-section img {
|
body.printing-rx>.rx-print-root .medicine-section img {
|
||||||
width: 86px !important;
|
width: 120px !important;
|
||||||
height: 86px !important;
|
height: 120px !important;
|
||||||
margin-top: 2px !important;
|
margin-top: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,6 +592,17 @@ defineExpose({ print: openPrintDialog });
|
|||||||
padding-top: 4px !important;
|
padding-top: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 页尾三条使用说明比正文再小一级,并优先保持单行展示。 */
|
||||||
|
body.printing-rx>.rx-print-root .footer-notice {
|
||||||
|
font-size: 12px !important;
|
||||||
|
line-height: 1.25 !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.printing-rx>.rx-print-root .footer-notice--first {
|
||||||
|
margin-top: 7px !important;
|
||||||
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root .signImage {
|
body.printing-rx>.rx-print-root .signImage {
|
||||||
width: 76px !important;
|
width: 76px !important;
|
||||||
height: 30px !important;
|
height: 30px !important;
|
||||||
@ -543,7 +622,11 @@ defineExpose({ print: openPrintDialog });
|
|||||||
margin-bottom: 4px !important;
|
margin-bottom: 4px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root--a5 .medicine-section img,
|
body.printing-rx>.rx-print-root--a5 .medicine-section img {
|
||||||
|
width: 120px !important;
|
||||||
|
height: 120px !important;
|
||||||
|
}
|
||||||
|
|
||||||
body.printing-rx>.rx-print-root--dense .medicine-section img {
|
body.printing-rx>.rx-print-root--dense .medicine-section img {
|
||||||
width: 72px !important;
|
width: 72px !important;
|
||||||
height: 72px !important;
|
height: 72px !important;
|
||||||
|
|||||||
@ -255,4 +255,13 @@ export default [
|
|||||||
title: "配送药品库",
|
title: "配送药品库",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "HNHLWSCHEDULE",
|
||||||
|
path: "/HNHLWSCHEDULE",
|
||||||
|
component: () => import("../views/hlw-schedule/hlw-schedule"),
|
||||||
|
meta: {
|
||||||
|
keepAlive: false,
|
||||||
|
title: "互联网医院排班",
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export const drugFieldDict = [
|
|||||||
// 放宽分类校验,允许任意字符串;前端选择仍可用固定下拉,导入/编辑不会因为新分类报错
|
// 放宽分类校验,允许任意字符串;前端选择仍可用固定下拉,导入/编辑不会因为新分类报错
|
||||||
{ name: '药品分类', field: 'category', verify: (val, label) => verifyString(val, label) },
|
{ name: '药品分类', field: 'category', verify: (val, label) => verifyString(val, label) },
|
||||||
{ name: '药品拼音码', field: 'pinyin_code', verify: (val, label) => verifyString(val, label) },
|
{ name: '药品拼音码', field: 'pinyin_code', verify: (val, label) => verifyString(val, label) },
|
||||||
{ name: 'ERP ID', field: 'erpId', required: true, libType: 'hlw', verify: (val, label) => verifyString(val, label, 1) },
|
{ name: 'ERP ID', field: 'erpId', libType: 'hlw', verify: (val, label) => verifyString(val, label) },
|
||||||
{ name: '药库货品ID', field: 'product_id', libType: 'online', verify: (val, label) => validateNumber(val, label) },
|
{ name: '药库货品ID', field: 'product_id', libType: 'online', verify: (val, label) => validateNumber(val, label) },
|
||||||
{ name: '通用名', field: 'generic_name', verify: (val, label) => verifyString(val, label) },
|
{ name: '通用名', field: 'generic_name', verify: (val, label) => verifyString(val, label) },
|
||||||
{ name: '通用名拼音码', field: 'generic_pinyin', verify: (val, label) => verifyString(val, label) },
|
{ name: '通用名拼音码', field: 'generic_pinyin', verify: (val, label) => verifyString(val, label) },
|
||||||
|
|||||||
@ -15,7 +15,9 @@
|
|||||||
:label="getOptionLabel(option)" :value="getOptionValue(option)" />
|
:label="getOptionLabel(option)" :value="getOptionValue(option)" />
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-input v-else clearable :model-value="form[item.prop]" :maxlength="item.maxlength"
|
<el-input v-else clearable :model-value="form[item.prop]" :maxlength="item.maxlength"
|
||||||
:placeholder="`请输入${item.label}`" @update:model-value="changeValue($event, item.prop)" />
|
:placeholder="item.placeholder || `请输入${item.label}`"
|
||||||
|
:readonly="item.readonly"
|
||||||
|
@update:model-value="changeValue($event, item.prop)" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -176,7 +178,7 @@ const onlineBasicInfoExtra = [
|
|||||||
const basicInfoList = computed(() => {
|
const basicInfoList = computed(() => {
|
||||||
const idField = props.libType === 'online'
|
const idField = props.libType === 'online'
|
||||||
? { label: '药库货品ID', prop: "product_id", maxlength: 100 }
|
? { label: '药库货品ID', prop: "product_id", maxlength: 100 }
|
||||||
: { label: 'ERP ID', prop: "erpId", maxlength: 100 };
|
: { label: 'ERP ID', readonly: true, prop: "erpId", maxlength: 100, placeholder: '系统自动生成' };
|
||||||
const fields = [...basicInfo.value.slice(0, 9), idField, ...basicInfo.value.slice(9)];
|
const fields = [...basicInfo.value.slice(0, 9), idField, ...basicInfo.value.slice(9)];
|
||||||
return props.libType === 'online' ? [...fields, ...onlineBasicInfoExtra] : fields;
|
return props.libType === 'online' ? [...fields, ...onlineBasicInfoExtra] : fields;
|
||||||
})
|
})
|
||||||
|
|||||||
180
src/views/hlw-schedule/hlw-schedule.vue
Normal file
180
src/views/hlw-schedule/hlw-schedule.vue
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading" class="schedule-page">
|
||||||
|
<el-calendar v-model="calendarDate">
|
||||||
|
<template #header>
|
||||||
|
<div class="calendar-header">
|
||||||
|
<el-button text @click="changeMonth(-1)">‹ 上个月</el-button>
|
||||||
|
<div class="calendar-title">{{ currentMonthLabel }}</div>
|
||||||
|
<div>
|
||||||
|
<el-button text @click="goToday">本月</el-button>
|
||||||
|
<el-button text @click="changeMonth(1)">下个月 ›</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #date-cell="{ data }">
|
||||||
|
<div class="schedule-cell" :class="{ 'is-other-month': data.type !== 'current-month', 'is-past': isPastDate(data.day) }"
|
||||||
|
@click="openSchedule(data)">
|
||||||
|
<div class="schedule-day">{{ dayNumber(data.day) }}</div>
|
||||||
|
<template v-if="data.type === 'current-month'">
|
||||||
|
<div v-if="scheduleByDate[data.day]?.doctors?.length" class="schedule-line doctor-line">
|
||||||
|
<span class="role-label">医生</span>
|
||||||
|
<el-tooltip :content="peopleNames(scheduleByDate[data.day].doctors)" placement="top" :show-after="300">
|
||||||
|
<span class="role-names">{{ displayPeople(scheduleByDate[data.day].doctors) }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
<span class="person-count">{{ scheduleByDate[data.day].doctors.length }}人</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="scheduleByDate[data.day]?.pharmacists?.length" class="schedule-line pharmacist-line">
|
||||||
|
<span class="role-label">药师</span>
|
||||||
|
<el-tooltip :content="peopleNames(scheduleByDate[data.day].pharmacists)" placement="top" :show-after="300">
|
||||||
|
<span class="role-names">{{ displayPeople(scheduleByDate[data.day].pharmacists) }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
<span class="person-count">{{ scheduleByDate[data.day].pharmacists.length }}人</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="!hasSchedule(data.day)" class="empty-schedule">暂无排班</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-calendar>
|
||||||
|
|
||||||
|
<el-dialog v-model="dialogVisible" :title="`${selectedDate} 排班`" width="560px" destroy-on-close>
|
||||||
|
<el-alert v-if="selectedDate && isPastDate(selectedDate)" title="过去日期的排班仅可查看,不能修改。" type="info" :closable="false" class="mb-16px" />
|
||||||
|
<el-form label-width="80px">
|
||||||
|
<el-form-item label="值班医生">
|
||||||
|
<el-select v-model="editingDoctorNos" multiple filterable clearable class="w-full" placeholder="请选择值班医生" :disabled="isSelectedDatePast">
|
||||||
|
<el-option v-for="person in doctors" :key="person.doctorNo" :label="person.doctorName" :value="person.doctorNo" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="值班药师">
|
||||||
|
<el-select v-model="editingPharmacistNos" multiple filterable clearable class="w-full" placeholder="请选择值班药师" :disabled="isSelectedDatePast">
|
||||||
|
<el-option v-for="person in pharmacists" :key="person.doctorNo" :label="person.doctorName" :value="person.doctorNo" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">{{ isSelectedDatePast ? '关闭' : '取消' }}</el-button>
|
||||||
|
<el-button v-if="!isSelectedDatePast" type="primary" :loading="saving" @click="save">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { getAllDoctorNos, getHlwSchedule, saveHlwSchedule } from '@/api/hlw';
|
||||||
|
import { memberStore } from '@/store/member';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
const { memberInfo } = storeToRefs(memberStore());
|
||||||
|
const calendarDate = ref(startOfMonth(new Date()));
|
||||||
|
const loading = ref(false);
|
||||||
|
const saving = ref(false);
|
||||||
|
const scheduleByDate = ref({});
|
||||||
|
const doctors = ref([]);
|
||||||
|
const pharmacists = ref([]);
|
||||||
|
const dialogVisible = ref(false);
|
||||||
|
const selectedDate = ref('');
|
||||||
|
const editingDoctorNos = ref([]);
|
||||||
|
const editingPharmacistNos = ref([]);
|
||||||
|
|
||||||
|
const currentMonth = computed(() => formatMonth(calendarDate.value));
|
||||||
|
const currentMonthLabel = computed(() => `${calendarDate.value.getFullYear()}年${calendarDate.value.getMonth() + 1}月`);
|
||||||
|
const isSelectedDatePast = computed(() => isPastDate(selectedDate.value));
|
||||||
|
|
||||||
|
function startOfMonth(date) { return new Date(date.getFullYear(), date.getMonth(), 1); }
|
||||||
|
function formatMonth(date) { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; }
|
||||||
|
function formatDate(date) { return `${formatMonth(date)}-${String(date.getDate()).padStart(2, '0')}`; }
|
||||||
|
function dayNumber(date) { return Number(date.slice(-2)); }
|
||||||
|
function isPastDate(date) { return Boolean(date) && date < formatDate(new Date()); }
|
||||||
|
function hasSchedule(date) {
|
||||||
|
const schedule = scheduleByDate.value[date];
|
||||||
|
return Boolean(schedule?.doctors?.length || schedule?.pharmacists?.length);
|
||||||
|
}
|
||||||
|
function peopleNames(people) { return people.map(person => person.doctorName).join('、'); }
|
||||||
|
function displayPeople(people) {
|
||||||
|
const names = people.map(person => person.doctorName).filter(Boolean);
|
||||||
|
return names.length > 2 ? `${names.slice(0, 2).join('、')}等` : names.join('、');
|
||||||
|
}
|
||||||
|
function changeMonth(offset) { calendarDate.value = new Date(calendarDate.value.getFullYear(), calendarDate.value.getMonth() + offset, 1); }
|
||||||
|
function goToday() { calendarDate.value = startOfMonth(new Date()); }
|
||||||
|
|
||||||
|
function openSchedule(data) {
|
||||||
|
if (data.type !== 'current-month') return;
|
||||||
|
const schedule = scheduleByDate.value[data.day] || {};
|
||||||
|
selectedDate.value = data.day;
|
||||||
|
editingDoctorNos.value = (schedule.doctors || []).map(person => person.doctorNo);
|
||||||
|
editingPharmacistNos.value = (schedule.pharmacists || []).map(person => person.doctorNo);
|
||||||
|
dialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSchedule() {
|
||||||
|
const corpId = memberInfo.value.corpId;
|
||||||
|
if (!corpId) return;
|
||||||
|
loading.value = true;
|
||||||
|
const res = await getHlwSchedule({ corpId, month: currentMonth.value });
|
||||||
|
loading.value = false;
|
||||||
|
if (!res.success) return ElMessage.error(res.message || '排班加载失败');
|
||||||
|
const schedules = Array.isArray(res.data?.data)
|
||||||
|
? res.data.data
|
||||||
|
: (Array.isArray(res.data) ? res.data : []);
|
||||||
|
scheduleByDate.value = schedules.reduce((map, item) => {
|
||||||
|
map[item.date] = item;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadPeople() {
|
||||||
|
const res = await getAllDoctorNos({ corpId: memberInfo.value.corpId, jobs: ['doctor', 'pharmacist'] });
|
||||||
|
if (!res.success) return ElMessage.error(res.message || '人员加载失败');
|
||||||
|
// useHlw responses wrap the service payload once, so personnel is normally
|
||||||
|
// in res.data.data. Keep the direct-array fallback for callers without it.
|
||||||
|
const people = Array.isArray(res.data?.data)
|
||||||
|
? res.data.data
|
||||||
|
: (Array.isArray(res.data) ? res.data : []);
|
||||||
|
doctors.value = people.filter(person => person.job === 'doctor');
|
||||||
|
pharmacists.value = people.filter(person => person.job === 'pharmacist');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function save() {
|
||||||
|
if (saving.value || isSelectedDatePast.value) return;
|
||||||
|
saving.value = true;
|
||||||
|
const res = await saveHlwSchedule({
|
||||||
|
corpId: memberInfo.value.corpId,
|
||||||
|
date: selectedDate.value,
|
||||||
|
doctorNos: editingDoctorNos.value,
|
||||||
|
pharmacistNos: editingPharmacistNos.value,
|
||||||
|
operator: memberInfo.value.anotherName,
|
||||||
|
operatorId: memberInfo.value._id || memberInfo.value.userid,
|
||||||
|
});
|
||||||
|
saving.value = false;
|
||||||
|
if (!res.success) return ElMessage.error(res.message || '保存排班失败');
|
||||||
|
ElMessage.success(res.message || '排班保存成功');
|
||||||
|
dialogVisible.value = false;
|
||||||
|
loadSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(currentMonth, loadSchedule);
|
||||||
|
onMounted(() => {
|
||||||
|
loadPeople();
|
||||||
|
loadSchedule();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.schedule-page { height: 100%; padding: 16px; overflow: auto; background: var(--el-bg-color-page); }
|
||||||
|
.schedule-page :deep(.el-calendar) { min-width: 900px; background: var(--el-bg-color); }
|
||||||
|
.calendar-header { display: flex; align-items: center; justify-content: space-between; width: 100%; }
|
||||||
|
.calendar-title { font-size: 18px; font-weight: 600; color: var(--el-text-color-primary); }
|
||||||
|
.schedule-page :deep(.el-calendar-day) { height: 118px; padding: 8px; }
|
||||||
|
.schedule-cell { height: 100%; cursor: pointer; }
|
||||||
|
.schedule-cell.is-other-month { cursor: default; opacity: .45; }
|
||||||
|
.schedule-cell.is-past:not(.is-other-month) { background: var(--el-fill-color-lighter); }
|
||||||
|
.schedule-day { margin-bottom: 5px; font-size: 14px; font-weight: 600; }
|
||||||
|
.schedule-line { display: flex; align-items: center; gap: 4px; min-width: 0; margin-top: 5px; font-size: 12px; line-height: 20px; }
|
||||||
|
.role-label { flex-shrink: 0; width: 28px; font-weight: 600; }
|
||||||
|
.doctor-line .role-label { color: var(--el-color-primary); }
|
||||||
|
.pharmacist-line .role-label { color: var(--el-color-success); }
|
||||||
|
.role-names { min-width: 0; flex: 1; overflow: hidden; color: var(--el-text-color-regular); text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.person-count { flex-shrink: 0; padding: 0 4px; border-radius: 8px; background: var(--el-fill-color); color: var(--el-text-color-secondary); font-size: 11px; }
|
||||||
|
.empty-schedule { margin-top: 18px; color: var(--el-text-color-placeholder); font-size: 12px; }
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user