feat: 病历模块优化
This commit is contained in:
@@ -3,15 +3,37 @@
|
||||
<view class="safe-area-inset-bottom page-bg page-flex">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container
|
||||
:if="pageBackGuardShow"
|
||||
:show="pageBackGuardShow"
|
||||
:overlay="false"
|
||||
@leave="onPageBackGuardLeave"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- 导航栏 -->
|
||||
<u-navbar class="navbar rx-flex-fixed" :is-back="true" :title="navbarTitle" :custom-back="boundCustomBack" title-color="#000" background="{ background: '#fff' }">
|
||||
<!-- 导航栏:background 用 data 对象,避免模板字面量在小程序渲染层报 not iterable -->
|
||||
<u-navbar
|
||||
class="navbar rx-flex-fixed"
|
||||
:is-back="true"
|
||||
:title="navbarTitle || '开方'"
|
||||
:custom-back="boundCustomBack"
|
||||
title-color="#000"
|
||||
:background="navbarBackground"
|
||||
>
|
||||
</u-navbar>
|
||||
|
||||
<!-- 进入开方页先校验门店开方种类权限 -->
|
||||
<view v-if="pageLoading" class="rx-permission-loading flex-col flex-ali-center flex-jus-center">
|
||||
<u-loading mode="circle" size="48" color="#6ACDBB"></u-loading>
|
||||
<text class="rx-permission-loading__text m-t-24">正在校验开方权限...</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-else-if="showRxDenied"
|
||||
class="rx-permission-loading flex-col flex-ali-center flex-jus-center"
|
||||
>
|
||||
<text class="rx-permission-loading__text">{{ prescriptionTypeDenyMessage || '当前门店未开通开方权限' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 用 block 替代 template:微信小程序对 template+v-else 大块切换易触发渲染层 not iterable -->
|
||||
<block v-else>
|
||||
<!-- 外层:处方 | 病历(VIP) -->
|
||||
<view v-if="canUseMedicalRecord" class="outer-tabs rx-flex-fixed flex-row">
|
||||
<view
|
||||
@@ -32,7 +54,7 @@
|
||||
|
||||
<!-- 以下处方区与 HEAD 一致:直接挂在 page-flex 下,禁止再包一层 flex 列(会压矮 swiper) -->
|
||||
<!-- 处方类型 Tab:与用户端订单列表一致,u-tabs + 下方 swiper 双向同步 -->
|
||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode" class="tabs-wrap shadow-sm rx-flex-fixed">
|
||||
<view v-if="isRxMainVisible && !isSalespersonTransferMode && prescriptionTabsList.length" class="tabs-wrap shadow-sm rx-flex-fixed">
|
||||
<u-tabs
|
||||
:list="prescriptionTabsList"
|
||||
:is-scroll="true"
|
||||
@@ -44,13 +66,12 @@
|
||||
></u-tabs>
|
||||
</view>
|
||||
|
||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12 rx-flex-fixed">
|
||||
<view v-if="isRxMainVisible && !isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12 rx-flex-fixed">
|
||||
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-show="isRxMainVisible"
|
||||
v-if="!isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
||||
v-if="isRxMainVisible && !isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
||||
class="special-rx-card mx-32 m-t-12 rx-flex-fixed"
|
||||
>
|
||||
<view class="special-rx-card__inner flex-row flex-ali-center">
|
||||
@@ -101,17 +122,17 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center rx-flex-fixed">
|
||||
<view v-if="isRxMainVisible && !isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center rx-flex-fixed">
|
||||
<u-button size="mini" :type="feeCategory === 1 ? 'success' : 'default'" @click="feeCategory = 1">自费</u-button>
|
||||
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
|
||||
</view>
|
||||
|
||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12 rx-flex-fixed">
|
||||
<view v-if="isRxMainVisible && !isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12 rx-flex-fixed">
|
||||
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 诊断:swiper 外固定(与改 VIP 前一致),线下可手输也可点选;在线复诊必须点选 -->
|
||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
|
||||
<!-- 诊断:病历 Tab 必须卸载(微信 v-show 藏不住 fixed/flex 固定块) -->
|
||||
<view v-if="isRxMainVisible && !isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
|
||||
<view class="top_title flex-row flex-ali-center">
|
||||
<image :src="require('@/static/image/js.png')" class="size-32"></image>
|
||||
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
|
||||
@@ -144,9 +165,9 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 滚动内容:swiper 必须是 page-flex 的直接子项,flex:1 + height:0 才能吃满剩余高度 -->
|
||||
<!-- 滚动内容:病历 Tab 用 v-if 卸载,避免微信 v-show 残留 -->
|
||||
<swiper
|
||||
v-show="isRxMainVisible"
|
||||
v-if="isRxMainVisible"
|
||||
class="rx-order-swiper"
|
||||
:current="swiperCurrent"
|
||||
:disable-touch="isSalespersonTransferMode || swiperCategories.length < 2"
|
||||
@@ -319,10 +340,10 @@
|
||||
>先煎/后下请在「选择中药」中设置</text>
|
||||
</view>
|
||||
<view class="flex-row">
|
||||
<template v-if="!isSpecialPrescriptionCartLocked">
|
||||
<block v-if="!isSpecialPrescriptionCartLocked">
|
||||
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
|
||||
<text class="fs-26 color-danger" @click="handleRemoveAllDrugs">清空</text>
|
||||
</template>
|
||||
</block>
|
||||
<text
|
||||
v-else
|
||||
class="fs-26 color-danger"
|
||||
@@ -453,7 +474,7 @@
|
||||
<text class="fs-28 color-title font-bold">本店已关闭推广员查看费用</text>
|
||||
<text class="fs-24 color-sub m-t-8">提交后由接诊医生确认价格</text>
|
||||
</view>
|
||||
<template v-else>
|
||||
<block v-else>
|
||||
<view v-if="!isSalespersonTransferMode" class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||
<text class="fs-28 color-sub">诊疗费</text>
|
||||
<view class="flex-row flex-ali-center">
|
||||
@@ -507,7 +528,7 @@
|
||||
<text class="fs-30 font-bold color-title">合计</text>
|
||||
<text class="fs-48 font-bold color-price">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
</template>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 提示信息(传方模式不展示) -->
|
||||
@@ -521,8 +542,8 @@
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 底部操作栏(fixed,不占 flex;须与 swiper 同为 page-flex 直接子项) -->
|
||||
<view v-show="isRxMainVisible" class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
||||
<!-- 底部操作栏:fixed;病历 Tab 必须 v-if 卸载,微信 v-show 对 fixed 无效会串到病历 -->
|
||||
<view v-if="isRxMainVisible" class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
||||
<view
|
||||
v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate"
|
||||
class="flex-row flex-ali-center"
|
||||
@@ -562,12 +583,14 @@
|
||||
:diagnosis="diagnosisText"
|
||||
:medical-advice="medicalAdvice"
|
||||
:diagnosis-readonly="isOnlineRevisit"
|
||||
:active="isMrMainVisible"
|
||||
@update:diagnosis="onMrDiagnosisUpdate"
|
||||
@update:medicalAdvice="onMrAdviceUpdate"
|
||||
@open-diagnosis="handleOpenDiagnosisModal"
|
||||
@open-doctor-order="handleOpenDoctorOrderModal"
|
||||
@vip-ok="onMedicalRecordVipOk"
|
||||
@vip-denied="onMedicalRecordVipDenied"
|
||||
@overlay-change="onMedicalRecordOverlayChange"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
@@ -677,6 +700,7 @@
|
||||
@close="closePreSendPriceAdjust"
|
||||
@confirm="onPreSendPriceAdjustConfirm"
|
||||
/>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -754,6 +778,7 @@ const PRESCRIPTION_OVERLAY_KEYS = [
|
||||
'showSaveCommonPrescriptionModal',
|
||||
'showTransferPatientDrawer',
|
||||
'showWarehouseSelectDrawer',
|
||||
'showMedicalRecordOverlay',
|
||||
];
|
||||
|
||||
export default {
|
||||
@@ -793,6 +818,13 @@ export default {
|
||||
],
|
||||
/** 后端下发的默认处方类型;无本地草稿/锁定时使用 */
|
||||
prescriptionTypeDefault: 2,
|
||||
/** 进入开方页校验种类权限中 */
|
||||
pageLoading: true,
|
||||
/** 门店是否有可开方种类(传方模式跳过) */
|
||||
prescriptionTypeAllowed: true,
|
||||
prescriptionTypeDenyMessage: '',
|
||||
/** u-navbar background,勿在模板写对象字面量 */
|
||||
navbarBackground: { background: '#fff' },
|
||||
/** swiper / u-tabs 当前下标;默认与 activeCategory=2(西药,列表第 2 项)对齐,避免首屏 Tab/内容错位 */
|
||||
swiperCurrent: 1,
|
||||
/** 防止 tabs 与 swiper 互相触发形成回路(同订单列表) */
|
||||
@@ -921,6 +953,8 @@ export default {
|
||||
transferDrawerSubmitting: false,
|
||||
/** 在线复诊选仓抽屉 */
|
||||
showWarehouseSelectDrawer: false,
|
||||
/** 病历面板引用/词条抽屉(子组件同步,供返回拦截) */
|
||||
showMedicalRecordOverlay: false,
|
||||
warehouseSelectRows: [],
|
||||
/** resolvePharmacyWarehouseMap 的 Promise resolve;false=取消,null=无需选,map=已选 */
|
||||
_warehouseSelectResolve: null,
|
||||
@@ -950,14 +984,20 @@ export default {
|
||||
isMrMainVisible() {
|
||||
return this.canUseMedicalRecord && this.mainTab === 'mr';
|
||||
},
|
||||
/** 无开方权限且非传方:展示拒绝态 */
|
||||
showRxDenied() {
|
||||
return !this.prescriptionTypeAllowed && !this.isSalespersonTransferMode;
|
||||
},
|
||||
/** u-tabs 需要 { name };有 icon_text 时拼到 name 后便于看见角标;始终返回数组避免 list 非数组警告 */
|
||||
prescriptionTabsList() {
|
||||
const list = this.visiblePrescriptionCategories || [];
|
||||
const list = Array.isArray(this.visiblePrescriptionCategories)
|
||||
? this.visiblePrescriptionCategories
|
||||
: [];
|
||||
return list.map((c) => {
|
||||
let name = c.label || '';
|
||||
if (c.icon_text) name = `${name} ${c.icon_text}`;
|
||||
return { name, value: c.value, icon: c.icon || '' };
|
||||
}) || [];
|
||||
let name = (c && c.label) || '';
|
||||
if (c && c.icon_text) name = `${name} ${c.icon_text}`;
|
||||
return { name, value: c ? c.value : 0, icon: (c && c.icon) || '' };
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 当前处方类型显示名:简单产品列表等共用区块按 Tab label 展示,
|
||||
@@ -1222,19 +1262,41 @@ export default {
|
||||
},
|
||||
// #endif
|
||||
async initializePageData() {
|
||||
this.pageLoading = true;
|
||||
try {
|
||||
this.restoreFromLocalStorage();
|
||||
if (this.isSalespersonTransferMode) {
|
||||
await this.loadSalespersonTransferConfig();
|
||||
await this.loadBasicConfigDataForTransfer();
|
||||
this.prescriptionTypeAllowed = true;
|
||||
} else {
|
||||
// 先校验开方种类权限,再加载其余数据
|
||||
const allowed = await this.loadPrescriptionTypeOptions();
|
||||
if (!allowed) {
|
||||
this.$toast(this.prescriptionTypeDenyMessage || '当前门店未开通开方权限');
|
||||
setTimeout(() => {
|
||||
uni.navigateBack({ fail: () => {} });
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
await this.loadPatientInfo();
|
||||
await this.loadRegisterOrderType();
|
||||
await this.loadBasicConfigData();
|
||||
await this.loadPrescriptionTypeOptions();
|
||||
await this.loadRegisterStoreInfo();
|
||||
// 无 URL/上次切换/草稿时,用后端 default 兜底
|
||||
// 指令 default 压过本地缓存(URL 锁定除外)
|
||||
this.applyDefaultCategoryFromOptions();
|
||||
// 当前 Tab 不在权限内时强制切到 default
|
||||
if (
|
||||
this.prescriptionCategories.length &&
|
||||
!this.prescriptionCategories.some((c) => Number(c.value) === Number(this.activeCategory))
|
||||
) {
|
||||
const next = Number(this.prescriptionTypeDefault) || Number(this.prescriptionCategories[0].value);
|
||||
this.activeCategory = next;
|
||||
if (this.registerId) {
|
||||
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||
}
|
||||
}
|
||||
this.focusActiveTab();
|
||||
}
|
||||
this.loadCurrentCategoryDrugs();
|
||||
this.syncSwiperCurrentFromCategory();
|
||||
@@ -1262,6 +1324,11 @@ export default {
|
||||
} catch (error) {
|
||||
console.error('初始化页面数据失败:', error);
|
||||
this.$toast('初始化失败,请重试');
|
||||
} finally {
|
||||
// 延后关掉 loading,避免小程序在同帧大块挂载时触发 clickCheckTask / not iterable
|
||||
this.$nextTick(() => {
|
||||
this.pageLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
async loadSalespersonTransferConfig() {
|
||||
@@ -1297,48 +1364,63 @@ export default {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 拉取后端处方类型列表与默认选中;失败时保留本地兜底列表
|
||||
* 拉取后端处方类型列表与默认选中;无绑定时返回 false 供页面阻断
|
||||
* 注意:Laravel jok 业务体在 result,需与页面其它接口一样先解包
|
||||
*/
|
||||
async loadPrescriptionTypeOptions() {
|
||||
try {
|
||||
const params = {};
|
||||
if (this.registerId) params.register_id = this.registerId;
|
||||
const storeId = uni.getStorageSync('store_id');
|
||||
if (storeId) params.store_id = storeId;
|
||||
const res = await getPrescriptionTypeOptionsApi(params);
|
||||
// req 已解包为业务 data:{ default, list }
|
||||
const data = res || {};
|
||||
// 与同页其它 newApi 一致:result(Laravel)/ data(Yii)兜底
|
||||
const data = (res && (res.result || res.data)) || res || {};
|
||||
const list = Array.isArray(data.list) ? data.list : [];
|
||||
if (list.length) {
|
||||
this.prescriptionCategories = list.map((item) => ({
|
||||
value: Number(item.value),
|
||||
label: item.label || '',
|
||||
icon: item.icon || '',
|
||||
icon_text: item.icon_text || '',
|
||||
}));
|
||||
}
|
||||
const def = Number(data.default);
|
||||
if (def) this.prescriptionTypeDefault = def;
|
||||
this.prescriptionCategories = list.map((item) => ({
|
||||
value: Number(item.value),
|
||||
label: item.label || '',
|
||||
icon: item.icon || '',
|
||||
icon_text: item.icon_text || '',
|
||||
}));
|
||||
const def = Number(data.default) || 0;
|
||||
this.prescriptionTypeDefault = def;
|
||||
const allowed = data.allowed !== false && list.length > 0;
|
||||
this.prescriptionTypeAllowed = allowed;
|
||||
this.prescriptionTypeDenyMessage = allowed
|
||||
? ''
|
||||
: (data.message || '当前门店未开通开方权限');
|
||||
return allowed;
|
||||
} catch (e) {
|
||||
console.error('加载处方类型失败:', e);
|
||||
this.prescriptionCategories = [];
|
||||
this.prescriptionTypeAllowed = false;
|
||||
this.prescriptionTypeDenyMessage = '处方类型加载失败,请稍后重试';
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
||||
* 使用后端下发的 default。优先级:URL > 上次切换 > 草稿 > 接口 default
|
||||
* 开方默认 Tab:后端「指令」default 暂高于本地缓存。
|
||||
* 优先级:URL 锁定 > 接口 default(指令)> 本地上次 Tab / 草稿分类
|
||||
* 为什么暂时压过缓存:门店绑定的默认种类要以运营配置为准,避免旧缓存挡住新指令
|
||||
*/
|
||||
applyDefaultCategoryFromOptions() {
|
||||
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
||||
if (!this.registerId) return;
|
||||
if (PrescriptionStorage.getActiveCategory(this.registerId)) return;
|
||||
if (PrescriptionStorage.hasDraftWithDrugs(this.registerId)) return;
|
||||
const next = Number(this.prescriptionTypeDefault) || 0;
|
||||
if (!next || next === this.activeCategory) return;
|
||||
if (!next) return;
|
||||
// 指令种类必须在当前可开列表内
|
||||
const allowed = Array.isArray(this.prescriptionCategories) ? this.prescriptionCategories : [];
|
||||
if (allowed.length && !allowed.some((c) => Number(c.value) === next)) return;
|
||||
if (next === this.activeCategory) {
|
||||
this.focusActiveTab();
|
||||
return;
|
||||
}
|
||||
this.activeCategory = next;
|
||||
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||
this.focusActiveTab();
|
||||
},
|
||||
/**
|
||||
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
||||
* 按诊所类型设中药(1)/西药(2)。优先级:URL > 上次切换 > 草稿 > clinic_type
|
||||
* @deprecated 已由 applyDefaultCategoryFromOptions 替代,保留兼容
|
||||
*/
|
||||
applyDefaultCategoryByClinicType() {
|
||||
@@ -1744,6 +1826,12 @@ export default {
|
||||
if (this.registerId) {
|
||||
PrescriptionStorage.saveRxMrTab(tab, this.registerId);
|
||||
}
|
||||
// 切回处方时 swiper 会因 v-if 重新挂载,对齐下标避免错位
|
||||
if (tab === 'rx') {
|
||||
this.$nextTick(() => {
|
||||
this.syncSwiperCurrentFromCategory();
|
||||
});
|
||||
}
|
||||
},
|
||||
/** 从本地恢复外层 Tab */
|
||||
restoreMainTab() {
|
||||
@@ -2983,12 +3071,23 @@ export default {
|
||||
for (let i = 0; i < PRESCRIPTION_OVERLAY_KEYS.length; i++) {
|
||||
const k = PRESCRIPTION_OVERLAY_KEYS[i];
|
||||
if (this[k]) {
|
||||
// 病历抽屉状态在子组件内,需先关子组件再清标志
|
||||
if (k === 'showMedicalRecordOverlay') {
|
||||
const panel = this.$refs.medicalRecordPanel;
|
||||
if (panel && typeof panel.closeOverlays === 'function') {
|
||||
panel.closeOverlays();
|
||||
}
|
||||
}
|
||||
this[k] = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** 病历面板抽屉开合 → 同步返回拦截标志 */
|
||||
onMedicalRecordOverlayChange(open) {
|
||||
this.showMedicalRecordOverlay = !!open;
|
||||
},
|
||||
/** 微信小程序:先关闭 page-container 拦截再退栈,避免 navigateBack 被吞 */
|
||||
safeNavigateBack(delayMs = 0) {
|
||||
const runNav = () => {
|
||||
@@ -3042,6 +3141,17 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rx-permission-loading {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 60vh;
|
||||
padding: 48rpx;
|
||||
}
|
||||
.rx-permission-loading__text {
|
||||
font-size: 28rpx;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
}
|
||||
/* 基础与通用样式 */
|
||||
.page-bg {
|
||||
background-color: #F4F6F8;
|
||||
@@ -3196,7 +3306,7 @@ export default {
|
||||
}
|
||||
/* 内部滚动区铺满 swiper;底部留白避开 fixed 操作栏 */
|
||||
.rx-order-scroll {
|
||||
height: 100%;
|
||||
height: 66vh;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: calc(200rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user