feat(mobile): web-ai chat rebuild (batch 3) + profile page APP-ification

用户三项决定落地:
1. 问AI(全网)页会话化:重写为微信式会话(欢迎/免责=AI气泡,
   脱敏与敏感信息提醒保留,未启用=系统气泡+输入条禁用,
   同意告知由勾选框改为首次发送前确认弹窗(会话内记忆),
   答案气泡带播报+来源N条折叠(外链可点),按住说话松开即发+
   选音频,ChatComposer 复用;无tab栏子页,输入条自带安全区)
2. 配置维持不变(active_timeout/姓名字段/固定验证码按原状)
3. 知识大库开放:财务报备/销售话术/行政制度/培训视频/图片素材
   (1004/1005/1007/1008/1009/1010)绑定移动端应用并授
   employee/supervisor READ;1006「验收测试」为测试库,
   为免污染员工检索结果暂不开放(报告中说明)。
   生产验证:员工问「财务报备流程」「销售话术要点」均返回真实答案。

新需求「H5 像 APP」首批:「我」页 APP 化——折叠不删减:
- 成长摘要保留核心数字,五维评分+成长路径收进「展开能力详情」
- 成长证据包默认收起(标题行+展开按钮)
- 最近练过默认3条,「展开全部N条」可切换
页面高度 3072→1976px,信息全部一次点击可达。

验证:本地5项断言+生产3项复核全过(webai气泡/禁用态/composer、
profile折叠/高度、大库检索)。发布备份 h5-before-20260718092234。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 09:23:51 +08:00
co-authored by Claude Fable 5
parent a90f241def
commit 6935476e3f
3 changed files with 635 additions and 143 deletions
+55 -4
View File
@@ -84,7 +84,10 @@
</view>
</view>
</view>
<view v-if="profile.dimensions?.length" class="dimension-list">
<button class="fold-toggle" @click="showGrowthDetail = !showGrowthDetail">
{{ showGrowthDetail ? '收起能力详情 ▲' : '展开能力详情(五维评分 · 成长路径)▼' }}
</button>
<view v-if="showGrowthDetail && profile.dimensions?.length" class="dimension-list">
<view v-for="item in profile.dimensions" :key="item.label" class="dimension-row">
<view>
<text class="dimension-title">{{ item.label }}</text>
@@ -95,7 +98,7 @@
</text>
</view>
</view>
<view v-if="profile.growthPath?.length" class="growth-section">
<view v-if="showGrowthDetail && profile.growthPath?.length" class="growth-section">
<text class="section-label">成长路径</text>
<view v-for="stage in profile.growthPath" :key="stage.level" class="growth-row">
<view class="growth-copy">
@@ -149,7 +152,13 @@
</view>
<view v-if="loggedIn && evidence" class="panel evidence-panel">
<text class="panel-title">成长证据包(待人工复核)</text>
<view class="fold-head">
<text class="panel-title">成长证据包(待人工复核)</text>
<button class="fold-toggle inline" @click="showEvidence = !showEvidence">
{{ showEvidence ? '收起 ▲' : '展开 ▼' }}
</button>
</view>
<template v-if="showEvidence">
<view class="evidence-grid">
<view class="evidence-metric">
<text>{{ evidence.currentLevel }}</text>
@@ -179,6 +188,7 @@
<view v-if="evidence.evidenceItems?.length" class="evidence-list">
<text v-for="item in evidence.evidenceItems" :key="item" class="evidence-item">{{ item }}</text>
</view>
</template>
</view>
<view v-if="loggedIn" class="panel history-panel">
@@ -188,7 +198,7 @@
</view>
<view v-if="history.length" class="history-list">
<view
v-for="item in history"
v-for="item in visibleHistory"
:key="item.id || item.sessionId || item.time"
class="history-row history-row-clickable"
@click="openHistory(item.id)"
@@ -202,6 +212,9 @@
<text>{{ item.status }}</text>
</view>
</view>
<button v-if="history.length > 3" class="fold-toggle" @click="showAllHistory = !showAllHistory">
{{ showAllHistory ? '收起 ▲' : `展开全部 ${history.length} 条 ▼` }}
</button>
</view>
<view v-else class="empty">暂无训练记录</view>
</view>
@@ -237,6 +250,10 @@ const loading = ref(false);
const message = ref('');
const profile = ref<CompetencyProfile | null>(null);
const history = ref<PracticeRecord[]>([]);
const showGrowthDetail = ref(false);
const showEvidence = ref(false);
const showAllHistory = ref(false);
const visibleHistory = computed(() => (showAllHistory.value ? history.value : history.value.slice(0, 3)));
const evidence = ref<PromotionEvidence | null>(null);
const positionProfile = ref(getSelectedPositionProfile());
const supervisorLearning = ref(false);
@@ -876,4 +893,38 @@ onShow(() => {
color: #8a93a0;
font-size: 12px;
}
.fold-toggle {
display: block;
width: 100%;
margin: 10px 0 0;
padding: 8px 0;
border: none;
border-top: 1px dashed #eceff2;
background: transparent;
color: #6b7480;
font-size: 13px;
text-align: center;
line-height: 1.5;
}
.fold-toggle::after {
border: none;
}
.fold-toggle.inline {
width: auto;
margin: 0;
padding: 2px 10px;
border: 1px solid #e3e6ea;
border-radius: 999px;
background: #f7f8fa;
font-size: 12px;
}
.fold-head {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>