This commit is contained in:
Your Name
2026-05-05 18:25:14 +08:00
parent cd51f55701
commit de78f3b218
11 changed files with 559 additions and 97 deletions
+14
View File
@@ -54,6 +54,20 @@ export function yejiStatsUnassignedBreakdown(params: {
return request.get({ url: '/stats.yejiStats/unassignedBreakdown', params })
}
/** 部门行「进线数据」逐条明细(与看板同口径) */
export function yejiStatsLeadLines(params: {
start_date: string
end_date: string
dept_id: number
dept_ids?: string
channel_code?: string
tag_id?: string
page?: number
page_size?: number
}) {
return request.get({ url: '/stats.yejiStats/leadLines', params })
}
export function doctorDailyStatsOverview(params: {
start_date?: string
end_date?: string
@@ -1912,6 +1912,12 @@
</div>
</template>
<div v-loading="prescriptionViewLoading" class="rx-wrap">
<!-- 处方签签 -->
<el-tabs v-model="prescriptionTabType" class="mx-4 mt-2" style="margin-bottom: 0px; z-index: 1; position: relative;" v-if="prescriptionViewData">
<el-tab-pane label="药房联" name="internal" />
<el-tab-pane label="处方联" name="user" />
</el-tabs>
<!-- 状态条(不进入打印/导出范围) -->
<div class="rx-statusbar" v-if="prescriptionViewData">
<el-tag
@@ -1962,7 +1968,7 @@
<!-- A4 处方纸(药房联) -->
<div v-if="prescriptionViewData" ref="prescriptionSlipPrintRef" class="rx-paper">
<div class="rx-title">处方单</div>
<div class="rx-title">{{ prescriptionTabType === 'internal' ? '药房联' : '处方单' }}</div>
<div class="rx-notice">
<span class="rx-notice-text">
服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点
@@ -2020,7 +2026,7 @@
</div>
<div class="rx-rp">
<div class="rx-watermark">药房联</div>
<div class="rx-watermark">{{ prescriptionTabType === 'internal' ? '药房联' : '处方联' }}</div>
<div class="rx-rp-head">
<div class="rx-rp-label">Rp.</div>
<div class="rx-rp-cols-head">
@@ -2036,8 +2042,10 @@
</div>
<div class="rx-herbs">
<div v-for="(h, i) in slipHerbsList" :key="i" class="rx-herb-cell">
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}</span>
<span class="rx-herb-name" v-if="prescriptionTabType === 'internal'">{{ h.name }} ({{ h.dosage }}克)</span>
<span class="rx-herb-name" v-else>{{ h.name }}</span>
<span class="rx-herb-total" v-if="prescriptionTabType === 'internal'">{{ rxHerbTotal(h.dosage) }}克</span>
<span class="rx-herb-total" v-else>{{ h.dosage }}克</span>
</div>
</div>
</div>
@@ -4408,6 +4416,7 @@ const SLIP_ADDRESS_LINE = '地址:四川省成都市双流区黄甲街道黄
const prescriptionViewVisible = ref(false)
const prescriptionViewLoading = ref(false)
const prescriptionViewData = ref<any>(null)
const prescriptionTabType = ref('internal')
const prescriptionSlipPrintRef = ref<HTMLElement | null>(null)
const prescriptionSlipExporting = ref(false)
@@ -4674,6 +4683,7 @@ async function openPrescriptionView(row: any) {
prescriptionViewVisible.value = true
prescriptionViewLoading.value = true
prescriptionViewData.value = null
prescriptionTabType.value = 'internal'
try {
const res: any = await prescriptionDetail({ id: row.prescription_id })
+165 -1
View File
@@ -603,7 +603,13 @@
@click="onYejiDeptRowClick(tb, r)"
>
<td class="col-dept">{{ r.dept_name }}</td>
<td>{{ formatInt(r.lead_count) }}</td>
<td @click="onLeadCountCellClick($event, tb, r)">
<span
v-if="r.dept_id > 0 && Number(r.lead_count) > 0"
class="yeji-lead-cell--link"
>{{ formatInt(r.lead_count) }}</span>
<template v-else>{{ formatInt(r.lead_count) }}</template>
</td>
<td>{{ formatMoney(r.performance_amount) }}</td>
<td v-if="tb.channel_name" class="col-completed">{{ formatMoney(r.completed_performance_amount) }}</td>
<td>{{ formatInt(r.consult_count) }}</td>
@@ -858,6 +864,58 @@
/>
</div>
</el-dialog>
<el-dialog
v-model="leadLinesDialogVisible"
width="min(920px, 96vw)"
destroy-on-close
class="yeji-leadlines-dialog"
>
<template #header>
<div class="yeji-unassigned-dialog__head">
<span class="yeji-unassigned-dialog__title">进线数据明细</span>
<p class="yeji-unassigned-dialog__sub">{{ leadLinesSubtitle }}</p>
</div>
</template>
<p v-if="leadLinesApiNote" class="yeji-unassigned-dialog__note">{{ leadLinesApiNote }}</p>
<div v-loading="leadLinesLoading" class="yeji-unassigned-dialog__body">
<el-table
v-if="leadLinesRows.length > 0 || leadLinesLoading"
:data="leadLinesRows"
size="small"
stripe
border
max-height="440"
class="yeji-unassigned-dialog__table"
:empty-text="leadLinesLoading ? '加载中…' : '暂无数据'"
>
<el-table-column prop="event_time_text" label="进线时间" min-width="156" show-overflow-tooltip />
<el-table-column prop="reception_admin_name" label="接待" width="88" show-overflow-tooltip />
<el-table-column prop="external_contact_name" label="客户" min-width="100" show-overflow-tooltip />
<el-table-column prop="external_userid" label="外部联系人ID" min-width="140" show-overflow-tooltip />
<el-table-column prop="user_id" label="企微成员ID" min-width="120" show-overflow-tooltip />
<el-table-column prop="state" label="渠道参数" width="96" show-overflow-tooltip />
</el-table>
<el-empty
v-if="!leadLinesLoading && leadLinesRows.length === 0"
description="暂无进线明细"
/>
<div v-if="leadLinesCount > 0" class="yeji-leadlines-dialog__pager">
<el-pagination
:current-page="leadLinesPage"
:page-size="leadLinesPageSize"
background
layout="total, sizes, prev, pager, next, jumper"
:total="leadLinesCount"
:page-sizes="[10, 20, 50, 100]"
:disabled="leadLinesLoading"
:hide-on-single-page="false"
@current-change="onLeadLinesPageChange"
@size-change="onLeadLinesPageSizeChange"
/>
</div>
</div>
</el-dialog>
</div>
</template>
@@ -874,6 +932,7 @@ import {
yejiStatsMulti,
yejiStatsOverview,
yejiStatsUnassignedBreakdown,
yejiStatsLeadLines,
} from '@/api/stats'
import { deptPerformanceTargetMonthMatrix } from '@/api/finance'
import { prescriptionOrderLists } from '@/api/tcm'
@@ -962,6 +1021,17 @@ interface YejiUnassignedBreakdownRow {
amount: number
}
interface YejiLeadLineRow {
id: number
event_time: number
event_time_text: string
external_userid: string
user_id: string
state: string
reception_admin_name: string
external_contact_name: string
}
interface YejiRow {
dept_id: number
dept_name: string
@@ -1045,6 +1115,16 @@ const unassignedDialogSubtitle = ref('')
const unassignedDialogNote = ref('')
const unassignedDialogRows = ref<YejiUnassignedBreakdownRow[]>([])
const leadLinesDialogVisible = ref(false)
const leadLinesLoading = ref(false)
const leadLinesSubtitle = ref('')
const leadLinesApiNote = ref('')
const leadLinesRows = ref<YejiLeadLineRow[]>([])
const leadLinesCount = ref(0)
const leadLinesPage = ref(1)
const leadLinesPageSize = ref(20)
const leadLinesContext = ref<{ tb: YejiTable; row: YejiRow } | null>(null)
/** 图表 / 数据表切换 */
const yejiDisplayTab = ref<'charts' | 'table'>('table')
@@ -2039,6 +2119,72 @@ async function openUnassignedBreakdown(tb: YejiTable) {
}
}
function onLeadCountCellClick(ev: MouseEvent, tb: YejiTable, row: YejiRow) {
if (row.dept_id <= 0 || Number(row.lead_count) <= 0) {
return
}
ev.stopPropagation()
void openLeadLinesDialog(tb, row)
}
async function fetchLeadLinesPage() {
const ctx = leadLinesContext.value
if (!ctx) {
return
}
const { tb, row } = ctx
leadLinesLoading.value = true
try {
const p: Record<string, string | number> = {
start_date: tb.start_date,
end_date: tb.end_date,
dept_id: row.dept_id,
page: leadLinesPage.value,
page_size: leadLinesPageSize.value,
}
if (selectedDeptIds.value.length > 0) {
p.dept_ids = selectedDeptIds.value.join(',')
}
if (selectedChannel.value) {
p.channel_code = selectedChannel.value
}
const res: any = await yejiStatsLeadLines(p as any)
leadLinesRows.value = Array.isArray(res?.lists) ? res.lists : []
leadLinesCount.value = Number(res?.count ?? 0)
leadLinesApiNote.value = typeof res?.note === 'string' ? res.note : ''
} catch (e: any) {
ElMessage.error(e?.msg || e?.message || '加载进线明细失败')
leadLinesRows.value = []
leadLinesCount.value = 0
leadLinesApiNote.value = ''
} finally {
leadLinesLoading.value = false
}
}
async function openLeadLinesDialog(tb: YejiTable, row: YejiRow) {
leadLinesContext.value = { tb, row }
leadLinesSubtitle.value = `${row.dept_name} · ${buildUnassignedBreakdownSubtitle(tb)}`
leadLinesApiNote.value = ''
leadLinesRows.value = []
leadLinesCount.value = 0
leadLinesPage.value = 1
leadLinesPageSize.value = 20
leadLinesDialogVisible.value = true
await fetchLeadLinesPage()
}
function onLeadLinesPageChange(p: number) {
leadLinesPage.value = p
void fetchLeadLinesPage()
}
function onLeadLinesPageSizeChange(size: number) {
leadLinesPageSize.value = size
leadLinesPage.value = 1
void fetchLeadLinesPage()
}
/**
* 部门业绩行:有子部门 → 仅筛选该节点(后端展开子行);末级 → 侧栏业务订单;「未归属中心」→ 按创建人拆解弹窗
*/
@@ -3379,6 +3525,24 @@ onMounted(async () => {
min-height: 120px;
}
.yeji-lead-cell--link {
color: var(--yj-brand, #2563eb);
font-weight: 600;
text-decoration: underline;
text-underline-offset: 2px;
cursor: pointer;
}
tbody tr.yeji-row--action:hover .yeji-lead-cell--link {
color: color-mix(in srgb, var(--yj-brand, #2563eb) 88%, #000);
}
.yeji-leadlines-dialog__pager {
margin-top: 14px;
display: flex;
justify-content: flex-end;
}
@media (prefers-reduced-motion: reduce) {
.date-pill,
.filter-btn,