This commit is contained in:
Your Name
2026-08-05 14:06:07 +08:00
parent abb4aced1c
commit a797743aa4
362 changed files with 486 additions and 339 deletions
@@ -113,7 +113,20 @@
</template>
<template v-else>{{ metric.value }}</template>
</div>
<div class="metric-foot">{{ metric.hint }}</div>
<div class="metric-comparisons">
<div
v-for="comparison in metric.comparisons"
:key="`${metric.key}-${comparison.label || 'total'}`"
class="metric-foot"
:class="comparisonDirectionClass(comparison.direction)"
>
<el-icon v-if="comparison.direction === 'up'"><CaretTop /></el-icon>
<el-icon v-else-if="comparison.direction === 'down'"><CaretBottom /></el-icon>
<el-icon v-else><Minus /></el-icon>
{{ formatMetricComparison(comparison) }}
</div>
</div>
<div class="metric-hint">{{ metric.hint }}</div>
</article>
</section>
@@ -275,6 +288,7 @@ import {
CaretTop,
InfoFilled,
Lock,
Minus,
Refresh,
TrendCharts,
} from '@element-plus/icons-vue'
@@ -291,6 +305,21 @@ interface RankingItem {
amount: number
}
type ComparisonDirection = 'up' | 'down' | 'flat'
interface MetricComparison {
direction: ComparisonDirection
rate: number | null
previous: number
label?: string
}
const emptyComparison = (): MetricComparison => ({
direction: 'flat',
rate: null,
previous: 0,
})
const createInitialDashboard = () => ({
scope: {
key: '',
@@ -320,6 +349,15 @@ const createInitialDashboard = () => ({
completed_order_amount: 0,
paid_appointment_rate: 0,
interview_receive_rate: 0,
comparisons: {
add_fans_count: emptyComparison(),
appointment_total_count: emptyComparison(),
interview_count: emptyComparison(),
completed_order_count: emptyComparison(),
completed_order_amount: emptyComparison(),
paid_appointment_rate: emptyComparison(),
interview_receive_rate: emptyComparison(),
},
},
rankings: {
appointments: {
@@ -386,36 +424,45 @@ const todayMetrics = computed(() => [
label: '今日加粉',
value: formatNumber(dashboard.today.add_fans_count),
hint: '按企微新增客户事件统计',
comparisons: [dashboard.today.comparisons.add_fans_count],
},
{
key: 'appointments',
label: '今日挂号',
value: formatNumber(dashboard.today.appointment_total_count),
hint: '状态为已预约、已完成或改期,排除已取消',
comparisons: [dashboard.today.comparisons.appointment_total_count],
},
{
key: 'orders',
label: '今日接诊 / 诊单金额',
value: '',
hint: '按业务订单统计,排除取消、拒收和退款',
comparisons: [
{ ...dashboard.today.comparisons.completed_order_count, label: '诊单' },
{ ...dashboard.today.comparisons.completed_order_amount, label: '金额' },
],
},
{
key: 'appointmentRate',
label: '付费挂号率',
value: formatPercent(dashboard.today.paid_appointment_rate),
hint: '付费挂号数 / 加粉数',
comparisons: [dashboard.today.comparisons.paid_appointment_rate],
},
{
key: 'receiveRate',
label: '面诊接诊率',
value: formatPercent(dashboard.today.interview_receive_rate),
hint: '诊单数 / 面诊数',
comparisons: [dashboard.today.comparisons.interview_receive_rate],
},
{
key: 'interviews',
label: '今日面诊',
value: formatNumber(dashboard.today.interview_count),
hint: '状态为已完成的挂号',
comparisons: [dashboard.today.comparisons.interview_count],
},
])
@@ -524,6 +571,22 @@ const comparisonClass = (value: number | null) => {
return value > 0 ? 'is-positive' : 'is-negative'
}
const comparisonDirectionClass = (direction: ComparisonDirection) => {
if (direction === 'up') return 'is-positive'
if (direction === 'down') return 'is-negative'
return 'is-flat'
}
const formatMetricComparison = (comparison: MetricComparison) => {
const prefix = comparison.label ? `${comparison.label} ` : ''
if (comparison.direction === 'flat') return `${prefix}较昨日持平`
const direction = comparison.direction === 'up' ? '上升' : '下降'
if (comparison.rate === null) return `${prefix}较昨日${direction}(昨日为 0`
return `${prefix}较昨日${direction} ${Math.abs(Number(comparison.rate)).toFixed(1)}%`
}
const rankingWidth = (value: number, maximum: number) => {
if (maximum <= 0 || value <= 0) return '0%'
return `${Math.max(6, Math.min(100, (Number(value) / maximum) * 100))}%`
@@ -783,6 +846,27 @@ onBeforeUnmount(() => {
&.is-negative {
color: #d04c55;
}
&.is-flat {
color: var(--el-text-color-secondary);
}
}
.metric-comparisons {
display: flex;
flex-wrap: wrap;
column-gap: 12px;
min-height: 18px;
}
.metric-hint {
margin-top: 2px;
overflow: hidden;
color: var(--el-text-color-placeholder);
font-size: 10px;
line-height: 1.4;
text-overflow: ellipsis;
white-space: nowrap;
}
.dashboard-grid {