feat: 病历模块优化、AI辅助问诊
This commit is contained in:
@@ -237,6 +237,17 @@
|
||||
:ripple="true">
|
||||
常用方
|
||||
</u-button>
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
size="mini"
|
||||
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
|
||||
plain
|
||||
@click="handleOpenAiPrescriptionDrawer">
|
||||
AI辅助出方
|
||||
</u-button>
|
||||
</view>
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
@@ -316,6 +327,17 @@
|
||||
:ripple="true">
|
||||
常用方
|
||||
</u-button>
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
size="mini"
|
||||
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
|
||||
plain
|
||||
@click="handleOpenAiPrescriptionDrawer">
|
||||
AI辅助出方
|
||||
</u-button>
|
||||
</view>
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
@@ -385,6 +407,17 @@
|
||||
<text class="fs-28 text-gray m-l-8">{{ activeCategoryLabel }}</text>
|
||||
</view>
|
||||
<view class="flex-row m-l-163" style="height: 50rpx;">
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
shape="circle"
|
||||
size="mini"
|
||||
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
|
||||
plain
|
||||
@click="handleOpenAiPrescriptionDrawer">
|
||||
AI辅助出方
|
||||
</u-button>
|
||||
</view>
|
||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
@@ -544,11 +577,18 @@
|
||||
|
||||
<!-- 底部操作栏: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"
|
||||
>
|
||||
<view @click="handleSaveAsCommonPrescription" class="btnText">另存常用方</view>
|
||||
<view class="flex-row flex-ali-center bottom-left-actions">
|
||||
<view
|
||||
v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate"
|
||||
@click="handleSaveAsCommonPrescription"
|
||||
class="btnText"
|
||||
>另存常用方</view>
|
||||
<!-- AI入口放底部,避免顶部三按钮挤出屏幕 -->
|
||||
<view
|
||||
v-if="!isSpecialPrescriptionCartLocked && !isSalespersonTransferMode && !isCommonPrescription"
|
||||
@click="handleOpenAiPrescriptionDrawer"
|
||||
class="btnText"
|
||||
>AI辅助出方</view>
|
||||
</view>
|
||||
<u-button
|
||||
:throttle-time="0"
|
||||
@@ -584,6 +624,7 @@
|
||||
:medical-advice="medicalAdvice"
|
||||
:diagnosis-readonly="isOnlineRevisit"
|
||||
:active="isMrMainVisible"
|
||||
:patient-name="(patientInfo && patientInfo.name) || ''"
|
||||
:patient-sex="(patientInfo && patientInfo.sex) || 0"
|
||||
:patient-age="(patientInfo && patientInfo.age) || 0"
|
||||
@update:diagnosis="onMrDiagnosisUpdate"
|
||||
@@ -702,6 +743,23 @@
|
||||
@close="closePreSendPriceAdjust"
|
||||
@confirm="onPreSendPriceAdjustConfirm"
|
||||
/>
|
||||
|
||||
<!-- AI 给处方:确认主诉 → 生成 → 预览对照 → 确认导入 -->
|
||||
<AiPrescriptionDrawer
|
||||
v-model="showAiPrescriptionDrawer"
|
||||
:register-id="registerId"
|
||||
:store-id="currentStoreId"
|
||||
:prescription-type="activeCategory"
|
||||
:patient-name="(patientInfo && patientInfo.name) || ''"
|
||||
:patient-sex="(patientInfo && patientInfo.sex) || 0"
|
||||
:patient-age="(patientInfo && patientInfo.age) || 0"
|
||||
:chief-complaint="aiRxChiefComplaint"
|
||||
:medical-record="aiRxMedicalRecord"
|
||||
@overlay-change="onAiPrescriptionOverlayChange"
|
||||
@import="handleImportAiPrescription"
|
||||
@search-drug="handleAiSearchUnmatchedDrug"
|
||||
@sync-medical-record="handleSyncAiMedicalRecord"
|
||||
/>
|
||||
</block>
|
||||
</view>
|
||||
</template>
|
||||
@@ -737,6 +795,7 @@ import { PrescriptionValidator } from './utils/prescriptionValidator.js';
|
||||
import DiagnosisModal from './components/modals/DiagnosisModal.vue';
|
||||
import DoctorOrderModal from './components/modals/DoctorOrderModal.vue';
|
||||
import MedicalRecordPanel from './components/MedicalRecordPanel.vue';
|
||||
import AiPrescriptionDrawer from './components/AiPrescriptionDrawer.vue';
|
||||
import { saveMedicalRecordApi } from '@/api/reception.js';
|
||||
import CommonPrescriptionModal from './components/modals/CommonPrescriptionModal.vue';
|
||||
import WesternMedicineModal from './components/modals/WesternMedicineModal.vue';
|
||||
@@ -781,6 +840,7 @@ const PRESCRIPTION_OVERLAY_KEYS = [
|
||||
'showTransferPatientDrawer',
|
||||
'showWarehouseSelectDrawer',
|
||||
'showMedicalRecordOverlay',
|
||||
'showAiPrescriptionDrawer',
|
||||
];
|
||||
|
||||
export default {
|
||||
@@ -789,6 +849,7 @@ export default {
|
||||
DiagnosisModal,
|
||||
DoctorOrderModal,
|
||||
MedicalRecordPanel,
|
||||
AiPrescriptionDrawer,
|
||||
CommonPrescriptionModal,
|
||||
WesternMedicineModal,
|
||||
ChineseMedicineModal,
|
||||
@@ -860,6 +921,11 @@ export default {
|
||||
showDiagnosisModal: false,
|
||||
showDoctorOrderModal: false,
|
||||
showCommonPrescriptionModal: false,
|
||||
showAiPrescriptionDrawer: false,
|
||||
/** 打开 AI 出方时带入的主诉(可在抽屉内再编辑) */
|
||||
aiRxChiefComplaint: '',
|
||||
/** 打开 AI 出方时带入的完整病历快照 */
|
||||
aiRxMedicalRecord: {},
|
||||
showWesternMedicineModal: false,
|
||||
westernModalInitialKeyword: '',
|
||||
showChineseMedicineModal: false,
|
||||
@@ -1113,15 +1179,16 @@ export default {
|
||||
isSpecialPrescriptionCartLocked() {
|
||||
return isSpecialPrescriptionCartLocked(this);
|
||||
},
|
||||
/** 底部发送按钮宽度(左侧仅「另存常用方」时需留出空间) */
|
||||
/** 底部发送按钮宽度(左侧有「另存常用方 / AI辅助出方」时收窄) */
|
||||
bottomSendBtnWidth() {
|
||||
if (this.isSalespersonTransferMode || this.isCommonPrescription) {
|
||||
return '686rpx';
|
||||
}
|
||||
// 左侧至少有 AI辅助出方;有常用方模板时再多一个入口
|
||||
if (this.supportsCommonPrescriptionTemplate) {
|
||||
return '400rpx';
|
||||
return '320rpx';
|
||||
}
|
||||
return '686rpx';
|
||||
return '480rpx';
|
||||
},
|
||||
navbarTitle() {
|
||||
if (this.isSalespersonTransferMode) {
|
||||
@@ -1975,6 +2042,175 @@ export default {
|
||||
this.saveToLocalStorage();
|
||||
},
|
||||
handleOpenCommonPrescriptionModal() { this.showCommonPrescriptionModal = true; },
|
||||
/**
|
||||
* 打开 AI 给处方抽屉:带入完整病历(含未保存),先确认再生成
|
||||
*/
|
||||
handleOpenAiPrescriptionDrawer() {
|
||||
if (guardSpecialPrescriptionCartEdit(this)) return;
|
||||
if (!this.registerId) {
|
||||
this.$toast('挂号无效');
|
||||
return;
|
||||
}
|
||||
if (!this.activeCategory) {
|
||||
this.$toast('请先选择处方类型');
|
||||
return;
|
||||
}
|
||||
const panel = this.$refs.medicalRecordPanel;
|
||||
const payload = panel && panel.getPayload ? panel.getPayload() : {};
|
||||
this.aiRxChiefComplaint = String((payload && payload.chief_complaint) || '');
|
||||
this.aiRxMedicalRecord = Object.assign({}, payload || {});
|
||||
this.showAiPrescriptionDrawer = true;
|
||||
},
|
||||
onAiPrescriptionOverlayChange(open) {
|
||||
this.showAiPrescriptionDrawer = !!open;
|
||||
},
|
||||
/**
|
||||
* AI 出方预览改病历字段时回写病历面板
|
||||
*/
|
||||
handleSyncAiMedicalRecord(partial) {
|
||||
const panel = this.$refs.medicalRecordPanel;
|
||||
if (panel && panel.patchFields) {
|
||||
panel.patchFields(partial || {});
|
||||
}
|
||||
// 本地快照也更新,避免再次打开仍是旧值
|
||||
this.aiRxMedicalRecord = Object.assign({}, this.aiRxMedicalRecord || {}, partial || {});
|
||||
if (partial && partial.chief_complaint != null) {
|
||||
this.aiRxChiefComplaint = String(partial.chief_complaint || '');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 未对照药名:打开对应选药弹窗并带入关键词
|
||||
*/
|
||||
handleAiSearchUnmatchedDrug(keyword) {
|
||||
const key = String(keyword || '');
|
||||
if (this.activeCategory === 1) {
|
||||
this.chineseModalInitialKeyword = key;
|
||||
this.showChineseMedicineModal = true;
|
||||
return;
|
||||
}
|
||||
if (this.activeCategory === 2) {
|
||||
this.westernModalInitialKeyword = key;
|
||||
this.showWesternMedicineModal = true;
|
||||
return;
|
||||
}
|
||||
this.showSimpleProductModal = true;
|
||||
},
|
||||
/**
|
||||
* 将 AI 对照结果覆盖导入当前处方(非追加)
|
||||
* 中药同步剂数/每日次数,usage 映射先煎后下 way_id
|
||||
* @param {Object} payload { rows, dosage, day_dosage }
|
||||
*/
|
||||
handleImportAiPrescription(payload) {
|
||||
if (guardSpecialPrescriptionCartEdit(this)) return;
|
||||
const list = Array.isArray(payload && payload.rows) ? payload.rows : (Array.isArray(payload) ? payload : []);
|
||||
if (!list.length) {
|
||||
this.$toast('没有可导入的药品');
|
||||
return;
|
||||
}
|
||||
const wayList = (this.drugUseList && this.drugUseList.drug_use_way) || [];
|
||||
const resolveWayId = (usage) => {
|
||||
const u = String(usage || '').trim();
|
||||
if (!u) return 0;
|
||||
const found = wayList.find((w) => String(w.name || '') === u || String(w.name || '').indexOf(u) >= 0);
|
||||
return found ? Number(found.id || 0) : 0;
|
||||
};
|
||||
const doOverwrite = () => {
|
||||
clearAppliedSpecialPrescription(this);
|
||||
const next = [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const row = list[i] || {};
|
||||
const c = row.candidate || {};
|
||||
const drugId = Number(c.drug_id || 0);
|
||||
if (!drugId) continue;
|
||||
if (next.find((item) => Number(item.id) === drugId || Number(item.drug_id) === drugId)) {
|
||||
continue;
|
||||
}
|
||||
const doseNum = parseFloat(row.dose);
|
||||
const qty = !isNaN(doseNum) && doseNum > 0 ? doseNum : (c.number || 1);
|
||||
if (this.activeCategory === 1) {
|
||||
const wayId = resolveWayId(row.usage || '');
|
||||
const newDrug = {
|
||||
index_id: c.id || drugId,
|
||||
id: drugId,
|
||||
drug_id: drugId,
|
||||
drug_name: c.drug_name || row.ai_name,
|
||||
name: c.drug_name || row.ai_name,
|
||||
number: qty,
|
||||
price: parseFloat(c.price) || 0,
|
||||
specification: c.specification || '',
|
||||
image: c.image || '',
|
||||
unit: c.unit || { id: 0, name: row.unit || 'g' },
|
||||
way_id: wayId,
|
||||
use_ways: wayList.find((item) => item.id === wayId),
|
||||
};
|
||||
next.push(this.prepareDrugForCart(newDrug));
|
||||
continue;
|
||||
}
|
||||
if (this.activeCategory === 2) {
|
||||
const d = c.drug || {};
|
||||
const newDrug = {
|
||||
index_id: c.id || drugId,
|
||||
id: drugId,
|
||||
drug_id: drugId,
|
||||
drug_name: c.drug_name || d.drug_name || row.ai_name,
|
||||
number: qty,
|
||||
select_number: qty,
|
||||
price: parseFloat(c.price) || 0,
|
||||
specification: c.specification || d.specification || '',
|
||||
image: c.image || d.image || '',
|
||||
time_id: c.time_id || d.time_id || 0,
|
||||
type_id: c.type_id || d.type_id || 0,
|
||||
frequency_id: c.frequency_id || d.frequency_id || 0,
|
||||
unit_id: c.unit_id || d.unit_id || 0,
|
||||
use_num: this.drugUseList.drug_time && this.drugUseList.drug_time.find((item) => item.id === (c.time_id || d.time_id || 0)),
|
||||
use_type: this.drugUseList.drug_use_type && this.drugUseList.drug_use_type.find((item) => item.id === (c.type_id || d.type_id || 0)),
|
||||
use_frequency: this.drugUseList.drug_use_frequency && this.drugUseList.drug_use_frequency.find((item) => item.id === (c.frequency_id || d.frequency_id || 0)),
|
||||
unit: this.drugUseList.drug_unit && this.drugUseList.drug_unit.find((item) => item.id === (c.unit_id || d.unit_id || 0)),
|
||||
};
|
||||
next.push(this.prepareDrugForCart(newDrug));
|
||||
continue;
|
||||
}
|
||||
const newProduct = {
|
||||
index_id: c.id || drugId,
|
||||
id: drugId,
|
||||
drug_id: drugId,
|
||||
drug_name: c.drug_name || row.ai_name,
|
||||
number: 1,
|
||||
select_number: 1,
|
||||
price: parseFloat(c.price) || 0,
|
||||
image: c.image || '',
|
||||
type: this.activeCategory,
|
||||
};
|
||||
next.push(this.prepareDrugForCart(newProduct));
|
||||
}
|
||||
if (!next.length) {
|
||||
this.$toast('没有可导入的药品');
|
||||
return;
|
||||
}
|
||||
this.currentDrugs = next;
|
||||
if (this.activeCategory === 1) {
|
||||
if (payload && Number(payload.dosage) > 0) {
|
||||
this.chineseConfig.dosage = Number(payload.dosage);
|
||||
}
|
||||
if (payload && Number(payload.day_dosage) > 0) {
|
||||
this.chineseConfig.dayDosage = Number(payload.day_dosage);
|
||||
}
|
||||
}
|
||||
this.saveToLocalStorage();
|
||||
this.$toast(`已覆盖导入 ${next.length} 个药品`);
|
||||
};
|
||||
if (this.currentDrugs && this.currentDrugs.length > 0) {
|
||||
uni.showModal({
|
||||
title: '覆盖当前处方',
|
||||
content: '导入将清空并覆盖现有处方药品,是否继续?',
|
||||
success: ({ confirm }) => {
|
||||
if (confirm) doOverwrite();
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
doOverwrite();
|
||||
},
|
||||
async handleApplySpecialPrescription() {
|
||||
if (!this.registerId || this.applyingSpecialPrescription) return;
|
||||
this.applyingSpecialPrescription = true;
|
||||
|
||||
Reference in New Issue
Block a user