Phase 2.3 chat 上下文捕获链路从未真正激活到完整工作: - 14 处 ai-float-button 补 sourcePage,chat.ts 三分支同步设 pageFilters.contextId - 后端 page_context 4 层 BUG 修(列名错位 + RLS site_id 未重设) - xcx_chat filters.pop 破坏 body.page_context 引用 — dict() 浅拷贝隔离 - chat 流式 markdown 实时解析(表格/标题/列表/加粗 + KPI 富卡) - reference_card KPI 富卡接入 SSE 路径,db 真写入 - 维客线索 source 显示规则:AI 来源用机器人 icon 替代长文字 数据库: - public.member_retention_clue 加 emoji + runtime_mode + sandbox_instance_id - biz.ai_run_logs 加 assistant_id + 复合索引 - chk_ai_cache_type CHECK 约束 8 类应用名 - cache_type / app_type 命名统一(app6_note / app7_customer / app8_consolidation) - 历史 emoji 抽取脚本 44/44 成功 后端 silent failure 修: - cleanup_service WHERE app_type → cache_type(90 天清理 + 20K 上限重新生效) - _build_ai_insight 字段错位修复(app4 → app7 + 字段对齐 prompt schema) - task_manager talkingPoints 改 app5_tactics + tactics 字段 - task_manager aiSuggestion 改取 one_line_summary - cache_service.CACHE_EXPIRY_DAYS 加 app2a_finance_area - WS /ws/ai-cache 加 token + JWT + site_id 校验(P0 信息泄露漏洞) - internal_ai token 改 hmac.compare_digest 工具/文档: - main.py 加 RotatingFileHandler logs/backend.log + uvicorn /health 过滤 - 新建 utils/clue_category.py(VI 6 类配色 + emoji fallback + source 显示规则) - 新建 utils/markdown.ts(轻量 md 转 rich-text 解析 + streaming 容错) - audit + 数据库变更说明 + backlog §七 #14 收口 + #15-#38 残余子任务 - backlog 追加 §十一 App1 参数/MCP/沙箱审计 + §十二 百炼/SQL MCP 主任务线 实地 MCP 走查:14 入口数据层 + 5 代表入口 sourcePage 注入 + customer-detail 全模块 + chat md 渲染 + reference_card 富卡 都已验证。9 项预先 BUG/UX 登记 §七 #29-#38 后续修复。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
284 lines
12 KiB
Plaintext
284 lines
12 KiB
Plaintext
<wxs src="../../utils/format.wxs" module="fmt" />
|
||
|
||
<!-- 空态 -->
|
||
<view class="page-empty" wx:if="{{pageState === 'empty'}}">
|
||
<t-icon name="info-circle" size="120rpx" color="#c5c5c5" />
|
||
<text class="empty-text">未找到任务信息</text>
|
||
</view>
|
||
|
||
<!-- 错误态 -->
|
||
<view class="page-error" wx:elif="{{pageState === 'error'}}">
|
||
<t-icon name="close-circle" size="120rpx" color="#e34d59" />
|
||
<text class="error-text">加载失败</text>
|
||
<view class="retry-btn" bindtap="onRetry" hover-class="retry-btn--hover">点击重试</view>
|
||
</view>
|
||
|
||
<!-- 正常态 -->
|
||
<block wx:elif="{{pageState === 'normal'}}">
|
||
|
||
<!-- Banner 区域(SVG 背景)-->
|
||
<view class="banner-area">
|
||
<image src="{{bannerBgSvg}}" class="banner-bg-svg" mode="scaleToFill" />
|
||
<view class="banner-content">
|
||
<!-- 放弃/取消放弃按钮 - 右上角 -->
|
||
<view class="banner-abandon-btn" bindtap="onAbandon" hover-class="banner-abandon-btn--hover">
|
||
<text class="banner-abandon-text">{{detail.status === 'abandoned' ? '取消放弃' : '放弃'}}</text>
|
||
</view>
|
||
|
||
<view class="customer-info">
|
||
<view class="avatar-box">
|
||
<text class="avatar-text">{{detail.customerName[0] || '?'}}</text>
|
||
</view>
|
||
<view class="info-right">
|
||
<view class="name-row">
|
||
<text class="customer-name">{{fmt.safe(detail.customerName)}}</text>
|
||
<text class="task-type-tag">{{detail.taskTypeLabel || '高优先召回'}}</text>
|
||
</view>
|
||
<view class="sub-info">
|
||
<text class="phone">{{phoneVisible ? fmt.safe(detail.customerPhone) : fmt.maskPhone(detail.customerPhone)}}</text>
|
||
<view class="phone-toggle-btn" bindtap="{{phoneVisible ? 'onCopyPhone' : 'onTogglePhone'}}" hover-class="phone-toggle-btn--hover">
|
||
<text class="phone-toggle-text">{{phoneVisible ? '复制' : '查看'}}</text>
|
||
</view>
|
||
<text class="storage-level-text">💰 储值 {{storageLevel}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 主体内容 -->
|
||
<view class="main-content">
|
||
|
||
<!-- 与我的关系(动态颜色)-->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<text class="section-title title-pink">与我的关系</text>
|
||
<ai-title-badge color="{{aiColor}}" />
|
||
</view>
|
||
<view class="relationship-row">
|
||
<view class="rel-tag rel-level-{{relationLevel}}">
|
||
<heart-icon score="{{detail.heartScore}}" />
|
||
<text class="rel-tag-text">{{relationLevelText}}</text>
|
||
</view>
|
||
<view class="rel-bar">
|
||
<view class="rel-bar-fill rel-fill-{{relationLevel}}" style="width: {{detail.heartScore * 10}}%; background: {{relationColor}};"></view>
|
||
</view>
|
||
<text class="rel-score" style="color: {{relationColor}};">{{fmt.toFixed(detail.heartScore, 1)}}</text>
|
||
</view>
|
||
<view class="card-desc-wrap">
|
||
<text class="card-desc">{{fmt.safe(detail.aiAnalysis.summary)}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 任务建议 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<text class="section-title title-orange">任务建议</text>
|
||
<ai-title-badge color="{{aiColor}}" />
|
||
</view>
|
||
<view class="suggestion-box">
|
||
<view class="suggestion-header">
|
||
<text class="suggestion-icon-text">💡 建议执行</text>
|
||
</view>
|
||
<view class="suggestion-body">
|
||
<view class="suggestion-intro-wrap">
|
||
<text class="suggestion-intro">{{detail.aiAnalysis.summary || '暂无AI分析'}}</text>
|
||
</view>
|
||
<view class="suggestion-item" wx:for="{{detail.aiAnalysis.suggestions}}" wx:key="index">
|
||
<text class="suggestion-item-text">• {{item}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="speech-section">
|
||
<text class="speech-title">💬 话术参考</text>
|
||
<view class="speech-list">
|
||
<view class="speech-bubble" wx:for="{{talkingPoints}}" wx:key="index">
|
||
<view class="speech-text-wrap">
|
||
<ai-inline-icon color="{{aiColor}}" />
|
||
<text class="speech-text">{{item.scenario ? item.scenario + ':' + item.script : item.script}}</text>
|
||
</view>
|
||
<view class="speech-copy-row">
|
||
<view class="copy-btn" bindtap="onCopySpeech" data-index="{{index}}" hover-class="copy-btn--hover">
|
||
<text class="copy-btn-text">{{copiedIndex === index ? '✓ 已复制' : '复制'}}</text>
|
||
</view>
|
||
</view>
|
||
<view class="speech-arrow"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 维客线索 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<text class="section-title title-green">维客线索</text>
|
||
<ai-title-badge color="{{aiColor}}" />
|
||
</view>
|
||
<view class="clue-list">
|
||
<clue-card
|
||
wx:for="{{retentionClues}}"
|
||
wx:key="index"
|
||
tag="{{item.tag}}"
|
||
category="{{item.tagColor}}"
|
||
emoji="{{item.emoji}}"
|
||
title="{{item.text}}"
|
||
source="{{item.source}}"
|
||
content="{{item.desc || ''}}"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- T4.3 行动建议(AI 生成) -->
|
||
<view class="card" wx:if="{{detail.actionSuggestions && detail.actionSuggestions.length > 0}}">
|
||
<view class="card-header">
|
||
<text class="section-title title-green">行动建议</text>
|
||
<ai-title-badge color="{{aiColor}}" />
|
||
</view>
|
||
<view class="action-suggestions-list">
|
||
<view class="action-suggestion-card" wx:for="{{detail.actionSuggestions}}" wx:key="index">
|
||
<text class="action-suggestion-index">{{index + 1}}</text>
|
||
<text class="action-suggestion-text">{{item}}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 我给TA的备注 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<text class="section-title title-blue">我给TA的备注</text>
|
||
<text class="note-count">{{sortedNotes.length}} 条备注</text>
|
||
</view>
|
||
<block wx:if="{{sortedNotes.length > 0}}">
|
||
<view class="note-item" wx:for="{{sortedNotes}}" wx:key="id">
|
||
<view class="note-top">
|
||
<view class="note-date-wrap">
|
||
<text class="note-date">{{fmt.safe(item.timeLabel || item.createdAt)}}</text>
|
||
</view>
|
||
<view class="note-top-right">
|
||
<!-- CHANGE 2026-03-27 | 备注联调:无评分时不显示星星 -->
|
||
<star-rating wx:if="{{item.score}}" score="{{item.score}}" size="28rpx" readonly="{{true}}" />
|
||
<view class="note-delete-btn" catchtap="onDeleteNote" data-id="{{item.id}}" hover-class="note-delete-btn--hover">
|
||
<t-icon name="delete" size="32rpx" color="#a6a6a6" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="note-content-wrap">
|
||
<text class="note-content">{{fmt.safe(item.content)}}</text>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
<view class="note-empty" wx:else>
|
||
<t-icon name="edit-1" size="80rpx" color="#dcdcdc" />
|
||
<text class="empty-hint">暂无备注</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 60天内服务记录 -->
|
||
<view class="card">
|
||
<view class="card-header">
|
||
<text class="section-title title-blue">60天内服务记录</text>
|
||
<text class="note-count">共 {{serviceRecords.length}} 次</text>
|
||
</view>
|
||
<!-- 汇总统计 -->
|
||
<view class="svc-summary">
|
||
<view class="svc-summary-item svc-summary-blue">
|
||
<view class="svc-summary-value-row">
|
||
<text class="svc-summary-value svc-val-blue">{{fmt.hoursH(serviceSummary.totalHours)}}</text>
|
||
</view>
|
||
<text class="svc-summary-label">总时长</text>
|
||
</view>
|
||
<view class="svc-summary-item svc-summary-green">
|
||
<text class="svc-summary-value svc-val-green">{{fmt.money(serviceSummary.totalIncome)}}</text>
|
||
<text class="svc-summary-label">总收入</text>
|
||
</view>
|
||
<view class="svc-summary-item svc-summary-orange">
|
||
<text class="svc-summary-value svc-val-orange">{{fmt.money(serviceSummary.avgIncome)}}</text>
|
||
<text class="svc-summary-label">场均</text>
|
||
</view>
|
||
</view>
|
||
<!-- 记录列表(使用 service-record-card 组件)-->
|
||
<view class="svc-list">
|
||
<service-record-card
|
||
wx:for="{{serviceRecords}}"
|
||
wx:key="index"
|
||
time="{{item.date}}"
|
||
course-label="{{item.type}}"
|
||
type-class="{{item.typeClass}}"
|
||
type="{{item.recordType || 'course'}}"
|
||
table-no="{{item.table}}"
|
||
hours="{{item.duration}}"
|
||
hours-raw="{{item.durationRaw}}"
|
||
drinks="{{item.drinks}}"
|
||
income="{{item.income}}"
|
||
is-estimate="{{item.isEstimate}}"
|
||
/>
|
||
</view>
|
||
<view class="svc-view-all" bindtap="onViewAllRecords" hover-class="svc-view-all--hover">
|
||
<text class="svc-view-all-text">查看全部服务记录 →</text>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<view class="bottom-bar safe-area-bottom">
|
||
<view class="btn-ask" bindtap="onAskAssistant" hover-class="btn-ask--hover">
|
||
<t-icon name="chat" size="36rpx" color="#ffffff" />
|
||
<text class="btn-text">问问助手</text>
|
||
</view>
|
||
<view class="btn-note" bindtap="onAddNote" hover-class="btn-note--hover">
|
||
<t-icon name="edit-1" size="36rpx" color="#242424" />
|
||
<text class="btn-text-dark">备注</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 备注弹窗 -->
|
||
<note-modal visible="{{noteModalVisible}}" customerName="{{detail.customerName || ''}}" initialScore="{{0}}" initialContent="" showExpandBtn="{{detail.taskType !== 'callback'}}" showRating="{{true}}" bind:confirm="onNoteConfirm" bind:cancel="onNoteCancel" />
|
||
|
||
<!-- 放弃弹窗 -->
|
||
<abandon-modal
|
||
visible="{{abandonModalVisible}}"
|
||
customerName="{{detail.customerName || ''}}"
|
||
bind:confirm="onAbandonConfirm"
|
||
bind:cancel="onAbandonCancel"
|
||
/>
|
||
|
||
<!-- 页面调试工具 -->
|
||
<view class="debug-panel" wx:if="{{showDebugPanel}}">
|
||
<view class="debug-header">
|
||
<text class="debug-title">🔧 页面调试</text>
|
||
<view class="debug-close" bindtap="toggleDebugPanel" hover-class="debug-close--hover">
|
||
<t-icon name="close" size="32rpx" color="#5e5e5e" />
|
||
</view>
|
||
</view>
|
||
<view class="debug-section">
|
||
<text class="debug-label">任务类型:</text>
|
||
<view class="debug-btn-group">
|
||
<view class="debug-btn {{debugTaskType === 'high_priority' ? 'debug-btn--active' : ''}}" bindtap="onDebugTaskType" data-type="high_priority">高优先召回</view>
|
||
<view class="debug-btn {{debugTaskType === 'priority_recall' ? 'debug-btn--active' : ''}}" bindtap="onDebugTaskType" data-type="priority_recall">优先召回</view>
|
||
<view class="debug-btn {{debugTaskType === 'relationship' ? 'debug-btn--active' : ''}}" bindtap="onDebugTaskType" data-type="relationship">关系构建</view>
|
||
<view class="debug-btn {{debugTaskType === 'callback' ? 'debug-btn--active' : ''}}" bindtap="onDebugTaskType" data-type="callback">客户回访</view>
|
||
</view>
|
||
</view>
|
||
<view class="debug-section">
|
||
<text class="debug-label">关系数值:{{debugHeartScore}}</text>
|
||
<slider class="debug-slider" min="0" max="10" step="0.5" value="{{debugHeartScore}}" bindchange="onDebugHeartScore" activeColor="#ec4899" backgroundColor="#f3f3f3" block-size="24" />
|
||
<view class="debug-hint">
|
||
<text>0-3.5: 待发展</text>
|
||
<text>3.5-6: 一般</text>
|
||
<text>6-8.5: 良好</text>
|
||
<text>>8.5: 很好</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 调试面板触发按钮 -->
|
||
<view class="debug-trigger" bindtap="toggleDebugPanel" hover-class="debug-trigger--hover">
|
||
<text>🔧</text>
|
||
</view>
|
||
|
||
<!-- AI 悬浮按钮(W1-AI-CLOSURE 组 6:补 task-detail 整页缺失的入口 + sourcePage) -->
|
||
<ai-float-button taskId="{{detail.id}}" sourcePage="task-detail" />
|
||
|
||
</block>
|