diff --git a/yunkes/src/components/management/AnomalyList.vue b/yunkes/src/components/management/AnomalyList.vue new file mode 100644 index 0000000..57b62b0 --- /dev/null +++ b/yunkes/src/components/management/AnomalyList.vue @@ -0,0 +1,101 @@ + + + + + 严重 + 关注 + + + + + {{ row.title }} + {{ row.reason }} + + + + + {{ formatValue(row.current_value, row.unit) }} + + {{ row.baseline_value === null ? '无基线' : `基线 ${formatValue(row.baseline_value, row.unit)}` }} + + + + + + + {{ row.affected_count }} + + + + + {{ row.suggested_action }} + + + + + + + + + + + diff --git a/yunkes/src/components/management/DataAsOfBadge.vue b/yunkes/src/components/management/DataAsOfBadge.vue new file mode 100644 index 0000000..7b50ccb --- /dev/null +++ b/yunkes/src/components/management/DataAsOfBadge.vue @@ -0,0 +1,49 @@ + + + 数据截至 {{ asOfText }} + 无数据 + 口径 {{ metricVersion }} + 试算 + + + + + + diff --git a/yunkes/src/components/management/ManagementFilterBar.vue b/yunkes/src/components/management/ManagementFilterBar.vue new file mode 100644 index 0000000..3bc0daf --- /dev/null +++ b/yunkes/src/components/management/ManagementFilterBar.vue @@ -0,0 +1,103 @@ + + + + {{ c.label }} + + + + + + + + + + + + + + + 指标口径 + + + + + + + diff --git a/yunkes/src/components/management/ManagementKpiCard.vue b/yunkes/src/components/management/ManagementKpiCard.vue new file mode 100644 index 0000000..d62c6d9 --- /dev/null +++ b/yunkes/src/components/management/ManagementKpiCard.vue @@ -0,0 +1,63 @@ + + + {{ label }} + + {{ valueText }}{{ unit }} + + {{ hint }} + + + + + + diff --git a/yunkes/src/components/management/MetricDefsDrawer.vue b/yunkes/src/components/management/MetricDefsDrawer.vue new file mode 100644 index 0000000..2c3f32d --- /dev/null +++ b/yunkes/src/components/management/MetricDefsDrawer.vue @@ -0,0 +1,199 @@ + + + + + + + + + {{ defs.caliber_notes }} + + + + + + {{ defs.score_config.name }} + v{{ defs.score_config.version }} + 试算 + + {{ defs.config_source === 'db' ? '自定义配置' : '默认配置' }} + + + 样本门槛:{{ guardsText }} + 等级线:{{ gradesText }}(低于 D 线为 E) + + + + {{ row.weight }}% + + + + 待数据 + 可用 + + + + + + + + + {{ d.name }} + {{ code }} + {{ d.unit }} + + {{ d.direction === 1 ? '越大越好' : '越小越好' }} + + + {{ d.formula }} + {{ d.caliber }} + + + + + + + + + diff --git a/yunkes/src/components/management/MetricDrilldownDrawer.vue b/yunkes/src/components/management/MetricDrilldownDrawer.vue new file mode 100644 index 0000000..1edc85a --- /dev/null +++ b/yunkes/src/components/management/MetricDrilldownDrawer.vue @@ -0,0 +1,369 @@ + + + + + + + 原因 + {{ card.reason }} + 建议动作 + {{ card.suggested_action }} + + + + + + + + + + + + {{ resp.summary.call_count ?? 0 }} + 期通话 + + + {{ resp.summary.answered_call_count ?? 0 }} + 接通 + + + {{ resp.summary.call_followup_count ?? 0 }} + 跟进 + + + + + + + + + + {{ chTag(row.channel) }} + + + + + {{ formatTime(row.thread_end_ts) }} + + + {{ formatTime(row.reply_ts) }} + + + {{ formatMsDuration(row.fr_ms) }} + + + + + + + + + + {{ chTag(row.channel) }} + + + + + {{ formatMsDuration(row.wait_ms) }} + + + {{ row.preview ?? '' }} + + + + + + + + + + {{ chTag(row.channel) }} + + + + + {{ fmtDay(row.last_day) }} + + + + 沉睡 + 降温 + {{ row.state }} + + + + + + + + {{ formatTime(row.start_call_time) }} + + + {{ callTypeLabel(row.channel) }} + + + {{ row.call_number ?? '--' }} + + + {{ formatCallDuration(row.call_duration) }} + + + + + + + + + + + diff --git a/yunkes/src/utils/format.ts b/yunkes/src/utils/format.ts index 90d84ec..3384be4 100644 --- a/yunkes/src/utils/format.ts +++ b/yunkes/src/utils/format.ts @@ -55,6 +55,28 @@ export function formatDuration(seconds?: number | null): string { return `${m}'${String(rem).padStart(2, '0')}"` } +// --------------------------------------------------------------------------- +// 经营管理板块共享格式化(批次C)——自 DashboardView 本地 fmtFr/fmtPct 提升, +// 驾驶舱 KPI / 异常榜 / 下钻抽屉与旧看板共用同一展示口径。 +// --------------------------------------------------------------------------- + +/** + * 毫秒时长 → `<1h 显「X分Y秒」/ ≥1h 显「X小时Y分」`;null(无样本)显 `-`。 + * 与 formatDuration(秒,聊天气泡口径)区分:此处是指标口径(首响等)。 + */ +export function formatMsDuration(ms?: number | null): string { + if (ms == null) return '-' + const totalSec = Math.round(ms / 1000) + const mins = Math.floor(totalSec / 60) + if (mins < 60) return `${mins}分${totalSec % 60}秒` + return `${Math.floor(mins / 60)}小时${mins % 60}分` +} + +/** 比率(0-1) → 百分比 1 位小数(回复率/接通率等);null 显 `-`。 */ +export function formatRatio(r?: number | null): string { + return r == null ? '-' : `${(r * 100).toFixed(1)}%` +} + /** Friendly label for a Channel value. */ export function channelLabel(channel?: string | null): string { switch (channel) { diff --git a/yunkes/src/views/analytics/DashboardView.vue b/yunkes/src/views/analytics/DashboardView.vue index 7f93e91..7ad379a 100644 --- a/yunkes/src/views/analytics/DashboardView.vue +++ b/yunkes/src/views/analytics/DashboardView.vue @@ -143,16 +143,16 @@ {{ fmt(row.first_contact_cnt) }} - {{ fmtFr(row.fr_avg_ms) }} + {{ formatMsDuration(row.fr_avg_ms) }} - {{ fmtPct(row.reply_rate) }} + {{ formatRatio(row.reply_rate) }} {{ fmtDur(row.call_seconds) }} - {{ fmtPct(row.answer_rate) }} + {{ formatRatio(row.answer_rate) }} {{ fmtDelBlocked(row) }} @@ -187,7 +187,7 @@ import { type StaffRankingRow, } from '@/api/analytics' import { useChart } from '@/composables/useChart' -import { formatTimeShort } from '@/utils/format' +import { formatMsDuration, formatRatio, formatTimeShort } from '@/utils/format' const router = useRouter() @@ -295,16 +295,8 @@ const fmt = (n: number | undefined | null) => (n ?? 0).toLocaleString() const assets = computed(() => data.value?.customer_assets ?? null) // ── 排行新列格式化 ── -// 首响:<1h 显「X分Y秒」,≥1h 显「X小时Y分」;null(无样本)显 '-' -const fmtFr = (ms: number | null) => { - if (ms == null) return '-' - const totalSec = Math.round(ms / 1000) - const mins = Math.floor(totalSec / 60) - if (mins < 60) return `${mins}分${totalSec % 60}秒` - return `${Math.floor(mins / 60)}小时${mins % 60}分` -} -// 百分比 1 位小数(回复率/接通率);null 显 '-' -const fmtPct = (v: number | null) => (v == null ? '-' : `${(v * 100).toFixed(1)}%`) +// 首响/百分比用共享版(formatMsDuration/formatRatio,utils/format——批次C +// 驾驶舱组件同口径,原本地 fmtFr/fmtPct 已提升去重) // 通话时长(秒):<1h 显「X分」,否则「X小时Y分」;0/null 显 '-' const fmtDur = (s: number | null) => { if (!s) return '-'
{{ code }}
{{ d.formula }}