支持删除历史会话

This commit is contained in:
kx_guo 2026-09-13 17:36:22 +08:00
parent 5582f168c2
commit 6402aaf83e
3 changed files with 67 additions and 3 deletions

View File

@ -29,6 +29,10 @@ export function fetchConversation(id: number, offset = 0) {
}); });
} }
export function deleteConversation(id: number) {
return http.delete<ApiResponse<null>>(`/v1/conversations/${id}`);
}
export function sendConversationMessage( export function sendConversationMessage(
id: number, id: number,
content: string, content: string,

View File

@ -95,6 +95,18 @@ async function openConversation(id: number) {
} }
} }
async function removeConversation(item: { id: number; title: string }) {
if (!window.confirm(`确定删除会话“${item.title}”吗?删除后无法恢复。`)) return;
try {
await conversationStore.remove(item.id);
if (conversationStore.current === null) {
await router.push({ name: "assessment", query: { new: String(Date.now()) } });
}
} catch {
// The store exposes the request error.
}
}
function searchConversations() { function searchConversations() {
void conversationStore.loadList(search.value.trim() || undefined); void conversationStore.loadList(search.value.trim() || undefined);
} }
@ -131,14 +143,15 @@ function searchConversations() {
<p v-else-if="!conversations.length" class="sidebar-state"> <p v-else-if="!conversations.length" class="sidebar-state">
暂无历史对话 暂无历史对话
</p> </p>
<button <div
v-for="item in conversations" v-for="item in conversations"
:key="item.id" :key="item.id"
class="sidebar-item" class="sidebar-item"
:class="{ :class="{
'sidebar-item--active': conversationStore.current?.id === item.id, 'sidebar-item--active': conversationStore.current?.id === item.id,
}" }"
type="button" role="button"
tabindex="0"
@click="openConversation(item.id)" @click="openConversation(item.id)"
> >
<span <span
@ -148,7 +161,16 @@ function searchConversations() {
></span> ></span>
<span v-else class="risk-dot risk-dot--empty"></span> <span v-else class="risk-dot risk-dot--empty"></span>
<span class="sidebar-item__label">{{ item.title }}</span> <span class="sidebar-item__label">{{ item.title }}</span>
<button
type="button"
class="sidebar-item__delete"
title="删除会话"
aria-label="删除会话"
@click.stop="removeConversation(item)"
>
×
</button> </button>
</div>
</section> </section>
<section> <section>
@ -264,6 +286,32 @@ section h2 {
white-space: nowrap; white-space: nowrap;
} }
.sidebar-item__delete {
display: none;
width: 22px;
height: 22px;
flex: 0 0 22px;
margin-left: auto;
border: 0;
border-radius: 6px;
background: transparent;
color: var(--color-text-faint);
font-size: 18px;
line-height: 1;
cursor: pointer;
}
.sidebar-item:hover .sidebar-item__delete,
.sidebar-item:focus-within .sidebar-item__delete {
display: inline-grid;
place-items: center;
}
.sidebar-item__delete:hover {
background: var(--color-danger-subtle);
color: var(--color-danger);
}
.risk-dot { .risk-dot {
width: 5px; width: 5px;
height: 5px; height: 5px;

View File

@ -3,6 +3,7 @@ import { computed, reactive, ref } from "vue";
import { import {
createConversation, createConversation,
deleteConversation,
fetchConversation, fetchConversation,
fetchConversations, fetchConversations,
withdrawConversationMessage, withdrawConversationMessage,
@ -132,6 +133,16 @@ export const useConversationStore = defineStore("conversation", () => {
return current.value; return current.value;
} }
async function remove(id: number) {
const active = current.value?.id === id;
await deleteConversation(id);
streamStates.get(id)?.controller.abort();
streamStates.delete(id);
conversationCache.delete(id);
items.value = items.value.filter((item) => item.id !== id);
if (active) current.value = null;
}
async function send( async function send(
content: string, content: string,
messageType: ConversationMessageType = "text", messageType: ConversationMessageType = "text",
@ -428,6 +439,7 @@ export const useConversationStore = defineStore("conversation", () => {
open, open,
loadOlder, loadOlder,
create, create,
remove,
send, send,
streamSend, streamSend,
stopGeneration, stopGeneration,