feat: 病历模块优化、AI辅助问诊

This commit is contained in:
李琦
2026-08-06 10:07:49 +08:00
parent b2ad3088be
commit 87a0c02fb0
2 changed files with 224 additions and 107 deletions

View File

@@ -225,7 +225,12 @@
<view class="mr-btn-text" @click="onOpenCiteDrawer">引用历史</view> <view class="mr-btn-text" @click="onOpenCiteDrawer">引用历史</view>
</view> </view>
<view class="flex-row flex-ali-center"> <view class="flex-row flex-ali-center">
<view class="mr-ai-btn" @click="onOpenAiModal"> <!-- VIP ai_medical_record 时隐藏入口 -->
<view
v-if="canUseAiMedicalRecord"
class="mr-ai-btn"
@click="onOpenAiModal"
>
<text class="mr-ai-btn__icon"></text> <text class="mr-ai-btn__icon"></text>
<text class="mr-ai-btn__text">AI写病历</text> <text class="mr-ai-btn__text">AI写病历</text>
</view> </view>
@@ -322,54 +327,64 @@
border-radius="24" border-radius="24"
:safe-area-inset-bottom="true" :safe-area-inset-bottom="true"
:mask-close-able="true" :mask-close-able="true"
height="70%" height="85%"
z-index="10090" z-index="10090"
@close="closeEntryDrawer" @close="closeEntryDrawer"
> >
<!-- 词条抽屉头固定 + 列表区可滑与诊断弹窗同结构避免 scroll-view 高度塌缩 -->
<view class="entry-drawer"> <view class="entry-drawer">
<view class="drawer-handle" /> <view class="entry-drawer__hd">
<view class="drawer-header"> <view class="drawer-handle" />
<text class="drawer-header__title">选择词条</text> <view class="drawer-header">
<text class="drawer-header__count">{{ entryKeyword ? ('搜到 ' + entryList.length + ' 条') : ('共 ' + entryList.length + ' 条') }}</text> <text class="drawer-header__title">选择词条</text>
<text class="drawer-header__count">{{ entryKeyword ? ('搜到 ' + entryList.length + ' 条') : ('共 ' + entryList.length + ' 条') }}</text>
</view>
<u-input
v-model="entryKeyword"
placeholder="搜索名称/内容/首拼"
:custom-style="entrySearchStyle"
@input="onEntryKeywordInput"
/>
</view> </view>
<u-input <scroll-view scroll-y class="entry-scroll" enable-flex>
v-model="entryKeyword" <view class="entry-scroll-inner">
placeholder="搜索词条名称/内容/首拼" <view
:custom-style="entrySearchStyle" v-for="row in entryList"
@input="onEntryKeywordInput" :key="row.id"
/> class="entry-row"
<scroll-view scroll-y class="entry-scroll"> @click="pickEntry(row)"
<view >
v-for="row in entryList" <view class="entry-row-top">
:key="row.id" <view class="entry-row-name">
class="entry-row" <block v-for="(p, pi) in (row._titleParts || [])" :key="'t' + pi">
@click="pickEntry(row)" <text v-if="p.hit" class="hit-text">{{ p.text }}</text>
> <text v-else>{{ p.text }}</text>
<view class="entry-row-top"> </block>
<view class="fs-28 font-bold entry-row-name"> </view>
<block v-for="(p, pi) in (row._titleParts || [])" :key="pi"> <text v-if="entryKeyword" class="entry-score">{{ row.match_score || 0 }}%</text>
</view>
<!-- 正文多行展示行高收紧以一屏多看几条 -->
<view v-if="row.content" class="entry-row-content">
<block v-for="(p, pi) in (row._contentParts || [])" :key="'c' + pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text> <text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text> <text v-else>{{ p.text }}</text>
</block> </block>
</view> </view>
<text v-if="entryKeyword" class="entry-score">{{ row.match_score || 0 }}%</text> <view
v-if="row.remark || (entryKeyword && row.pinyin_initials)"
class="entry-row-meta"
>
<text v-if="row.remark" class="entry-remark-tag">{{ row.remark }}</text>
<text v-if="entryKeyword && row.pinyin_initials" class="entry-py-tag">
<block v-for="(p, pi) in (row._pyParts || [])" :key="'p' + pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</text>
</view>
</view> </view>
<view v-if="row.pinyin_initials" class="fs-22 color-sub m-t-4"> <view v-if="!entryList.length" class="drawer-empty">暂无词条</view>
<text>首拼(全拼)</text>
<block v-for="(p, pi) in (row._pyParts || [])" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<view class="fs-24 color-sub m-t-8">
<block v-for="(p, pi) in (row._contentParts || [])" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<text v-if="row.remark" class="entry-remark-tag">{{ row.remark }}</text>
</view> </view>
<view v-if="!entryList.length" class="drawer-empty">暂无词条</view>
</scroll-view> </scroll-view>
</view> </view>
</u-popup> </u-popup>
@@ -487,12 +502,16 @@ export default {
diagnosisReadonly: { type: Boolean, default: false }, diagnosisReadonly: { type: Boolean, default: false },
/** 是否处于病历 Tab控制底部栏probe 时必须为 false否则 fixed 会穿透到处方) */ /** 是否处于病历 Tab控制底部栏probe 时必须为 false否则 fixed 会穿透到处方) */
active: { type: Boolean, default: false }, active: { type: Boolean, default: false },
/** 门店是否开通病历 VIPmedical_record无则不请求病历接口避免弹错 */
canUseMedicalRecord: { type: Boolean, default: false },
/** 就诊人姓名AI 确认步展示) */ /** 就诊人姓名AI 确认步展示) */
patientName: { type: String, default: '' }, patientName: { type: String, default: '' },
/** 就诊人性别 1男2女 */ /** 就诊人性别 1男2女 */
patientSex: { type: [Number, String], default: 0 }, patientSex: { type: [Number, String], default: 0 },
/** 就诊人年龄 */ /** 就诊人年龄 */
patientAge: { type: [Number, String], default: 0 }, patientAge: { type: [Number, String], default: 0 },
/** 门店是否开通 AI写病历 VIPai_medical_record */
canUseAiMedicalRecord: { type: Boolean, default: false },
}, },
data() { data() {
const tcmCaseSubFields = [ const tcmCaseSubFields = [
@@ -509,7 +528,12 @@ export default {
entryList: [], entryList: [],
entryKeyword: '', entryKeyword: '',
entrySearchTimer: null, entrySearchTimer: null,
entrySearchStyle: { background: '#f4f6f8', borderRadius: '14rpx', padding: '14rpx 20rpx', marginBottom: '20rpx', border: '1rpx solid #e8ecef' }, entrySearchStyle: {
background: '#f4f6f8',
borderRadius: '14rpx',
padding: '12rpx 20rpx',
border: '1rpx solid #e8ecef',
},
/** 引用历史抽屉 */ /** 引用历史抽屉 */
citeShow: false, citeShow: false,
citeLoading: false, citeLoading: false,
@@ -599,10 +623,13 @@ export default {
watch: { watch: {
registerId: { registerId: {
immediate: true, immediate: true,
handler(v) { handler() {
if (v) this.load() this.tryLoad()
}, },
}, },
canUseMedicalRecord() {
this.tryLoad()
},
diagnosis(v) { diagnosis(v) {
this.localDiagnosis = v || '' this.localDiagnosis = v || ''
this.schedulePersistDraft() this.schedulePersistDraft()
@@ -629,6 +656,15 @@ export default {
}, },
}, },
methods: { methods: {
/**
* 仅在有病历 VIP 时拉病历;普通诊所静默跳过,不弹「未开通 VIP」
*/
tryLoad() {
if (!this.registerId || !this.canUseMedicalRecord) {
return
}
this.load()
},
/** /**
* 通知父页:病历侧是否有弹层打开(与处方弹层共用返回拦截) * 通知父页:病历侧是否有弹层打开(与处方弹层共用返回拦截)
*/ */
@@ -655,6 +691,10 @@ export default {
* 打开 AI 写病历:先进历史抽屉(无历史则自动弹出生成框) * 打开 AI 写病历:先进历史抽屉(无历史则自动弹出生成框)
*/ */
onOpenAiModal() { onOpenAiModal() {
if (!this.canUseAiMedicalRecord) {
uni.showToast({ title: '当前门店未开通AI写病历', icon: 'none' })
return
}
if (!this.registerId) { if (!this.registerId) {
uni.showToast({ title: '挂号无效', icon: 'none' }) uni.showToast({ title: '挂号无效', icon: 'none' })
return return
@@ -723,11 +763,12 @@ export default {
async load() { async load() {
this.hydrating = true this.hydrating = true
try { try {
const res = await getMedicalRecordApi({ const mrParams = { register_id: this.registerId }
register_id: this.registerId, const mrStoreId = Number(this.storeId || 0)
store_id: this.storeId || undefined, if (mrStoreId > 0) mrParams.store_id = mrStoreId
}) const res = await getMedicalRecordApi(mrParams)
const data = (res && res.data) || res || {} // Laravel jok 在 result兼容 data / 已解包
const data = (res && (res.result != null ? res.result : res.data)) || res || {}
this.form = Object.assign(emptyForm(), data) this.form = Object.assign(emptyForm(), data)
const draft = PrescriptionStorage.loadMedicalRecordDraft(this.registerId) const draft = PrescriptionStorage.loadMedicalRecordDraft(this.registerId)
if (draft) { if (draft) {
@@ -744,17 +785,11 @@ export default {
} }
if (this.diagnosis) this.localDiagnosis = this.diagnosis if (this.diagnosis) this.localDiagnosis = this.diagnosis
if (this.medicalAdvice) this.localAdvice = this.medicalAdvice if (this.medicalAdvice) this.localAdvice = this.medicalAdvice
this.$emit('vip-ok')
} catch (e) { } catch (e) {
// 有 VIP 才请求;若仍失败(网络等)仅回填本地草稿,不再靠 VIP 文案探测
const draft = PrescriptionStorage.loadMedicalRecordDraft(this.registerId) const draft = PrescriptionStorage.loadMedicalRecordDraft(this.registerId)
if (draft) { if (draft) {
this.applyDraft(draft) this.applyDraft(draft)
this.$emit('vip-ok')
} else {
const msg = (e && e.message) || (e && e.msg) || ''
if (String(msg).indexOf('VIP') > -1 || String(msg).indexOf('vip') > -1 || String(msg).indexOf('病历') > -1) {
this.$emit('vip-denied')
}
} }
} finally { } finally {
this.hydrating = false this.hydrating = false
@@ -827,10 +862,10 @@ export default {
uni.showToast({ title: '已导入,请核对后保存', icon: 'none' }) uni.showToast({ title: '已导入,请核对后保存', icon: 'none' })
}, },
async onClear() { async onClear() {
await clearMedicalRecordApi({ const clearParams = { register_id: this.registerId }
register_id: this.registerId, const clearStoreId = Number(this.storeId || 0)
store_id: this.storeId || undefined, if (clearStoreId > 0) clearParams.store_id = clearStoreId
}) await clearMedicalRecordApi(clearParams)
const keepPatient = this.effectivePatientId const keepPatient = this.effectivePatientId
this.form = emptyForm() this.form = emptyForm()
this.form.user_patient_id = keepPatient this.form.user_patient_id = keepPatient
@@ -855,11 +890,13 @@ export default {
this.citeList = [] this.citeList = []
this.citeActiveId = 0 this.citeActiveId = 0
try { try {
const res = await listMedicalRecordByPatientApi({ const citeParams = {
user_patient_id: patientId, user_patient_id: patientId,
store_id: this.storeId || undefined,
limit: 50, limit: 50,
}) }
const citeStoreId = Number(this.storeId || 0)
if (citeStoreId > 0) citeParams.store_id = citeStoreId
const res = await listMedicalRecordByPatientApi(citeParams)
const list = this.unwrapCiteList(res) const list = this.unwrapCiteList(res)
const exclude = Number(this.registerId || 0) const exclude = Number(this.registerId || 0)
this.citeList = list.map((row) => { this.citeList = list.map((row) => {
@@ -991,11 +1028,12 @@ export default {
if (this.entryTcmType) { if (this.entryTcmType) {
list = await this.fetchTcmDictList(this.entryTcmType, kw) list = await this.fetchTcmDictList(this.entryTcmType, kw)
} else { } else {
const res = await searchMedicalRecordEntryApi({ // uni 会把 undefined 序列化成字符串 "undefined",无值时不要塞进 params
field_code: this.entryField, const params = { field_code: this.entryField }
keyword: kw || undefined, if (kw) params.keyword = kw
store_id: this.storeId || undefined, const sid = Number(this.storeId || 0)
}) if (sid > 0) params.store_id = sid
const res = await searchMedicalRecordEntryApi(params)
list = this.unwrapEntryList(res) list = this.unwrapEntryList(res)
if (list[0] && list[0].rank_mode) { if (list[0] && list[0].rank_mode) {
mode = String(list[0].rank_mode) mode = String(list[0].rank_mode)
@@ -1521,51 +1559,96 @@ export default {
padding: 20rpx 0 28rpx; padding: 20rpx 0 28rpx;
} }
/* ─── 词条选择抽屉 ─── */ /* ─── 词条选择抽屉(紧凑列表 + 可滑) ─── */
.entry-drawer { .entry-drawer {
height: 100%; height: 100%;
min-height: 0;
background: #fff; background: #fff;
padding: 28rpx 28rpx 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden;
}
.entry-drawer__hd {
flex-shrink: 0;
padding: 20rpx 28rpx 12rpx;
}
.entry-drawer__hd .drawer-handle {
margin-bottom: 16rpx;
}
.entry-drawer__hd .drawer-header {
margin-bottom: 16rpx;
} }
.entry-scroll { .entry-scroll {
flex: 1; flex: 1;
height: 0; height: 0;
min-height: 200rpx; min-height: 0;
overflow: hidden;
}
.entry-scroll-inner {
padding: 0 28rpx;
padding-bottom: calc(40rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
} }
.entry-row { .entry-row {
padding: 22rpx 0; padding: 16rpx 0;
border-bottom: 1rpx solid #f0f2f4; border-bottom: 1rpx solid #f0f2f4;
} }
.entry-row-top { .entry-row-top {
display: flex; display: flex;
align-items: flex-start; flex-direction: row;
align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 16rpx; gap: 12rpx;
} }
.entry-row-name { .entry-row-name {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
font-size: 28rpx;
font-weight: 700;
color: #1a2530;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.entry-score { .entry-score {
flex-shrink: 0; flex-shrink: 0;
font-size: 22rpx; font-size: 20rpx;
font-weight: 700; font-weight: 700;
color: #ff7043; color: #ff7043;
background: rgba(255, 112, 67, 0.1); background: rgba(255, 112, 67, 0.1);
padding: 4rpx 14rpx; padding: 2rpx 12rpx;
border-radius: 10rpx; border-radius: 10rpx;
} }
.entry-remark-tag { .entry-row-content {
display: inline-block; margin-top: 6rpx;
font-size: 26rpx;
color: #4a5560;
line-height: 1.45;
/* 约 3 行高度;用 max-height 兼容高亮多 text 节点 */
max-height: 114rpx;
overflow: hidden;
word-break: break-all;
}
.entry-row-meta {
margin-top: 8rpx; margin-top: 8rpx;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
gap: 8rpx;
}
.entry-remark-tag,
.entry-py-tag {
font-size: 20rpx; font-size: 20rpx;
color: #8a9399; color: #8a9399;
background: #f0f2f4; background: #f0f2f4;
padding: 4rpx 14rpx; padding: 2rpx 12rpx;
border-radius: 8rpx; border-radius: 8rpx;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
.hit-text { .hit-text {
color: #e64e1f; color: #e64e1f;

View File

@@ -127,9 +127,9 @@
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button> <u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
</view> </view>
<view v-if="isRxMainVisible && !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> <!-- <u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>-->
</view> <!-- </view>-->
<!-- 诊断病历 Tab 必须卸载微信 v-show 藏不住 fixed/flex 固定块 --> <!-- 诊断病历 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 v-if="isRxMainVisible && !isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
@@ -552,9 +552,8 @@
>另存常用方</view> >另存常用方</view>
</view> </view>
<view class="flex-row flex-ali-center"> <view class="flex-row flex-ali-center">
<!-- AI 胶囊与病历AI写病历同款功能暂不可用时仍保留入口样式 -->
<view <view
v-if="!isSpecialPrescriptionCartLocked && !isSalespersonTransferMode && !isCommonPrescription" v-if="!isSpecialPrescriptionCartLocked && !isSalespersonTransferMode && !isCommonPrescription && canUseAiPrescription"
class="rx-ai-btn" class="rx-ai-btn"
@click="handleOpenAiPrescriptionDrawer" @click="handleOpenAiPrescriptionDrawer"
> >
@@ -578,8 +577,9 @@
</view> </view>
<!-- <!--
病历常挂载保草稿 / 探测 VIP 病历常挂载保草稿
处方 Tabfixed 离屏探测绝不参与 page-flex 占高 是否有 VIP 由门店 permissions 判断 VIP 不请求病历接口不展示 Tab
处方 Tabfixed 离屏绝不参与 page-flex 占高
病历 Tabflex:1 吃满剩余高度 病历 Tabflex:1 吃满剩余高度
--> -->
<view <view
@@ -596,15 +596,15 @@
:medical-advice="medicalAdvice" :medical-advice="medicalAdvice"
:diagnosis-readonly="isOnlineRevisit" :diagnosis-readonly="isOnlineRevisit"
:active="isMrMainVisible" :active="isMrMainVisible"
:can-use-medical-record="canUseMedicalRecord"
:patient-name="(patientInfo && patientInfo.name) || ''" :patient-name="(patientInfo && patientInfo.name) || ''"
:patient-sex="(patientInfo && patientInfo.sex) || 0" :patient-sex="(patientInfo && patientInfo.sex) || 0"
:patient-age="(patientInfo && patientInfo.age) || 0" :patient-age="(patientInfo && patientInfo.age) || 0"
:can-use-ai-medical-record="canUseAiMedicalRecord"
@update:diagnosis="onMrDiagnosisUpdate" @update:diagnosis="onMrDiagnosisUpdate"
@update:medicalAdvice="onMrAdviceUpdate" @update:medicalAdvice="onMrAdviceUpdate"
@open-diagnosis="handleOpenDiagnosisModal" @open-diagnosis="handleOpenDiagnosisModal"
@open-doctor-order="handleOpenDoctorOrderModal" @open-doctor-order="handleOpenDoctorOrderModal"
@vip-ok="onMedicalRecordVipOk"
@vip-denied="onMedicalRecordVipDenied"
@overlay-change="onMedicalRecordOverlayChange" @overlay-change="onMedicalRecordOverlayChange"
/> />
</view> </view>
@@ -841,6 +841,8 @@ export default {
mainTab: 'rx', mainTab: 'rx',
/** 是否开通病历 VIP拉取病历成功后为 true */ /** 是否开通病历 VIP拉取病历成功后为 true */
canUseMedicalRecord: false, canUseMedicalRecord: false,
/** 当前诊所 VIP permissions来自 get-current-store-type.vip */
storeVipPermissions: [],
pendingReusePrescriptionId: '', pendingReusePrescriptionId: '',
patientInfo: null, patientInfo: null,
prescriptionCategories: [ prescriptionCategories: [
@@ -1159,12 +1161,20 @@ export default {
if (this.isSalespersonTransferMode || this.isCommonPrescription) { if (this.isSalespersonTransferMode || this.isCommonPrescription) {
return '686rpx'; return '686rpx';
} }
// 右侧 AI 胶囊 + 发送,主按钮与病历「保存病历」同量级 // 右侧 AI 胶囊 + 发送
if (!this.isSpecialPrescriptionCartLocked) { if (!this.isSpecialPrescriptionCartLocked && this.canUseAiPrescription) {
return '240rpx'; return '240rpx';
} }
return '480rpx'; return '480rpx';
}, },
/** 门店 VIPAI辅助出方 */
canUseAiPrescription() {
return this.hasStoreVipPermission('ai_prescription');
},
/** 门店 VIPAI写病历 */
canUseAiMedicalRecord() {
return this.hasStoreVipPermission('ai_medical_record');
},
navbarTitle() { navbarTitle() {
if (this.isSalespersonTransferMode) { if (this.isSalespersonTransferMode) {
return '传方'; return '传方';
@@ -1710,6 +1720,30 @@ export default {
this.applyPreSendPriceDiscount(payload.priceDiscount); this.applyPreSendPriceDiscount(payload.priceDiscount);
this.closePreSendPriceAdjust(); this.closePreSendPriceAdjust();
}, },
/**
* 判断当前诊所 VIP 是否含指定权益码
*/
hasStoreVipPermission(code) {
const list = this.storeVipPermissions;
return Array.isArray(list) && list.indexOf(code) >= 0;
},
/**
* 从门店类型接口结果写入 VIP permissions
* 有 medical_record 才显示病历 Tab无则静默留在处方不打 get-medical-record 避免弹 VIP 错)
*/
applyStoreVipFromRes(res) {
const data = (res && (res.result != null ? res.result : res.data != null ? res.data : res)) || {};
const vip = data.vip || {};
const perms = vip.permissions;
this.storeVipPermissions = Array.isArray(perms) ? perms.slice() : [];
const allowMr = this.hasStoreVipPermission('medical_record');
this.canUseMedicalRecord = allowMr;
if (allowMr) {
this.restoreMainTab();
} else if (this.mainTab === 'mr') {
this.mainTab = 'rx';
}
},
async fetchStoreSeeRate() { async fetchStoreSeeRate() {
try { try {
const storeId = uni.getStorageSync('store_id'); const storeId = uni.getStorageSync('store_id');
@@ -1725,8 +1759,10 @@ export default {
} }
const res = await getCurrentStoreTypeApi(params); const res = await getCurrentStoreTypeApi(params);
if (res) { if (res) {
this.seeRate = Number(res.see_rate ?? 0); this.applyStoreVipFromRes(res);
this.enableOrderPricePercentAdjust = Number(res.enable_order_price_percent_adjust ?? 0); const data = (res.result != null ? res.result : res.data != null ? res.data : res) || res;
this.seeRate = Number(data.see_rate ?? 0);
this.enableOrderPricePercentAdjust = Number(data.enable_order_price_percent_adjust ?? 0);
await this.loadPriceAdjustConfigForStore(params.store_id || storeId); await this.loadPriceAdjustConfigForStore(params.store_id || storeId);
} }
} catch (error) { } catch (error) {
@@ -1745,21 +1781,23 @@ export default {
...(storeId ? { store_id: storeId } : {}) ...(storeId ? { store_id: storeId } : {})
}); });
if (res) { if (res) {
this.registerStoreInfo = res; this.applyStoreVipFromRes(res);
this.seeRate = Number(res.see_rate ?? 0); const data = (res.result != null ? res.result : res.data != null ? res.data : res) || res;
if (Number(res.allow_insurance_category ?? 0) !== 1) { this.registerStoreInfo = data;
this.seeRate = Number(data.see_rate ?? 0);
if (Number(data.allow_insurance_category ?? 0) !== 1) {
this.feeCategory = 1; this.feeCategory = 1;
} }
this.checkSalespersonTransfer(); this.checkSalespersonTransfer();
if (res.is_from_transfer === 1 && res.delegate_store_id) { if (data.is_from_transfer === 1 && data.delegate_store_id) {
this.selectedStoreId = res.delegate_store_id; this.selectedStoreId = data.delegate_store_id;
this.sendMode = 1; this.sendMode = 1;
} else if (res.store_id) { } else if (data.store_id) {
this.selectedStoreId = res.store_id; this.selectedStoreId = data.store_id;
this.sendMode = res.can_change ? 1 : 0; this.sendMode = data.can_change ? 1 : 0;
} }
const priceStoreId = this.selectedStoreId || res.store_id || storeId; const priceStoreId = this.selectedStoreId || data.store_id || storeId;
this.enableOrderPricePercentAdjust = Number(res.enable_order_price_percent_adjust ?? 0); this.enableOrderPricePercentAdjust = Number(data.enable_order_price_percent_adjust ?? 0);
await this.loadPriceAdjustConfigForStore(priceStoreId); await this.loadPriceAdjustConfigForStore(priceStoreId);
} }
} catch (error) { } catch (error) {
@@ -1924,14 +1962,6 @@ export default {
onMrAdviceUpdate(v) { onMrAdviceUpdate(v) {
this.medicalAdvice = v || ''; this.medicalAdvice = v || '';
}, },
onMedicalRecordVipDenied() {
this.canUseMedicalRecord = false;
if (this.mainTab === 'mr') this.mainTab = 'rx';
},
onMedicalRecordVipOk() {
this.canUseMedicalRecord = true;
this.restoreMainTab();
},
/** 发送处方成功后同步病历(有 VIP 时) */ /** 发送处方成功后同步病历(有 VIP 时) */
async syncMedicalRecordAfterSend() { async syncMedicalRecordAfterSend() {
if (!this.canUseMedicalRecord) return; if (!this.canUseMedicalRecord) return;
@@ -2021,6 +2051,10 @@ export default {
* 打开 AI 给处方抽屉:带入完整病历(含未保存),先确认再生成 * 打开 AI 给处方抽屉:带入完整病历(含未保存),先确认再生成
*/ */
handleOpenAiPrescriptionDrawer() { handleOpenAiPrescriptionDrawer() {
if (!this.canUseAiPrescription) {
this.$toast('当前门店未开通AI辅助出方VIP功能');
return;
}
if (guardSpecialPrescriptionCartEdit(this)) return; if (guardSpecialPrescriptionCartEdit(this)) return;
if (!this.registerId) { if (!this.registerId) {
this.$toast('挂号无效'); this.$toast('挂号无效');