更新
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user