diff --git a/.cursor/rules/Code-Standards.mdc b/.cursor/rules/Code-Standards.mdc
new file mode 100644
index 0000000..99af2df
--- /dev/null
+++ b/.cursor/rules/Code-Standards.mdc
@@ -0,0 +1,9 @@
+---
+description:
+alwaysApply: true
+---
+
+1. 写代码的时候要补充详细的中文注释(每个方法是干嘛的,为什么要这样写),如果是工作区,则所有项目都适用该规则
+2. 注意不要生成太多的空行,上一部分代码和下一部分代码中间的空行不要大于2行
+3. 有封装好的方法、组件需要复用,不要重复造轮子
+4. 小程序端的抽屉全部需要用page-container来防止用户意外退出页面
diff --git a/.gitignore b/.gitignore
index dc76fc1..5c99acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,4 +114,5 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
-package-lock.json
\ No newline at end of file
+package-lock.json
+/.cursor/skills/
diff --git a/subPackages/sub_workbench/prescription_v2/components/ChineseMedicineConfig.vue b/subPackages/sub_workbench/prescription_v2/components/ChineseMedicineConfig.vue
index 9d89cf4..3c13a6f 100644
--- a/subPackages/sub_workbench/prescription_v2/components/ChineseMedicineConfig.vue
+++ b/subPackages/sub_workbench/prescription_v2/components/ChineseMedicineConfig.vue
@@ -9,15 +9,15 @@
自制剂
委托调剂
@@ -36,8 +36,8 @@
v-for="item in packageMethodList"
:key="item.id"
class="modern-pill outline"
- :class="{ active: localConfig.packageMethodId === item.id }"
- @click="handlePackageMethodTagClick(item)"
+ :class="{ active: localConfig.packageMethodId === item.id, disabled: disabled }"
+ @click="!disabled && handlePackageMethodTagClick(item)"
>
{{ item.name }}
@@ -56,8 +56,8 @@
v-for="item in processRuleList"
:key="item.id"
class="modern-pill outline"
- :class="{ active: localConfig.processRuleId === item.id }"
- @click="handleProcessRuleTagClick(item)"
+ :class="{ active: localConfig.processRuleId === item.id, disabled: disabled }"
+ @click="!disabled && handleProcessRuleTagClick(item)"
>
{{ item.name }}
@@ -73,8 +73,8 @@
v-for="item in childProcessRuleList"
:key="item.id"
class="modern-pill outline"
- :class="{ active: localConfig.childProcessRuleId === item.id }"
- @click="handleChildProcessRuleTagClick(item)"
+ :class="{ active: localConfig.childProcessRuleId === item.id, disabled: disabled }"
+ @click="!disabled && handleChildProcessRuleTagClick(item)"
>
{{ item.name }}
@@ -90,8 +90,8 @@
v-for="item in processRuleNoteList"
:key="item.id"
class="modern-pill outline"
- :class="{ active: localConfig.processRuleNoteId === item.id }"
- @click="handleProcessRuleNoteTagClick(item)"
+ :class="{ active: localConfig.processRuleNoteId === item.id, disabled: disabled }"
+ @click="!disabled && handleProcessRuleNoteTagClick(item)"
>
{{ item.note || item.name }}
@@ -108,6 +108,7 @@
[]
+ },
+ /**
+ * 特色方锁定后禁止修改配置
+ */
+ disabled: {
+ type: Boolean,
+ default: false
}
},
data() {
@@ -368,5 +377,10 @@ export default {
border-color: #00A88A;
}
}
+
+ &.disabled {
+ opacity: 0.5;
+ pointer-events: none;
+ }
}
\ No newline at end of file
diff --git a/subPackages/sub_workbench/prescription_v2/index.vue b/subPackages/sub_workbench/prescription_v2/index.vue
index 0f55474..42a0a20 100644
--- a/subPackages/sub_workbench/prescription_v2/index.vue
+++ b/subPackages/sub_workbench/prescription_v2/index.vue
@@ -2,7 +2,7 @@
@@ -54,6 +54,9 @@
剂量:{{ specialPrescriptionRecord.dose_count }} 剂
+
+ 规格:{{ specialPrescriptionPurchaseLabel }}
+
常用方
-
+
-
+
删除
-
+
调整用量
@@ -236,7 +239,7 @@
-
+
@@ -260,7 +263,7 @@
:ripple="true">
常用方
-
+
药材清单 (共{{currentDrugs.length}}味)
- 修改剂量
- 清空
+
+ 修改剂量
+ 清空
+
+ 清空药方
@@ -289,7 +299,7 @@
v-for="(it, tcmIndex) in currentDrugs"
:key="tcmTagKey(it, tcmIndex)"
:data-idx="tcmIndex"
- @longpress.stop="onTcmTagLongPress">
+ @longpress.stop="!isSpecialPrescriptionCartLocked && onTcmTagLongPress">
{{it.drug_name || it.name}}
{{String(it.number)}}{{it.unit ? it.unit.name : 'g'}}
-
+
@@ -313,7 +323,7 @@
产品/器械
-
+
-
+
删除
@@ -375,15 +385,20 @@
-
+
-
+
- 另存常用方
+
+ 另存常用方
+
{{ isCommonPrescription ? '保存常用方' : (isSalespersonTransferMode ? '提交传方' : '发送处方') }}
@@ -624,6 +644,10 @@ import {
import {
pickSpecialPrescriptionRecord,
applySpecialPrescriptionPayload,
+ resolveSpecialPrescriptionPurchaseLabel,
+ clearAppliedSpecialPrescription,
+ isSpecialPrescriptionCartLocked,
+ guardSpecialPrescriptionCartEdit,
} from './utils/specialPrescription.js';
const PENDING_RX_FROM_CHAT = 'xk_pending_rx_from_chat';
@@ -733,6 +757,11 @@ export default {
specialPrescriptionRecord: null,
applyingSpecialPrescription: false,
pendingImportSpecial: false,
+ /** 医生已导入的特色方 ID,仅 >0 时提交走特色方计价 */
+ appliedSpecialPrescriptionId: 0,
+ appliedSpecialPrescriptionFreeShipping: false,
+ /** 特色方 apply 返回的套餐/贴数总价,用于底部合计 */
+ appliedSpecialPrescriptionExpectedTotal: null,
/** 在线复诊中药:术语表(HTTP 拉取,内存缓存) */
traditionalTcmData: null,
@@ -795,7 +824,9 @@ export default {
return Number(this.registerStoreInfo?.allow_insurance_category ?? 0) === 1;
},
canPreSendPriceAdjust() {
- return Number(this.enableOrderPricePercentAdjust) === 1 && this.currentDrugs.length > 0;
+ return Number(this.enableOrderPricePercentAdjust) === 1
+ && this.currentDrugs.length > 0
+ && !this.isSpecialPrescriptionCartLocked;
},
preSendDiscountLabel() {
return formatPriceDiscountLabel(this.priceDiscount);
@@ -825,6 +856,13 @@ export default {
return PrescriptionCalculator.calculateProcessingFee(processRule, this.chineseConfig.dosage, totalDrugQuantity);
},
totalPrice() {
+ const expected = this.appliedSpecialPrescriptionExpectedTotal;
+ if (expected != null && !Number.isNaN(Number(expected)) && Number(expected) > 0) {
+ const base = parseFloat(expected);
+ const fee = parseFloat(this.treatmentPrice || 0);
+ const process = this.activeCategory === 1 ? parseFloat(this.processingFee || 0) : 0;
+ return (base + fee + process).toFixed(2);
+ }
const total = PrescriptionCalculator.calculateTotalPrice({
category: this.activeCategory,
drugs: this.currentDrugs,
@@ -863,6 +901,24 @@ export default {
hasSpecialPrescriptionRecord() {
return !!this.specialPrescriptionRecord;
},
+ /** 导入卡片:SKU 名或单剂 */
+ specialPrescriptionPurchaseLabel() {
+ return resolveSpecialPrescriptionPurchaseLabel(this.specialPrescriptionRecord);
+ },
+ /** 已导入特色方时锁定药方不可编辑 */
+ isSpecialPrescriptionCartLocked() {
+ return isSpecialPrescriptionCartLocked(this);
+ },
+ /** 底部发送按钮宽度(左侧仅「另存常用方」时需留出空间) */
+ bottomSendBtnWidth() {
+ if (this.isSalespersonTransferMode || this.isCommonPrescription) {
+ return '686rpx';
+ }
+ if (this.supportsCommonPrescriptionTemplate) {
+ return '400rpx';
+ }
+ return '686rpx';
+ },
navbarTitle() {
if (this.isSalespersonTransferMode) {
return '传方';
@@ -1241,9 +1297,11 @@ export default {
uni.showToast({ title: discount === 100 ? '已清除浮动' : '调价成功', icon: 'none' });
},
clearPreSendPriceDiscount() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
this.applyPreSendPriceDiscount(100);
},
openPreSendOrderPriceAdjust() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
if (!this.canPreSendPriceAdjust) return;
this.currentDrugs = this.snapshotDrugOrigins(this.currentDrugs);
this.priceAdjustVisible = true;
@@ -1394,7 +1452,9 @@ export default {
console.error('获取诊所信息失败:', error);
}
},
- handleOpenDiagnosisModal() { this.showDiagnosisModal = true; },
+ handleOpenDiagnosisModal() {
+ this.showDiagnosisModal = true;
+ },
async ensureTraditionalTcmData() {
if (this.traditionalTcmData) return this.traditionalTcmData;
if (this.traditionalTcmLoadPromise) return this.traditionalTcmLoadPromise;
@@ -1448,7 +1508,9 @@ export default {
this.diagnosisText = this.diagnoses.map(item => item.name).join(',');
this.saveToLocalStorage();
},
- handleOpenDoctorOrderModal() { this.showDoctorOrderModal = true; },
+ handleOpenDoctorOrderModal() {
+ this.showDoctorOrderModal = true;
+ },
handleDoctorOrderConfirm(doctorOrderText) {
this.medicalAdvice = doctorOrderText;
this.saveToLocalStorage();
@@ -1463,6 +1525,13 @@ export default {
applySpecialPrescriptionPayload(this, data);
await this.loadRegisterOrderType();
this.$toast(`已导入特色方:${data.special_prescription_name || ''}`);
+ if (data.price_hint) {
+ uni.showModal({
+ title: '价格提示',
+ content: data.price_hint,
+ showCancel: false,
+ });
+ }
} catch (error) {
console.error('导入特色方失败:', error);
this.$toast('导入特色方失败,请稍后重试');
@@ -1472,6 +1541,7 @@ export default {
},
async handleSelectCommonPrescription(item) {
try {
+ clearAppliedSpecialPrescription(this);
const payload = item && item.apply_payload;
if (!payload || !Array.isArray(payload.drugs)) {
this.$toast('常用方数据异常');
@@ -1636,10 +1706,12 @@ export default {
}
},
handleOpenWesternMedicineModal() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
this.westernModalInitialKeyword = '';
this.showWesternMedicineModal = true;
},
handleSelectWesternDrug(drug) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
if (PrescriptionValidator.isDrugExists(this.currentDrugs, drug)) {
this.$toast('该药品已在列表中');
return;
@@ -1671,6 +1743,7 @@ export default {
this.$toast(`已添加 ${newDrug.drug_name}`);
},
handleOpenChineseMedicineModal() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
this.chineseModalInitialKeyword = '';
this.showChineseMedicineModal = true;
},
@@ -1684,6 +1757,7 @@ export default {
* 中药标签长按菜单(小程序 showActionSheet 回调里勿依赖闭包 item,用 dataset.idx + currentDrugs)
*/
onTcmTagLongPress(e) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
const raw = e && e.currentTarget && e.currentTarget.dataset ? e.currentTarget.dataset.idx : undefined;
const idx = raw === undefined || raw === '' ? NaN : Number(raw);
if (Number.isNaN(idx) || idx < 0 || idx >= this.currentDrugs.length) return;
@@ -1702,6 +1776,7 @@ export default {
});
},
handleSelectChineseDrug(drugs) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
this.currentDrugs = applyRatioToDrugs(
this.snapshotDrugOrigins(drugs || []),
this.priceDiscount,
@@ -1709,8 +1784,12 @@ export default {
);
this.saveToLocalStorage();
},
- handleOpenSimpleProductModal() { this.showSimpleProductModal = true; },
+ handleOpenSimpleProductModal() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
+ this.showSimpleProductModal = true;
+ },
handleSelectSimpleProduct(product) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
if (PrescriptionValidator.isDrugExists(this.currentDrugs, product)) {
this.$toast('该产品已在列表中');
return;
@@ -1731,6 +1810,7 @@ export default {
this.$toast(`已添加 ${newProduct.drug_name}`);
},
handleEditWesternDrug(drug, index) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
this.editingDrug = JSON.parse(JSON.stringify(drug));
this.editingDrugIndex = index;
this.showWesternUsageModal = true;
@@ -1754,6 +1834,7 @@ export default {
this.editingDrugIndex = -1;
},
handleRemoveDrug(index) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
uni.showModal({
title: '移除药品',
content: '确定要从处方中移除该药品吗?',
@@ -1787,6 +1868,7 @@ export default {
}
},
handleRemoveAllDrugs() {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
uni.showModal({
title: '清空药材',
content: '确定要清空所有中药材吗?',
@@ -1800,6 +1882,7 @@ export default {
});
},
async handleUpdateChineseConfig(config) {
+ if (guardSpecialPrescriptionCartEdit(this)) return;
const oldRuleType = this.chineseConfig.ruleType;
const oldProcessRuleId = this.chineseConfig.processRuleId;
const oldChildProcessRuleId = this.chineseConfig.childProcessRuleId;
@@ -2158,6 +2241,7 @@ export default {
patient: patientData, drugs: drugsData, diagnosis: clinical_diagnose, medicalAdvice: this.medicalAdvice, total: parseFloat(this.totalPrice), category: this.feeCategory, drug_type: 2, register_id: parseInt(this.registerId), treatment_price: parseFloat(this.treatmentPrice || 0), prescription_type: this.activeCategory, doctor_second_sign: doctorSecondSignValue, send_mode: finalSendMode, custom_store_id: finalSendMode === 1 ? finalStoreId : null,
salesperson_transfer_prescription_id: this.salespersonTransferPrescriptionId || undefined,
price_discount: this.priceDiscount,
+ special_prescription_id: Number(this.appliedSpecialPrescriptionId) || 0,
};
if (this.activeCategory === 1) {
params.package_method_id = this.chineseConfig.packageMethodId || null; params.process_rule_id = this.chineseConfig.processRuleId || null; params.process_rule_note_id = this.chineseConfig.processRuleNoteId || null; params.child_process_rule_id = this.chineseConfig.childProcessRuleId || null; params.process_rule_type = this.chineseConfig.ruleType || 1; params.processing_fee = this.processingFee; params.dosage = this.chineseConfig.dosage || 7; params.day_dosage = this.chineseConfig.dayDosage || 2;
@@ -2235,6 +2319,33 @@ export default {
this.onlineTcmDiseasesId = null;
this.onlineTcmMethodId = null;
this.onlineTcmSyndromeId = null;
+ clearAppliedSpecialPrescription(this);
+ },
+ /**
+ * 清空药方(含特色方导入状态),与 PC 接诊「清空药方」一致
+ */
+ handleClearPrescriptionCart() {
+ const hasContent = this.currentDrugs.length > 0
+ || this.diagnoses.length > 0
+ || !!(this.diagnosisText && String(this.diagnosisText).trim())
+ || !!(this.medicalAdvice && String(this.medicalAdvice).trim())
+ || Number(this.appliedSpecialPrescriptionId) > 0;
+ if (!hasContent) {
+ this.$toast('当前无药方内容');
+ return;
+ }
+ uni.showModal({
+ title: '清空药方',
+ content: '确定清空当前药方吗?特色方将需重新导入。',
+ confirmColor: '#FC3636',
+ success: ({ confirm }) => {
+ if (!confirm) return;
+ this.clearPrescriptionData();
+ this.saveToLocalStorage();
+ PrescriptionStorage.clearAllPrescriptionData(this.registerId);
+ this.$toast('已清空药方');
+ },
+ });
},
handleSaveAsCommonPrescription() {
if (this.currentDrugs.length === 0) { this.$toast('请先添加药品后再保存为常用方'); return; }
@@ -2657,4 +2768,7 @@ export default {
padding: 0 32rpx;
flex-shrink: 0;
}
+.btnText--danger {
+ color: #FC3636;
+}
\ No newline at end of file
diff --git a/subPackages/sub_workbench/prescription_v2/utils/specialPrescription.js b/subPackages/sub_workbench/prescription_v2/utils/specialPrescription.js
index aaff35b..b672b0d 100644
--- a/subPackages/sub_workbench/prescription_v2/utils/specialPrescription.js
+++ b/subPackages/sub_workbench/prescription_v2/utils/specialPrescription.js
@@ -12,6 +12,54 @@ export function pickSpecialPrescriptionRecord(data) {
);
}
+/**
+ * 导入卡片规格文案:SKU 套餐名或单剂
+ * @param record {{ sku_id?, sku_name? }|null}
+ */
+export function resolveSpecialPrescriptionPurchaseLabel(record) {
+ if (!record) return ''
+ return Number(record.sku_id) > 0
+ ? (record.sku_name || 'SKU套餐')
+ : '单剂'
+}
+
+/**
+ * 是否已导入特色方并锁定药方编辑
+ * @param page 处方页实例
+ */
+export function isSpecialPrescriptionCartLocked(page) {
+ if (!page) return false
+ return Number(page.appliedSpecialPrescriptionId) > 0
+}
+
+/** 特色方锁定提示文案 */
+export const SP_CART_LOCK_MSG = '特色方药方不可修改,请先清空药方'
+
+/**
+ * 特色方锁定中则 toast 并返回 true
+ * @param page 处方页实例
+ */
+export function guardSpecialPrescriptionCartEdit(page) {
+ if (!isSpecialPrescriptionCartLocked(page)) return false
+ if (page.$toast) {
+ page.$toast(SP_CART_LOCK_MSG)
+ } else {
+ uni.showToast({ title: SP_CART_LOCK_MSG, icon: 'none' })
+ }
+ return true
+}
+
+/**
+ * 清空「已导入特色方」状态(普通开方/常用方时调用)
+ * @param page 处方页实例
+ */
+export function clearAppliedSpecialPrescription(page) {
+ if (!page) return
+ page.appliedSpecialPrescriptionId = 0
+ page.appliedSpecialPrescriptionExpectedTotal = null
+ page.appliedSpecialPrescriptionFreeShipping = false
+}
+
/**
* 将 apply 接口返回的数据写入处方页 state
*/
@@ -42,13 +90,18 @@ export function applySpecialPrescriptionPayload(page, data) {
dayDosage: data.day_dosage ?? 2,
packageMethodId: data.package_method_id ?? 2,
};
- if (data.clinical_diagnose) {
+ if (data.clinical_diagnose !== undefined && data.clinical_diagnose !== null) {
page.diagnosisText = data.clinical_diagnose;
const names = String(data.clinical_diagnose).split(',').filter(Boolean);
page.diagnoses = names.map((name, index) => ({ id: `temp_${index}`, name }));
}
- if (data.doctor_order) {
+ if (data.doctor_order !== undefined && data.doctor_order !== null) {
page.medicalAdvice = data.doctor_order;
}
+ page.appliedSpecialPrescriptionId = data.special_prescription_id || 0;
+ page.appliedSpecialPrescriptionFreeShipping = Number(data.is_free_shipping) === 1;
+ page.appliedSpecialPrescriptionExpectedTotal = data.expected_total_price != null
+ ? parseFloat(data.expected_total_price)
+ : null;
page.saveToLocalStorage();
}
diff --git a/subPackages/sub_workbench/workbench_infoPatient.vue b/subPackages/sub_workbench/workbench_infoPatient.vue
index 494d60f..514acb5 100644
--- a/subPackages/sub_workbench/workbench_infoPatient.vue
+++ b/subPackages/sub_workbench/workbench_infoPatient.vue
@@ -57,6 +57,10 @@
剂量
{{ infoList.special_prescription_patient_record.dose_count }} 剂
+
+ 规格
+ {{ specialPrescriptionPurchaseLabel }}
+
{
+ this.$go(this.buildPrescriptionV2Url(registerId, patientId, importSpecial));
+ }, 800);
+ },
// 接诊
- accept(e, pid) {
- receptionApi(e).then((res) => {
- if (this.apiOk(res)) {
- this.$toast('已接诊');
- this.getList()
- if (infoList.special_prescription_patient_record) {
- return;
- }
- setTimeout(() => {
- let url = `/subPackages/sub_workbench/prescription_v2/index?register_id=${e}`;
- if (pid) url += `&patient_id=${pid}`;
- this.$go(url);
- }, 800)
- } else {
+ accept(registerId, patientId) {
+ // 接诊前缓存特色方记录,避免 getList 异步刷新导致判断丢失
+ const hasSp = !!this.infoList?.special_prescription_patient_record;
+ receptionApi(registerId).then((res) => {
+ if (!this.apiOk(res)) {
this.$toast(this.apiMsg(res));
+ return;
}
- })
+ this.$toast('已接诊');
+ this.getList();
+ if (!hasSp) {
+ this.navigateToPrescriptionV2(registerId, patientId, false);
+ return;
+ }
+ uni.showModal({
+ title: '导入特色方',
+ content: '该患者已选择特色方,是否立即导入?',
+ confirmColor: '#00A88A',
+ cancelColor: '#8A92A3',
+ success: (modalRes) => {
+ this.navigateToPrescriptionV2(registerId, patientId, !!modalRes.confirm);
+ },
+ });
+ });
},
// 结束问诊
acceptEnds(e) {
@@ -540,7 +580,7 @@ export default {
goToPrescriptionV2() {
const registerId = this.infoList.id;
const patientId = this.infoList.user_patient_id;
- this.$go(`/subPackages/sub_workbench/prescription_v2/index?register_id=${registerId}&patient_id=${patientId}`);
+ this.$go(this.buildPrescriptionV2Url(registerId, patientId, false));
},
goImportSpecialPrescription() {
const registerId = this.infoList.id;
@@ -549,11 +589,7 @@ export default {
this.$toast('挂号信息无效');
return;
}
- let url = `/subPackages/sub_workbench/prescription_v2/index?register_id=${registerId}&import_special=1`;
- if (patientId) {
- url += `&patient_id=${patientId}`;
- }
- this.$go(url);
+ this.$go(this.buildPrescriptionV2Url(registerId, patientId, true));
},
goToReusePrescription(prescriptionId) {
const registerId = this.infoList.id;