feat: integrate Luoyang pharmacy workflow
This commit is contained in:
@@ -528,6 +528,21 @@ export function prescriptionOrderSubmitGancaoRecipel(params: { id: number }) {
|
||||
return request.post({ url: '/tcm.prescriptionOrder/submitGancaoRecipel', params })
|
||||
}
|
||||
|
||||
/** 按业务订单 ship_mode 上传:gancao 走甘草,direct 走洛阳药房 ERP */
|
||||
export function prescriptionOrderUploadToPharmacy(params: { id: number }) {
|
||||
return request.post({ url: '/tcm.prescriptionOrder/uploadToPharmacy', params })
|
||||
}
|
||||
|
||||
/** 人工核对甘草不确定提交结果。 */
|
||||
export function prescriptionOrderConfirmGancaoSubmission(params: {
|
||||
id: number
|
||||
resolution: 'CONFIRM_SUCCESS' | 'CONFIRM_NOT_CREATED'
|
||||
remote_order_no?: string
|
||||
note: string
|
||||
}) {
|
||||
return request.post({ url: '/tcm.prescriptionOrder/confirmGancaoSubmission', params })
|
||||
}
|
||||
|
||||
/** 甘草药管家:预下单测试(仅 CTM_PREVIEW,不提交订单) */
|
||||
export function prescriptionOrderPreviewGancaoRecipel(params: {
|
||||
id: number
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span v-if="disabled" class="medicine-name-readonly">{{ modelValue || '—' }}</span>
|
||||
<el-select
|
||||
v-else
|
||||
:model-value="modelValue"
|
||||
:model-value="selectedMedicineId"
|
||||
class="medicine-name-select w-full"
|
||||
filterable
|
||||
remote
|
||||
@@ -14,7 +14,14 @@
|
||||
@visible-change="onVisibleChange"
|
||||
@update:model-value="onUpdate"
|
||||
>
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.name" />
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id">
|
||||
<div class="medicine-name-option">
|
||||
<span>{{ item.name }}</span>
|
||||
<span class="medicine-name-option__meta">
|
||||
{{ item.id > 0 ? [item.supplier, item.unit, `ID ${item.id}`].filter(Boolean).join(' · ') : '历史名称,请重新选择' }}
|
||||
</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
@@ -24,6 +31,7 @@ import { medicineLists } from '@/api/medicine'
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
medicineId?: number | null
|
||||
disabled?: boolean
|
||||
}>(),
|
||||
{ disabled: false }
|
||||
@@ -31,18 +39,30 @@ const props = withDefaults(
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: string]
|
||||
'update:medicineId': [value: number | undefined]
|
||||
}>()
|
||||
|
||||
const loading = ref(false)
|
||||
const options = ref<{ id: number; name: string }[]>([])
|
||||
type MedicineOption = { id: number; name: string; supplier?: string; unit?: string }
|
||||
const options = ref<MedicineOption[]>([])
|
||||
|
||||
const selectedMedicineId = computed<number | ''>(() => {
|
||||
const id = Number(props.medicineId)
|
||||
if (Number.isInteger(id) && id > 0) {
|
||||
return id
|
||||
}
|
||||
return (props.modelValue || '').trim() ? 0 : ''
|
||||
})
|
||||
|
||||
function ensureCurrentInOptions() {
|
||||
const v = (props.modelValue || '').trim()
|
||||
if (!v) {
|
||||
return
|
||||
}
|
||||
if (!options.value.some((o) => o.name === v)) {
|
||||
options.value = [{ id: 0, name: v }, ...options.value]
|
||||
const id = Number(props.medicineId)
|
||||
const currentId = Number.isInteger(id) && id > 0 ? id : 0
|
||||
if (!options.value.some((o) => o.id === currentId)) {
|
||||
options.value = [{ id: currentId, name: v }, ...options.value]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +76,7 @@ const remoteMethod = async (query: string) => {
|
||||
page_size: 100,
|
||||
status: 1
|
||||
})
|
||||
options.value = res.lists || []
|
||||
options.value = (res.lists || []) as MedicineOption[]
|
||||
ensureCurrentInOptions()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
@@ -72,13 +92,19 @@ const onVisibleChange = (open: boolean) => {
|
||||
}
|
||||
}
|
||||
|
||||
const onUpdate = (val: string) => {
|
||||
emit('update:modelValue', val || '')
|
||||
const onUpdate = (val: number | string) => {
|
||||
const id = Number(val)
|
||||
const selected = id > 0 ? options.value.find((item) => item.id === id) : undefined
|
||||
emit('update:modelValue', selected?.name || '')
|
||||
emit('update:medicineId', selected?.id)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => [props.modelValue, props.medicineId],
|
||||
() => {
|
||||
if (!(props.modelValue || '').trim() && Number(props.medicineId) > 0) {
|
||||
emit('update:medicineId', undefined)
|
||||
}
|
||||
ensureCurrentInOptions()
|
||||
},
|
||||
{ immediate: true }
|
||||
@@ -93,4 +119,23 @@ watch(
|
||||
.medicine-name-select.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.medicine-name-option {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 60%);
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.medicine-name-option__meta {
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -139,7 +139,12 @@
|
||||
:class="{ 'herb-editor-card--dup': isDuplicateHerbName(row.index) }"
|
||||
>
|
||||
<div class="herb-editor-card__title">主方 {{ row.index + 1 }}</div>
|
||||
<MedicineNameSelect v-model="formData.herbs[row.index].name" :disabled="herbsLocked" class="herb-editor-card__select" />
|
||||
<MedicineNameSelect
|
||||
v-model="formData.herbs[row.index].name"
|
||||
v-model:medicine-id="formData.herbs[row.index].medicine_id"
|
||||
:disabled="herbsLocked"
|
||||
class="herb-editor-card__select"
|
||||
/>
|
||||
<div
|
||||
v-if="isDuplicateHerbName(row.index)"
|
||||
class="herb-editor-card__dup-hint text-amber-600 text-xs mb-1"
|
||||
@@ -176,7 +181,12 @@
|
||||
:class="{ 'herb-editor-card--dup': isDuplicateHerbName(row.index) }"
|
||||
>
|
||||
<div class="herb-editor-card__title">辅方</div>
|
||||
<MedicineNameSelect v-model="formData.herbs[row.index].name" :disabled="herbsLocked" class="herb-editor-card__select" />
|
||||
<MedicineNameSelect
|
||||
v-model="formData.herbs[row.index].name"
|
||||
v-model:medicine-id="formData.herbs[row.index].medicine_id"
|
||||
:disabled="herbsLocked"
|
||||
class="herb-editor-card__select"
|
||||
/>
|
||||
<div
|
||||
v-if="isDuplicateHerbName(row.index)"
|
||||
class="herb-editor-card__dup-hint text-amber-600 text-xs mb-1"
|
||||
@@ -1045,6 +1055,7 @@ import { Search } from '@element-plus/icons-vue'
|
||||
type FormulaType = '主方' | '辅方'
|
||||
|
||||
interface Herb {
|
||||
medicine_id?: number
|
||||
name: string
|
||||
dosage: number
|
||||
formula_type?: FormulaType
|
||||
@@ -1071,6 +1082,10 @@ function normalizeHerbRow(raw: any): Herb {
|
||||
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
|
||||
}
|
||||
@@ -1578,8 +1593,8 @@ function parseRecipeToHerbs(text: string): { name: string; dosage: number }[] {
|
||||
return herbs
|
||||
}
|
||||
|
||||
/** 仅在药品库中存在「完全同名」药材时返回规范药名,否则返回 null(不模糊猜测) */
|
||||
async function resolveHerbNameFromLibrary(rawName: string): Promise<string | null> {
|
||||
/** 仅有一条完全同名记录时返回稳定药材身份,否则不猜测。 */
|
||||
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<string | nul
|
||||
status: 1
|
||||
})
|
||||
const lists = (res.lists || []) as { id: number; name: string }[]
|
||||
const exact = lists.find((item) => (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) {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<template v-if="needsReconcile">
|
||||
<el-button
|
||||
v-perms="['tcm.prescriptionOrder/confirmGancaoSubmission']"
|
||||
type="danger"
|
||||
size="small"
|
||||
plain
|
||||
@click="openDialog"
|
||||
>核对甘草提交</el-button
|
||||
>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="人工核对甘草提交"
|
||||
width="min(92vw, 520px)"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-alert
|
||||
title="请先在甘草后台核对。本操作会写入不可变更的审计记录。"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
class="mb-4"
|
||||
/>
|
||||
<el-form label-width="100px" @submit.prevent="submit">
|
||||
<el-form-item label="核对结果" required>
|
||||
<el-radio-group v-model="form.resolution">
|
||||
<el-radio label="CONFIRM_SUCCESS">确认已创建</el-radio>
|
||||
<el-radio label="CONFIRM_NOT_CREATED">确认未创建</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.resolution === 'CONFIRM_SUCCESS'"
|
||||
label="甘草单号"
|
||||
required
|
||||
>
|
||||
<el-input v-model="form.remote_order_no" maxlength="64" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="核对依据" required>
|
||||
<el-input
|
||||
v-model="form.note"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
maxlength="1000"
|
||||
show-word-limit
|
||||
placeholder="例如:核对时间、甘草后台查询条件及结果"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submit"
|
||||
>确认并记录</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { prescriptionOrderConfirmGancaoSubmission } from '@/api/tcm'
|
||||
import feedback from '@/utils/feedback'
|
||||
|
||||
const props = defineProps<{ order: Record<string, any> | null | undefined }>()
|
||||
const emit = defineEmits<{ resolved: [] }>()
|
||||
|
||||
const needsReconcile = computed(() => {
|
||||
const target = String(props.order?.pharmacy_claim_target || '')
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
const status = String(props.order?.pharmacy_claim_status || '')
|
||||
.trim()
|
||||
.toUpperCase()
|
||||
const leaseExpiresAt = Number(props.order?.pharmacy_claim_lease_expires_at || 0)
|
||||
const expiredPending =
|
||||
status === 'PENDING' &&
|
||||
leaseExpiresAt > 0 &&
|
||||
leaseExpiresAt <= Math.floor(Date.now() / 1000)
|
||||
return (
|
||||
target === 'gancao' && (expiredPending || ['UNKNOWN', 'PENDING_RECONCILE'].includes(status))
|
||||
)
|
||||
})
|
||||
const visible = ref(false)
|
||||
const submitting = ref(false)
|
||||
const form = reactive({
|
||||
resolution: 'CONFIRM_SUCCESS' as 'CONFIRM_SUCCESS' | 'CONFIRM_NOT_CREATED',
|
||||
remote_order_no: '',
|
||||
note: ''
|
||||
})
|
||||
|
||||
function openDialog() {
|
||||
form.resolution = 'CONFIRM_SUCCESS'
|
||||
form.remote_order_no = ''
|
||||
form.note = ''
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
const id = Number(props.order?.id)
|
||||
if (!id) return
|
||||
if (form.resolution === 'CONFIRM_SUCCESS' && !form.remote_order_no.trim()) {
|
||||
feedback.msgError('请填写甘草药方单号')
|
||||
return
|
||||
}
|
||||
if (!form.note.trim()) {
|
||||
feedback.msgError('请填写甘草后台核对依据')
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
await prescriptionOrderConfirmGancaoSubmission({
|
||||
id,
|
||||
resolution: form.resolution,
|
||||
remote_order_no:
|
||||
form.resolution === 'CONFIRM_SUCCESS' ? form.remote_order_no.trim() : '',
|
||||
note: form.note.trim()
|
||||
})
|
||||
feedback.msgSuccess('甘草提交核对已记录')
|
||||
visible.value = false
|
||||
emit('resolved')
|
||||
} catch {
|
||||
// Request interceptor presents the server-side reconciliation error.
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -30,6 +30,13 @@
|
||||
round
|
||||
class="ml-2"
|
||||
>甘草 {{ detailData.gancao_reciperl_order_no }}</el-tag>
|
||||
<el-tag
|
||||
v-if="detailData?.ej_pharmacy_order_no"
|
||||
type="warning"
|
||||
effect="plain"
|
||||
round
|
||||
class="ml-2"
|
||||
>洛阳 {{ detailData.ej_pharmacy_order_no }}</el-tag>
|
||||
<!-- 完整版:发货类型切换等 -->
|
||||
<slot v-if="detailData" name="header-extra" :detail="detailData" />
|
||||
</div>
|
||||
|
||||
@@ -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<string, unknown> | 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<string, unknown> | 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<string, unknown> | null | undefined): string {
|
||||
const mode = supplyModeKey(row)
|
||||
if (mode === 'direct') return '洛阳直发'
|
||||
return mode === 'gancao' ? '甘草' : '自营'
|
||||
}
|
||||
|
||||
export function supplyModeTagType(
|
||||
row: Record<string, unknown> | 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: '修改订单金额',
|
||||
|
||||
@@ -696,7 +696,12 @@
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<el-table-column label="药材名称" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<MedicineNameSelect v-model="editForm.herbs[row.index].name" :disabled="herbsLocked" class="w-full" />
|
||||
<MedicineNameSelect
|
||||
v-model="editForm.herbs[row.index].name"
|
||||
v-model:medicine-id="editForm.herbs[row.index].medicine_id"
|
||||
:disabled="herbsLocked"
|
||||
class="w-full"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剂量(克)" min-width="120">
|
||||
@@ -732,7 +737,12 @@
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<el-table-column label="药材名称" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<MedicineNameSelect v-model="editForm.herbs[row.index].name" :disabled="herbsLocked" class="w-full" />
|
||||
<MedicineNameSelect
|
||||
v-model="editForm.herbs[row.index].name"
|
||||
v-model:medicine-id="editForm.herbs[row.index].medicine_id"
|
||||
:disabled="herbsLocked"
|
||||
class="w-full"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剂量(克)" min-width="120">
|
||||
@@ -1294,6 +1304,14 @@
|
||||
|
||||
<div v-show="createOrderStep === 1" class="create-order-step-panel">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="发货药房" prop="ship_mode">
|
||||
<el-radio-group v-model="createOrderForm.ship_mode">
|
||||
<el-radio-button label="gancao">甘草药房</el-radio-button>
|
||||
<el-radio-button label="direct">洛阳药房</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="8">
|
||||
<el-form-item label="复诊">
|
||||
<el-switch v-model="createOrderForm.is_follow_up" :active-value="1" :inactive-value="0" />
|
||||
@@ -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<string | null> {
|
||||
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<string
|
||||
status: 1
|
||||
})
|
||||
const lists = (res.lists || []) as { id: number; name: string }[]
|
||||
const exact = lists.find((item) => (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) {
|
||||
|
||||
@@ -157,7 +157,11 @@
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<el-table-column label="药材名称" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<MedicineNameSelect v-model="row.name" :disabled="editMode === 'view'" />
|
||||
<MedicineNameSelect
|
||||
v-model="row.name"
|
||||
v-model:medicine-id="row.medicine_id"
|
||||
:disabled="editMode === 'view'"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剂量(克)" min-width="150">
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -373,9 +373,9 @@
|
||||
<el-tag
|
||||
size="small"
|
||||
effect="plain"
|
||||
:type="String(row.gancao_reciperl_order_no || '').trim() ? 'success' : 'info'"
|
||||
:type="supplyModeTagType(row)"
|
||||
>
|
||||
{{ String(row.gancao_reciperl_order_no || '').trim() ? '甘草' : '自营' }}
|
||||
{{ supplyModeLabel(row) }}
|
||||
</el-tag>
|
||||
<span class="text-gray-400">#{{ row.id }}</span>
|
||||
</div>
|
||||
@@ -594,13 +594,13 @@
|
||||
@click="confirmWithdraw(row)"
|
||||
>撤回</el-button>
|
||||
<el-button
|
||||
v-if="canUploadGancaoRow(row)"
|
||||
v-perms="['tcm.prescriptionOrder/submitGancaoRecipel']"
|
||||
v-if="canUploadPharmacyRow(row)"
|
||||
v-perms="['tcm.prescriptionOrder/uploadToPharmacy']"
|
||||
type="warning"
|
||||
link
|
||||
:loading="gancaoSubmitId === row.id"
|
||||
@click="confirmSubmitGancaoRecipel(row)"
|
||||
>上传药方</el-button>
|
||||
:loading="pharmacySubmitId === row.id"
|
||||
@click="confirmUploadToPharmacy(row)"
|
||||
>上传药房</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -638,7 +638,7 @@
|
||||
<el-radio-button label="gancao">甘草药房</el-radio-button>
|
||||
<el-radio-button
|
||||
label="direct"
|
||||
:disabled="isShipModeLockedToGancao(detail)"
|
||||
:disabled="isShipModeLocked(detail)"
|
||||
>洛阳药房</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-tag
|
||||
@@ -650,6 +650,19 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #header-actions="{ detail }">
|
||||
<gancao-submission-reconcile-button
|
||||
:order="detail"
|
||||
@resolved="handleGancaoSubmissionResolved"
|
||||
/>
|
||||
<el-button
|
||||
v-if="canUploadPharmacyRow(detail)"
|
||||
v-perms="['tcm.prescriptionOrder/uploadToPharmacy']"
|
||||
type="warning"
|
||||
size="small"
|
||||
plain
|
||||
:loading="pharmacySubmitId === detail.id"
|
||||
@click="confirmUploadToPharmacy(detail)"
|
||||
>上传药房</el-button>
|
||||
<el-button
|
||||
v-if="canRxAudit(detail)"
|
||||
v-perms="['tcm.prescriptionOrder/auditPrescription']"
|
||||
@@ -2205,6 +2218,7 @@ import { useRoute } from 'vue-router'
|
||||
import { ArrowDown, InfoFilled, QuestionFilled, Search, Calendar, Document, Link as LinkIcon, Wallet } from '@element-plus/icons-vue'
|
||||
import ListTimeFilter from '@/components/list-time-filter/index.vue'
|
||||
import PrescriptionOrderDetailDrawer from './components/PrescriptionOrderDetailDrawer.vue'
|
||||
import GancaoSubmissionReconcileButton from './components/GancaoSubmissionReconcileButton.vue'
|
||||
import {
|
||||
TCM_ASSISTANT_ROLE_ID,
|
||||
PRESCRIPTION_AUDIT_ROLE_IDS,
|
||||
@@ -2231,7 +2245,10 @@ import {
|
||||
type ServicePackageOption,
|
||||
normalizeServicePackageOptions,
|
||||
parseServicePackageValues,
|
||||
mergeServicePackageSelectOptions
|
||||
mergeServicePackageSelectOptions,
|
||||
isRemoteSnapshotLocked,
|
||||
supplyModeLabel,
|
||||
supplyModeTagType
|
||||
} from './components/prescription-order-utils'
|
||||
import { useListTimeFilter } from '@/hooks/useListTimeFilter'
|
||||
import {
|
||||
@@ -2256,7 +2273,7 @@ import {
|
||||
prescriptionOrderBatchAssignAssistant,
|
||||
prescriptionOrderPatchPrescriptionPatient,
|
||||
prescriptionOrderLinkPayOrder,
|
||||
prescriptionOrderSubmitGancaoRecipel,
|
||||
prescriptionOrderUploadToPharmacy,
|
||||
prescriptionOrderPreviewGancaoRecipel,
|
||||
prescriptionDetail,
|
||||
prescriptionLibraryLists,
|
||||
@@ -2576,12 +2593,13 @@ function handleFulfillmentStatusTabClick(value: number | '') {
|
||||
const activeFocusKey = ref<'' | 'pendingRx' | 'pendingPay' | 'pendingShip' | 'risk'>('')
|
||||
|
||||
const supplyModeTabs = [
|
||||
{ label: '全部', value: '' as '' | 'gancao' | 'self' },
|
||||
{ label: '全部', value: '' as '' | 'gancao' | 'direct' | 'self' },
|
||||
{ label: '甘草', value: 'gancao' as const },
|
||||
{ label: '洛阳直发', value: 'direct' as const },
|
||||
{ label: '自营', value: 'self' as const }
|
||||
]
|
||||
|
||||
function handleSupplyModeTabClick(value: '' | 'gancao' | 'self') {
|
||||
function handleSupplyModeTabClick(value: '' | 'gancao' | 'direct' | 'self') {
|
||||
queryParams.supply_mode = value
|
||||
resetPage()
|
||||
}
|
||||
@@ -2615,8 +2633,8 @@ const queryParams = reactive({
|
||||
fulfillment_status: '' as number | '',
|
||||
prescription_audit_status: '' as number | '',
|
||||
payment_slip_audit_status: '' as number | '',
|
||||
/** 供货方式:甘草(已传甘草药方单号)/ 自营(无甘草单号) */
|
||||
supply_mode: '' as '' | 'gancao' | 'self',
|
||||
/** 供货方式:甘草 / 洛阳直发(含未上传)/ 自营 */
|
||||
supply_mode: '' as '' | 'gancao' | 'direct' | 'self',
|
||||
/** 服务渠道:'' 不限;'0' 未指派(库内 '' 或 '0') */
|
||||
service_channel: '' as '' | '0',
|
||||
/** 是否含辅方:'' 不限;'1' 含辅方;'0' 不含辅方 */
|
||||
@@ -3071,14 +3089,7 @@ function canEditRow(row: {
|
||||
return false
|
||||
}
|
||||
|
||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
const gcTime = Number(row.gancao_submit_time || 0)
|
||||
const gcLocked = gcNo !== '' || gcTime > 0
|
||||
|
||||
// 甘草已提交:仅允许在待双审/履约中/已发货/进行中下修改快递(已签收 6 不可改单号;后端 edit 亦拦截)
|
||||
if (gcLocked) {
|
||||
return [1, 2, 5, 7].includes(fs)
|
||||
}
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
|
||||
// 未提交甘草:仅待双审(1)、履约中(2)可全量编辑
|
||||
return fs === 1 || fs === 2
|
||||
@@ -3106,6 +3117,7 @@ function canRevokeRxAudit(row: {
|
||||
payment_slip_audit_status?: number
|
||||
fulfillment_status?: number
|
||||
}) {
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
const fs = Number(row.fulfillment_status)
|
||||
if (fs === 3 || fs === 4 || fs === 6) return false
|
||||
const rxStatus = Number(row.prescription_audit_status)
|
||||
@@ -3128,6 +3140,7 @@ function canRevokePayAudit(row: {
|
||||
}
|
||||
|
||||
function canWithdrawRow(row: { fulfillment_status?: number }) {
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
// 只有「待双审通过」可撤回,已发货后不可撤回
|
||||
return Number(row.fulfillment_status) === 1
|
||||
}
|
||||
@@ -3152,13 +3165,13 @@ function shipModeLabel(v: unknown): string {
|
||||
return normalizeShipMode(v) === 'direct' ? '洛阳药房' : '甘草药房'
|
||||
}
|
||||
|
||||
function isShipModeLockedToGancao(row: { gancao_reciperl_order_no?: string | null }) {
|
||||
return String(row.gancao_reciperl_order_no || '').trim() !== ''
|
||||
function isShipModeLocked(row: { gancao_reciperl_order_no?: string | null; ej_pharmacy_order_no?: string | null }) {
|
||||
return isRemoteSnapshotLocked(row as Record<string, unknown>)
|
||||
}
|
||||
|
||||
function canEditShipMode(row: { fulfillment_status?: number }) {
|
||||
function canEditShipMode(row: { fulfillment_status?: number; gancao_reciperl_order_no?: string; ej_pharmacy_order_no?: string }) {
|
||||
const fs = Number(row.fulfillment_status)
|
||||
return fs !== 3 && fs !== 4
|
||||
return fs !== 3 && fs !== 4 && !isShipModeLocked(row)
|
||||
}
|
||||
|
||||
const shipModeSaving = ref(false)
|
||||
@@ -3200,6 +3213,11 @@ async function onDetailShipModeChange(mode: string | number | boolean | undefine
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGancaoSubmissionResolved() {
|
||||
await refreshCurrentPrescriptionOrderDetail()
|
||||
getLists()
|
||||
}
|
||||
|
||||
function canAddPayOrderRow(row: {
|
||||
fulfillment_status?: number
|
||||
amount?: number | string
|
||||
@@ -3230,14 +3248,27 @@ function canQuickTrackRow(row: { fulfillment_status?: number }) {
|
||||
return Number(row.fulfillment_status) === 5
|
||||
}
|
||||
|
||||
function canUploadGancaoRow(row: {
|
||||
function canUploadPharmacyRow(row: {
|
||||
prescription_audit_status?: number
|
||||
gancao_reciperl_order_no?: string
|
||||
fulfillment_status?: number
|
||||
ship_mode?: string
|
||||
gancao_reciperl_order_no?: string
|
||||
ej_pharmacy_order_no?: string
|
||||
ej_pharmacy_status?: string
|
||||
ej_pharmacy_review_status?: string
|
||||
can_upload_pharmacy?: boolean
|
||||
}) {
|
||||
// 处方审核已通过(1) 且没有甘草订单号时才显示
|
||||
if (row.can_upload_pharmacy === false) return false
|
||||
const rxStatus = Number(row.prescription_audit_status)
|
||||
const gancaoOrderNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
return rxStatus === 1 && gancaoOrderNo === ''
|
||||
const fs = Number(row.fulfillment_status)
|
||||
const ejRejected = ['REJECTED'].includes(String(row.ej_pharmacy_status || '').toUpperCase())
|
||||
|| ['REJECTED'].includes(String(row.ej_pharmacy_review_status || '').toUpperCase())
|
||||
if (rxStatus !== 1 || ([3, 4, 8, 9, 10, 11, 12].includes(fs) && !(fs === 9 && ejRejected))) return false
|
||||
if (normalizeShipMode(row.ship_mode) === 'direct') {
|
||||
return (String(row.ej_pharmacy_order_no || '').trim() === '' || ejRejected)
|
||||
&& String(row.gancao_reciperl_order_no || '').trim() === ''
|
||||
}
|
||||
return String(row.gancao_reciperl_order_no || '').trim() === '' && String(row.ej_pharmacy_order_no || '').trim() === ''
|
||||
}
|
||||
|
||||
// ─── 详情抽屉(共享组件 PrescriptionOrderDetailDrawer):状态桥接 ───
|
||||
@@ -3863,7 +3894,7 @@ async function confirmWithdraw(row: { id: number }) {
|
||||
}
|
||||
}
|
||||
|
||||
const gancaoSubmitId = ref(0)
|
||||
const pharmacySubmitId = ref(0)
|
||||
const gancaoPreviewDrawerVisible = ref(false)
|
||||
const gancaoPreviewLoading = ref(false)
|
||||
const gancaoPreviewData = ref<any>(null)
|
||||
@@ -4002,18 +4033,24 @@ async function testGancaoPreviewFromDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmSubmitGancaoRecipel(row: { id: number }) {
|
||||
async function confirmUploadToPharmacy(row: { id: number; ship_mode?: string }) {
|
||||
const isLuoyang = normalizeShipMode(row.ship_mode) === 'direct'
|
||||
const pharmacyName = isLuoyang ? '洛阳药房' : '甘草药房'
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
'将把本单关联处方提交至甘草药管家开放平台(先 CTM_PREVIEW 预检查再 CTM_SUBMIT_RECIPEL 正式下单;成功后将按甘草侧规则扣费,详见 https://apidoc.igancao.com/service-doc/scm-outer-recipel.html )。确定继续?',
|
||||
'上传甘草药方',
|
||||
`将把本单关联处方提交至${pharmacyName},提交成功后不可切换发货药房。确定继续?`,
|
||||
'上传药房',
|
||||
{ type: 'warning', confirmButtonText: '确定上传', cancelButtonText: '取消' }
|
||||
)
|
||||
gancaoSubmitId.value = row.id
|
||||
const res: any = await prescriptionOrderSubmitGancaoRecipel({ id: row.id })
|
||||
pharmacySubmitId.value = row.id
|
||||
const res: any = await prescriptionOrderUploadToPharmacy({ id: row.id })
|
||||
const d = res?.data ?? res
|
||||
const no = d?.recipel_order_no != null ? String(d.recipel_order_no) : ''
|
||||
feedback.msgSuccess(no ? `上传成功,甘草处方单号:${no}` : '上传成功')
|
||||
const no = d?.pharmacy_order_no != null
|
||||
? String(d.pharmacy_order_no)
|
||||
: d?.recipel_order_no != null
|
||||
? String(d.recipel_order_no)
|
||||
: ''
|
||||
feedback.msgSuccess(no ? `上传成功,${pharmacyName}单号:${no}` : '上传成功')
|
||||
getLists()
|
||||
await detailDrawerRef.value?.refreshIfCurrent(row.id)
|
||||
} catch (e: any) {
|
||||
@@ -4021,7 +4058,7 @@ async function confirmSubmitGancaoRecipel(row: { id: number }) {
|
||||
/* 拦截器已提示 */
|
||||
}
|
||||
} finally {
|
||||
gancaoSubmitId.value = 0
|
||||
pharmacySubmitId.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -365,8 +365,8 @@
|
||||
<el-tag
|
||||
size="small"
|
||||
effect="plain"
|
||||
:type="String(row.gancao_reciperl_order_no || '').trim() ? 'success' : 'info'"
|
||||
>{{ String(row.gancao_reciperl_order_no || '').trim() ? '甘草' : '自营' }}</el-tag>
|
||||
:type="supplyModeTagType(row)"
|
||||
>{{ supplyModeLabel(row) }}</el-tag>
|
||||
<span class="po-card__id">#{{ row.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -500,7 +500,11 @@
|
||||
<el-dropdown-item v-if="canRefundRow(row)" command="refund">
|
||||
<span class="text-red-500">退款</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="canUploadGancaoRow(row)" command="submitGancao">上传药方</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
v-if="canUploadPharmacyRow(row)"
|
||||
v-perms="['tcm.prescriptionOrder/uploadToPharmacy']"
|
||||
command="uploadPharmacy"
|
||||
>上传药房</el-dropdown-item>
|
||||
<el-dropdown-item v-if="canWithdrawRow(row)" command="withdraw" divided>
|
||||
<span class="text-red-500">撤回订单</span>
|
||||
</el-dropdown-item>
|
||||
@@ -544,6 +548,10 @@
|
||||
>甘草 {{ detailData.gancao_reciperl_order_no }}</el-tag>
|
||||
</div>
|
||||
<div v-if="detailData" class="po-detail-drawer-actions flex flex-wrap items-center gap-2">
|
||||
<gancao-submission-reconcile-button
|
||||
:order="detailData"
|
||||
@resolved="handleGancaoSubmissionResolved"
|
||||
/>
|
||||
<el-button
|
||||
v-if="canRxAudit(detailData)"
|
||||
v-perms="['tcm.prescriptionOrder/auditPrescription']"
|
||||
@@ -1885,10 +1893,12 @@
|
||||
/>
|
||||
<el-form v-loading="shipSaving" label-width="90px" class="pr-2" @submit.prevent="submitShip">
|
||||
<el-form-item label="发货方式">
|
||||
<el-radio-group v-model="shipForm.ship_mode">
|
||||
<el-radio label="gancao">甘草药方发</el-radio>
|
||||
<el-radio label="direct">药房直发</el-radio>
|
||||
</el-radio-group>
|
||||
<el-tag
|
||||
:type="shipForm.ship_mode === 'direct' ? 'warning' : 'success'"
|
||||
effect="plain"
|
||||
size="default"
|
||||
>{{ shipDialogModeDisplay }}</el-tag>
|
||||
<span class="text-xs text-gray-400 ml-2">请在订单详情顶部「发货类型」中设置,此处不可修改</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="承运商">
|
||||
<el-select v-model="shipForm.express_company" class="w-full">
|
||||
@@ -2697,6 +2707,7 @@ import {
|
||||
User
|
||||
} from '@element-plus/icons-vue'
|
||||
import ListTimeFilter from '@/components/list-time-filter/index.vue'
|
||||
import GancaoSubmissionReconcileButton from './components/GancaoSubmissionReconcileButton.vue'
|
||||
import { useListTimeFilter } from '@/hooks/useListTimeFilter'
|
||||
import {
|
||||
prescriptionOrderAuditPayment,
|
||||
@@ -2719,7 +2730,7 @@ import {
|
||||
prescriptionOrderPatchPrescriptionUsage,
|
||||
prescriptionOrderLinkPayOrder,
|
||||
prescriptionOrderRequestCompletion,
|
||||
prescriptionOrderSubmitGancaoRecipel,
|
||||
prescriptionOrderUploadToPharmacy,
|
||||
prescriptionOrderPreviewGancaoRecipel,
|
||||
prescriptionDetail,
|
||||
getDoctors,
|
||||
@@ -2733,7 +2744,10 @@ import {
|
||||
mergeServicePackageSelectOptions,
|
||||
formatServicePackageLabels,
|
||||
normalizeSlipAuxUsageForm,
|
||||
prescriptionHasAuxFormula
|
||||
prescriptionHasAuxFormula,
|
||||
isRemoteSnapshotLocked,
|
||||
supplyModeLabel,
|
||||
supplyModeTagType
|
||||
} from './components/prescription-order-utils'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { jsPDF } from 'jspdf'
|
||||
@@ -2984,12 +2998,13 @@ function handleFulfillmentStatusTabClick(value: number | '') {
|
||||
const activeFocusKey = ref<'' | 'pendingRx' | 'pendingPay' | 'pendingShip' | 'risk'>('')
|
||||
|
||||
const supplyModeTabs = [
|
||||
{ label: '全部', value: '' as '' | 'gancao' | 'self' },
|
||||
{ label: '全部', value: '' as '' | 'gancao' | 'direct' | 'self' },
|
||||
{ label: '甘草', value: 'gancao' as const },
|
||||
{ label: '洛阳直发', value: 'direct' as const },
|
||||
{ label: '自营', value: 'self' as const }
|
||||
]
|
||||
|
||||
function handleSupplyModeTabClick(value: '' | 'gancao' | 'self') {
|
||||
function handleSupplyModeTabClick(value: '' | 'gancao' | 'direct' | 'self') {
|
||||
queryParams.supply_mode = value
|
||||
resetPage()
|
||||
}
|
||||
@@ -3023,8 +3038,8 @@ const queryParams = reactive({
|
||||
fulfillment_status: '' as number | '',
|
||||
prescription_audit_status: '' as number | '',
|
||||
payment_slip_audit_status: '' as number | '',
|
||||
/** 供货方式:甘草(已传甘草药方单号)/ 自营(无甘草单号) */
|
||||
supply_mode: '' as '' | 'gancao' | 'self',
|
||||
/** 供货方式:甘草 / 洛阳直发(含未上传)/ 自营 */
|
||||
supply_mode: '' as '' | 'gancao' | 'direct' | 'self',
|
||||
/** 下单人(关联操作日志 audit_rx_* / audit_pay_*) */
|
||||
audit_admin_id: '' as number | ''
|
||||
})
|
||||
@@ -3459,13 +3474,7 @@ function canEditRow(row: {
|
||||
return false
|
||||
}
|
||||
|
||||
const gcNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
const gcTime = Number(row.gancao_submit_time || 0)
|
||||
const gcLocked = gcNo !== '' || gcTime > 0
|
||||
|
||||
if (gcLocked) {
|
||||
return [1, 2, 5, 7].includes(fs)
|
||||
}
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
|
||||
return fs === 1 || fs === 2
|
||||
}
|
||||
@@ -3492,6 +3501,7 @@ function canRevokeRxAudit(row: {
|
||||
payment_slip_audit_status?: number
|
||||
fulfillment_status?: number
|
||||
}) {
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
const fs = Number(row.fulfillment_status)
|
||||
if (fs === 3 || fs === 4 || fs === 6) return false
|
||||
const rxStatus = Number(row.prescription_audit_status)
|
||||
@@ -3514,6 +3524,7 @@ function canRevokePayAudit(row: {
|
||||
}
|
||||
|
||||
function canWithdrawRow(row: { fulfillment_status?: number }) {
|
||||
if (isRemoteSnapshotLocked(row as Record<string, unknown>)) return false
|
||||
// 只有「待双审通过」可撤回,已发货后不可撤回
|
||||
return Number(row.fulfillment_status) === 1
|
||||
}
|
||||
@@ -3544,14 +3555,26 @@ function canQuickTrackRow(row: { fulfillment_status?: number }) {
|
||||
return Number(row.fulfillment_status) === 5
|
||||
}
|
||||
|
||||
function canUploadGancaoRow(row: {
|
||||
function canUploadPharmacyRow(row: {
|
||||
prescription_audit_status?: number
|
||||
gancao_reciperl_order_no?: string
|
||||
fulfillment_status?: number
|
||||
gancao_reciperl_order_no?: string
|
||||
ej_pharmacy_order_no?: string
|
||||
ej_pharmacy_status?: string
|
||||
ej_pharmacy_review_status?: string
|
||||
can_upload_pharmacy?: boolean
|
||||
}) {
|
||||
// 处方审核已通过(1) 且没有甘草订单号时才显示
|
||||
if (row.can_upload_pharmacy === false) return false
|
||||
const rxStatus = Number(row.prescription_audit_status)
|
||||
const fulfillmentStatus = Number(row.fulfillment_status)
|
||||
const gancaoOrderNo = String(row.gancao_reciperl_order_no || '').trim()
|
||||
return rxStatus === 1 && gancaoOrderNo === ''
|
||||
const ejOrderNo = String(row.ej_pharmacy_order_no || '').trim()
|
||||
const ejRejected = String(row.ej_pharmacy_status || '').toUpperCase() === 'REJECTED'
|
||||
|| String(row.ej_pharmacy_review_status || '').toUpperCase() === 'REJECTED'
|
||||
return rxStatus === 1
|
||||
&& (![3, 4, 8, 9, 10, 11, 12].includes(fulfillmentStatus) || (fulfillmentStatus === 9 && ejRejected))
|
||||
&& gancaoOrderNo === ''
|
||||
&& (ejOrderNo === '' || ejRejected)
|
||||
}
|
||||
|
||||
function orderStatusText(s: number | undefined) {
|
||||
@@ -3589,7 +3612,7 @@ const h5ActiveFilterCount = computed(() => {
|
||||
|
||||
// H5: 判断列表卡片「更多」下拉是否需要展示
|
||||
function hasMoreCardActions(row: Record<string, any>) {
|
||||
return canAddPayOrderRow(row) || canCompleteRow(row) || canRefundRow(row) || canUploadGancaoRow(row) || canWithdrawRow(row)
|
||||
return canAddPayOrderRow(row) || canCompleteRow(row) || canRefundRow(row) || canUploadPharmacyRow(row) || canWithdrawRow(row)
|
||||
}
|
||||
|
||||
// H5: 处理列表卡片「更多」下拉指令
|
||||
@@ -3597,7 +3620,7 @@ function handleCardMoreCommand(cmd: string, row: Record<string, any>) {
|
||||
if (cmd === 'addPayOrder') openAddPayOrder(row as any)
|
||||
else if (cmd === 'complete') confirmComplete(row as any)
|
||||
else if (cmd === 'refund') openRefundOrder(row as any)
|
||||
else if (cmd === 'submitGancao') confirmSubmitGancaoRecipel(row as any)
|
||||
else if (cmd === 'uploadPharmacy') confirmUploadToPharmacy(row as any)
|
||||
else if (cmd === 'withdraw') confirmWithdraw(row as any)
|
||||
}
|
||||
|
||||
@@ -3929,6 +3952,8 @@ function logActionText(act: string) {
|
||||
revoke_rx_audit: '撤回处方审核',
|
||||
revoke_pay_audit: '撤回支付审核',
|
||||
gancao_submit: '甘草下单',
|
||||
ej_pharmacy_submit: '洛阳药房下单',
|
||||
ej_pharmacy_callback: '洛阳药房状态',
|
||||
patch_rx_patient: '处方患者信息',
|
||||
update_amount: '修改订单金额',
|
||||
complete: '完成订单',
|
||||
@@ -4721,7 +4746,7 @@ async function confirmWithdraw(row: { id: number }) {
|
||||
}
|
||||
}
|
||||
|
||||
const gancaoSubmitId = ref(0)
|
||||
const pharmacySubmitId = ref(0)
|
||||
const gancaoPreviewDrawerVisible = ref(false)
|
||||
const gancaoPreviewLoading = ref(false)
|
||||
const gancaoPreviewData = ref<any>(null)
|
||||
@@ -4813,18 +4838,19 @@ async function testGancaoPreviewFromDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmSubmitGancaoRecipel(row: { id: number }) {
|
||||
async function confirmUploadToPharmacy(row: { id: number; ship_mode?: string }) {
|
||||
try {
|
||||
const target = String(row.ship_mode || 'gancao') === 'direct' ? '洛阳药房' : '甘草药房'
|
||||
await ElMessageBox.confirm(
|
||||
'将把本单关联处方提交至甘草药管家开放平台(先 CTM_PREVIEW 预检查再 CTM_SUBMIT_RECIPEL 正式下单;成功后将按甘草侧规则扣费,详见 https://apidoc.igancao.com/service-doc/scm-outer-recipel.html )。确定继续?',
|
||||
'上传甘草药方',
|
||||
`确认将本单关联处方上传至${target}?`,
|
||||
'上传药房',
|
||||
{ type: 'warning', confirmButtonText: '确定上传', cancelButtonText: '取消' }
|
||||
)
|
||||
gancaoSubmitId.value = row.id
|
||||
const res: any = await prescriptionOrderSubmitGancaoRecipel({ id: row.id })
|
||||
pharmacySubmitId.value = row.id
|
||||
const res: any = await prescriptionOrderUploadToPharmacy({ id: row.id })
|
||||
const d = res?.data ?? res
|
||||
const no = d?.recipel_order_no != null ? String(d.recipel_order_no) : ''
|
||||
feedback.msgSuccess(no ? `上传成功,甘草处方单号:${no}` : '上传成功')
|
||||
const no = String(d?.pharmacy_order_no || d?.recipel_order_no || '').trim()
|
||||
feedback.msgSuccess(no ? `上传成功,药房单号:${no}` : '上传成功')
|
||||
getLists()
|
||||
if (detailVisible.value && Number(detailData.value?.id) === row.id) {
|
||||
try {
|
||||
@@ -4840,7 +4866,7 @@ async function confirmSubmitGancaoRecipel(row: { id: number }) {
|
||||
/* 拦截器已提示 */
|
||||
}
|
||||
} finally {
|
||||
gancaoSubmitId.value = 0
|
||||
pharmacySubmitId.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4943,6 +4969,10 @@ const shipForm = reactive({
|
||||
tracking_number: ''
|
||||
})
|
||||
|
||||
const shipDialogModeDisplay = computed(() =>
|
||||
shipForm.ship_mode === 'direct' ? '洛阳药房直发' : '甘草药房直发'
|
||||
)
|
||||
|
||||
function openShip(row: { id: number; express_company?: unknown; tracking_number?: unknown; ship_mode?: unknown }) {
|
||||
shipRowId.value = row.id
|
||||
shipForm.ship_mode = String(row.ship_mode || 'gancao') || 'gancao'
|
||||
@@ -4982,6 +5012,13 @@ async function submitShip() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGancaoSubmissionResolved() {
|
||||
getLists()
|
||||
if (detailData.value?.id) {
|
||||
await openDetail(Number(detailData.value.id))
|
||||
}
|
||||
}
|
||||
|
||||
const completeOrderStatusOptions = [
|
||||
{ value: 3, label: '已完成' },
|
||||
{ value: 7, label: '进行中' },
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
<el-table-column label="序号" type="index" width="60" />
|
||||
<el-table-column label="药材名称" min-width="220">
|
||||
<template #default="{ row }">
|
||||
<MedicineNameSelect v-model="row.name" />
|
||||
<MedicineNameSelect v-model="row.name" v-model:medicine-id="row.medicine_id" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="剂量(克)" min-width="120">
|
||||
@@ -321,7 +321,7 @@ const formData = reactive({
|
||||
pulse: '',
|
||||
pulse_condition: '',
|
||||
clinical_diagnosis: '',
|
||||
herbs: [] as Array<{ name: string; dosage: number }>,
|
||||
herbs: [] as Array<{ medicine_id?: number; name: string; dosage: number }>,
|
||||
dose_count: 7,
|
||||
dose_unit: '剂',
|
||||
usage_days: 7,
|
||||
|
||||
Reference in New Issue
Block a user