本次更新
支持:
- 复制单条消息
- 删除单条消息
- 重新生成上一条 AI 回复
1)改 web/src/App.vue
新增方法
javascript
const copyMessage = async content => {
try {
await navigator.clipboard.writeText(content || '')
window.alert('消息已复制')
} catch (e) {
console.error(e)
window.alert('复制失败')
}
}
const handleDeleteMessage = index => {
if (!currentSession.value) return
const nextMessages = currentSession.value.messages.filter((_, i) => i !== index)
sessions.value = sortSessions(
sessions.value.map(item =>
item.id === currentSessionId.value
? {
...item,
updatedAt: Date.now(),
messages: nextMessages,
}
: item
)
)
}
const handleRegenerate = async index => {
if (!currentSession.value || loading.value) return
const messages = [...currentSession.value.messages]
const current = messages[index]
const prev = messages[index - 1]
if (!current || current.role !== 'assistant') return
if (!prev || prev.role !== 'user') {
window.alert('只能重新生成上一条 AI 回复')
return
}
const nextMessages = messages.slice(0, index)
sessions.value = sortSessions(
sessions.value.map(item =>
item.id === currentSessionId.value
? {
...item,
updatedAt: Date.now(),
messages: nextMessages,
}
: item
)
)
loading.value = true
try {
await sendMessageStream(nextMessages)
} catch (error) {
updateCurrentSession(session => ({
...session,
updatedAt: Date.now(),
messages: [
...session.messages,
{
role: 'assistant',
content: '重新生成失败,请稍后再试。',
},
],
}))
console.error(error)
} finally {
loading.value = false
abortController.value = null
}
}
改消息模板
ini
<div
v-for="(item, index) in currentMessages"
:key="index"
:class="['msg', item.role]"
>
<div class="role-row">
<div class="role">
{{
item.role === 'user'
? '我'
: item.role === 'assistant'
? 'AI'
: 'system'
}}
</div>
<div v-if="item.role !== 'system'" class="msg-actions">
<span class="msg-action-btn" @click="copyMessage(item.content)">复制</span>
<span class="msg-action-btn delete" @click="handleDeleteMessage(index)">删除</span>
<span
v-if="item.role === 'assistant'"
class="msg-action-btn"
@click="handleRegenerate(index)"
>
重新生成
</span>
</div>
</div>
<div
v-if="item.role === 'assistant'"
class="content markdown-body"
v-html="renderMarkdown(item.content)"
/>
<div v-else class="content">{{ item.content }}</div>
</div>
新增样式
css
.role-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 6px;
}
.msg-actions {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.msg-action-btn {
font-size: 12px;
color: #6b7280;
cursor: pointer;
user-select: none;
}
.msg-action-btn:hover {
color: #111827;
}
.msg-action-btn.delete:hover {
color: #dc2626;
}
2)验证
复制消息
点任意一条消息右侧的 复制
删除消息
点 删除 后,这条消息会从当前会话里移除
重新生成
找一条 AI 回复,点 重新生成
它会:
- 删除当前这条 AI 回复
- 保留前一条用户消息
- 重新走流式生成




nice 这一节非常简单