docs(management): 批次D员工页计划——总览+详情+环比+预设补全(Task D0-D4)
评审3真问题全修:排序白名单四列(余列不排序)/服务页签图改P50P90双线 (员工级无每日率序列)/CSV扩csvExport注释行+rows适配;advisory全采: 异常卡补employee_name(一行后端)/showEmployee复用开关/formula_note归位/ 伪卡片下钻衔接/compare仅显示态
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# 云客经营管理后台·批次D(员工总览+员工详情)实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 补上管理闭环最大断点——员工总览(41 人多维一张表)与员工详情(五维/页签/四对比/事实下钻),打通「异常→员工全景→事实」链路;附带驾驶舱 KPI 环比与时间预设补全(体验级小项)。
|
||||
|
||||
**Architecture:** 纯前端为主(消费批次B已就绪的 employees/detail/metrics sections/drilldown 端点)+ 两处小后端增强(dashboard kpis 加 prev 块;无新端点)。新页 `EmployeeAnalyticsView`/`EmployeeAnalyticsDetailView` + 对比抽屉;复用 C3 六组件(FilterBar/DataAsOfBadge/AnomalyList/MetricDrilldownDrawer/MetricDefsDrawer)与 useManagementFilters。依据 [v2 计划](./2026-09-08-yunke-management-console-implementation-v2.md) §6/§7/§13 + 批次B契约清单。
|
||||
|
||||
**Tech Stack:** 同批次C(Vue3+TS+Element Plus+useChart+vitest);后端 FastAPI 小改。
|
||||
|
||||
---
|
||||
|
||||
## 执行者必读
|
||||
|
||||
1. **消费的端点 as-built 形态**(写 client 前已核对 management.py:321-443):
|
||||
- `GET /employees`(行结构见批次C client 的 EmployeeRow——已含 metrics/comparisons/score 摘要/anomaly_count/weight);
|
||||
- `GET /employees/{id}`:`{user_id,name,is_on_job,accounts[{channel,account_id,nick_name,user_name}],metrics(22字段dict 含 user_id 键),score{total_score,grade,score_status,trial,dimensions{name,weight,score,status,metrics{code:{value,score,sample_size,sample_status,formula_note}}},metrics_detail{code:{value,prev,baseline,median,sample_size,sample_status,score,unit,name}}},anomalies[],prev(dict|null),baseline(dict|null),scope,data_as_of,metric_version,config_source}`;
|
||||
- `GET /employees/{id}/metrics?section=`:`{section,metrics(该域字段dict),trend[{day,cols...}](无数据日计数0/分位None),comparisons(源指标名键),scope,data_as_of,metric_version}`;
|
||||
- drilldown/metric-definitions 已有 client。
|
||||
2. **前端惯例**同批次C必读(seq 守卫/useChart/URL query/csvExport.ts 全量翻页+中文 BOM/命名路由跳转)。
|
||||
3. **不做**:辅导/快照(E);员工对比不新开 API(勾选 2-5 行客户端拼数据);「指标解释抽屉」= 复用 MetricDefsDrawer(点击列头 ? 图标打开)——不做每指标单独解释面板(v1 §6.5 的完整版二期)。
|
||||
4. 仓库并行会话照旧;中文 conventional 提交;master 直提。
|
||||
|
||||
## 文件地图
|
||||
|
||||
| 文件 | 动作 |
|
||||
|---|---|
|
||||
| `yunkes/src/api/management.ts` | 补 getEmployeeDetail/getEmployeeMetrics + types(D0) |
|
||||
| `yunkes/src/composables/useManagementFilters.ts` | 预设补 今日/昨日/本周/上周(D0) |
|
||||
| `yunkes/src/views/management/EmployeeAnalyticsView.vue` | 新建(D1) |
|
||||
| `yunkes/src/components/management/EmployeeCompareDrawer.vue` | 新建(D1,对比抽屉) |
|
||||
| `yunkes/src/views/management/EmployeeAnalyticsDetailView.vue` | 新建(D2) |
|
||||
| `yunkes/src/router/index.ts` + `MainLayout.vue` | 两路由+两菜单(D1/D2 各自) |
|
||||
| `yunke-server/app/api/routers/management.py` + `api_assembly.py` | dashboard kpis 加 prev 块(D3) |
|
||||
| `yunkes/src/components/management/ManagementKpiCard.vue` + `AnomalyList.vue` + 驾驶舱页 | delta 显示 + 员工跳详情(D3) |
|
||||
|
||||
---
|
||||
|
||||
### Task D0: client 补全 + 预设扩展
|
||||
|
||||
- [ ] `api/management.ts` 追加:
|
||||
|
||||
```typescript
|
||||
export interface EmployeeAccount { channel: string; account_id: string; nick_name: string | null; user_name: string | null }
|
||||
export interface ScoreMetricDetail { value: number | null; prev: number | null; baseline: number | null; median: number | null; sample_size: number; sample_status: string; score: number | null; unit: string; name: string }
|
||||
// formula_note 在 dimensions[dim].metrics[code] 上(scoring.py 维度级明细),不在 metrics_detail
|
||||
export interface EmployeeDetailResp {
|
||||
user_id: string; name: string | null; is_on_job: boolean | null
|
||||
accounts: EmployeeAccount[]
|
||||
metrics: Record<string, number | string | null>
|
||||
score: {
|
||||
total_score: number | null; grade: string | null; score_status: string; trial: boolean
|
||||
dimensions: Record<string, { name: string; weight: number; score: number | null; status: string; metrics?: Record<string, { value: number | null; score: number | null; sample_size: number; sample_status: string; formula_note?: string }> }>
|
||||
metrics_detail: Record<string, ScoreMetricDetail>
|
||||
}
|
||||
anomalies: AnomalyCard[]
|
||||
prev: Record<string, number | string | null> | null
|
||||
baseline: Record<string, number | string | null> | null
|
||||
scope: MgmtScope; data_as_of: number | null; metric_version: string; config_source: 'db' | 'default'
|
||||
}
|
||||
export interface SectionResp {
|
||||
section: string; metrics: Record<string, number | null>
|
||||
trend: Record<string, number | null>[]
|
||||
comparisons: Record<string, { current: number | null; prev: number | null; baseline: number | null; median: number | null }>
|
||||
scope: MgmtScope; data_as_of: number | null; metric_version: string
|
||||
}
|
||||
export type MetricsSection = 'overview' | 'service' | 'customer' | 'workload' | 'calls'
|
||||
export const getEmployeeDetail = (userId: string, channel: MgmtChannel, start: number, end: number) =>
|
||||
getJson<EmployeeDetailResp>(`/management/employees/${userId}`, { channel, start, end })
|
||||
export const getEmployeeMetrics = (userId: string, section: MetricsSection, channel: MgmtChannel, start: number, end: number) =>
|
||||
getJson<SectionResp>(`/management/employees/${userId}/metrics`, { section, channel, start, end })
|
||||
```
|
||||
|
||||
(写入前 curl 真机抽查 detail 响应键核对——同批次C C1 惯例,以真实响应为准修。)
|
||||
|
||||
- [ ] `useManagementFilters.ts` 预设补:`Preset` 加 `'today' | 'yesterday' | 'thisWeek' | 'lastWeek'`;`presetRange` 四分支(today=当天单日窗;yesterday=昨日单日;thisWeek=本周一..今天;lastWeek=上周一..周日,**周一为一周起点**);vitest 补 5 例(today/yesterday/thisWeek 周中+周一/lastWeek 跨月)。FilterBar 的预设选项组同步加(el-select options),分组显示:常用(今日/昨日/近7/近30)/日历(本周/上周/本月/上月)/自定义。
|
||||
- [ ] vue-tsc + `npm run test`(23+ 过)+ commit `feat(management): client补员工详情/分段接口+时间预设补今日昨日本周上周`
|
||||
|
||||
### Task D1: 员工总览页
|
||||
|
||||
**Files:** Create `EmployeeAnalyticsView.vue` + `EmployeeCompareDrawer.vue`;改 router(`/management/employees`)+ MainLayout(管理驾驶舱下加「员工分析」菜单项)。
|
||||
|
||||
**页面结构**:
|
||||
|
||||
```text
|
||||
ManagementFilterBar(复用;compare 选择器在此页透出——el-select 对比方式:上期/个人基线/团队中位数→apply({compare}))
|
||||
工具行:keyword el-input(placeholder 姓名/ID搜索) | status el-select(全部/在职/离线) | 视图 el-radio-group×4
|
||||
(management=概览/workload=负载/service=服务/customer=客户经营——切换列组)| 指标口径按钮 | 导出 CSV | DataAsOfBadge
|
||||
主表 el-table(服务端排序 sort-change→query;行选择框多选 2-5 人→「对比」按钮开抽屉):
|
||||
固定列:姓名(链接进详情)+ID 小字、在职 tag
|
||||
management 视图列:综合分(total_score+grade tag+trial*)、异常(anomaly_count 红数字+weight)、活跃客户、消息量、串/被回、维度三列(customer/service/workload score 各 0-100 或「--」)
|
||||
workload 视图列:活跃客户、活跃群(日均)、消息收/发、通话量/秒、串
|
||||
service 视图列:首响P50/P90(formatMsDuration)、2h回复率、24h回复率(formatRatio)、被回/串
|
||||
customer 视图列:新客/降温/沉睡/回流(期末+期内注)、活跃占比
|
||||
列头 ? 图标→打开 MetricDefsDrawer(复用)
|
||||
空数据/样本不足单元格显示「--」灰字(sample_status=insufficient 时 total 亦然)
|
||||
分页 el-pagination(服务端 page/page_size 20;total)
|
||||
```
|
||||
|
||||
**行为**:query 真源(view/keyword/status/sort_by/sort_order/page 全进 URL);seq 守卫;**服务端排序仅四列**(后端 sort_by 白名单:anomaly_severity[默认]/total_score/active_friends/cust_threads——其余列头不显示排序箭头,tooltip「该列不支持服务端排序」);`sort_by=total_score` 时 null 排最后由后端保证;compare 参数仅客户端显示态(employees 端点无此参数,行恒带全部四对比值);**CSV**:扩展 `utils/csvExport.ts` 支持可选前置注释行(`exportListToCsv(..., preamble?: string[])`——含口径说明如「1:1口径·窗口 20260810-20260908·试算」)+ fetchPage 适配器(/employees 返回 `{rows,total}`,适配为 csvExport 期望的 `{items,total}`),导出当前视图列全量翻页。
|
||||
|
||||
**EmployeeCompareDrawer**:props `{ rows: EmployeeRow[] }`(勾选行已有数据,不另发请求);内容:五维分对比条(3 可用维度每人一行,横向 bar 0-100)+ 关键指标表(活跃客户/P90/2h率/异常数 每人一列)+ 顶部说明「同口径同时窗对比,不产生优劣结论」;>5 人禁开。
|
||||
|
||||
**路由/菜单**:
|
||||
|
||||
```typescript
|
||||
{ path: '/management/employees', name: 'management-employees',
|
||||
component: () => import('@/views/management/EmployeeAnalyticsView.vue'),
|
||||
meta: { title: '员工分析' } },
|
||||
```
|
||||
|
||||
- [ ] vue-tsc/test/build + 真机初查(表格渲染 41 人/切视图列变/排序翻页/keyword/CSV 打开内容对)+ commit `feat(management): 员工总览页——四视图/服务端排序分页/对比抽屉/CSV导出`
|
||||
|
||||
### Task D2: 员工详情页
|
||||
|
||||
**Files:** Create `EmployeeAnalyticsDetailView.vue`;router 加 `/management/employees/:userId`(props:true;**activeMenu 需高亮父菜单「员工分析」**——MainLayout 的 el-menu :default-active 绑定的是路径,嵌套路由时用 route.meta 或 watch 设 index——看 MainLayout 现有 active 处理方式,最简方案:菜单 index 用路径匹配 + 详情页 route path 前缀匹配高亮,若现结构不支持则 meta.activeMenu='/management/employees' 并在 el-menu :default-active 计算)。
|
||||
|
||||
**页面结构**:
|
||||
|
||||
```text
|
||||
头部卡:姓名(+在职 tag)+ID | 账号清单(渠道 tag+account_id+昵称,点击跳 staff-chat/通讯录按渠道)
|
||||
| 本期试算分大数字+grade tag+score_status(insufficient 时灰注「样本不足·不参与考核」)+trial*
|
||||
| DataAsOfBadge | 返回列表链接
|
||||
五维条(v1 §7.2 概览的核心):横向五条 bar——3 可用维度实分数值,action/risk 灰「待数据」占位
|
||||
(ScoreBreakdown 简版:直接在页内画,不新建组件,el-progress/自定义 div)
|
||||
el-tabs 五页签(懒加载:激活才调 getEmployeeMetrics):
|
||||
概览: 六数卡取自**detail.metrics**(msg/活跃/串/通话——section 端点字段有限,期值以 detail 为准)
|
||||
+ 四对比小表(comparisons 直渲染,源指标名行)
|
||||
服务质量: 数卡 P50/P90 + 2h/24h回复率(**客户端从 metrics.reply_2h_count/cust_threads 派生,
|
||||
与 derive_metrics 同式**——section 趋势无每日率序列) + 趋势图=**首响 P50/P90 双线(ms 轴)
|
||||
±串数(次轴可选)**——不做率右轴(员工级无每日率数据,率的团队级序列在驾驶舱)
|
||||
客户经营: 期末四态数卡(取自 detail.metrics) + 降温/沉睡下钻按钮(→DrilldownDrawer lifecycle 分支)
|
||||
工作负载: 数卡取 detail.metrics(收发/活跃客户/群/通话量) + 趋势图用 section=workload 的
|
||||
active_friends/active_rooms 双线(trend 列为日值 active_rooms)
|
||||
通话: 通话四数(detail.metrics) + call_followup 下钻按钮
|
||||
**页签下钻按钮与 MetricDrilldownDrawer 的衔接**:抽屉是 card 驱动——按钮合成最小伪卡片
|
||||
`{title:'首响P90明细', drilldown:{type:'metric',query:{metric_code:'first_reply_p90',user_id}}}` 传入
|
||||
异常区:AnomalyList(复用,@drill→MetricDrilldownDrawer 复用)
|
||||
评分明细折叠面板(el-collapse「评分怎么算出来的」): metrics_detail 表(code/名称/当前/上期/基线/中位/样本/得分)
|
||||
+ prev/baseline 原始 PeriodMetrics 两个 JSON 折叠(调试用,灰字注明)
|
||||
```
|
||||
|
||||
**行为**:query 真源(channel/start/end/compare/section 进 URL,页签切换 replace);seq 守卫每页签独立(切页签取消旧渲染);404 员工→el-result 错误页+返回链接;「创建辅导记录」按钮**占位禁用**(tooltip「批次E上线」)。
|
||||
|
||||
- [ ] vue-tsc/test/build + 真机初查(王云青详情:五维条/五页签懒加载/异常/评分明细展开/账号跳聊天)+ commit `feat(management): 员工详情页——五维条/五页签懒加载/四对比/评分明细/事实下钻复用`
|
||||
|
||||
### Task D3: 驾驶舱 KPI 环比 + 异常跳员工
|
||||
|
||||
**后端**(`management.py` dashboard + `api_assembly.py`):①dashboard 端点 kpis 加 `prev_kpis` 块(active_customers/active_employees/msg_total/cust_threads——dashboard 走 assemble_employees(with_prev=False),需加一次 prev 窗 period_metrics 全员调用,B5 实测三窗合计 ~0.3s,加一窗 +0.1s 量级)+ `msg_delta`((cur-prev)/prev,prev=0→null)。lifecycle/通话不加环比(期末/全渠道口径环比意义弱,注明)。②**异常卡补 `employee_name`**(assemble_employees 手里已有全员 name——组装 anomalies 时带上,一行);同步 AnomalyCard type。测试补:dashboard 响应含 prev_kpis 且数字=prev 窗聚合 + anomalies[0].employee_name 非空。
|
||||
**前端**:KpiCard 加 `delta?: number | null` prop(↑绿↓红,方向语义由调用方定——涨=好传正绿;不传不显示);驾驶舱三卡接 delta(活跃客户/活跃员工/消息量);AnomalyList 的 title 下加员工行(`employee_name` 链接→`/management/employees/{user_id}`)+ 新增 `showEmployee?: boolean` prop(默认 true;D2 员工详情页复用时传 false 免自链接)。
|
||||
- [ ] 后端 pytest(test_management_api.py 补 prev_kpis 断言)+ 前端走查三卡箭头/异常跳员工 + commit `feat(management): 驾驶舱KPI环比(prev_kpis+delta箭头)+异常卡跳员工详情`
|
||||
|
||||
### Task D4: 真机走查 + 验收
|
||||
|
||||
- [ ] 全链路走查(Playwright):驾驶舱→异常卡→**员工详情**(新链路!)→页签→下钻→聊天;员工总览→对比抽屉→CSV;1366px;刷新保持;
|
||||
- [ ] 验收对照(v1 §19.1 员工子集):能查看所有员工多维状态 ✓/任一分数展开为指标公式样本事实 ✓(评分明细面板)/四对比 ✓/样本不足不评分显示 ✓/异常两次点击进事实 ✓/默认排序非总分榜 ✓;
|
||||
- [ ] 四门禁(vue-tsc/vitest/build/后端 pytest);
|
||||
- [ ] commit 收尾零散 + 走查记录追加核实文档「批次D走查」小节。
|
||||
|
||||
---
|
||||
|
||||
## 批次D验收清单
|
||||
|
||||
- [ ] 员工总览:41 人四视图/服务端排序分页/keyword/对比 2-5 人/CSV 含口径注释;
|
||||
- [ ] 员工详情:五维条 3+2 占位/五页签懒加载/四对比/评分明细可展开/三处下钻按钮进抽屉/账号跳聊天;
|
||||
- [ ] 驾驶舱:三 KPI 有环比箭头/异常卡可跳员工详情(闭环打通);
|
||||
- [ ] 时间预设全套(今日/昨日/近7/近30/本周/上周/本月/上月/自定义);
|
||||
- [ ] URL 全参数保持;四门禁绿;后端仅 dashboard 一处小改且测试补齐。
|
||||
|
||||
## 批次E-F
|
||||
|
||||
E:followups 辅导闭环(写端点+schemas_management+详情页按钮激活)+ 考核快照(生成/冻结/列表/导出)。F:调度接入(analytics_tick 第三步+full_sync 13/14 步+scheduler 镜像重建还 9d3cc8e 旧账)+ dashboard 性能修复(unreplied 索引三选一)+ 服务器部署+回测试运行。
|
||||
Reference in New Issue
Block a user