更新
This commit is contained in:
@@ -100,6 +100,7 @@ export function doctorDailyStatsOverview(params: {
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
channel_code?: string
|
||||
dept_ids?: number[] | string
|
||||
}) {
|
||||
return request.get({ url: '/stats.doctorDailyStats/overview', params })
|
||||
}
|
||||
|
||||
@@ -1707,6 +1707,8 @@ type AuxUsageForm = {
|
||||
bags_per_dose: number
|
||||
times_per_day: number
|
||||
usage_days: number
|
||||
/** 从处方库导入辅方时写入的模板名称,供药房联展示 */
|
||||
prescription_name?: string
|
||||
}
|
||||
|
||||
function defaultAuxUsage(prescriptionType = '浓缩水丸'): AuxUsageForm {
|
||||
@@ -1753,7 +1755,8 @@ function normalizeAuxUsageForm(raw: unknown, prescriptionType: string): AuxUsage
|
||||
need_decoction: o.need_decoction === 1 || o.need_decoction === true,
|
||||
bags_per_dose: o.bags_per_dose != null ? Number(o.bags_per_dose) || 1 : base.bags_per_dose,
|
||||
times_per_day: o.times_per_day != null ? Number(o.times_per_day) || 3 : base.times_per_day,
|
||||
usage_days: o.usage_days != null ? Number(o.usage_days) || 7 : base.usage_days
|
||||
usage_days: o.usage_days != null ? Number(o.usage_days) || 7 : base.usage_days,
|
||||
prescription_name: String(o.prescription_name ?? '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3448,6 +3451,10 @@ const handleImportLibrary = (row: any) => {
|
||||
} else {
|
||||
editForm.herbs.push(...imported)
|
||||
}
|
||||
if (formulaType === '辅方') {
|
||||
editForm.aux_usage.prescription_name = String(row.prescription_name ?? '').trim()
|
||||
}
|
||||
|
||||
const modeHint = libraryImportMode.value === 'append' ? '(已追加)' : ''
|
||||
if (findDuplicateHerbNamesLocal().length) {
|
||||
feedback.msgWarning(
|
||||
|
||||
@@ -293,7 +293,7 @@
|
||||
:fetch-fun="prescriptionOrderExport"
|
||||
:params="prescriptionOrderExportParams"
|
||||
:page-size="pager.size"
|
||||
export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:优先取该单关联处方登记的挂号;无则诊单下同患者挂号取 id 最大的一条(与前台挂号选择的记录一致);业绩侧栏带渠道筛选导出时与同页列表高亮挂号同源。"
|
||||
export-hint="导出范围与上方筛选一致(履约状态、创建时间及其他条件均会生效)。含「自媒体渠道(挂号渠道来源)」:优先取该单关联处方登记的挂号;无则诊单下同患者挂号取 id 最大的一条(与前台挂号选择的记录一致);业绩侧栏带渠道筛选导出时与同页列表高亮挂号同源。「服务套餐」按字典 server_order 解析展示;「签收日期」与详情/业绩看板提成口径一致:以快递主表 sign_time 为主,无效时回退至签收类轨迹时间或已签收状态下的最新轨迹时间。"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -2449,7 +2449,9 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="slipAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">
|
||||
辅方<span v-if="slipAuxLibraryName" class="rx-herb-library-name">({{ slipAuxLibraryName }})</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(h, i) in slipAuxHerbs"
|
||||
:key="'aux-' + i"
|
||||
@@ -2461,10 +2463,28 @@
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-for="(h, i) in slipHerbsList" :key="i" class="rx-herb-cell">
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
<template v-if="slipMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipMainHerbs"
|
||||
:key="'user-main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="slipAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipAuxHerbs"
|
||||
:key="'user-aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2714,6 +2734,7 @@ import {
|
||||
prescriptionOrderSubmitGancaoRecipel,
|
||||
prescriptionOrderPreviewGancaoRecipel,
|
||||
prescriptionDetail,
|
||||
prescriptionLibraryLists,
|
||||
getDoctors,
|
||||
getAssistants
|
||||
} from '@/api/tcm'
|
||||
@@ -5480,6 +5501,8 @@ const SLIP_ADDRESS_LINE = '地址:四川省成都市双流区黄甲街道黄
|
||||
const prescriptionViewVisible = ref(false)
|
||||
const prescriptionViewLoading = ref(false)
|
||||
const prescriptionViewData = ref<any>(null)
|
||||
/** 药房联辅方标题:处方库中该辅方模板的 prescription_name(导入或药材匹配解析) */
|
||||
const slipAuxLibraryName = ref('')
|
||||
const prescriptionTabType = ref('internal')
|
||||
const prescriptionSlipPrintRef = ref<HTMLElement | null>(null)
|
||||
const prescriptionSlipExporting = ref(false)
|
||||
@@ -5883,6 +5906,60 @@ function openPrescriptionViewFromEdit() {
|
||||
openPrescriptionView(editForm)
|
||||
}
|
||||
|
||||
function herbSetMatchKey(herbs: Array<{ name?: unknown; dosage?: unknown }>): string {
|
||||
return herbs
|
||||
.map((h) => `${String(h.name ?? '').trim()}:${Number(h.dosage) || 0}`)
|
||||
.sort()
|
||||
.join('|')
|
||||
}
|
||||
|
||||
function herbsMatchPrescriptionLibrary(
|
||||
prescriptionHerbs: Array<{ name?: unknown; dosage?: unknown }>,
|
||||
libraryHerbs: Array<{ name?: unknown; dosage?: unknown }>
|
||||
): boolean {
|
||||
if (!prescriptionHerbs.length || !libraryHerbs.length) return false
|
||||
if (prescriptionHerbs.length !== libraryHerbs.length) return false
|
||||
return herbSetMatchKey(prescriptionHerbs) === herbSetMatchKey(libraryHerbs)
|
||||
}
|
||||
|
||||
function readAuxLibraryNameFromUsage(raw: unknown): string {
|
||||
if (!raw || typeof raw !== 'object') return ''
|
||||
const o = raw as Record<string, unknown>
|
||||
return String(o.prescription_name ?? o.library_name ?? '').trim()
|
||||
}
|
||||
|
||||
/** 解析药房联辅方对应的处方库名称:优先 aux_usage 内持久化字段,否则按开方医师处方库辅方模板药材精确匹配 */
|
||||
async function resolveSlipAuxLibraryName(data: Record<string, unknown>): Promise<string> {
|
||||
const fromUsage = readAuxLibraryNameFromUsage(data.aux_usage)
|
||||
if (fromUsage) return fromUsage
|
||||
|
||||
const herbs = Array.isArray(data.herbs) ? data.herbs : []
|
||||
const auxHerbs = herbs.filter((h) => normalizeSlipFormulaType((h as any)?.formula_type) === '辅方')
|
||||
if (!auxHerbs.length) return ''
|
||||
|
||||
const doctorId = Number(data.creator_id) > 0 ? Number(data.creator_id) : 0
|
||||
if (!doctorId) return ''
|
||||
|
||||
try {
|
||||
const res: any = await prescriptionLibraryLists({
|
||||
page_no: 1,
|
||||
page_size: 200,
|
||||
formula_type: '辅方',
|
||||
prescribing_creator_id: doctorId
|
||||
})
|
||||
const lists = Array.isArray(res?.lists) ? res.lists : []
|
||||
for (const row of lists) {
|
||||
const libHerbs = Array.isArray(row?.herbs) ? row.herbs : []
|
||||
if (herbsMatchPrescriptionLibrary(auxHerbs, libHerbs)) {
|
||||
return String(row?.prescription_name ?? '').trim()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* 静默:无匹配时仅展示「辅方」 */
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
async function openPrescriptionView(row: any) {
|
||||
if (!row.prescription_id) {
|
||||
feedback.msgWarning('该订单未关联处方')
|
||||
@@ -5892,6 +5969,7 @@ async function openPrescriptionView(row: any) {
|
||||
prescriptionViewVisible.value = true
|
||||
prescriptionViewLoading.value = true
|
||||
prescriptionViewData.value = null
|
||||
slipAuxLibraryName.value = ''
|
||||
prescriptionTabType.value = 'internal'
|
||||
|
||||
try {
|
||||
@@ -5926,12 +6004,15 @@ async function openPrescriptionView(row: any) {
|
||||
merged.appointment_id = Number(linkedAp.id)
|
||||
}
|
||||
prescriptionViewData.value = merged
|
||||
slipAuxLibraryName.value = await resolveSlipAuxLibraryName(merged)
|
||||
} else {
|
||||
prescriptionViewData.value = null
|
||||
slipAuxLibraryName.value = ''
|
||||
}
|
||||
} catch (e: any) {
|
||||
feedback.msgError(e?.message || '加载处方详情失败')
|
||||
prescriptionViewVisible.value = false
|
||||
slipAuxLibraryName.value = ''
|
||||
} finally {
|
||||
prescriptionViewLoading.value = false
|
||||
}
|
||||
@@ -6585,6 +6666,11 @@ async function downloadPrescriptionSlipPdf() {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.rx-herb-library-name {
|
||||
font-weight: 500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.rx-herb-cell {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 64px;
|
||||
|
||||
@@ -181,9 +181,44 @@
|
||||
<template #default="{ row }">¥ {{ formatDoctorDailyMoney(row.deal_amount) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deal_order_count" label="接诊诊单" min-width="88" align="right" />
|
||||
<el-table-column
|
||||
prop="appointment_total"
|
||||
label="总挂号"
|
||||
min-width="88"
|
||||
align="right"
|
||||
>
|
||||
<template #header>
|
||||
<span>总挂号</span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
placement="top"
|
||||
content="区间内该医生 doctor_appointment 全部状态合计(已预约/已完成/已过号/已取消);选部门时仅计经手医助归属在所选部门子树的挂号(与业绩看板「预约诊单」医助口径一致)"
|
||||
>
|
||||
<el-icon class="doctor-daily-th__hint"><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="appointment_completed" label="挂号完成" min-width="88" align="right" />
|
||||
<el-table-column prop="appointment_missed" label="过号" min-width="72" align="right" />
|
||||
<el-table-column prop="appointment_cancelled" label="取消" min-width="72" align="right" />
|
||||
<el-table-column
|
||||
prop="appointment_conversion_rate"
|
||||
label="挂号率"
|
||||
min-width="92"
|
||||
align="right"
|
||||
>
|
||||
<template #header>
|
||||
<span>挂号率</span>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
placement="top"
|
||||
content="挂号率 = 成交单数 / 总挂号数 × 100;总挂号为 0 时显示「—」"
|
||||
>
|
||||
<el-icon class="doctor-daily-th__hint"><InfoFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template #default="{ row }">{{ formatDoctorDailyRate(row.appointment_conversion_rate) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-empty v-else description="当前账号无医生统计权限" />
|
||||
@@ -2078,6 +2113,10 @@ function buildDoctorDailyRequestParams(): Record<string, any> {
|
||||
if (selectedChannel.value) {
|
||||
p.channel_code = selectedChannel.value
|
||||
}
|
||||
// 选中父级时由后端 resolvePrimaryDepts 自动展开所有子级,再用 admin_dept 收窄医生集合。
|
||||
if (selectedDeptIds.value.length > 0) {
|
||||
p.dept_ids = selectedDeptIds.value.join(',')
|
||||
}
|
||||
if (customRange.value?.[0] && customRange.value?.[1]) {
|
||||
p.start_date = customRange.value[0]
|
||||
p.end_date = customRange.value[1]
|
||||
@@ -2096,6 +2135,15 @@ function formatDoctorDailyMoney(n: number | string | undefined) {
|
||||
return x.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
}
|
||||
|
||||
/** 挂号率展示:null 或非有限数显示「—」;保留 2 位小数 + % 后缀 */
|
||||
function formatDoctorDailyRate(v: number | string | null | undefined): string {
|
||||
if (v === null || v === undefined || v === '') return '—'
|
||||
const n = Number(v)
|
||||
if (!Number.isFinite(n)) return '—'
|
||||
|
||||
return `${n.toFixed(2)}%`
|
||||
}
|
||||
|
||||
function getDoctorDailySummaries(param: { columns: any[] }) {
|
||||
const t = doctorDailyTotal.value
|
||||
const sums: string[] = []
|
||||
@@ -2118,6 +2166,9 @@ function getDoctorDailySummaries(param: { columns: any[] }) {
|
||||
case 'deal_order_count':
|
||||
sums[index] = String(t.deal_order_count ?? 0)
|
||||
break
|
||||
case 'appointment_total':
|
||||
sums[index] = String(t.appointment_total ?? 0)
|
||||
break
|
||||
case 'appointment_completed':
|
||||
sums[index] = String(t.appointment_completed ?? 0)
|
||||
break
|
||||
@@ -2127,6 +2178,9 @@ function getDoctorDailySummaries(param: { columns: any[] }) {
|
||||
case 'appointment_cancelled':
|
||||
sums[index] = String(t.appointment_cancelled ?? 0)
|
||||
break
|
||||
case 'appointment_conversion_rate':
|
||||
sums[index] = formatDoctorDailyRate(t.appointment_conversion_rate)
|
||||
break
|
||||
default:
|
||||
sums[index] = ''
|
||||
}
|
||||
@@ -4100,6 +4154,19 @@ onMounted(async () => {
|
||||
background: var(--yj-accent-soft) !important;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.doctor-daily-th__hint {
|
||||
margin-left: 4px;
|
||||
color: var(--yj-muted);
|
||||
cursor: help;
|
||||
vertical-align: -2px;
|
||||
font-size: 13px;
|
||||
transition: color 0.15s;
|
||||
|
||||
&:hover {
|
||||
color: var(--yj-brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-surface {
|
||||
|
||||
Reference in New Issue
Block a user