diff --git a/apps/web-antd/src/store/prescription.ts b/apps/web-antd/src/store/prescription.ts index 8fcfc114..b542602f 100644 --- a/apps/web-antd/src/store/prescription.ts +++ b/apps/web-antd/src/store/prescription.ts @@ -11,6 +11,7 @@ import { addWestPrescription, checkChineseMedicineConflictApi, getCurrentStoreTypeApi, + getPrescriptionTypeOptionsApi, getDrugUseList, getMyStoreListApi, getPatientItem, @@ -54,6 +55,16 @@ export const usePrescriptionStore = defineStore('prescription', () => { // 处方状态 const myStoreId = ref(0); const activeCategory = ref(2); + /** 后端下发的处方类型列表(含可选 icon / icon_text) */ + const categories = ref([ + { label: '中药', value: 1, icon: '', icon_text: '' }, + { label: '西(中成)药', value: 2, icon: '', icon_text: '' }, + { label: '保健食品', value: 3, icon: '', icon_text: '' }, + { label: '产品服务包', value: 5, icon: '', icon_text: '' }, + { label: '非药品', value: 6, icon: '', icon_text: '' }, + { label: '医疗器械', value: 7, icon: '', icon_text: '' }, + ]); + const prescriptionTypeDefault = ref(2); const diagnosis = ref(''); const medicalAdvice = ref(''); const treatmentPrice = ref(0); @@ -192,6 +203,33 @@ export const usePrescriptionStore = defineStore('prescription', () => { return calcItemMarginPercent(drug.price, drug.buy_price); }; + /** + * 拉取后端处方类型列表与默认选中 + */ + const loadPrescriptionTypeOptions = async (registerId?: number | string) => { + try { + const params: { register_id?: number; store_id?: number } = {}; + const rid = Number(registerId || currentRegisterId.value); + if (rid) params.register_id = rid; + if (myStoreId.value) params.store_id = myStoreId.value; + const res = await getPrescriptionTypeOptionsApi(params); + const list = Array.isArray(res?.list) ? res.list : []; + if (list.length) { + categories.value = list.map((item) => ({ + value: Number(item.value), + label: item.label || '', + icon: item.icon || '', + icon_text: item.icon_text || '', + })); + } + const def = Number(res?.default); + if (def) prescriptionTypeDefault.value = def; + } catch (error) { + console.error('加载处方类型失败:', error); + message.warning('处方类型加载失败,请稍后重试'); + } + }; + // localStorage同步方法 const syncToLocalStorage = () => { try { @@ -260,6 +298,12 @@ export const usePrescriptionStore = defineStore('prescription', () => { // 设置当前注册ID currentRegisterId.value = registerId; + // 先拉类型选项,再决定默认 Tab(无本地记忆时用后端 default) + if (!isInitialized.value) { + await initializeBasicData(); + } + await loadPrescriptionTypeOptions(registerId); + // 恢复之前保存的 activeCategory const savedCategory = localStorage.getItem( `${storagePrefix.value}activeCategory${registerId}` @@ -278,17 +322,14 @@ export const usePrescriptionStore = defineStore('prescription', () => { activeCategory.value = 1; } else if (westData && JSON.parse(westData).length > 0) { activeCategory.value = 2; + } else if (prescriptionTypeDefault.value) { + activeCategory.value = prescriptionTypeDefault.value; } } // 加载localStorage数据 loadFromLocalStorage(); - // 如果基础数据未初始化,先初始化基础数据 - if (!isInitialized.value) { - await initializeBasicData(); - } - await fetchStoreSeeRate(); // 获取患者信息(只在需要时调用,如 PrescriptionModal) @@ -1113,6 +1154,11 @@ export const usePrescriptionStore = defineStore('prescription', () => { } else { currentDrugs.value.splice(0, currentDrugs.value.length); } + // 切换后聚焦 Tab 栏当前项 + nextTick(() => { + const el = document.getElementById(`rx-modal-tab-${categoryValue}`); + el?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); + }); }; // 工具函数 @@ -1152,6 +1198,8 @@ export const usePrescriptionStore = defineStore('prescription', () => { myStoreList, myStoreId, activeCategory, + categories, + prescriptionTypeDefault, diagnosis, medicalAdvice, treatmentPrice, @@ -1188,6 +1236,7 @@ export const usePrescriptionStore = defineStore('prescription', () => { initializePrescription, initializeForModal, initializeBasicData, + loadPrescriptionTypeOptions, resetInitializationState, getDrugList, addProducts, diff --git a/apps/web-antd/src/views/business/chat/components/PrescriptionModal.vue b/apps/web-antd/src/views/business/chat/components/PrescriptionModal.vue index 17b430a1..520a5379 100644 --- a/apps/web-antd/src/views/business/chat/components/PrescriptionModal.vue +++ b/apps/web-antd/src/views/business/chat/components/PrescriptionModal.vue @@ -77,18 +77,15 @@ const commonPrescriptionName = ref(''); /** 是否正在保存常用方 */ const isSavingCommonPrescription = ref(false); -const categories = [ - { label: '中药', value: 1 }, - { label: '中成(西)药', value: 2 }, - { label: '保健食品', value: 3 }, - { label: '产品服务包', value: 5 }, - { label: '非药品', value: 6 }, - { label: '医疗器械', value: 7 }, -]; - // 使用 Pinia store const prescriptionStore = usePrescriptionStore(); +/** 处方类型来自后端下发(store.categories) */ +const categories = computed(() => prescriptionStore.categories); + +const rxSwipeStartX = ref(0); +const rxSwipeStartY = ref(0); + const allowInsuranceCategory = computed( () => Number(prescriptionStore.registerStoreInfo?.allow_insurance_category ?? 0) === 1, ); @@ -133,6 +130,8 @@ const [Modal, modalApi] = useVbenModal({ storagePrefix, ); prescriptionStore.loadFromLocalStorage(); + // 开方弹窗打开时再拉一次类型,确保 Network 可见且数据最新 + await prescriptionStore.loadPrescriptionTypeOptions(modalData.value.registerId); // 获取挂号诊所信息 await prescriptionStore.fetchRegisterStoreInfo(); @@ -446,6 +445,26 @@ const tabChange = async (id: number) => { } }; +/** 记录左右滑起点 */ +const onRxPanelPointerDown = (e: PointerEvent) => { + rxSwipeStartX.value = e.clientX; + rxSwipeStartY.value = e.clientY; +}; + +/** 左右滑切换相邻处方类型 */ +const onRxPanelPointerUp = (e: PointerEvent) => { + const dx = e.clientX - rxSwipeStartX.value; + const dy = e.clientY - rxSwipeStartY.value; + if (Math.abs(dx) < 80 || Math.abs(dx) <= Math.abs(dy)) return; + const list = categories.value || []; + if (list.length < 2) return; + const idx = list.findIndex((c) => Number(c.value) === Number(prescriptionStore.activeCategory)); + if (idx < 0) return; + const nextIdx = dx < 0 ? idx + 1 : idx - 1; + if (nextIdx < 0 || nextIdx >= list.length) return; + tabChange(list[nextIdx].value); +}; + /** * 检测并显示转诊提示 * @description 当切换到中药处方时,如果当前诊所为西医诊所,显示转诊提示 @@ -1021,16 +1040,30 @@ const cancelSaveCommonPrescription = () => {
+
+
@@ -1067,6 +1100,13 @@ const cancelSaveCommonPrescription = () => {
+ +
+ 已选 {{ prescriptionStore.currentDrugs.length }} 种药 +
{
+ @@ -1697,6 +1738,8 @@ const cancelSaveCommonPrescription = () => { display: flex; gap: 1rem; /* margin: 1rem 0; 已在类中通过 sticky 处理 */ + overflow-x: auto; + flex-wrap: nowrap; } .drug-categories button { @@ -1707,6 +1750,11 @@ const cancelSaveCommonPrescription = () => { background: transparent; color: inherit; transition: all 0.3s; + display: inline-flex; + align-items: center; + gap: 6px; + flex-shrink: 0; + white-space: nowrap; } .dark .drug-categories button { @@ -1720,6 +1768,25 @@ const cancelSaveCommonPrescription = () => { background: #455cda; } +.tab-type-icon { + width: 16px; + height: 16px; + object-fit: contain; +} + +.tab-type-badge { + font-size: 11px; + color: #e6a23c; + background: #fdf6ec; + padding: 0 6px; + border-radius: 4px; + line-height: 1.4; +} + +.rx-swipe-panel { + touch-action: pan-y; +} + .selected-drugs { margin: 1rem 0; border: 1px solid #eee; diff --git a/apps/web-antd/src/views/business/express/express-company/components/modal.vue b/apps/web-antd/src/views/business/express/express-company/components/modal.vue index b979ff65..509e74c9 100644 --- a/apps/web-antd/src/views/business/express/express-company/components/modal.vue +++ b/apps/web-antd/src/views/business/express/express-company/components/modal.vue @@ -57,7 +57,7 @@ const [Modal, modalApi] = useVbenModal({ }); diff --git a/apps/web-antd/src/views/business/order/prescription/components/source.vue b/apps/web-antd/src/views/business/order/prescription/components/source.vue index a05c8d9b..2cc55f84 100644 --- a/apps/web-antd/src/views/business/order/prescription/components/source.vue +++ b/apps/web-antd/src/views/business/order/prescription/components/source.vue @@ -37,7 +37,7 @@ const [Modal, modalApi] = useVbenModal({ diff --git a/apps/web-antd/src/views/business/order/product-order/api/index.ts b/apps/web-antd/src/views/business/order/product-order/api/index.ts index f45b2456..af56d78d 100644 --- a/apps/web-antd/src/views/business/order/product-order/api/index.ts +++ b/apps/web-antd/src/views/business/order/product-order/api/index.ts @@ -79,6 +79,18 @@ export async function expressDetailByOrderId(data: Record) { return requestClient.post(`express-detail/detail-by-order`, data); } +/** + * 修改已发货运单:快递单号 / 查件手机号 / 快递公司 + */ +export async function updateExpressNosApi(data: { + express_no_id: number; + express_no?: string; + mobile?: string; + express_company_code?: string; +}) { + return requestClient.post(`express-detail/update-express-nos`, data); +} + /** * 超管:将历史订单级运单同步到分包裹 shipment */ diff --git a/apps/web-antd/src/views/business/order/product-order/components/detail.vue b/apps/web-antd/src/views/business/order/product-order/components/detail.vue index 1fd244e1..b7d47b72 100644 --- a/apps/web-antd/src/views/business/order/product-order/components/detail.vue +++ b/apps/web-antd/src/views/business/order/product-order/components/detail.vue @@ -188,6 +188,20 @@ function packageTabLabel(pkg: Record, idx: number): string { return name ? `包裹${no}(${name})` : `包裹${no}`; } +/** + * 收件人姓名脱敏:保留首字,其余用 *(与物流动态弹窗一致) + */ +function maskExpressName(name: unknown): string { + const str = String(name || '').trim(); + if (!str) { + return '-'; + } + if (str.length === 1) { + return `${str}*`; + } + return str.slice(0, 1) + '*'.repeat(str.length - 1); +} + /** * 打开独立物流动态弹窗(完整轨迹) */ @@ -249,7 +263,7 @@ function prescriptionStatusColor() { } + + + +
+ + + + + + + + + +
+