fix(analytics): 趋势图可读性——摘要行/轴刻度稀疏与大数万化/实线细网格/峰值标注/均值参考线
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -55,11 +55,15 @@
|
|||||||
|
|
||||||
<el-card v-loading="loading" shadow="never" class="trend-card">
|
<el-card v-loading="loading" shadow="never" class="trend-card">
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-radio-group v-model="trendMetric" size="small">
|
<div class="trend-header">
|
||||||
<el-radio-button value="msg">消息量</el-radio-button>
|
<el-radio-group v-model="trendMetric" size="small">
|
||||||
<el-radio-button value="active_customers">活跃客户</el-radio-button>
|
<el-radio-button value="msg">消息量</el-radio-button>
|
||||||
<el-radio-button value="call_cnt">通话量</el-radio-button>
|
<el-radio-button value="active_customers">活跃客户</el-radio-button>
|
||||||
</el-radio-group>
|
<el-radio-button value="call_cnt">通话量</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
<!-- 数字摘要行:图之前先给一眼可读的合计/日均/峰值 -->
|
||||||
|
<span v-if="trendSummary" class="trend-summary">近30天 {{ trendSummary }}</span>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div ref="chartEl" class="trend-chart"></div>
|
<div ref="chartEl" class="trend-chart"></div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -208,6 +212,10 @@ const anomalyView = computed<AnomalyView | null>(() => {
|
|||||||
|
|
||||||
const fmt = (n: number | undefined | null) => (n ?? 0).toLocaleString()
|
const fmt = (n: number | undefined | null) => (n ?? 0).toLocaleString()
|
||||||
|
|
||||||
|
// 趋势摘要:先给数字再看图(合计/日均/峰值及日期)
|
||||||
|
const trendVals = (): number[] =>
|
||||||
|
(data.value?.trend ?? []).map((p) => p[trendMetric.value] as number)
|
||||||
|
|
||||||
// 预警行动入口:计数来自 overview 载荷 actions(0 灰显仍可点,直达处置页)
|
// 预警行动入口:计数来自 overview 载荷 actions(0 灰显仍可点,直达处置页)
|
||||||
const actionCards = computed(() => {
|
const actionCards = computed(() => {
|
||||||
const a = data.value?.actions
|
const a = data.value?.actions
|
||||||
@@ -239,24 +247,90 @@ const cardList = computed<CardItem[]>(() => {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 大数压缩:轴刻度/标注统一 1.1万 口径(裸 11254 难读)
|
||||||
|
const compact = (n: number) => (n >= 10000 ? `${(n / 10000).toFixed(1)}万` : n.toLocaleString())
|
||||||
|
|
||||||
|
// 趋势摘要:先给数字再看图(合计/日均/峰值及日期)
|
||||||
|
const trendSummary = computed(() => {
|
||||||
|
const t = data.value?.trend
|
||||||
|
if (!t || !t.length) return ''
|
||||||
|
const vals = trendVals()
|
||||||
|
const total = vals.reduce((a, b) => a + b, 0)
|
||||||
|
const max = Math.max(...vals)
|
||||||
|
const maxDay = t[vals.indexOf(max)].day
|
||||||
|
const dstr = `${String(maxDay).slice(4, 6)}-${String(maxDay).slice(6)}`
|
||||||
|
return `合计 ${total.toLocaleString()} · 日均 ${Math.round(total / vals.length).toLocaleString()} · 峰值 ${max.toLocaleString()}(${dstr})`
|
||||||
|
})
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
const t = data.value?.trend
|
const t = data.value?.trend
|
||||||
if (!t) return
|
if (!t || !t.length) return
|
||||||
|
const labels = t.map((p) => `${String(p.day).slice(4, 6)}-${String(p.day).slice(6)}`)
|
||||||
|
const vals = trendVals()
|
||||||
|
// X 轴 ~6 个刻度(30 个日期全排会挤成一串不可读)
|
||||||
|
const interval = Math.max(0, Math.ceil(labels.length / 6) - 1)
|
||||||
|
const maxIdx = vals.indexOf(Math.max(...vals))
|
||||||
|
const avg = Math.round(vals.reduce((a, b) => a + b, 0) / vals.length)
|
||||||
render({
|
render({
|
||||||
grid: { left: 48, right: 16, top: 24, bottom: 28 },
|
grid: { left: 56, right: 56, top: 28, bottom: 28 },
|
||||||
tooltip: { trigger: 'axis' },
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
valueFormatter: (v: unknown) => compact(Number(v)),
|
||||||
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
// day 是 yyyymmdd int(20260904 → "09-04")
|
data: labels,
|
||||||
data: t.map((p) => `${String(p.day).slice(4, 6)}-${String(p.day).slice(6)}`),
|
axisLabel: { interval, hideOverlap: true, color: '#909399' },
|
||||||
|
axisLine: { lineStyle: { color: '#dcdfe6' } },
|
||||||
|
axisTick: { show: false },
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: { formatter: (v: number) => compact(v), color: '#909399' },
|
||||||
|
// 实线细网格(虚线网格是可视化反模式——虚线该留给参考线)
|
||||||
|
splitLine: { lineStyle: { type: 'solid', color: '#f0f1f3' } },
|
||||||
},
|
},
|
||||||
yAxis: { type: 'value' },
|
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
areaStyle: { opacity: 0.12 },
|
lineStyle: { width: 2 },
|
||||||
data: t.map((p) => p[trendMetric.value]),
|
areaStyle: { opacity: 0.1 },
|
||||||
|
data: vals,
|
||||||
|
showSymbol: false,
|
||||||
|
// 选择性标注:终点一个数(其余靠轴+tooltip,逐点标数是反模式)
|
||||||
|
endLabel: {
|
||||||
|
show: true,
|
||||||
|
formatter: () => compact(vals[vals.length - 1]),
|
||||||
|
color: '#606266',
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
// 峰值:小圆点+2px 表面描边环(默认大水滴太响);标注峰值日的数
|
||||||
|
markPoint: {
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 9,
|
||||||
|
itemStyle: { borderColor: '#fff', borderWidth: 2 },
|
||||||
|
label: {
|
||||||
|
formatter: () => compact(vals[maxIdx]),
|
||||||
|
fontSize: 11,
|
||||||
|
color: '#606266',
|
||||||
|
offset: [0, -12],
|
||||||
|
},
|
||||||
|
data: [{ coord: [maxIdx, vals[maxIdx]] }],
|
||||||
|
},
|
||||||
|
// 均值参考线:虚线在此语义正确(参考阈值)
|
||||||
|
markLine: {
|
||||||
|
silent: true,
|
||||||
|
symbol: 'none',
|
||||||
|
lineStyle: { type: 'dashed', color: '#c0c4cc', width: 1 },
|
||||||
|
label: {
|
||||||
|
formatter: `均值 ${compact(avg)}`,
|
||||||
|
position: 'insideEndTop',
|
||||||
|
fontSize: 11,
|
||||||
|
color: '#909399',
|
||||||
|
},
|
||||||
|
data: [{ type: 'average', name: '均值' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@@ -324,6 +398,16 @@ onMounted(() => {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
.trend-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.trend-summary {
|
||||||
|
margin-left: auto;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
.trend-chart {
|
.trend-chart {
|
||||||
height: 320px;
|
height: 320px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user