diff --git a/api/reception.js b/api/reception.js
index ad9a689..3238f02 100644
--- a/api/reception.js
+++ b/api/reception.js
@@ -547,3 +547,48 @@ export function searchMedicalRecordEntryApi(data) {
data
})
}
+
+/** AI 写病历(需先填主诉;返回草稿字段,不自动保存) */
+export function aiGenerateMedicalRecordApi(data) {
+ return req.request({
+ url: '/newApi/doctor-reception-wx/ai-generate-medical-record',
+ method: 'POST',
+ data
+ })
+}
+
+/** AI 给处方(按病历+处方类型生成,含药名对照) */
+export function aiGeneratePrescriptionApi(data) {
+ return req.request({
+ url: '/newApi/doctor-reception-wx/ai-generate-prescription',
+ method: 'POST',
+ data
+ })
+}
+
+/** 本挂号 AI 生成历史(轻量列表) */
+export function aiListGenerationsApi(data) {
+ return req.request({
+ url: '/newApi/doctor-reception-wx/ai-list-generations',
+ method: 'GET',
+ data
+ })
+}
+
+/** AI 生成详情(点选历史) */
+export function aiGenerationDetailApi(data) {
+ return req.request({
+ url: '/newApi/doctor-reception-wx/ai-generation-detail',
+ method: 'GET',
+ data
+ })
+}
+
+/** AI 处方药名模糊对照 */
+export function aiMatchPrescriptionDrugsApi(data) {
+ return req.request({
+ url: '/newApi/doctor-reception-wx/ai-match-prescription-drugs',
+ method: 'POST',
+ data
+ })
+}
diff --git a/subPackages/sub_workbench/prescription_v2/components/AiMedicalRecordDrawer.vue b/subPackages/sub_workbench/prescription_v2/components/AiMedicalRecordDrawer.vue
new file mode 100644
index 0000000..111222a
--- /dev/null
+++ b/subPackages/sub_workbench/prescription_v2/components/AiMedicalRecordDrawer.vue
@@ -0,0 +1,546 @@
+
+
+
+
+
+ AI写病历
+ {{ patientName || '—' }} · {{ sexLabel }} · {{ patientAge || 0 }}岁
+
+
+
+
+ 历史
+
+ 加载中…
+ 暂无记录,可生成病历
+
+ {{ row.nameText }}
+ {{ row.timeText }}
+
+
+
+ AI 处理中…
+
+
+ 请选择历史或生成病历
+
+
+
+ 主诉
+ {{ previewChief }}
+
+
+
+ {{ row.label }}
+ {{ row.value }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 生成病历
+ 关闭
+
+ 确认主诉后开始生成,将关闭本弹窗并在主层等待
+
+ 患者
+ {{ patientName || '—' }} · {{ sexLabel }} · {{ patientAge || 0 }}岁
+
+
+ 主诉(可编辑)
+
+
+
+
+
+
+
+
+
+
diff --git a/subPackages/sub_workbench/prescription_v2/components/AiPrescriptionDrawer.vue b/subPackages/sub_workbench/prescription_v2/components/AiPrescriptionDrawer.vue
new file mode 100644
index 0000000..4d6e7a9
--- /dev/null
+++ b/subPackages/sub_workbench/prescription_v2/components/AiPrescriptionDrawer.vue
@@ -0,0 +1,997 @@
+
+
+
+
+
+ AI辅助出方
+ {{ patientName || '—' }} · {{ sexLabel }} · {{ patientAge || 0 }}岁
+
+
+ 配伍禁忌提示
+ · {{ line }}
+
+
+
+
+ 历史
+
+ 加载中…
+ 暂无记录,可生成处方
+
+ {{ row.nameText }}
+ {{ row.typeText }}
+
+
+
+ AI 处理中…
+
+ 请选择历史或生成处方
+
+ 已对照({{ matchedCount }})
+
+
+
+
+ {{ m.ai_name }}
+
+
+ {{ m.doseUnit }}{{ m.usageText }}
+
+
+ {{ c.drug_name }}
+
+
+
+
+
+
+
+ {{ m.ai_name }}
+ {{ m.doseUnit }}{{ m.usageText }}
+
+
+
+
+ {{ c.drug_name }}
+
+
+
+
+ 未对照({{ unmatchedCount }})
+
+ {{ u.ai_name }}
+ 去手动搜索
+
+
+
+
+ 药方名
+ {{ rxPrescriptionName || '未命名药方' }}
+
+
+ 推荐剂数
+ {{ rxDosage || '—' }}剂
+
+
+ 每天几次
+ {{ rxDayDosage || '—' }}次
+
+
+ 为什么出这个方
+ {{ rxReason }}
+
+
+ 根据哪些
+ {{ rxBasis }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 生成处方
+ 关闭
+
+ 点击文字可编辑,将同步到病历 · {{ typeLabel }}
+
+
+
+ {{ row.label }}
+
+ {{ row.value || '(空,点击填写)' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/subPackages/sub_workbench/prescription_v2/components/MedicalRecordPanel.vue b/subPackages/sub_workbench/prescription_v2/components/MedicalRecordPanel.vue
index d41d2f2..7f647be 100644
--- a/subPackages/sub_workbench/prescription_v2/components/MedicalRecordPanel.vue
+++ b/subPackages/sub_workbench/prescription_v2/components/MedicalRecordPanel.vue
@@ -7,288 +7,343 @@
-->
-
-
-
-
- {{ diagnosisReadonly ? '去选择常用诊断' : '选择常用诊断' }}
-
-
-
-
-
-
-
-
-
- 舌象词条
+
+
+
+
+
-
- 脉象词条
+
+ +
+ {{ diagnosisReadonly ? '去选择常用诊断' : '选择常用诊断' }}
+ ›
+
-
-
-
-
-
- 选择辨证/病案词条
-
-
-
-
-
-
-
- 选择{{ sub.label }}
+
+
+
+
+
+
+
+ +
+ 舌象词条
+ ›
+
+
+ +
+ 脉象词条
+ ›
+
+
+
+
+
+
+
+ +
+ 选择辨证/病案词条
+ ›
+
+
+
+
+
+ {{ sub.label }}
+
+ +
+ 选择{{ sub.label }}
+ ›
+
+
+
-
+
+ +
+ 选择常用医嘱
+ ›
+
+
+ +
+ 选择{{ item.label }}词条
+ ›
+
+
+
+
+
+
+
+ +
+ 选择{{ item.label }}词条
+ ›
+
+
+
+
+ 体温
+ °C
+
+
+
+ 身高
+ cm
+
+
+
+ 体重
+ kg
+
+
+
+ 呼吸
+ 次/分
+
+
+
+ 血压
+ mmHg
+
+
+ /
+
+
+
+
+
+
+
-
- 选择常用医嘱
-
-
- 选择{{ item.label }}词条
-
-
-
-
-
-
-
- 选择{{ item.label }}词条
-
-
-
-
- 体温(°C)
-
-
-
- 身高(cm)
-
-
-
- 体重(KG)
-
-
-
- 呼吸(次/分)
-
-
-
- 高压 mmHg
-
-
- /
-
- 低压 mmHg
-
-
-
-
-
-
+
- 清空
- 引用历史
+ 清空
+ 引用历史
+
+
+
+ ✦
+ AI写病历
+
+
+ 保存病历
+
-
- 保存病历
-
+
-
- 引用历史病历
- 共 {{ citeList.length }} 条
+
+
- 加载中…
+ 加载中…
- 暂无历史病历
+ 暂无历史病历
- {{ row._title }}
-
- 挂号 #{{ row.register_id || '-' }}
- 本次挂号
- {{ row.created_at || '—' }}
+
+ ✓
+
+
+ {{ row._title }}
+
+ 挂号 #{{ row.register_id || '-' }}
+ 本次挂号
+ {{ row.created_at || '—' }}
+
+ 主诉:{{ row.chief_complaint }}
- 主诉:{{ row.chief_complaint }}
- 诊断
- {{ citeActiveRecord.diagnosis || '—' }}
- 医嘱
- {{ citeActiveRecord.doctor_order || '—' }}
+
+ 诊断
+ {{ citeActiveRecord.diagnosis || '—' }}
+
+
+ 医嘱
+ {{ citeActiveRecord.doctor_order || '—' }}
+
当前选中的是本次挂号病历,引用后会覆盖编辑区内容
+
-
- 选择词条
- {{ entryKeyword ? ('搜到 ' + entryList.length + ' 条') : ('共 ' + entryList.length + ' 条') }}
+
+
@@ -312,12 +367,25 @@
{{ p.text }}
- {{ row.remark }}
+
- 暂无词条
+ 暂无词条
+
+
+
diff --git a/subPackages/sub_workbench/prescription_v2/index.vue b/subPackages/sub_workbench/prescription_v2/index.vue
index f75d005..3c46e4c 100644
--- a/subPackages/sub_workbench/prescription_v2/index.vue
+++ b/subPackages/sub_workbench/prescription_v2/index.vue
@@ -237,6 +237,17 @@
:ripple="true">
常用方
+
+
+ AI辅助出方
+
+
常用方
+
+
+ AI辅助出方
+
+
{{ activeCategoryLabel }}
+
+
+ AI辅助出方
+
+
-
- 另存常用方
+
+ 另存常用方
+
+ AI辅助出方
+
+
+
@@ -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;