|
@@ -34,7 +34,7 @@ import { useRouter } from 'vue-router'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import StructDataPlugin from '/@/components/markdown/plugins/struct-data'
|
|
|
|
|
|
-const plugins: Array<MarkdownPlugin<any>> = [EChartsPlugin(), ToolsLoadingPlugin(), TablePlugin(),StructDataPlugin()]
|
|
|
+const plugins: Array<MarkdownPlugin<any>> = [EChartsPlugin(), ToolsLoadingPlugin(), TablePlugin(), StructDataPlugin()]
|
|
|
|
|
|
//聊天管理接口
|
|
|
// 消息列表
|
|
@@ -108,7 +108,7 @@ const promptList = ref<Prompt[]>([])
|
|
|
const promptListParams = ref<PromptListParams>({
|
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
|
- keyWord: ''
|
|
|
+ keyWord: '',
|
|
|
})
|
|
|
const promptListTotal = ref(0)
|
|
|
|
|
@@ -125,7 +125,7 @@ const modelLabel = computed(() => {
|
|
|
})
|
|
|
|
|
|
const { loading: loadingModels, doLoading: loadModel } = useLoading(async () => {
|
|
|
- const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList({modelType: 'chat'}).catch(() => {
|
|
|
+ const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList({ modelType: 'chat' }).catch(() => {
|
|
|
return {
|
|
|
list: [],
|
|
|
}
|
|
@@ -149,7 +149,7 @@ const embeddingModelLabel = computed(() => {
|
|
|
})
|
|
|
|
|
|
const { loading: loadingEmbeddingModels, doLoading: loadEmbeddingModel } = useLoading(async () => {
|
|
|
- const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList({modelType: 'embedding'}).catch(() => {
|
|
|
+ const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList({ modelType: 'embedding' }).catch(() => {
|
|
|
return {
|
|
|
list: [],
|
|
|
}
|
|
@@ -160,7 +160,7 @@ const { loading: loadingEmbeddingModels, doLoading: loadEmbeddingModel } = useLo
|
|
|
{
|
|
|
id: -1,
|
|
|
modelName: '不启用词嵌入',
|
|
|
- status: true
|
|
|
+ status: true,
|
|
|
},
|
|
|
]
|
|
|
|
|
@@ -336,7 +336,7 @@ const chatInternal = (rtn: Message, context: Message[] = messages.value) => {
|
|
|
]
|
|
|
: context,
|
|
|
modelClassId: selectedModel.value,
|
|
|
- modelEmbeddingId: (selectedEmbeddingModel.value ?? -1) > 0 ? selectedEmbeddingModel.value : undefined
|
|
|
+ modelEmbeddingId: (selectedEmbeddingModel.value ?? -1) > 0 ? selectedEmbeddingModel.value : undefined,
|
|
|
},
|
|
|
onReceive: (resp: ChatResponse) => {
|
|
|
switch (resp.type) {
|
|
@@ -369,7 +369,7 @@ ${resp.response.data.replace('\n', '')}
|
|
|
}
|
|
|
|
|
|
//防止重试时底部有聊天记录无脑插入到底部出现后端无法识别的问题
|
|
|
- messages.value.splice(messages.value.indexOf(rtn) + 1,0,{
|
|
|
+ messages.value.splice(messages.value.indexOf(rtn) + 1, 0, {
|
|
|
id: messages.value.length,
|
|
|
tool_call_id: resp.response.id,
|
|
|
role: 'tool',
|
|
@@ -476,7 +476,7 @@ const { loading: loadConversations, doLoading: doLoadConversations } = useLoadin
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- conversations.value = data.list
|
|
|
+ conversations.value = data.list ?? []
|
|
|
})
|
|
|
|
|
|
onMounted(doLoadConversations)
|
|
@@ -557,30 +557,50 @@ const { loading: loadingDeleteConversation, doLoading: deleteConversation } = us
|
|
|
await doLoadConversations()
|
|
|
})
|
|
|
|
|
|
-const { loading: loadingDeleteAllConversion, doLoading: deleteAllConversion } = useLoading(async () => {
|
|
|
- const confirm = await ElMessageBox.confirm(`确定要删除所有对话吗?此操作不可恢复!`, '警告', {
|
|
|
- confirmButtonText: '确定',
|
|
|
- cancelButtonText: '取消',
|
|
|
- type: 'error',
|
|
|
- })
|
|
|
+const multiDeleteConversationModel = ref({
|
|
|
+ visible: false,
|
|
|
+ selectedConversations: [] as number[],
|
|
|
+})
|
|
|
+
|
|
|
+const multiDeleteConversationClear = () => {
|
|
|
+ multiDeleteConversationModel.value.selectedConversations = []
|
|
|
+ multiDeleteConversationModel.value.visible = false
|
|
|
+}
|
|
|
+
|
|
|
+// 多选删除会话
|
|
|
+const startMultidelete = async () => {
|
|
|
+ if (multiDeleteConversationModel.value.selectedConversations.length === 0) {
|
|
|
+ ElMessage.warning('请选择要删除的会话')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const confirm = await ElMessageBox.confirm(
|
|
|
+ `确定要删除选中的 ${multiDeleteConversationModel.value.selectedConversations.length} 个会话吗?此操作不可恢复!`,
|
|
|
+ '警告',
|
|
|
+ {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'error',
|
|
|
+ }
|
|
|
+ )
|
|
|
|
|
|
if (confirm !== 'confirm') {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
const res = await assist.session
|
|
|
- .del(conversations.value.map((it) => it.session_id))
|
|
|
+ .del(multiDeleteConversationModel.value.selectedConversations)
|
|
|
.then(() => true)
|
|
|
.catch(() => false)
|
|
|
- .finally(() => (currentDeletingConversation.value = -1))
|
|
|
+
|
|
|
if (!res) {
|
|
|
return
|
|
|
}
|
|
|
+
|
|
|
ElMessage.success('删除成功')
|
|
|
- activeConversationId.value = undefined
|
|
|
- messages.value = []
|
|
|
- conversations.value = []
|
|
|
-})
|
|
|
+
|
|
|
+ await nextTick()
|
|
|
+ await doLoadConversations()
|
|
|
+}
|
|
|
|
|
|
// 创建新对话
|
|
|
const { loading: creatingConversation, doLoading: createConversationAndSetItActive } = useLoading(async () => {
|
|
@@ -718,11 +738,6 @@ const handleBookmarkReset = async () => {
|
|
|
await doLoadingMessage(-1)
|
|
|
}
|
|
|
|
|
|
-// 计算总页数
|
|
|
-const bookmarkTotalPages = computed(() => {
|
|
|
- return Math.ceil(bookmarkTotal.value / bookmarkOptions.value.pageSize)
|
|
|
-})
|
|
|
-
|
|
|
// 每页数目选项
|
|
|
const pageSizeOptions = [3, 10, 20, 30, 50, 100]
|
|
|
|
|
@@ -846,7 +861,7 @@ const handlePromptReset = async () => {
|
|
|
</el-icon>
|
|
|
</el-button>
|
|
|
<template #dropdown>
|
|
|
- <el-dropdown-menu>
|
|
|
+ <el-dropdown-menu v-if="!multiDeleteConversationModel.visible">
|
|
|
<el-dropdown-item class="settings-item">
|
|
|
<div class="settings-row">
|
|
|
<span class="settings-label">新对话自动记录工具调用</span>
|
|
@@ -856,14 +871,28 @@ const handlePromptReset = async () => {
|
|
|
<el-dropdown-item @click="redirectToModelManager">
|
|
|
<span class="settings-label">模型管理</span>
|
|
|
</el-dropdown-item>
|
|
|
- <el-dropdown-item @click="deleteAllConversion">
|
|
|
- <span class="settings-label">清空所有对话</span>
|
|
|
+ <el-dropdown-item @click="multiDeleteConversationModel.visible = true">
|
|
|
+ <span class="settings-label">对话管理</span>
|
|
|
+ </el-dropdown-item>
|
|
|
+ </el-dropdown-menu>
|
|
|
+ <el-dropdown-menu v-else>
|
|
|
+ <el-dropdown-item @click="multiDeleteConversationClear">
|
|
|
+ <el-icon>
|
|
|
+ <Close />
|
|
|
+ </el-icon>
|
|
|
+ <span>取消选择</span>
|
|
|
+ </el-dropdown-item>
|
|
|
+ <el-dropdown-item @click="startMultidelete">
|
|
|
+ <el-icon>
|
|
|
+ <Delete />
|
|
|
+ </el-icon>
|
|
|
+ <span>删除选中</span>
|
|
|
</el-dropdown-item>
|
|
|
</el-dropdown-menu>
|
|
|
</template>
|
|
|
</el-dropdown>
|
|
|
</div>
|
|
|
- <el-scrollbar class="conversation-list" v-loading="loadConversations || loadingDeleteAllConversion">
|
|
|
+ <el-scrollbar class="conversation-list" v-loading="loadConversations">
|
|
|
<div
|
|
|
v-for="conv in displayConversations"
|
|
|
:key="conv.session_id"
|
|
@@ -872,6 +901,13 @@ const handlePromptReset = async () => {
|
|
|
>
|
|
|
<!-- 非编辑状态 -->
|
|
|
<div v-if="editingConversationId !== conv.session_id" class="conversation-content">
|
|
|
+ <el-checkbox
|
|
|
+ v-if="multiDeleteConversationModel.visible && conv.session_id !== -1 && conv.session_id !== activeConversationId"
|
|
|
+ :model-value="multiDeleteConversationModel.selectedConversations.includes(conv.session_id)"
|
|
|
+ @change="(enable: boolean) => enable ? multiDeleteConversationModel.selectedConversations.push(conv.session_id) : multiDeleteConversationModel.selectedConversations = multiDeleteConversationModel.selectedConversations.filter((id: number) => id !== conv.session_id)"
|
|
|
+ :disabled="isConversationActive && activeConversationId === conv.session_id"
|
|
|
+ @click.stop
|
|
|
+ />
|
|
|
<span class="conversation-title">{{ conv.title }}</span>
|
|
|
</div>
|
|
|
|
|
@@ -887,7 +923,7 @@ const handlePromptReset = async () => {
|
|
|
</div>
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
- <div class="conversation-actions" v-if="conv.session_id !== -1">
|
|
|
+ <div class="conversation-actions" v-if="conv.session_id !== -1 && !multiDeleteConversationModel.visible">
|
|
|
<!-- 非编辑状态的三点菜单 -->
|
|
|
<template v-if="editingConversationId !== conv.session_id">
|
|
|
<el-dropdown trigger="click" placement="bottom-end" @click.stop>
|
|
@@ -1249,9 +1285,7 @@ const handlePromptReset = async () => {
|
|
|
@clear="handlePromptReset"
|
|
|
style="width: 200px"
|
|
|
/>
|
|
|
- <el-button type="primary" :icon="Search" @click="handlePromptSearch" :loading="loadingPromptList" size="small">
|
|
|
- 搜索
|
|
|
- </el-button>
|
|
|
+ <el-button type="primary" :icon="Search" @click="handlePromptSearch" :loading="loadingPromptList" size="small"> 搜索 </el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
@@ -1261,12 +1295,7 @@ const handlePromptReset = async () => {
|
|
|
<el-empty description="暂无提示词模板" />
|
|
|
</div>
|
|
|
<div v-else class="prompt-list">
|
|
|
- <div
|
|
|
- v-for="item in promptList"
|
|
|
- :key="item.id"
|
|
|
- class="prompt-item"
|
|
|
- @click="selectPrompt(item)"
|
|
|
- >
|
|
|
+ <div v-for="item in promptList" :key="item.id" class="prompt-item" @click="selectPrompt(item)">
|
|
|
<div class="prompt-item-header">
|
|
|
<span class="prompt-title">{{ item.title }}</span>
|
|
|
</div>
|
|
@@ -1421,6 +1450,12 @@ const handlePromptReset = async () => {
|
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
|
cursor: pointer;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .el-checkbox {
|
|
|
+ margin-right: 8px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.conversation-edit-content {
|