diff --git a/docs/员工端版本功能对比与功能对齐清单-20260717.md b/docs/员工端版本功能对比与功能对齐清单-20260717.md
index c388e6a8..95273074 100644
--- a/docs/员工端版本功能对比与功能对齐清单-20260717.md
+++ b/docs/员工端版本功能对比与功能对齐清单-20260717.md
@@ -86,3 +86,9 @@ Figma 已完成全文件结构和视觉复核,当前实测为 81 个顶层页
- 阶段与产品定义:[银城员工端APP分阶段实施总纲.md](银城员工端APP分阶段实施总纲.md)、[20260708/数字师傅学练问报整合方案.md](20260708/数字师傅学练问报整合方案.md)。
- API 与运行边界:[API_INTEGRATION.md](API_INTEGRATION.md)、[DEMO_ACCEPTANCE.md](DEMO_ACCEPTANCE.md)。
- 历史可运行源码:`mobile/src/App.vue`;当前权威源码:`mobile-uni/`、`frontend/`、`backend/ruoyi-modules/ruoyi-aihr/`。
+
+## 2026-07-18 追加:问答/对练交互形态(客户新要求)
+
+| 功能 | 客户要求 | Figma | 当前代码 | 线上状态 | 处理决定 |
+| --- | --- | --- | --- | --- | --- |
+| 语音文字交互形态 | 2026-07-18 客户原话:凡语音文字交互处应做成微信聊天式(消息流+按住说话),现表单式操作太复杂 | 表单式布局(输入框+按钮组) | 问页已改会话流:ChatComposer(⊕面板/按住说话松开即发/键盘切换)+气泡消息流,引用/总结卡/反馈/播报收入答案气泡操作行 | 已发布并生产冒烟通过(2026-07-18) | 按客户要求执行,客户原话 > Figma;练页为第二批,web-ai 页随批次二统一 |
diff --git a/mobile-uni/src/components/chat/ChatComposer.vue b/mobile-uni/src/components/chat/ChatComposer.vue
new file mode 100644
index 00000000..538f0a81
--- /dev/null
+++ b/mobile-uni/src/components/chat/ChatComposer.vue
@@ -0,0 +1,408 @@
+
+
+
+
+
+
+
+
+
+ {{ mediaKind === 'video' ? '现场视频' : '现场图片' }}
+ {{ mediaName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 图片≤10MB,视频≤100MB;仅用于本次现场分析
+
+
+
+
+
+
diff --git a/mobile-uni/src/pages/user/sop/index.vue b/mobile-uni/src/pages/user/sop/index.vue
index 063998e1..e1e82bfb 100644
--- a/mobile-uni/src/pages/user/sop/index.vue
+++ b/mobile-uni/src/pages/user/sop/index.vue
@@ -1,170 +1,134 @@
-
-
- SOP · 话术 · 工作流程
- 问
- 数字师傅 · SOP问答
- {{ positionLabel }} · 住宅项目
- 先把问题问清楚,再按需生成总结卡,不伪造分类能力。
+
+
+
+ 问 · 数字师傅
+ {{ positionLabel }} · 住宅项目
+
+
+
+
+
-
-
-
-
-
-
-
-
- 问师傅
- 直接问工作中的 SOP、话术和流程
+
+
+
+ 师
+
+ 你好,我是老师傅。工作里的 SOP、话术、流程直接问我,答案都带引用依据;也可以按住下面的按钮说话。
+
-
- •••
- •••
+
+
+
+
+ 🎤 语音转文字
+ 📎 {{ msg.mediaKind === 'video' ? '现场视频' : '现场图片' }} · {{ msg.mediaName }}
+ {{ msg.text }}
+
+
+
+
+ {{ msg.text }}
+
+
+
+ 师
+
+ 师傅思考中…
+
+
+
+
+ 师
+
+ {{ msg.result.answer || '未生成直接答案,请查看引用依据。' }}
+
+
+ {{ index + 1 }}. {{ cleanOrderedText(point) }}
+
+
+
+ ⚠ {{ item }}
+
+ 当前授权空间内没有足够依据。
+
+
+
+
+
+
+
+
+
+
+ {{ msg.feedbackVerdict === 'up' ? '已记录' : '已进入复核' }}
+
+
+
+
+
+ {{ snippet.docTitle }}
+ 知识空间:{{ snippet.spaceCode }}
+ {{ snippetExcerpt(snippet.content) }}
+ 片段 #{{ snippet.fragmentId }}
+
+
+
+
+
+
+
+ 师
+
+ 登录后才能查询 SOP、生成总结卡并反馈答案。
+
+
+
+
-
-
-
-
-
-
- 录音会先转成文字,确认问题后再提交;麦克风不可用时可选择音频。
-
-
- 图片≤10MB,视频≤100MB;仅用于本次现场分析
-
-
-
- {{ selectedMedia.kind === 'video' ? '现场视频' : '现场图片' }}
- {{ selectedMedia.name }}
-
-
-
- 常见问题可以直接点下面快捷问法,结果会保留引用依据。
-
-
-
-
-
- {{ message }}
-
-
- ⌕
-
- 知识库引用可追溯
- 答案来自标准 SOP、附件来源,查得清楚、用得放心
+
- ›
-
+
-
- •••
- 暂无相关回答
- 换个问题试试吧
-
-
-
-
- 需要登录
- 登录后才能查询 SOP、生成总结卡并反馈答案。
-
-
-
-
-
-
- 师傅回答
-
-
-
- 我的问题
- {{ query }}
-
-
- {{ result.answer || '未生成直接答案,请查看下方引用。' }}
-
- 当前授权空间内没有足够依据。
-
-
-
- {{ index + 1 }}. {{ cleanOrderedText(point) }}
-
-
-
-
- {{ item }}
-
-
-
-
-
-
-
- {{ feedbackVerdict === 'up' ? '已记录为有帮助' : '已提交未解决反馈,后续会继续优化这条答案' }}
-
-
-
-
- 引用依据
-
- {{ snippet.docTitle }}
- 知识空间:{{ snippet.spaceCode }}
- {{ snippetExcerpt(snippet.content) }}
- 片段 #{{ snippet.fragmentId }}
-
-
-
-
- 总结卡
- 需要时再生成,适合把一段 SOP 整理成步骤卡、话术卡和提醒卡。
-
-
+
@@ -245,7 +209,7 @@
@@ -1815,4 +1745,277 @@ onUnload(() => {
.modal-footer .footer-close {
grid-column: span 2;
}
+
+/* ===== chat layout (WeChat-style rebuild) ===== */
+.sop-chat-page {
+ display: flex;
+ flex-direction: column;
+ height: calc(100vh - var(--window-top, 0px) - var(--window-bottom, 0px));
+ background: #f2f3f5;
+ overflow: hidden;
+}
+
+.chat-head {
+ flex-shrink: 0;
+ padding: 10px 14px 8px;
+ background: #ffffff;
+ border-bottom: 1px solid #ebedf0;
+}
+
+.chat-head-row {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+
+.chat-title {
+ color: #18202b;
+ font-size: 18px;
+ font-weight: 800;
+}
+
+.chat-context {
+ color: #8a93a0;
+ font-size: 12px;
+}
+
+.chat-source-switch {
+ display: flex;
+ padding: 3px;
+ border-radius: 999px;
+ background: #f2f3f5;
+}
+
+.chat-source-switch .source-option {
+ flex: 1;
+ margin: 0;
+ padding: 6px 0;
+ border: none;
+ border-radius: 999px;
+ background: transparent;
+ color: #5a6270;
+ font-size: 14px;
+ line-height: 1.3;
+}
+
+.chat-source-switch .source-option.active {
+ background: #ffffff;
+ color: #c8001a;
+ font-weight: 700;
+ box-shadow: 0 1px 4px rgba(20, 24, 31, 0.08);
+}
+
+.chat-thread {
+ flex: 1;
+ min-height: 0;
+}
+
+.thread-inner {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 14px 12px 6px;
+}
+
+.bubble-row {
+ display: flex;
+ align-items: flex-start;
+ gap: 8px;
+}
+
+.bubble-row.right {
+ justify-content: flex-end;
+}
+
+.avatar {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 34px;
+ height: 34px;
+ border-radius: 8px;
+ font-size: 15px;
+ font-weight: 700;
+}
+
+.master-avatar {
+ background: #e70014;
+ color: #ffffff;
+}
+
+.bubble {
+ max-width: 82%;
+ padding: 10px 12px;
+ border-radius: 12px;
+ word-break: break-word;
+}
+
+.bubble.master {
+ background: #ffffff;
+ border-top-left-radius: 4px;
+ box-shadow: 0 1px 3px rgba(20, 24, 31, 0.06);
+}
+
+.bubble.user {
+ background: #ffd8dc;
+ border-top-right-radius: 4px;
+}
+
+.bubble-text {
+ display: block;
+ color: #232a33;
+ font-size: 15px;
+ line-height: 1.55;
+ white-space: pre-wrap;
+}
+
+.bubble-tag {
+ display: inline-block;
+ margin-bottom: 6px;
+ padding: 2px 8px;
+ border-radius: 6px;
+ background: rgba(200, 0, 26, 0.08);
+ color: #a04a00;
+ font-size: 11px;
+}
+
+.bubble.user .bubble-tag {
+ background: rgba(255, 255, 255, 0.7);
+}
+
+.bubble-keypoints {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 8px;
+ padding-top: 8px;
+ border-top: 1px dashed #eceff2;
+}
+
+.bubble-keypoint {
+ color: #38424e;
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+.bubble-cautions {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-top: 8px;
+}
+
+.bubble-caution {
+ color: #a04a00;
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+.bubble-noevidence {
+ display: block;
+ margin-top: 8px;
+ color: #c8001a;
+ font-size: 13px;
+}
+
+.bubble-actions {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 8px;
+ margin-top: 10px;
+}
+
+.bubble-action {
+ margin: 0;
+ padding: 4px 12px;
+ border: 1px solid #e3e6ea;
+ border-radius: 999px;
+ background: #f7f8fa;
+ color: #38424e;
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+.bubble-action.active {
+ border-color: #1f9d55;
+ color: #168243;
+}
+
+.bubble-action.negative {
+ border-color: #e07f88;
+ color: #bd0010;
+}
+
+.bubble-action.primary {
+ border-color: #e70014;
+ background: #e70014;
+ color: #ffffff;
+}
+
+.feedback-note {
+ color: #8a93a0;
+ font-size: 12px;
+}
+
+.bubble-citations {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-top: 10px;
+ padding-top: 10px;
+ border-top: 1px dashed #eceff2;
+}
+
+.citation {
+ padding: 8px 10px;
+ border-left: 3px solid #f0c0c5;
+ border-radius: 6px;
+ background: #fafbfc;
+}
+
+.citation-title {
+ display: block;
+ color: #232a33;
+ font-size: 13px;
+ font-weight: 700;
+}
+
+.citation-meta {
+ display: block;
+ margin-top: 2px;
+ color: #98a1ad;
+ font-size: 11px;
+}
+
+.citation-content {
+ display: block;
+ margin-top: 4px;
+ color: #4d5560;
+ font-size: 12px;
+ line-height: 1.55;
+}
+
+.system-row {
+ display: flex;
+ justify-content: center;
+ padding: 2px 0;
+}
+
+.system-text {
+ max-width: 86%;
+ padding: 4px 12px;
+ border-radius: 999px;
+ background: rgba(20, 24, 31, 0.06);
+ color: #6b7480;
+ font-size: 12px;
+ text-align: center;
+ line-height: 1.5;
+}
+
+.pending-bubble .bubble-text {
+ color: #8a93a0;
+}