This commit is contained in:
Your Name
2026-08-07 09:16:15 +08:00
parent 8bbd6f7885
commit 16d301f302
354 changed files with 967 additions and 456 deletions
@@ -153,7 +153,23 @@
<h2>{{ dashboard.rankings.appointments.title }}</h2>
<p>{{ appointmentRankingSubtitle }}</p>
</div>
<span class="panel-meta">实时</span>
<div class="panel-header-actions">
<el-tree-select
v-if="dashboard.rankings.appointments.kind !== 'doctor'"
v-model="rankingDeptId"
:data="dashboard.filters.ranking_departments"
:props="departmentTreeProps"
node-key="id"
check-strictly
clearable
filterable
default-expand-all
placeholder="全部可见部门"
class="ranking-dept-select"
@change="handleRankingDeptChange"
/>
<span class="panel-meta">实时</span>
</div>
</div>
<div v-if="dashboard.rankings.appointments.items.length" class="ranking-list">
<div
@@ -363,6 +379,7 @@ const createInitialDashboard = () => ({
today: {
add_fans_count: 0,
appointment_total_count: 0,
low_amount_payment_count: 0,
interview_count: 0,
completed_order_count: 0,
completed_order_amount: 0,
@@ -372,6 +389,7 @@ const createInitialDashboard = () => ({
comparisons: {
add_fans_count: emptyComparison(),
appointment_total_count: emptyComparison(),
low_amount_payment_count: emptyComparison(),
interview_count: emptyComparison(),
completed_order_count: emptyComparison(),
completed_order_amount: emptyComparison(),
@@ -392,6 +410,10 @@ const createInitialDashboard = () => ({
items: [] as RankingItem[],
},
},
filters: {
ranking_departments: [] as any[],
ranking_dept_id: 0,
},
trend: {
date_range: [] as string[],
dates: [] as string[],
@@ -421,8 +443,10 @@ const loading = ref(false)
const loaded = ref(false)
const errorMessage = ref('')
const activeTrend = ref<TrendKey>('appointments')
const rankingDeptId = ref<number | undefined>()
const now = ref(new Date())
let clockTimer: ReturnType<typeof setInterval> | undefined
const departmentTreeProps = { value: 'id', label: 'name', children: 'children' }
const trendOptions = [
{ label: '挂号', value: 'appointments' },
@@ -453,6 +477,13 @@ const todayMetrics = computed(() => [
hint: '状态为已预约、已完成或改期,排除已取消',
comparisons: [dashboard.today.comparisons.appointment_total_count],
},
{
key: 'lowAmountPayments',
label: '今日 10 元及以下收款',
value: `${formatNumber(dashboard.today.low_amount_payment_count)} 笔`,
hint: '实收金额大于 0 且不超过 10 元的已支付订单',
comparisons: [dashboard.today.comparisons.low_amount_payment_count],
},
{
key: 'orders',
label: '今日接诊 / 诊单金额',
@@ -472,7 +503,7 @@ const todayMetrics = computed(() => [
},
{
key: 'receiveRate',
label: '面诊接诊率',
label: '接诊率',
value: formatPercent(dashboard.today.interview_receive_rate),
hint: '诊单数 / 面诊数',
comparisons: [dashboard.today.comparisons.interview_receive_rate],
@@ -621,8 +652,9 @@ const loadDashboard = async () => {
loading.value = true
errorMessage.value = ''
try {
const res: any = await performanceDashboardOverview()
const res: any = await performanceDashboardOverview({ ranking_dept_id: rankingDeptId.value })
Object.assign(dashboard, createInitialDashboard(), res || {})
rankingDeptId.value = Number(dashboard.filters.ranking_dept_id || 0) || undefined
loaded.value = true
} catch (error: any) {
errorMessage.value = error?.msg || error?.message || '驾驶舱数据加载失败,请稍后重试'
@@ -631,6 +663,10 @@ const loadDashboard = async () => {
}
}
const handleRankingDeptChange = () => {
loadDashboard()
}
onMounted(() => {
loadDashboard()
clockTimer = setInterval(() => {
@@ -775,7 +811,7 @@ onBeforeUnmount(() => {
}
.operating-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(4, minmax(0, 1fr));
margin-bottom: 14px;
}
@@ -921,6 +957,17 @@ onBeforeUnmount(() => {
}
}
.panel-header-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.ranking-dept-select {
width: 180px;
}
.panel-meta {
padding: 4px 7px;
color: var(--dash-text-muted);
@@ -1088,6 +1135,10 @@ onBeforeUnmount(() => {
.dashboard-grid--bottom {
grid-template-columns: 1fr;
}
.operating-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 900px) {
@@ -1132,6 +1183,15 @@ onBeforeUnmount(() => {
flex-direction: column;
}
.panel-header-actions {
align-items: flex-end;
flex-direction: column;
}
.ranking-dept-select {
width: 150px;
}
.ranking-copy span {
display: none;
}