feat(speaking): 支持用户消息自动语音合成与播放
This commit is contained in:
parent
124edfa678
commit
812d0557f7
|
|
@ -340,10 +340,14 @@ const sendMessage = async () => {
|
|||
isSending.value = true;
|
||||
|
||||
// 插入用户消息
|
||||
const userMsg = { id: ++msgIdCounter, role: "user", content: text, audioUrl: null, isPlaying: false, isLoading: false };
|
||||
const userMsgId = ++msgIdCounter;
|
||||
const userMsg = { id: userMsgId, role: "user", content: text, audioUrl: null, isPlaying: false, isLoading: false };
|
||||
messages.value.push(userMsg);
|
||||
await scrollToBottom();
|
||||
|
||||
// 用户消息 TTS(不阻塞主流程)
|
||||
synthesizeAndPlay(text, userMsgId).catch((e) => console.error("User TTS error:", e));
|
||||
|
||||
// 插入 AI loading 占位
|
||||
const aiMsgId = ++msgIdCounter;
|
||||
const aiMsg = { id: aiMsgId, role: "assistant", content: "", audioUrl: null, isPlaying: false, isLoading: true };
|
||||
|
|
@ -701,9 +705,10 @@ onUnmounted(() => {
|
|||
<span v-else>{{ msg.content }}</span>
|
||||
</div>
|
||||
|
||||
<!-- AI 消息播放按钮 -->
|
||||
<div v-if="msg.role === 'assistant' && !msg.isLoading && msg.content" class="audio-controls">
|
||||
<button class="play-btn" :class="{ playing: msg.isPlaying }" @click="replayMessage(msg)" :title="msg.isPlaying ? '暂停' : '播放'">
|
||||
<!-- 消息播放按钮(AI 和用户消息均显示) -->
|
||||
<div v-if="!msg.isLoading && msg.content" class="audio-controls" :class="{ 'user-audio-controls': msg.role === 'user' }">
|
||||
<span v-if="!msg.audioUrl" class="tts-hint">合成中...</span>
|
||||
<button v-else class="play-btn" :class="{ playing: msg.isPlaying }" @click="replayMessage(msg)" :title="msg.isPlaying ? '暂停' : '播放'">
|
||||
<!-- 播放中:音波动画 -->
|
||||
<div v-if="msg.isPlaying" class="wave-bars">
|
||||
<span></span><span></span><span></span><span></span>
|
||||
|
|
@ -713,7 +718,6 @@ onUnmounted(() => {
|
|||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.347a1.125 1.125 0 0 1 0 1.972l-11.54 6.347a1.125 1.125 0 0 1-1.667-.986V5.653Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<span v-if="!msg.audioUrl && !msg.isLoading" class="tts-hint">合成中...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -991,6 +995,17 @@ onUnmounted(() => {
|
|||
|
||||
.tts-hint { font-size: 0.75rem; color: var(--text-secondary); }
|
||||
|
||||
/* 用户消息播放按钮右对齐 */
|
||||
.user-audio-controls { justify-content: flex-end; }
|
||||
.user-audio-controls .play-btn {
|
||||
background: rgba(139,92,246,0.2);
|
||||
border-color: rgba(139,92,246,0.4);
|
||||
color: #a78bfa;
|
||||
}
|
||||
.user-audio-controls .play-btn:hover { background: rgba(139,92,246,0.35); }
|
||||
.user-audio-controls .play-btn.playing { background: rgba(139,92,246,0.3); border-color: #a78bfa; }
|
||||
.user-audio-controls .wave-bars span { background: #a78bfa; }
|
||||
|
||||
/* Wave bars animation */
|
||||
.wave-bars {
|
||||
display: flex;
|
||||
|
|
|
|||
Loading…
Reference in New Issue