Files
wecom_it_smart_desk/docs/01-产品文档/04-坐席工作台/PRD-REQ-坐席-012-会话条目操作菜单-v1.0.md
T

36 KiB
Raw Blame History

PRD — 坐席端会话条目操作菜单(左栏三点菜单 + 右键菜单)

REQ编号: REQ-坐席-012 版本: v1.0 状态: 🟡 待评审 — 交互形态与中栏取舍已由需求方拍板,菜单矩阵与组件边界待技术评审 优先级: P1 日期: 2026-08-09 作者: 许清楚(产品经理) 需求来源: 宋献(IT 支持组组长)口述需求 4 条 技术依据: docs/02-技术文档/技术架构/技术核查-坐席会话条目操作菜单-v1.0.md(高见远,2026-08-09 配套原型: 原型-REQ-坐席-000-坐席工作台-v1.7.html 关联: PRD-REQ-坐席-011(统一工作队列重构)、REQ-坐席-004、REQ-坐席-009、REQ-坐席-010 性质: 增量 PRD。仅描述本次新增/变更,不复述既有功能。


1. 背景

1.1 需求原文与映射

# 宋献原始需求 本 PRD 对应
1 确认现有坐席架构,控件能否在「选中对象-右键调出菜单」模式实现 §3.1 D-3、§4.3
2 左栏每个会话表单右边增加菜单按钮(三个点),点击弹出接单/置顶/代办/转接 §4.1、§4.2、§5
3 上述完成后,去除中栏上方「接单/置顶/代办/转接」4 个按钮 §3.1 D-2、§6
4 先基于最新原型更新细致原型图,与用户确认后再改代码 §3.1 D-1、§10

1.2 🔴 前置事实修正(必须在评审开场说明)

需求提出时的前置认知是「V1.0–V1.6 原型中这 4 个按钮丢失了」。经架构师代码核查,该认知需要修正两处

认知 事实
「按钮丢失了」 4 个按钮在真实运行代码中从未丢失。接单 UserInfoBar.vue:116-124、置顶 :127-133、代办 :136-142、转接 :145-164,且四者均为服务端真闭环(store → API → 后端端点,非 Mock)
「从 v1.5 开始丢失」 丢失只发生在原型 HTML 侧,且起始版本是 v1.4。v1.0–v1.3 均含 4 按钮(关键词计数实证:接单 11-12 次 / 置顶 3 次 / 代办 1 次 / 转接 2 次),v1.4 起归零

根因v1.4 起原型换了血统。v1.0v1.3 是 24002700 行的全量高保真原型;v1.4 是为论证「B 方案三栏分栏比例」与「排查步骤全屏流程图」而新画的专题示意图(961 行),并非 v1.3 的增量演进。v1.5/v1.6 沿此专题线继续演进。

后果(这是本次必须先做字段回填的原因)v1.6 的左栏 .conv-item 只有「头像/姓名/时间/摘要/未读数」5 个字段,而真实代码 ConversationItem.vue 的条目含:

元素 真实代码 v1.6 原型
置顶 📌 / 代办 📋 图标 :50 :52
tag-badgeVIP/招手/需介入/情绪/坐席名/待确认) 6 类 :56-82
优先级图标(👥🔁 最多 4 个 :84-95
紧急度星级 5 星 :107-114
接手 / 退出 link 按钮 :116-136
处理对象缩略头像 :141-148

若直接在 v1.6 上加三点按钮,会得出「条目右侧有大片空槽」的错误结论——真实条目右侧已被 .conv-target-avatar 占满(section !== 'history' 时恒显示)。这是 §3.1 D-1 选择 v1.3 而非 v1.6 作为 v1.7 基线的直接原因。

1.3 本次真正的新增工作量在哪里

架构核查指出:现有 4 个按钮几乎没有基于会话归属的可见性控制——置顶/代办在中栏任何状态下都常驻且不禁用。这在中栏可接受(中栏只显示当前打开的会话,多为自己的);但一旦下沉到左栏,左栏同时呈现「我的会话 / 同事会话 / 历史会话」三个分区ConversationList.vue:43-76),菜单项必须新增归属维度的可见性规则。

本次改造的产品设计核心不是"控件搬家",而是补齐一张此前不存在的「状态 × 归属 × 分区」菜单项可见性矩阵(§5)。控件本身是低成本工作。


2. 产品目标

