辅方
-
+
0) {
+ row.medicine_id = medicineId
+ }
if (raw?.locked === true || raw?.locked === 1) {
row.locked = true
}
@@ -1578,8 +1593,8 @@ function parseRecipeToHerbs(text: string): { name: string; dosage: number }[] {
return herbs
}
-/** 仅在药品库中存在「完全同名」药材时返回规范药名,否则返回 null(不模糊猜测) */
-async function resolveHerbNameFromLibrary(rawName: string): Promise {
+/** 仅有一条完全同名记录时返回稳定药材身份,否则不猜测。 */
+async function resolveHerbFromLibrary(rawName: string): Promise<{ medicine_id: number; name: string } | null> {
const q = rawName.trim()
if (!q) return null
try {
@@ -1590,8 +1605,10 @@ async function resolveHerbNameFromLibrary(rawName: string): Promise (item.name ?? '').trim() === q)
- return exact ? exact.name.trim() : null
+ const exact = lists.filter((item) => (item.name ?? '').trim() === q)
+ return exact.length === 1
+ ? { medicine_id: Number(exact[0].id), name: exact[0].name.trim() }
+ : null
} catch {
return null
}
@@ -1612,12 +1629,12 @@ async function handlePasteRecipeImport() {
const resolved: Herb[] = []
const skippedNames: string[] = []
for (const row of parsed) {
- const name = await resolveHerbNameFromLibrary(row.name)
- if (!name) {
+ const medicine = await resolveHerbFromLibrary(row.name)
+ if (!medicine) {
skippedNames.push(row.name.trim())
continue
}
- resolved.push({ name, dosage: row.dosage, formula_type: '主方' })
+ resolved.push({ ...medicine, dosage: row.dosage, formula_type: '主方' })
}
const skippedUnique = [...new Set(skippedNames.filter(Boolean))]
if (resolved.length === 0) {
diff --git a/admin/src/views/consumer/prescription/components/GancaoSubmissionReconcileButton.vue b/admin/src/views/consumer/prescription/components/GancaoSubmissionReconcileButton.vue
new file mode 100644
index 00000000..06b9025b
--- /dev/null
+++ b/admin/src/views/consumer/prescription/components/GancaoSubmissionReconcileButton.vue
@@ -0,0 +1,129 @@
+
+
+ 核对甘草提交
+
+
+
+
+
+ 确认已创建
+ 确认未创建
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 确认并记录
+
+
+
+
+
+
diff --git a/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue b/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue
index 053b7c2c..d2e25fc0 100644
--- a/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue
+++ b/admin/src/views/consumer/prescription/components/PrescriptionOrderDetailDrawer.vue
@@ -30,6 +30,13 @@
round
class="ml-2"
>甘草 {{ detailData.gancao_reciperl_order_no }}
+ 洛阳 {{ detailData.ej_pharmacy_order_no }}
diff --git a/admin/src/views/consumer/prescription/components/prescription-order-utils.ts b/admin/src/views/consumer/prescription/components/prescription-order-utils.ts
index cce70c16..8113b916 100644
--- a/admin/src/views/consumer/prescription/components/prescription-order-utils.ts
+++ b/admin/src/views/consumer/prescription/components/prescription-order-utils.ts
@@ -11,6 +11,45 @@ export const TCM_ASSISTANT_ROLE_ID = 2
/** 与 server/config/project.php prescription_audit_roles 默认一致,可处方审核的角色 */
export const PRESCRIPTION_AUDIT_ROLE_IDS = [0, 3, 6]
+export function isRemoteSnapshotLocked(row: Record
| null | undefined): boolean {
+ if (!row) return false
+ if (String(row.gancao_reciperl_order_no || '').trim()) return true
+ if (String(row.ej_pharmacy_order_no || '').trim()) return true
+ if (Number(row.gancao_submit_time || 0) > 0 || Number(row.ej_pharmacy_submit_time || 0) > 0) {
+ return true
+ }
+ return ['PENDING', 'UNKNOWN', 'PENDING_RECONCILE', 'SUCCESS'].includes(
+ String(row.pharmacy_claim_status || '').toUpperCase()
+ )
+}
+
+export type SupplyMode = 'gancao' | 'direct' | 'self'
+
+export function supplyModeKey(row: Record | null | undefined): SupplyMode {
+ if (
+ String(row?.ship_mode || '')
+ .trim()
+ .toLowerCase() === 'direct'
+ )
+ return 'direct'
+ if (String(row?.gancao_reciperl_order_no || '').trim()) return 'gancao'
+ return 'self'
+}
+
+export function supplyModeLabel(row: Record | null | undefined): string {
+ const mode = supplyModeKey(row)
+ if (mode === 'direct') return '洛阳直发'
+ return mode === 'gancao' ? '甘草' : '自营'
+}
+
+export function supplyModeTagType(
+ row: Record | null | undefined
+): 'success' | 'warning' | 'info' {
+ const mode = supplyModeKey(row)
+ if (mode === 'direct') return 'warning'
+ return mode === 'gancao' ? 'success' : 'info'
+}
+
export function formatTime(v: unknown) {
if (v === null || v === undefined || v === '') return '—'
if (typeof v === 'number' && v > 1e9 && v < 1e11) {
@@ -134,6 +173,8 @@ export function logActionText(act: string) {
revoke_rx_audit: '撤回处方审核',
revoke_pay_audit: '撤回支付审核',
gancao_submit: '甘草下单',
+ ej_pharmacy_submit: '洛阳药房下单',
+ ej_pharmacy_callback: '洛阳药房状态',
patch_rx_patient: '处方患者信息',
patch_rx_usage: '服用参数',
update_amount: '修改订单金额',
diff --git a/admin/src/views/consumer/prescription/index.vue b/admin/src/views/consumer/prescription/index.vue
index c3efe1f3..3bb92825 100644
--- a/admin/src/views/consumer/prescription/index.vue
+++ b/admin/src/views/consumer/prescription/index.vue
@@ -696,7 +696,12 @@
-
+
@@ -732,7 +737,12 @@
-
+
@@ -1294,6 +1304,14 @@
+
+
+
+ 甘草药房
+ 洛阳药房
+
+
+
@@ -1709,7 +1727,7 @@ import jsPDF from 'jspdf'
const TcmDiagnosisEditView = defineAsyncComponent(() => import('@/views/tcm/diagnosis/edit.vue'))
type FormulaType = '主方' | '辅方'
-type HerbRow = { name: string; dosage: number; formula_type: FormulaType; locked?: boolean }
+type HerbRow = { medicine_id?: number; name: string; dosage: number; formula_type: FormulaType; locked?: boolean }
type AuxUsageForm = {
dosage_amount?: number
@@ -1814,6 +1832,10 @@ function normalizeHerbRow(raw: any): HerbRow {
dosage: Number(raw?.dosage) || 0,
formula_type: normalizeFormulaType(raw?.formula_type)
}
+ const medicineId = Number(raw?.medicine_id ?? raw?.id ?? 0)
+ if (Number.isInteger(medicineId) && medicineId > 0) {
+ row.medicine_id = medicineId
+ }
if (raw?.locked === true || raw?.locked === 1) {
row.locked = true
}
@@ -2006,6 +2028,7 @@ const createOrderForm = reactive({
service_package: [] as string[],
express_company: 'auto',
tracking_number: '',
+ ship_mode: 'gancao' as 'gancao' | 'direct',
fee_type: 3,
amount: 0,
internal_cost: undefined as number | undefined,
@@ -2252,6 +2275,7 @@ function resetCreateOrderForm() {
createOrderForm.service_package = []
createOrderForm.express_company = 'auto'
createOrderForm.tracking_number = ''
+ createOrderForm.ship_mode = 'gancao'
createOrderForm.fee_type = 3
createOrderForm.amount = 0
createOrderForm.internal_cost = undefined
@@ -2425,6 +2449,7 @@ async function submitCreateOrderFromPrescription() {
: '',
express_company: createOrderForm.express_company || 'auto',
tracking_number: createOrderForm.tracking_number || '',
+ ship_mode: createOrderForm.ship_mode,
fee_type: createOrderForm.fee_type,
amount: createOrderForm.amount,
remark_extra: createOrderForm.remark_extra || '',
@@ -3601,7 +3626,7 @@ function parseRecipePasteToHerbs(text: string): { name: string; dosage: number }
return herbs
}
-async function resolvePasteHerbNameFromLibrary(rawName: string): Promise {
+async function resolvePasteHerbFromLibrary(rawName: string): Promise<{ medicine_id: number; name: string } | null> {
const q = rawName.trim()
if (!q) return null
try {
@@ -3612,8 +3637,10 @@ async function resolvePasteHerbNameFromLibrary(rawName: string): Promise (item.name ?? '').trim() === q)
- return exact ? exact.name.trim() : null
+ const exact = lists.filter((item) => (item.name ?? '').trim() === q)
+ return exact.length === 1
+ ? { medicine_id: Number(exact[0].id), name: exact[0].name.trim() }
+ : null
} catch {
return null
}
@@ -3634,12 +3661,12 @@ async function handlePasteRecipeImport() {
const resolved: HerbRow[] = []
const skippedNames: string[] = []
for (const row of parsed) {
- const name = await resolvePasteHerbNameFromLibrary(row.name)
- if (!name) {
+ const medicine = await resolvePasteHerbFromLibrary(row.name)
+ if (!medicine) {
skippedNames.push(row.name.trim())
continue
}
- resolved.push({ name, dosage: row.dosage, formula_type: '主方' })
+ resolved.push({ ...medicine, dosage: row.dosage, formula_type: '主方' })
}
const skippedUnique = [...new Set(skippedNames.filter(Boolean))]
if (resolved.length === 0) {
diff --git a/admin/src/views/consumer/prescription/list.vue b/admin/src/views/consumer/prescription/list.vue
index b182d2a9..9992724e 100644
--- a/admin/src/views/consumer/prescription/list.vue
+++ b/admin/src/views/consumer/prescription/list.vue
@@ -157,7 +157,11 @@
-
+
@@ -257,7 +261,7 @@ const editForm = reactive({
id: 0,
prescription_name: '',
formula_type: '主方',
- herbs: [] as Array<{ name: string; dosage: number }>,
+ herbs: [] as Array<{ medicine_id?: number; name: string; dosage: number }>,
is_public: 0,
disable_edit: 0
})
diff --git a/admin/src/views/consumer/prescription/order_list.vue b/admin/src/views/consumer/prescription/order_list.vue
index b8408da2..7dba0f51 100644
--- a/admin/src/views/consumer/prescription/order_list.vue
+++ b/admin/src/views/consumer/prescription/order_list.vue
@@ -373,9 +373,9 @@
- {{ String(row.gancao_reciperl_order_no || '').trim() ? '甘草' : '自营' }}
+ {{ supplyModeLabel(row) }}
#{{ row.id }}
@@ -594,13 +594,13 @@
@click="confirmWithdraw(row)"
>撤回
上传药方
+ :loading="pharmacySubmitId === row.id"
+ @click="confirmUploadToPharmacy(row)"
+ >上传药房
@@ -638,7 +638,7 @@