feat(mobile): rebuild practice session as WeChat-style chat (batch 2)
对练进行中(practiceView=active)改为微信式会话,复用问页同款形态; 预习(场景卡/最近练过/每日三题)与评分/满意度视图保持不变, start/turn/finish/求助/风格重生成/幂等/红线/满意度脚本逻辑零改动。 ChatComposer:新增 panelItems 属性(自定义⊕面板项+panel事件), sop 页默认面板不受影响。 practice/index.vue: - 会话视图:固定铺满窗口区;头部=场景名+第N/3轮+情绪/信任细条+红线提示 - 业主台词=语音优先气泡(TTS预合成+时长+未读红点+转文字/收起, TTS不可用回退文本);我的回复右侧粉色气泡;老师傅提示/求助SOP= 蓝色教练气泡(带播报/引用来源);提交中「业主回应中…」系统气泡 - 风格chips(再严肃/再温柔/更细致/更专业)横排贴输入条上方 - 输入条:按住说话松开即发送(转写→带audioOssId直接提交回合), 键盘切换,⊕面板=选音频转文字/求助SOP/结束评分(结束评分随 canFinishPractice置灰) - 错题重说(红线)、退避回滚(提交失败恢复对话)原逻辑保留 验证: - 本地全链路:开始练习→业主语音气泡6"+红点→转文字→键盘回复→ 老师傅提示+4风格chips→风格调整→⊕求助SOP气泡→第二轮→ ⊕结束评分→评分74+老师傅建议+满意度,8张截图目检 - 生产冒烟:语音气泡+红点+按住说话+老师傅提示+风格chips全就位 - H5已发布(备份 h5-before-20260718052458);冲突表已更新 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -69,20 +69,34 @@
|
||||
</view>
|
||||
|
||||
<view v-if="panelOpen" class="composer-panel">
|
||||
<button class="panel-item" :disabled="disabled" @click="panelEmit('pick-media')">
|
||||
<text class="panel-item-icon">🖼</text>
|
||||
<text class="panel-item-label">图片/视频</text>
|
||||
</button>
|
||||
<button class="panel-item" :disabled="disabled || transcribing" @click="panelEmit('pick-audio')">
|
||||
<text class="panel-item-icon">🎵</text>
|
||||
<text class="panel-item-label">选音频转文字</text>
|
||||
</button>
|
||||
<button v-if="toolLabel" class="panel-item" :disabled="disabled" @click="panelEmit('tool')">
|
||||
<text class="panel-item-icon">📊</text>
|
||||
<text class="panel-item-label">{{ toolLabel }}</text>
|
||||
</button>
|
||||
<template v-if="panelItems.length">
|
||||
<button
|
||||
v-for="item in panelItems"
|
||||
:key="item.key"
|
||||
class="panel-item"
|
||||
:disabled="disabled || item.disabled"
|
||||
@click="panelAction(item.key)"
|
||||
>
|
||||
<text class="panel-item-icon">{{ item.icon }}</text>
|
||||
<text class="panel-item-label">{{ item.label }}</text>
|
||||
</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="panel-item" :disabled="disabled" @click="panelEmit('pick-media')">
|
||||
<text class="panel-item-icon">🖼</text>
|
||||
<text class="panel-item-label">图片/视频</text>
|
||||
</button>
|
||||
<button class="panel-item" :disabled="disabled || transcribing" @click="panelEmit('pick-audio')">
|
||||
<text class="panel-item-icon">🎵</text>
|
||||
<text class="panel-item-label">选音频转文字</text>
|
||||
</button>
|
||||
<button v-if="toolLabel" class="panel-item" :disabled="disabled" @click="panelEmit('tool')">
|
||||
<text class="panel-item-icon">📊</text>
|
||||
<text class="panel-item-label">{{ toolLabel }}</text>
|
||||
</button>
|
||||
</template>
|
||||
</view>
|
||||
<text v-if="panelOpen" class="panel-hint">图片≤10MB,视频≤100MB;仅用于本次现场分析</text>
|
||||
<text v-if="panelOpen && !panelItems.length" class="panel-hint">图片≤10MB,视频≤100MB;仅用于本次现场分析</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -98,6 +112,7 @@ const props = withDefaults(defineProps<{
|
||||
mediaName?: string;
|
||||
mediaKind?: string;
|
||||
toolLabel?: string;
|
||||
panelItems?: Array<{ key: string; icon: string; label: string; disabled?: boolean }>;
|
||||
}>(), {
|
||||
disabled: false,
|
||||
transcribing: false,
|
||||
@@ -106,7 +121,8 @@ const props = withDefaults(defineProps<{
|
||||
quickQuestions: () => [],
|
||||
mediaName: '',
|
||||
mediaKind: '',
|
||||
toolLabel: ''
|
||||
toolLabel: '',
|
||||
panelItems: () => []
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -117,6 +133,7 @@ const emit = defineEmits<{
|
||||
(e: 'clear-media'): void;
|
||||
(e: 'quick', question: string): void;
|
||||
(e: 'tool'): void;
|
||||
(e: 'panel', key: string): void;
|
||||
}>();
|
||||
|
||||
const draft = ref('');
|
||||
@@ -150,6 +167,11 @@ const panelEmit = (event: 'pick-media' | 'pick-audio' | 'tool') => {
|
||||
emit(event);
|
||||
};
|
||||
|
||||
const panelAction = (key: string) => {
|
||||
panelOpen.value = false;
|
||||
emit('panel', key);
|
||||
};
|
||||
|
||||
const stopTracks = () => {
|
||||
stream?.getTracks().forEach((track) => track.stop());
|
||||
stream = null;
|
||||
|
||||
Reference in New Issue
Block a user