# 目标 衡量方式
G-1 把会话的列表管理动作(接单/置顶/代办)下沉到动作对象所在处,消除「操作在中栏、效果在左栏」的焦点跳跃 置顶/代办操作后无需视线迁移即可看到条目重排
G-2 在不牺牲对话过程效率的前提下精简中栏顶栏 中栏操作控件由 6 个降至 3 个(转接/摇人/结单),且转接路径不退化(保持 3 步)
G-3 菜单壳一次抽对,避免 PRD-011 Phase 2 与虚拟滚动落地时二次重写 新增 kind(审批/工单)时只加一个菜单项工厂,不改菜单壳

2.1 用户故事

# 故事
US-1 作为坐席,我想在左栏直接对某条会话置顶/代办,这样我不必先打开它、再去中栏操作、再回来看列表变化
US-2 作为坐席,我想在扫视排队会话时就地接单,这样我不必"先点开、再接单"两步走
US-3 作为熟练坐席,我想右键会话条目直接出菜单,这样我不必先 hover 找到三点按钮
US-4 作为坐席,我不想在同事的会话上看到「置顶」——点了会 403 报错,还不给我任何提示
US-5 作为坐席,我在中栏读完员工描述判断"这不归我管"时,希望能就地转接,而不是回左栏重新定位这条会话

3. 已锁定决策

3.1 需求方已拍板项(D-1 ~ D-6)

# 决策项 结论 依据
D-1 原型基线 以 v1.3 为基线复制为 v1.7(保留高保真左/中/右三栏),再增量叠加 v1.4~v1.6 演进特征(B 方案分栏比例、排查步骤全屏流程图、智能推荐竖排、工具栏重组、Emoji 图标统一)。不以 v1.6 为基线 §1.2;架构报告 §1.4 / R-2
D-2 中栏按钮处理 删除「接单/置顶/代办」3 个,保留「转接」。转接与既有的「摇人」「结单」构成语义一致的对话过程动作组(转接=交出去 / 摇人=拉进来 / 结单=做完了) §6;架构报告 §5.1
D-3 交互形态 三点按钮为主入口 + 右键为快捷方式,两者并存。二者共用同一份菜单项定义与 handler §4.3;架构报告 §2.3 选项 3
D-4 菜单实例架构 菜单为单例,挂在 ConversationList 层级,不挂在条目内部 §7;架构报告 §2.4-5
D-5 三点按钮落位 替换 .conv-target-avatar(同栅格位淡入淡出),非覆盖、非并列 §4.1;架构报告 §4.3
D-6 菜单弹层定位 placement="bottom-end",宽 140px,视口下缘不足时翻转 top-endDOM 必须 position:fixed 或 Teleport 到 body §4.2;架构报告 §1.3.4

3.2 D-2 的完整论证(为什么不是 4 个全删)

需求原文是「去除中栏上方 4 个按钮」。经操作路径推演,4 个一刀切全删会造成分档不同的退化

操作 当前路径 全删后路径 退化判定
接单 中栏 1 步 左栏 定位→hover→三点→接单 不退化,删除是净收益。接单发生在打开会话之前/之时,左栏本就是接单的自然场所(Zendesk / ServiceNow 同构)。且中栏该按钮在 status !== 'queued' 时恒为 disabled 的「已接单」,长期占位不可点
置顶 中栏 1 步 左栏 4 步 🟡 轻微退化,可接受。置顶是列表组织行为,其效果(条目上浮)只在左栏可见。操作与反馈同处一栏,语义反而更内聚
代办 中栏 1 步 左栏 4 步 🟡 同上
转接 点转接 → 选坐席 → 确认 = 3 步 视线移左栏 → 定位当前条目 → hover → 点三点 → 点转接 → 选坐席 → 确认 = 6+ 步 🔴 显著退化,不接受

转接不可下放的三条理由

  1. 触发时机根本不同。置顶/代办是坐席扫视队列时的列表管理动作;转接是坐席读完员工描述、判断"这不归我管"那一刻的对话过程动作——此时注意力与鼠标都在中栏。要求回左栏是注意力焦点的强制迁移
  2. "定位当前条目"本身有成本。左栏三分区、可滚动,且条目会因置顶/紧急度动态重排,当前会话未必在视口内。这一步不是零成本的。
  3. 删了转接反而制造不一致。中栏保留「摇人」「结单」是既定事实。若转接被赶到左栏,结果是同为对话过程中的协作类动作,入口被劈成两处——这比"全部在中栏"或"全部在左栏"都差。

同时,左栏三点菜单仍提供「转接」。两个入口共用同一 handler 与同一坐席选择 Dialog,符合"高频动作允许多入口"原则,不构成语义分裂。

