|
|
|
@@ -344,7 +344,13 @@
|
|
|
|
|
>
|
|
|
|
|
{{ speechButtonLabel(`turn-${index}`) }}
|
|
|
|
|
</button>
|
|
|
|
|
<button class="bubble-action" @click="adoptCoachHint(turn.text)">用这句话回复</button>
|
|
|
|
|
<button
|
|
|
|
|
class="bubble-action"
|
|
|
|
|
:disabled="replySuggestionLoading !== null || busy"
|
|
|
|
|
@click="adoptCoachHint(turn, index)"
|
|
|
|
|
>
|
|
|
|
|
{{ replySuggestionLoading === index ? '生成中…' : '用这句话回复' }}
|
|
|
|
|
</button>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
@@ -556,6 +562,7 @@ const helpRefs = ref<string[]>([]);
|
|
|
|
|
const lastTraineeText = ref('');
|
|
|
|
|
const lastCoachRoundIndex = ref<number | null>(null);
|
|
|
|
|
const adjustingStyle = ref('');
|
|
|
|
|
const replySuggestionLoading = ref<number | null>(null);
|
|
|
|
|
const emotionScore = ref(0);
|
|
|
|
|
const trustScore = ref(0);
|
|
|
|
|
const redFlagActive = ref(false);
|
|
|
|
@@ -808,12 +815,30 @@ const handleComposerSend = (text: string) => {
|
|
|
|
|
void submit();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 旧 H5「建议回复」逻辑的会话版:把老师傅建议填入输入框,员工可改后发送。 */
|
|
|
|
|
const adoptCoachHint = (text: string) => {
|
|
|
|
|
const value = (text || '').trim();
|
|
|
|
|
if (!value) return;
|
|
|
|
|
composerRef.value?.setDraft(value);
|
|
|
|
|
uni.showToast({ title: '已填入输入框,可修改后发送', icon: 'none' });
|
|
|
|
|
const adoptCoachHint = async (turn: PracticeTurn, index: number) => {
|
|
|
|
|
if (busy.value || replySuggestionLoading.value !== null || !sessionId.value || !Number.isInteger(turn.roundIndex)) return;
|
|
|
|
|
const request = requests.begin('reply-suggestion');
|
|
|
|
|
replySuggestionLoading.value = index;
|
|
|
|
|
message.value = '';
|
|
|
|
|
try {
|
|
|
|
|
const data = await submitPracticeTurn({
|
|
|
|
|
sessionId: sessionId.value,
|
|
|
|
|
roundIndex: turn.roundIndex as number,
|
|
|
|
|
traineeText: '',
|
|
|
|
|
suggestReply: true
|
|
|
|
|
});
|
|
|
|
|
if (!requests.isCurrent(request)) return;
|
|
|
|
|
const value = (data.suggestedReply || '').trim();
|
|
|
|
|
if (!value) throw new Error(data.message || '未生成可用回复');
|
|
|
|
|
composerRef.value?.setDraft(value);
|
|
|
|
|
uni.showToast({ title: '已生成回复,可修改后发送', icon: 'none' });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
if (!requests.isCurrent(request)) return;
|
|
|
|
|
handleError(error, '回复生成失败');
|
|
|
|
|
uni.showToast({ title: message.value || '回复生成失败', icon: 'none' });
|
|
|
|
|
} finally {
|
|
|
|
|
if (requests.isCurrent(request)) replySuggestionLoading.value = null;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const submitTranscribed = async (text: string, audioUrl: string, ossId?: string) => {
|
|
|
|
@@ -953,6 +978,7 @@ const clearPracticeData = () => {
|
|
|
|
|
lastTraineeText.value = '';
|
|
|
|
|
lastCoachRoundIndex.value = null;
|
|
|
|
|
adjustingStyle.value = '';
|
|
|
|
|
replySuggestionLoading.value = null;
|
|
|
|
|
emotionScore.value = 0;
|
|
|
|
|
trustScore.value = 0;
|
|
|
|
|
redFlagActive.value = false;
|
|
|
|
@@ -975,9 +1001,9 @@ const requireLogin = () => {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const appendTurn = (role: PracticeRole, text?: string, emotion = 0, synthesizeCustomerVoice = true, customerTurnIndex?: number) => {
|
|
|
|
|
const appendTurn = (role: PracticeRole, text?: string, emotion = 0, synthesizeCustomerVoice = true, customerTurnIndex?: number, coachRoundIndex?: number) => {
|
|
|
|
|
if (!text) return;
|
|
|
|
|
turns.value.push({ role, text });
|
|
|
|
|
turns.value.push({ role, text, roundIndex: coachRoundIndex });
|
|
|
|
|
if (role === 'customer' && synthesizeCustomerVoice) {
|
|
|
|
|
void prepareTurnVoice(turns.value.length - 1, text, emotion, customerTurnIndex);
|
|
|
|
|
}
|
|
|
|
@@ -1586,7 +1612,7 @@ const replaceLastCoachHint = (text?: string) => {
|
|
|
|
|
if (!text) return;
|
|
|
|
|
for (let index = turns.value.length - 1; index >= 0; index -= 1) {
|
|
|
|
|
if (turns.value[index].role === 'coach') {
|
|
|
|
|
turns.value[index] = { role: 'coach', text };
|
|
|
|
|
turns.value[index] = { ...turns.value[index], text };
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -1661,7 +1687,7 @@ const submit = async () => {
|
|
|
|
|
emotionScore.value = data.emotion ?? emotionScore.value;
|
|
|
|
|
trustScore.value = data.trust ?? trustScore.value;
|
|
|
|
|
redFlagActive.value = Boolean(data.redFlag);
|
|
|
|
|
appendTurn('coach', data.redFlag ? data.coachHint || '先承接诉求,再给处理节点。' : data.coachHint);
|
|
|
|
|
appendTurn('coach', data.redFlag ? data.coachHint || '先承接诉求,再给处理节点。' : data.coachHint, 0, true, undefined, submittedRound);
|
|
|
|
|
if (data.redFlag) {
|
|
|
|
|
message.value = '命中红线,请按老师傅建议重说本轮';
|
|
|
|
|
status.value = 'active';
|
|
|
|
@@ -1757,6 +1783,7 @@ const invalidateRequests = () => {
|
|
|
|
|
feedbackSubmittingId.value = null;
|
|
|
|
|
helpLoading.value = false;
|
|
|
|
|
adjustingStyle.value = '';
|
|
|
|
|
replySuggestionLoading.value = null;
|
|
|
|
|
satisfactionBusy.value = false;
|
|
|
|
|
if (status.value === 'starting') status.value = 'idle';
|
|
|
|
|
if (status.value === 'submitting') status.value = sessionId.value ? 'active' : 'idle';
|
|
|
|
|