422a726ee8
Co-Authored-By: Claude Code <noreply@anthropic.com>
95 lines
2.6 KiB
Vue
95 lines
2.6 KiB
Vue
<template>
|
|
<el-card shadow="never" class="kpi-card" :class="{ link: !!to }" @click="onClick">
|
|
<div class="kpi-label">{{ label }}</div>
|
|
<div class="kpi-value" :class="{ danger }">
|
|
{{ valueText }}<span v-if="unit && value !== null" class="kpi-unit">{{ unit }}</span>
|
|
</div>
|
|
<div v-if="deltaText" class="kpi-delta" :class="deltaClass">
|
|
环比{{ deltaText }}
|
|
</div>
|
|
<div v-if="hint" class="kpi-hint">{{ hint }}</div>
|
|
</el-card>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
// 经营管理 KPI 卡:label 上小灰字 + 主值大号字(千分位/unit 后缀/danger 红)
|
|
// + 环比行(批次D,delta prop)+ hint 下小灰字。to 存在时整卡可点
|
|
// (hover 手型)跳转——纯展示+导航,不取数。null 值语义(无样本)显 `--`,
|
|
// 由父级决定传 null。
|
|
import { computed } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
const props = defineProps<{
|
|
label: string
|
|
value: number | null
|
|
unit?: string
|
|
hint?: string
|
|
to?: string
|
|
danger?: boolean
|
|
// 较前窗环比 (cur-prev)/prev:null/undefined/NaN(前窗缺数据)不显示。
|
|
// 只支持升好语义(升绿↓红)——本批接入三卡(活跃客户/活跃员工/消息量)
|
|
// 均为越高越好;将来若有降好指标(如首响时长)需先加方向语义位
|
|
delta?: number | null
|
|
}>()
|
|
|
|
const router = useRouter()
|
|
|
|
const valueText = computed(() => (props.value === null ? '--' : props.value.toLocaleString()))
|
|
|
|
const deltaText = computed(() => {
|
|
if (props.delta == null || !Number.isFinite(props.delta)) return null
|
|
const arrow = props.delta > 0 ? '↑' : props.delta < 0 ? '↓' : '→'
|
|
return `${arrow}${(Math.abs(props.delta) * 100).toFixed(1)}%`
|
|
})
|
|
const deltaClass = computed(() =>
|
|
props.delta == null || !Number.isFinite(props.delta)
|
|
? ''
|
|
: props.delta > 0 ? 'up' : props.delta < 0 ? 'down' : 'flat')
|
|
|
|
function onClick(): void {
|
|
if (props.to) router.push(props.to)
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.kpi-card.link {
|
|
cursor: pointer;
|
|
}
|
|
.kpi-label {
|
|
color: var(--el-text-color-secondary);
|
|
font-size: 13px;
|
|
}
|
|
.kpi-value {
|
|
font-size: 26px;
|
|
font-weight: 600;
|
|
margin: 6px 0 2px;
|
|
color: var(--el-text-color-primary);
|
|
}
|
|
.kpi-value.danger {
|
|
color: var(--el-color-danger);
|
|
}
|
|
.kpi-unit {
|
|
font-size: 13px;
|
|
font-weight: 400;
|
|
color: var(--el-text-color-secondary);
|
|
margin-left: 2px;
|
|
}
|
|
.kpi-delta {
|
|
font-size: 12px;
|
|
margin-bottom: 2px;
|
|
}
|
|
.kpi-delta.up {
|
|
color: var(--el-color-success);
|
|
}
|
|
.kpi-delta.down {
|
|
color: var(--el-color-danger);
|
|
}
|
|
.kpi-delta.flat {
|
|
color: var(--el-text-color-secondary);
|
|
}
|
|
.kpi-hint {
|
|
font-size: 11px;
|
|
color: var(--el-text-color-secondary);
|
|
}
|
|
</style>
|