给需求方的备选(如坚持"顶栏太挤"是主要动机):可将 3 个被删按钮收进中栏自己的「⋯」总菜单,视觉上顶栏只剩「摇人 / 结单 / ⋯」,功能零退化。此形态未在 v1.7 中作为主方案呈现,如需要请在评审时提出。


4. 交互设计

4.1 三点按钮(主入口)

规格 依据
位置 .conversation-item 最右侧,替换 .conv-target-avatar 所在栅格位 D-5
切换方式 同一栅格位内:缩略头像 opacity:0 淡出、三点 opacity:1 淡入(transition ≈ 140ms)。不改变布局盒模型,无抖动 架构报告 R-7
命中区 24 × 24 px;图标 MoreFilled@element-plus/icons-vue@^2.3.0 已装)1416px D-5
默认态 隐藏(opacity:0; pointer-events:none
显示触发 .conversation-item:hover .is-menu-open 按钮 :focus-visible 保证键盘可达
菜单打开期间 按钮强制常显,条目加 .is-menu-open 类(背景保持 hover 态),避免"鼠标移出→按钮消失→菜单悬空"的观感断裂 架构报告 §2.3 选项 2 风险项
历史分区 section === 'history' 本无缩略头像,三点直接占该位(可常显淡色 opacity:.35hover 加深) D-5
事件 必须 @click.stop —— 根节点 @click="$emit('click')"ConversationItem.vue:20,不阻断会连带切换会话。项目内已有正确先例(:122 :133@click.stop 架构报告 §2.4-4

为什么不选"覆盖"或"并列"

  • 覆盖(absolute 盖在缩略头像上):hover 时缩略头像被遮挡,信息丢失且视觉脏;
  • 并列(插到缩略头像右侧):再吃掉 ~24px,.conversation-info 从 ~132px 压到 ~108px,第一行 6 类 tag + 4 个优先级图标严重挤压。左栏条目内部可用宽度仅 ≈ 228px260 margin 12 padding 20),没有余量。

4.2 菜单弹层

规格
宽度 140px(4 项中文两字 + 图标;不得超过侧栏 260px)
定位 placement="bottom-end"(右对齐向下)
翻转 视口下缘空间不足时自动翻转 top-end;右缘不足时自动贴边
DOM 归属 🔴 必须 position:fixed + 视口坐标,或 Teleport 到 body禁止position:absolute 挂在 .conversation-item 内部
层级 使用 EP popper 默认 z-index2000+ 自增)或新引入的 --z-dropdown: 2000 token。不得沿用 MessageItem.vue 的 1000(低于 EP popper,会被 dropdown/messagebox 盖住)
关闭时机 点击菜单外 ∪ Esc ∪ 列表滚动 ∪ 窗口 resize ∪ 切换会话 ∪ 在另一条目再次右键
互斥 全局仅一个菜单可见(单例天然满足,见 §7)

为什么 DOM 必须脱离左栏(这是硬约束,不是优化建议):

.workspace-sidebar        { overflow: hidden; }      /* global.css:277 */
.conversation-list-scroll { overflow-x: hidden; }    /* global.css:359 */

双重裁剪导致:① 横向——260px 窄栏内放不下 140px 菜单向右展开,必被裁;② 纵向——列表首尾条目的菜单会被滚动容器裁掉。

4.3 右键(快捷方式)

规格
绑定 .conversation-item 根节点 @contextmenu.prevent.stop
定位 以鼠标 clientX/clientY 为锚点,position:fixed
菜单内容 与三点按钮完全一致——由同一个 computed 生成的菜单项数组驱动,不允许两套定义
覆写原生菜单 是(.prevent)。仅在会话条目上覆写,列表空白区、消息区、输入框保留浏览器原生右键
右键是否需先选中条目 。右键直接对该条目生效,不改变当前打开的会话(避免误切换丢失输入草稿)

为什么两者并存而不是二选一

  • 纯右键发现性为零。左栏是坐席每天使用频率最高的区域,且服务台存在多人轮岗与新人,不能假设全员知晓。零发现性交互在此位置是产品事故。
  • 需求 1(问右键)与需求 2(要三点)本就是同一菜单的两个触发器,不是二选一。并存的边际成本仅 0.3–0.5 人日。

4.4 反馈与错误处理

场景 反馈
操作成功 条目即时反映状态变化(📌/📋 图标出现或消失、条目重排)。不额外弹 toast——视觉变化本身即反馈
接单成功 条目从「排队区」移入「我的会话」,并自动打开该会话
转接成功 条目移出「我的会话」,ElMessage.success('已转接给 XXX')
后端 403 / 失败 🔴 必须给明确 ElMessage.error 文案。现状是既有缺陷stores/conversation.ts:539-541console.error,用户零感知。本次一并修复(P0-7
无可用坐席 「转接」项 disabled + 灰字提示「暂无可用坐席」

5. 菜单项可见性矩阵(核心交付物)

按 会话 status × 归属(is_mine / is_collaborator / can_grab)× 分区(section)分派。 与架构报告 §4.4 完全一致,本节为其产品侧收口版本。

菜单项 显示条件 禁用条件 动态文案 后端权限 备注
接单 status === 'queued' 「接单」 update:all 排队会话专属
置顶 / 取消置顶 is_mine || is_collaborator is_pinned → 「取消置顶」;否则「置顶」 update:own ⚠️ 非本人会话点击必 403,前端必须靠显示条件拦住
代办 / 取消代办 is_mine || is_collaborator is_todo → 「取消代办」;否则「代办」 update:own ⚠️ 同上
转接 status === 'serving' && is_mine availableAgents.length === 0 → 禁用,提示「暂无可用坐席」 「转接…」(省略号表示会开 Dialog) update:all 点击开独立 Dialog,见 Q-3
接手 can_grab && !is_mine && status === 'serving' 「接手」 update:all 与第三行 grab-btn 同义,双入口,见 Q-4
退出协作 is_collaborator && status === 'serving' 「退出协作」(danger 样式) 与第三行 leave-btn 同义,见 Q-4

⚠️ 实现细节校准(影响 A-11 双入口一致性验收):第三行 grab-btn 的显示条件在两个分区并不相同—— ConversationList.vue:49(我的会话区)传入 show-grab = status==='serving' && !is_mine && !is_collaborator ConversationList.vue:62(同事会话区)传入 show-grab = status==='serving' && !is_mine少了 !is_collaborator)。 条目内再与 conversation.can_grab 取交集(ConversationItem.vue:117)。 菜单工厂 useConversationMenuItems.ts 必须复用同一套条件(建议把该判断上提为一个共享 computed,两处引用), 否则会出现「第三行有接手 link、菜单里没有」或反之的不一致,直接违反 A-11。

5.1 分区实例推演(原型必须逐一演示)

实例 状态 菜单实际渲染项 说明
A. 排队会话 status=queuedis_mine=false 接单 仅 1 项。置顶/代办不显示(非本人)
B. 我的会话(serving is_mine=true, is_pinned=false, is_todo=false 置顶 / 代办 / 转接… 3 项。无接单(非 queued)、无接手(非 can_grab
C. 我的会话(已置顶已代办) is_mine=true, is_pinned=true, is_todo=true 取消置顶 / 取消代办 / 转接… 文案动态翻转
D. 同事会话 is_mine=false, can_grab=true, status=serving 接手 仅 1 项。置顶/代办被显示条件拦住(点了会 403
E. 我协作中的会话 is_collaborator=true, status=serving 置顶 / 代办 / 退出协作(danger) 无转接(is_mine=false
F. 历史会话 section=history, status=resolved (空) 全部条件不满足 → 菜单显示「无可用操作」占位,或三点按钮直接不可点
G. 转接无可用坐席 is_mine=true, availableAgents=[] 置顶 / 代办 / 转接(灰) 转接项 disabled,hover 提示「暂无可用坐席」

F 的产品决策:历史会话菜单为空时,三点按钮仍渲染但呈禁用态opacity:.35cursor:default,点击无响应),不弹空菜单。理由:弹出一个写着"无可用操作"的空菜单是无效交互;但完全不渲染按钮会让历史分区条目右侧出现空洞,与其它分区视觉不齐。

5.2 与「结单」的边界

「结单」不进入左栏菜单,理由见 Q-5。中栏保留其原有入口。


6. 中栏改造

6.1 变更清单(UserInfoBar.vue:114-194

# 控件 代码位置 本次处理
1 接单 :116-124 删除
2 置顶 :127-133 删除
3 代办 :136-142 删除
4 转接 :145-164 保留D-2
5 摇人 :167-174 保留(不在本次范围)
6 结单 / 等待确认 :177-193 保留(不在本次范围)
历史会话开关 chip :100-110 保留(在 chips 区,非 actions 区)

改造后中栏 actions 区 = 转接 / 摇人 / 结单,语义收敛为「对话过程动作组」。

6.2 转接入口一致性要求

中栏「转接」与左栏菜单「转接」必须:

  • 调用同一 store actiontransferConv);
  • 打开同一个坐席选择 Dialog 组件Q-3 结论);
  • 保留现有的 ElMessageBox.confirm 二次确认(ChatArea.vue:531-547)。

不得出现"中栏是下拉、左栏是 Dialog"的两套 UI。


7. 组件抽象边界与 PRD-011 兼容

7.1 抽象原则

抽「菜单壳」,不抽「业务动作」。

components/common/WorkItemActionMenu.vue          【本次新建】纯 UI 壳,零业务逻辑
  职责:单例渲染 / fixed 或 Teleport 定位 / 视口翻转
        / Esc·scroll·resize·outside 关闭 / role=menu + ↑↓ 键盘导航
        / z-index token / 分组分隔线 / danger 项样式
  Props : items: MenuItem[]   visible: boolean   anchor: {x,y} | HTMLElement
  Emits : select(action: string)   close()
  ⛔ 不认识 conversation / approval / ticket,不 import 任何 store 与 api

composables/useConversationMenuItems.ts           【本次新建】会话菜单项工厂
  入参:conversation + currentAgentId + section('my'|'colleague'|'history')
  出参:MenuItem[](已按 §5 矩阵算好 hidden / disabled / label
  ✅ 承载全部可见性规则,可单测,PRD-011 Phase 2 原样复用

composables/useTodoMenuItems.ts                   【PRD-011 Phase 2 新建】任务菜单项工厂

components/conversation/ConversationList.vue      【本次改造】持有单例菜单 + 路由 action 到 store
components/conversation/ConversationItem.vue      【本次改造】仅加三点按钮与 @contextmenu
                                                   只 emit('open-menu', {id, x, y}),不含菜单 DOM

7.2 MenuItem 类型契约

interface MenuItem {
  key: string
  label: string
  icon?: string
  danger?: boolean
  disabled?: boolean
  hidden?: boolean
  tooltip?: string
  /** 🔴 关键字段:决定容器采用哪种反馈策略 */
  execMode: 'direct' | 'redirect' | 'disabled'
}

execMode 为什么是必需字段(对宋献"未来待办条目进入左栏后菜单怎么分派"的直接回答):

kind 操作性质 执行路径 反馈模型
conversation 服务端真闭环 store → API → 后端端点 → 刷新列表 同步成功/失败
approval 降级跳转 <a target="_blank"> 跳企微 → sys_approval_change 回调回写 → WS 推送 异步最终一致
ticket 当前无对象 ITSMService.get_todo_list() 无条件 return [] 无(PRD-011 U-1.2

必须分派,且不只是文案不同——是执行语义与反馈模型的根本不同。 会话操作点完即生效;审批操作点完只是跳走,真正的状态变更在企微侧异步回写。若三类共用一套「点击 → toast 成功」的反馈模型,就会重演 PRD-011 §3.1 定性为 P0 的那个错误(绿色成功提示 + 外部系统实际未变更)。

7.3 与 PRD-011 Phase 2 的衔接检查表

PRD-011 Phase 2 任务 本设计是否兼容 说明
定义统一 ListItem(含 kind 判别式) MenuItem[] 由 kind 专属工厂生成,菜单壳不感知 kind
条目组件支持会话态 / 任务态两种渲染 条目只 emit('open-menu'),两态共用同一菜单壳
左栏 Tab 分层(会话/待办/全部) 「全部」Tab 混排时,容器按 item.kind 选工厂
U-5 虚拟滚动 单例架构天然兼容(见下)
U-6 待办未读/变更标识 与菜单无关

D-4 单例架构的强制理由PRD-011 U-5 已把虚拟滚动挂起。虚拟滚动会在滚动时回收并复用条目 DOM——若菜单实例挂在条目内部,reference 元素被销毁会导致菜单错位或残留。单例架构的实现成本与逐条挂载相同,却可免除未来上虚拟滚动时的整体返工。这正是宋献「避免三个月后第二次重做」诉求的具体着力点。


8. 产品经理自决项结论(Q-3 / Q-4 / Q-5

架构报告 §4.4 将三项留给产品决策。以下为结论与论证。

Q-3 转接的坐席选择:二级菜单 还是 独立 Dialog?

结论:独立 Dialog。菜单项文案为「转接…」(带省略号,遵循"点击会开弹窗"的通用约定)。

论据 说明
① 窄栏二级翻转过于脆弱 主菜单已 140px、bottom-end 右对齐。二级菜单需再向左或向右展开 ~160px。在 260px 侧栏 + 视口右缘的组合下,Popper 需同时做水平翻转 + 垂直翻转,边缘 case 组合爆炸,稳定性无法保证
② 坐席列表承载不下 二级菜单需展示 姓名 (当前负载/最大负载),且在线坐席可能十余人,需要滚动 + 搜索。二级 popover 里塞搜索框是反模式
③ 悬停路径问题 二级菜单靠 hover 展开时,鼠标从主菜单项斜向移动到二级区域会经过其它菜单项,导致二级意外收起(经典 "diagonal problem"),需额外做安全三角形算法——成本高于直接开 Dialog
④ 与中栏入口天然统一 中栏保留的「转接」也需要选坐席。共用同一个 Dialog 组件后,两个入口 UI 完全一致,符合 §6.2 的一致性要求。若左栏用二级菜单、中栏用下拉,就是两套 UI
⑤ 转接是有后果的动作 转接后会话脱手。现有实现已带 ElMessageBox.confirm 二次确认。Dialog 形态与"需要慎重决策"的语义匹配,二级菜单的轻量感反而不合适

Dialog 规格:宽 380px,标题「转接会话」,含搜索框 + 坐席列表(头像 / 姓名 / 负载/上限)+ 满载坐席置灰不可选 + 取消按钮。选中坐席后走原有二次确认。

反方意见记录:二级菜单少一次弹窗、路径更短。但转接日频次不高(非置顶/代办那类高频动作),为低频动作牺牲稳定性不划算。


Q-4 「接手」「退出协作」是否收进三点菜单(收则移除第三行按钮)?

结论:收进菜单,但第三行 grab-btn / leave-btn 保留不动 —— 双入口并存。

这与架构建议("本次不动,保持第三行按钮,菜单只放 4 项")有分歧,理由如下:

论据 说明
① 右键路径的完整性 D-3 已定右键并存。坐席右键条目时,鼠标位置是任意的——若菜单里没有「接手」,坐席仍需移动鼠标到第三行那个小 link 按钮才能接手。这让右键快捷方式在最需要它的同事会话场景下失效,自相矛盾
② 移除第三行按钮才是真损失 第三行 grab/leave 是 v-if 数据驱动的常显 link,零 hover 成本、一次点击即达。三点菜单需 hover→点击→点击共 3 步。移除它是明确的效率退化,不能为了"避免重复"而做
③ "同一动作两个入口"不是缺陷 这与 §3.2 中栏保留转接是同一条原则:高频/关键动作允许多入口,只要共用同一 handler。真正的缺陷是两个入口行为不一致,而非入口数量
④ 变更面反而更小 保留第三行按钮 = 不动既有 DOM,只在菜单工厂里多产出两个 MenuItem。移除按钮才需要改 ConversationItem.vue:116-136 并回归测试

约束:两个入口必须 emit 同一事件(grab / leave),走同一 store action。禁止在菜单里另写一份逻辑。

验收要求:同事会话条目上,第三行「接手」link 与菜单「接手」项必须同时可见、同时消失(受同一 can_grab 条件驱动)。


Q-5 「结单」是否进左栏菜单?

结论:不进。维持中栏唯一入口。

论据 说明
① 路径错位 结单需走摘要确认 DialogChatArea.vue:448-502),坐席要在弹窗里确认/编辑会话摘要。在左栏一个 24px 的三点按钮里触发一个需要阅读与编辑的模态流程,是明确的路径错位
② 语义归属 结单属「对话过程动作组」(转接/摇人/结单),与置顶/代办的"列表管理"性质不同。§3.2 已确立该分组,结单进左栏会破坏刚刚建立的分组一致性
③ 结单前需要看对话内容 结单意味着"问题已解决",这个判断必须基于中栏的对话内容。坐席不可能在左栏扫视时判断某条会话可以结单——动作发生地必然是中栏
④ 误操作成本高 结单会触发员工端确认流程(pending_close 态)。在密集列表中一个 hover 出现的小菜单里放置这种动作,误触成本过高
⑤ 用户未提 需求原文只提 4 个操作,不做需求扩张

9. 需求池(Requirements Pool

P0 — Must have

# 需求
P0-1 新建 WorkItemActionMenu.vue 单例菜单壳:fixed/Teleport 定位、视口翻转、Esc / 点击外部 / 滚动 / resize / 切换会话 关闭、z-index ≥ 2000、danger 项样式
P0-2 新建 useConversationMenuItems.ts:完整实现 §5 矩阵,含动态文案翻转与 disabled 计算,附单元测试覆盖 §5.1 的 A–G 七个实例
P0-3 ConversationItem.vue 新增三点按钮:§4.1 全部规格(同栅格位淡入淡出、24×24、hover/.is-menu-open/:focus-visible 显示、@click.stop
P0-4 ConversationItem.vue 新增 @contextmenu.prevent.stop,与三点按钮共用同一菜单项定义
P0-5 ConversationList.vue 持有单例菜单实例,按 action 路由到对应 store action
P0-6 UserInfoBar.vue 删除接单/置顶/代办 3 个按钮,保留转接
P0-7 修复既有缺陷:stores/conversation.ts 中 4 个操作的失败分支由 console.error 改为 ElMessage.error 明确文案(403 场景必须有用户可见反馈)
P0-8 转接坐席选择 Dialog 组件化,中栏与左栏共用

P1 — Should have

# 需求
P1-1 引入 z-index token--z-dropdown: 2000 / --z-modal: 3000 / --z-toast: 9000),替换菜单相关硬编码
P1-2 菜单 role="menu" / 项 role="menuitem",三点按钮用原生 <button>el-button(可 Tab 聚焦、Enter/Space 激活)
P1-3 历史分区三点按钮禁用态(§5.1 F 的处理)
P1-4 转接 Dialog 支持坐席姓名搜索

P2 — Nice to have

# 需求
P2-1 菜单 ↑↓ 方向键导航 + Enter 确认(可挂接既有 useKeyboardShortcuts.ts
P2-2 MessageItem.vue 的自研右键菜单迁移到 WorkItemActionMenu,顺带修复其 5 项缺陷(无边界翻转 / z-index 1000 偏低 / 关闭时机不全 / 无 a11y / 无单例互斥)
P2-3 中栏按钮删除做成配置开关,支持 2 周内灰度回滚
P2-4 为转接注册全局快捷键(如 Ctrl+Shift+T

10. 验收标准

10.1 功能验收

# 验收项 通过标准
A-1 三点按钮显隐 默认不可见;hover 条目出现且缩略头像淡出;条目高度与各元素位置零位移
A-2 点击不误触 点击三点按钮不切换当前会话(当前打开的会话保持不变)
A-3 右键一致性 同一条目上,右键菜单项与三点菜单项逐项完全一致(项数、顺序、文案、禁用态)
A-4 菜单不被裁剪 左栏第一条最后一条会话的菜单完整可见,不被侧栏或滚动容器裁掉;视口下缘不足时向上翻转
A-5 矩阵正确性 §5.1 的 A–G 七个实例逐一验证,菜单项与预期完全一致
A-6 403 拦截 同事会话菜单中不出现置顶/代办项;构造非法请求时前端有明确错误提示
A-7 单例互斥 打开条目 A 的菜单后,右键条目 B,A 的菜单自动关闭,同屏只有一个菜单
A-8 关闭时机 Esc / 点击外部 / 滚动列表 / 窗口 resize / 切换会话 —— 五种情况菜单均关闭
A-9 中栏改造 中栏 actions 区仅剩 转接 / 摇人 / 结单;转接路径仍为 3 步
A-10 转接双入口 中栏转接与左栏菜单转接打开同一个 Dialog,行为一致
A-11 接手双入口 同事会话条目第三行「接手」link 与菜单「接手」项同时可见/同时消失,点击效果相同
A-12 状态即时反映 置顶/代办后条目 📌/📋 图标即时出现或消失,列表按新权重重排

10.2 架构验收(防返工)

# 验收项 通过标准
B-1 单例 全局 DOM 中 .work-item-action-menu 实例数恒 ≤ 1不随会话条目数增长
B-2 壳零业务 WorkItemActionMenu.vueimport 不含任何 store / api / Conversation 类型
B-3 工厂可单测 useConversationMenuItems.ts 为纯函数,可脱离组件树单测
B-4 DOM 归属 菜单 DOM 的 parentElementbody 或使用 position: fixed

10.3 明确不作为验收依据

  • toast 成功提示(沿用 PRD-011 §6 Phase 0 的验收原则)
  • 原型截图(原型是设计依据,不是实现验收标准)

11. 风险登记

编号 风险 等级 缓解
R-1 转接若被误删将造成 3→6 步路径退化 🔴 高 → 🟢 已缓解 D-2 决定中栏保留转接;若评审推翻,必须同时落地 P2-4 快捷键
R-2 v1.7 若沿用 v1.6 低保真基线,菜单落位设计与实现对不上 🔴 高 → 🟢 已缓解 D-1 改以 v1.3 为基线并回填真实字段
R-3 z-index 冲突(项目现有 100/999/1000/1200/3000/9999/999999 混用,EP popper 默认 2000+ 🟡 P1-1 引入 token;优先用 EP popover 交由其管理
R-4 菜单可见性与后端权限不一致 → 403。pin/todoupdate:own 🟡 §5 矩阵前端严格拦截 + P0-7 失败提示
R-5 复制 MessageItem.vue 右键实现,带入其 5 项缺陷 🟡 强制走 WorkItemActionMenu 新壳,禁止复制粘贴
R-6 未来虚拟滚动导致条目内菜单错位 🟡 中 → 🟢 已缓解 D-4 本次即采用单例架构
R-7 三点按钮与缩略头像争位导致布局抖动 🟢 D-5 同栅格位淡入淡出,不改盒模型;A-1 专项验收
R-8 右键在 VDI / 远程桌面 / 触摸屏环境下不可靠 🟢 三点按钮为主入口,右键仅为快捷方式,降级不影响可用性。需宋献确认坐席实际办公环境(架构 Q-7)
R-9 坐席习惯迁移成本:老坐席习惯在中栏点置顶 🟢 变更公告 + 首次使用时的引导气泡(可选);P2-3 保留灰度回滚

12. 明确不在本次范围

  • 中栏「摇人」「结单」的任何改动
  • 待办条目(审批/工单)进入左栏 —— 属 PRD-011 Phase 2
  • 左栏虚拟滚动 —— 属 PRD-011 U-5(本次仅在架构上预留兼容)
  • 会话列表排序权重调整
  • 后端任何改动(4 个端点及权限装饰器均已就绪,无需变更)
  • 键盘方向键导航(列为 P2-1,不阻塞本次)

13. 待需求方(宋献)评审确认清单

# 待确认项 本 PRD 的默认取值 若推翻的影响
C-1 中栏保留转接是否接受?(原需求是 4 个全删) 保留(D-2 推翻则转接路径 3→6 步,必须补 P2-4 快捷键,工期 +0.3 人日
C-2 是否接受右键并存 接受(D-3 推翻则去掉 P0-4,省 0.3–0.5 人日,但熟练坐席效率损失
C-3 是否接受 v1.7 回填左栏真实字段的额外工作量? 接受(D-1 推翻则原型不可作为开发依据
C-4 坐席实际办公环境是否存在 VDI / 远程桌面 / 触摸屏 假定为普通 PC 浏览器 若有 VDI,右键可靠性下降,需重新评估 C-2
C-5 历史会话条目的三点按钮:禁用态还是完全不渲染 禁用态(§5.1 F 视觉偏好项,成本相同
C-6 备选形态:是否需要看「中栏 3 按钮收进中栏自己的『⋯』菜单」的 A/B 对比? 未在 v1.7 中呈现 若需要,v1.7 需追加一版对比图
C-7 Q-4 结论(接手/退出双入口,不移除第三行按钮)是否认可? 双入口 若要求单入口,需明确保留哪一个

14. 工期估算

粗估,用于排序参考,不作为承诺。基于架构报告 §4.1 的分解并按本 PRD 结论调整。

阶段 内容 估时
S1 WorkItemActionMenu.vue 单例壳 1.0 人日
S2 useConversationMenuItems.ts + 单测(§5 矩阵) 0.5 人日
S3 ConversationItem 三点按钮 + 右键触发 0.5 人日
S4 ConversationList 单例接线 + action 路由 0.5 人日
S5 中栏删 3 留转接 0.2 人日
S6 转接 Dialog 组件化(Q-3)+ 中栏复用 0.5 人日
S7 P0-7 失败反馈修复 0.2 人日
S8 a11yP1-2+ 键盘导航(P2-1 0.3 人日(可 P2 单排)
合计 3.43.7 人日

15. 变更记录

日期 版本 变更内容 变更人
2026-08-09 v1.0 创建。基于架构核查报告 v1.0 与宋献口述需求,固化 D-1D-6 六项决策;产出菜单项可见性矩阵(§5)与七实例推演;给出 Q-3/Q-4/Q-5 三项自决结论(Q-4 与架构建议存在分歧并说明理由);定义 WorkItemActionMenu + useConversationMenuItems 组件边界与 PRD-011 Phase 2 兼容检查表;登记 R-1R-9 风险与 C-1~C-7 待确认项 许清楚(产品经理)