浏览代码

删除收藏

kagg886 2 月之前
父节点
当前提交
632ea15920
共有 2 个文件被更改,包括 1159 次插入1136 次删除
  1. 1 0
      src/api/assist/index.ts
  2. 1158 1136
      src/views/assistant/index.vue

+ 1 - 0
src/api/assist/index.ts

@@ -52,6 +52,7 @@ export default {
 					messageId,
 					like,
 				}),
+			bookmark_list: () => get('/system/lmsessions/messages/like/list')
 		},
 	},
 

+ 1158 - 1136
src/views/assistant/index.vue

@@ -1,18 +1,18 @@
 <script setup lang="ts">
-import { ref, nextTick, onMounted, computed, onUnmounted, reactive, watch, isReactive } from 'vue'
-import { Local } from '/@/utils/storage'
-import { User, ChatDotRound, Delete, Edit, Check, Close, ArrowDown, Star } from '@element-plus/icons-vue'
-import { MarkdownPlugin } from '/@/components/markdown/type/markdown'
+import {ref, nextTick, onMounted, computed, onUnmounted, reactive, watch, isReactive} from 'vue'
+import {Local} from '/@/utils/storage'
+import {User, ChatDotRound, Delete, Edit, Check, Close, ArrowDown, Star, StarFilled} from '@element-plus/icons-vue'
+import {MarkdownPlugin} from '/@/components/markdown/type/markdown'
 import EChartsPlugin from '/@/components/markdown/plugins/echarts'
 import ToolsLoadingPlugin from '/@/components/markdown/plugins/tools-loading'
 import TablePlugin from '/@/components/markdown/plugins/table'
 import Markdown from '/@/components/markdown/Markdown.vue'
 import assist from '/@/api/assist'
-import { ChatResponse, LmConfigInfo, LmSession, Message } from '/@/api/assist/type'
-import { useLoading } from '/@/utils/loading-util'
-import { Setting as EleSetting } from '@element-plus/icons-vue'
-import { useRouter } from 'vue-router'
-import { ElMessage } from 'element-plus'
+import {ChatResponse, LmConfigInfo, LmSession, Message} from '/@/api/assist/type'
+import {useLoading} from '/@/utils/loading-util'
+import {Setting as EleSetting} from '@element-plus/icons-vue'
+import {useRouter} from 'vue-router'
+import {ElMessage} from 'element-plus'
 
 const plugins: Array<MarkdownPlugin<any>> = [EChartsPlugin(), ToolsLoadingPlugin(), TablePlugin()]
 
@@ -28,52 +28,52 @@ const messagesContainer = ref<HTMLElement>()
 const selectedTool = ref([])
 // 工具选择
 const toolOptions = ref([
-	{
-		value: 'code',
-		label: '代码工具',
-		children: [
-			{
-				value: 'codebase-retrieval',
-				label: '代码库检索',
-			},
-			{
-				value: 'str-replace-editor',
-				label: '代码编辑器',
-			},
-			{
-				value: 'save-file',
-				label: '文件保存',
-			},
-		],
-	},
-	{
-		value: 'web',
-		label: '网络工具',
-		children: [
-			{
-				value: 'web-search',
-				label: '网络搜索',
-			},
-			{
-				value: 'web-fetch',
-				label: '网页获取',
-			},
-		],
-	},
-	{
-		value: 'system',
-		label: '系统工具',
-		children: [
-			{
-				value: 'execute-command',
-				label: '命令执行',
-			},
-			{
-				value: 'launch-process',
-				label: '进程启动',
-			},
-		],
-	},
+  {
+    value: 'code',
+    label: '代码工具',
+    children: [
+      {
+        value: 'codebase-retrieval',
+        label: '代码库检索',
+      },
+      {
+        value: 'str-replace-editor',
+        label: '代码编辑器',
+      },
+      {
+        value: 'save-file',
+        label: '文件保存',
+      },
+    ],
+  },
+  {
+    value: 'web',
+    label: '网络工具',
+    children: [
+      {
+        value: 'web-search',
+        label: '网络搜索',
+      },
+      {
+        value: 'web-fetch',
+        label: '网页获取',
+      },
+    ],
+  },
+  {
+    value: 'system',
+    label: '系统工具',
+    children: [
+      {
+        value: 'execute-command',
+        label: '命令执行',
+      },
+      {
+        value: 'launch-process',
+        label: '进程启动',
+      },
+    ],
+  },
 ])
 
 const prompt = ref<string>('')
@@ -82,15 +82,15 @@ const openPromptDialog = ref(false)
 // 模型选择
 const modelOptions = ref<LmConfigInfo[]>([])
 
-const { loading: loadingModels, doLoading: loadModel } = useLoading(async () => {
-	const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList().catch(() => {
-		return {
-			list: [],
-		}
-	})
+const {loading: loadingModels, doLoading: loadModel} = useLoading(async () => {
+  const data: { list: LmConfigInfo[]; total: number } = await assist.model.getList().catch(() => {
+    return {
+      list: [],
+    }
+  })
 
-	modelOptions.value = data.list
-	selectedModel.value = data.list[0]?.id ?? undefined
+  modelOptions.value = data.list
+  selectedModel.value = data.list[0]?.id ?? undefined
 })
 
 onMounted(loadModel)
@@ -102,137 +102,137 @@ onUnmounted(() => chatInstance.value?.())
 // 是否正在对话
 const isConversationActive = computed(() => chatInstance.value !== undefined)
 
-const { loading: loadingClearMessage, doLoading: clearMessage } = useLoading(async () => {
-	stopConversation()
-	if (messages.value.length !== 0 && activeConversationId.value !== undefined) {
-		const res = await assist.session.message
-			.save({
-				sessionId: activeConversationId.value,
-				messages: [],
-			})
-			.then(() => true)
-			.catch(() => false)
-		if (!res) {
-			return
-		}
-	}
-	messages.value = []
+const {loading: loadingClearMessage, doLoading: clearMessage} = useLoading(async () => {
+  stopConversation()
+  if (messages.value.length !== 0 && activeConversationId.value !== undefined) {
+    const res = await assist.session.message
+        .save({
+          sessionId: activeConversationId.value,
+          messages: [],
+        })
+        .then(() => true)
+        .catch(() => false)
+    if (!res) {
+      return
+    }
+  }
+  messages.value = []
 })
 // 发送消息
 const sendMessage = async () => {
-	if (!inputMessage.value.trim()) return
-
-	if (activeConversationId.value === undefined) {
-		//未选中任何会话则创建新会话
-		await createConversationAndSetItActive()
-	}
-	await nextTick()
-	messages.value.push({
-		id: messages.value.length,
-		role: 'user',
-		render_content: inputMessage.value,
-		content: inputMessage.value,
-		timestamp: Date.now(),
-	})
-
-	const rtn = reactive<Message>({
-		id: messages.value.length,
-		role: 'assistant',
-		render_content: '',
-		content: '',
-		timestamp: Date.now(),
-		tool_calls: [],
-	})
-
-	inputMessage.value = ''
-	scrollToBottom()
-	chatInternal(rtn)
-	messages.value.push(rtn)
+  if (!inputMessage.value.trim()) return
+
+  if (activeConversationId.value === undefined) {
+    //未选中任何会话则创建新会话
+    await createConversationAndSetItActive()
+  }
+  await nextTick()
+  messages.value.push({
+    id: messages.value.length,
+    role: 'user',
+    render_content: inputMessage.value,
+    content: inputMessage.value,
+    timestamp: Date.now(),
+  })
+
+  const rtn = reactive<Message>({
+    id: messages.value.length,
+    role: 'assistant',
+    render_content: '',
+    content: '',
+    timestamp: Date.now(),
+    tool_calls: [],
+  })
+
+  inputMessage.value = ''
+  scrollToBottom()
+  chatInternal(rtn)
+  messages.value.push(rtn)
 }
 
 const replaceMessage = (index: number) => {
-	// 获取当前用户消息
-	const userMessage = messages.value[index]
-	if (!userMessage || userMessage.role !== 'user') {
-		return
-	}
-
-	// 查找对应的AI回复消息(通常是下一条消息)
-	let aiMessageIndex = -1
-	for (let i = index + 1; i < messages.value.length; i++) {
-		if (messages.value[i].role === 'assistant') {
-			aiMessageIndex = i
-			break
-		}
-	}
-
-	let rtn: Message
-
-	if (aiMessageIndex !== -1) {
-		// 找到了AI回复消息
-		const aiMessage = messages.value[aiMessageIndex]
-
-		if (isReactive(aiMessage)) {
-			// 如果是reactive对象,清空内容
-			aiMessage.render_content = ''
-			aiMessage.content = ''
-			aiMessage.tool_calls = []
-			rtn = aiMessage
-		} else {
-			// 如果不是reactive对象,创建新的reactive对象替换
-			rtn = reactive<Message>({
-				id: aiMessage.id,
-				role: 'assistant',
-				render_content: '',
-				content: '',
-				timestamp: aiMessage.timestamp,
-				tool_calls: [],
-			})
-			messages.value[aiMessageIndex] = rtn
-		}
-	} else {
-		// 没有找到AI回复消息,创建新的
-		rtn = reactive<Message>({
-			id: messages.value.length,
-			role: 'assistant',
-			render_content: '',
-			content: '',
-			timestamp: Date.now(),
-			tool_calls: [],
-		})
-		messages.value.push(rtn)
-	}
-
-	// 重新发起对话
-	chatInternal(rtn, messages.value.slice(0, aiMessageIndex))
+  // 获取当前用户消息
+  const userMessage = messages.value[index]
+  if (!userMessage || userMessage.role !== 'user') {
+    return
+  }
+
+  // 查找对应的AI回复消息(通常是下一条消息)
+  let aiMessageIndex = -1
+  for (let i = index + 1; i < messages.value.length; i++) {
+    if (messages.value[i].role === 'assistant') {
+      aiMessageIndex = i
+      break
+    }
+  }
+
+  let rtn: Message
+
+  if (aiMessageIndex !== -1) {
+    // 找到了AI回复消息
+    const aiMessage = messages.value[aiMessageIndex]
+
+    if (isReactive(aiMessage)) {
+      // 如果是reactive对象,清空内容
+      aiMessage.render_content = ''
+      aiMessage.content = ''
+      aiMessage.tool_calls = []
+      rtn = aiMessage
+    } else {
+      // 如果不是reactive对象,创建新的reactive对象替换
+      rtn = reactive<Message>({
+        id: aiMessage.id,
+        role: 'assistant',
+        render_content: '',
+        content: '',
+        timestamp: aiMessage.timestamp,
+        tool_calls: [],
+      })
+      messages.value[aiMessageIndex] = rtn
+    }
+  } else {
+    // 没有找到AI回复消息,创建新的
+    rtn = reactive<Message>({
+      id: messages.value.length,
+      role: 'assistant',
+      render_content: '',
+      content: '',
+      timestamp: Date.now(),
+      tool_calls: [],
+    })
+    messages.value.push(rtn)
+  }
+
+  // 重新发起对话
+  chatInternal(rtn, messages.value.slice(0, aiMessageIndex))
 }
 
 const chatInternal = (rtn: Message, context: Message[] = messages.value) => {
-	chatInstance.value = assist.chat({
-		chatRequest: {
-			message: prompt.value
-				? [
-						{
-							id: messages.value.length,
-							role: 'system',
-							render_content: prompt.value,
-							content: prompt.value,
-							timestamp: Date.now(),
-						},
-						...context,
-				  ]
-				: context,
-			modelClassId: selectedModel.value,
-		},
-		onReceive: (resp: ChatResponse) => {
-			switch (resp.type) {
-				case 'message':
-					rtn.render_content += resp.message
-					rtn.content += resp.message
-					break
-				case 'toolres': {
-					if (showToolCalls.value) {
-						rtn.render_content += `
+  chatInstance.value = assist.chat({
+    chatRequest: {
+      message: prompt.value
+          ? [
+            {
+              id: messages.value.length,
+              role: 'system',
+              render_content: prompt.value,
+              content: prompt.value,
+              timestamp: Date.now(),
+            },
+            ...context,
+          ]
+          : context,
+      modelClassId: selectedModel.value,
+    },
+    onReceive: (resp: ChatResponse) => {
+      switch (resp.type) {
+        case 'message':
+          rtn.render_content += resp.message
+          rtn.content += resp.message
+          break
+        case 'toolres': {
+          if (showToolCalls.value) {
+            rtn.render_content += `
 \`\`\`tools-loading
 resp
 ${resp.response.name}
@@ -240,24 +240,24 @@ ${resp.response.data.replace('\n', '')}
 \`\`\`
 
 `
-					}
-
-					messages.value.push({
-						id: messages.value.length,
-						tool_call_id: resp.response.id,
-						role: 'tool',
-						render_content: '',
-						name: resp.response.name,
-						content: resp.response.data,
-						timestamp: Date.now(),
-						tool_calls: [],
-					})
-					break
-				}
-
-				case 'toolcall': {
-					if (showToolCalls.value) {
-						rtn.render_content += `
+          }
+
+          messages.value.push({
+            id: messages.value.length,
+            tool_call_id: resp.response.id,
+            role: 'tool',
+            render_content: '',
+            name: resp.response.name,
+            content: resp.response.data,
+            timestamp: Date.now(),
+            tool_calls: [],
+          })
+          break
+        }
+
+        case 'toolcall': {
+          if (showToolCalls.value) {
+            rtn.render_content += `
 \`\`\`tools-loading
 request
 ${resp.request.name}
@@ -265,79 +265,85 @@ ${resp.request.data.replace('\n', '')}
 \`\`\`
 
 `
-					}
-
-					rtn.tool_calls?.push({
-						id: resp.request.id,
-						type: 'function',
-						function: {
-							name: resp.request.name,
-							arguments: resp.request.data,
-						},
-					})
-					break
-				}
-				case 'error':
-					rtn.render_content += `
+          }
+
+          rtn.tool_calls?.push({
+            id: resp.request.id,
+            type: 'function',
+            function: {
+              name: resp.request.name,
+              arguments: resp.request.data,
+            },
+          })
+          break
+        }
+        case 'error':
+          rtn.render_content += `
 > ### 系统报错
 >
 >
 > ${resp.error.split('\n').join('\n> ')}
 
 `
-					break
-			}
-		},
-		onComplete: async (e) => {
-			if (e !== undefined) {
-				rtn.content += `
+          break
+      }
+    },
+    onComplete: async (e) => {
+      if (e !== undefined) {
+        rtn.content += `
 
 				`
-			}
-			rtn.render_content += '\n'
+      }
+      rtn.render_content += '\n'
 
-			await assist.session.message.save({
-				sessionId: activeConversationId.value!,
-				messages: messages.value,
-			})
+      await assist.session.message.save({
+        sessionId: activeConversationId.value!,
+        messages: messages.value,
+      })
 
-			chatInstance.value = undefined
-		},
-	})
+      chatInstance.value = undefined
+    },
+  })
 }
 
 // 终止对话
 const stopConversation = () => {
-	chatInstance.value?.()
-	chatInstance.value = undefined
+  chatInstance.value?.()
+  chatInstance.value = undefined
 }
 
 // 滚动到底部
 const scrollToBottom = () => {
-	nextTick(() => {
-		if (messagesContainer.value) {
-			messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
-		}
-	})
+  nextTick(() => {
+    if (messagesContainer.value) {
+      messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
+    }
+  })
 }
 
 // 会话管理模块
 // 所有会话
 const conversations = ref<LmSession[]>([])
-const { loading: loadConversations, doLoading: doLoadConversations } = useLoading(async () => {
-	const data: { list: LmSession[]; total: number } = await assist.session
-		.list({
-			pageNum: 1,
-			pageSize: 30,
-		})
-		.catch(() => {
-			return {
-				list: [],
-				total: 0,
-			}
-		})
-
-	conversations.value = data.list
+const {loading: loadConversations, doLoading: doLoadConversations} = useLoading(async () => {
+  const data: { list: LmSession[]; total: number } = await assist.session
+      .list({
+        pageNum: 1,
+        pageSize: 30,
+      })
+      .catch(() => {
+        return {
+          list: [],
+          total: 0,
+        }
+      })
+
+  conversations.value = [
+    {
+      session_id: -1,
+      title: '收藏对话'
+    },
+    ...data.list
+  ]
 })
 
 onMounted(doLoadConversations)
@@ -352,63 +358,79 @@ const activeConversationId = ref<number | undefined>(undefined)
 // 	await doLoadingMessage(newVal)
 // })
 
-const { loading: loadingMessage, doLoading: doLoadingMessage } = useLoading(async (id: number) => {
-	const data: {
-		messages: Message[]
-		total: number
-	} = await assist.session.message.list({ sessionId: id }).catch(() => {
-		return {
-			list: [],
-			total: 0,
-		}
-	})
-
-	messages.value = data.messages ?? []
+const {loading: loadingMessage, doLoading: doLoadingMessage} = useLoading(async (id: number) => {
+  //如果id是-1,拉取收藏的记录
+  if (id === -1) {
+    const data: {
+      messages: Message[]
+      total: number
+    } = await assist.session.message.bookmark_list().catch(() => {
+      return {
+        list: [],
+        total: 0,
+      }
+    })
+    messages.value = data.messages ?? []
+    return
+  }
+
+
+  const data: {
+    messages: Message[]
+    total: number
+  } = await assist.session.message.list({sessionId: id}).catch(() => {
+    return {
+      list: [],
+      total: 0,
+    }
+  })
+
+  messages.value = data.messages ?? []
 })
 
 // 选择会话
 const selectConversation = async (id: number) => {
-	if (activeConversationId.value === id) {
-		return
-	}
-	activeConversationId.value = id
-	await doLoadingMessage(id)
+  if (activeConversationId.value === id) {
+    return
+  }
+  activeConversationId.value = id
+  await doLoadingMessage(id)
 }
 
 // 删除会话
 const currentDeletingConversation = ref(-1)
-const { loading: loadingDeleteConversation, doLoading: deleteConversation } = useLoading(async (id: number) => {
-	currentDeletingConversation.value = id
-	const res = await assist.session
-		.del([id])
-		.then(() => true)
-		.catch(() => false)
-		.finally(() => (currentDeletingConversation.value = -1))
-	if (!res) {
-		return
-	}
-	ElMessage.success('删除成功')
-	activeConversationId.value = undefined
-	messages.value = []
-	await nextTick()
-	await doLoadConversations()
+const {loading: loadingDeleteConversation, doLoading: deleteConversation} = useLoading(async (id: number) => {
+  currentDeletingConversation.value = id
+  const res = await assist.session
+      .del([id])
+      .then(() => true)
+      .catch(() => false)
+      .finally(() => (currentDeletingConversation.value = -1))
+  if (!res) {
+    return
+  }
+  ElMessage.success('删除成功')
+  activeConversationId.value = undefined
+  messages.value = []
+  await nextTick()
+  await doLoadConversations()
 })
 
 // 创建新对话
-const { loading: creatingConversation, doLoading: createConversationAndSetItActive } = useLoading(async () => {
-	try {
-		// 调用API创建新对话,默认标题为"新对话"
-		const { id } = await assist.session.add('新对话')
-		// 刷新对话列表
-		await doLoadConversations()
-
-		await nextTick()
-		activeConversationId.value = id
-	} catch (error) {
-		console.error('创建对话失败:', error)
-		// 可以在这里添加错误提示
-		throw error
-	}
+const {loading: creatingConversation, doLoading: createConversationAndSetItActive} = useLoading(async () => {
+  try {
+    // 调用API创建新对话,默认标题为"新对话"
+    const {id} = await assist.session.add('新对话')
+    // 刷新对话列表
+    await doLoadConversations()
+
+    await nextTick()
+    activeConversationId.value = id
+  } catch (error) {
+    console.error('创建对话失败:', error)
+    // 可以在这里添加错误提示
+    throw error
+  }
 })
 
 // 编辑会话状态管理
@@ -416,58 +438,58 @@ const editingConversationId = ref<number | undefined>(undefined)
 const editingTitle = ref('')
 // 编辑会话摘要
 const editSummary = (id: number) => {
-	const conversation = conversations.value.find((conv) => conv.session_id === id)
-	if (conversation) {
-		// 设置当前编辑的会话ID
-		editingConversationId.value = id
-		editingTitle.value = conversation.title
-		// 下一帧聚焦到输入框
-		nextTick(() => {
-			const editInput = document.querySelector('.edit-input .el-input__inner') as HTMLInputElement
-			if (editInput) {
-				editInput.focus()
-				editInput.select()
-			}
-		})
-	}
+  const conversation = conversations.value.find((conv) => conv.session_id === id)
+  if (conversation) {
+    // 设置当前编辑的会话ID
+    editingConversationId.value = id
+    editingTitle.value = conversation.title
+    // 下一帧聚焦到输入框
+    nextTick(() => {
+      const editInput = document.querySelector('.edit-input .el-input__inner') as HTMLInputElement
+      if (editInput) {
+        editInput.focus()
+        editInput.select()
+      }
+    })
+  }
 }
 // 确认编辑
-const { doLoading: confirmEdit, loading: loadingConfirmEdit } = useLoading(async (id: number) => {
-	const conversation = conversations.value.find((conv) => conv.session_id === id)
-	if (conversation && editingTitle.value.trim()) {
-		const edit = await assist.session
-			.edit(id, editingTitle.value.trim())
-			.then(() => true)
-			.catch(() => false)
-		if (!edit) {
-			return
-		}
-		conversation.title = editingTitle.value.trim()
-		// 清除编辑状态
-		editingConversationId.value = undefined
-		editingTitle.value = ''
-	}
+const {doLoading: confirmEdit, loading: loadingConfirmEdit} = useLoading(async (id: number) => {
+  const conversation = conversations.value.find((conv) => conv.session_id === id)
+  if (conversation && editingTitle.value.trim()) {
+    const edit = await assist.session
+        .edit(id, editingTitle.value.trim())
+        .then(() => true)
+        .catch(() => false)
+    if (!edit) {
+      return
+    }
+    conversation.title = editingTitle.value.trim()
+    // 清除编辑状态
+    editingConversationId.value = undefined
+    editingTitle.value = ''
+  }
 })
 
 // 取消编辑
 const cancelEdit = () => {
-	// 清除编辑状态
-	editingConversationId.value = undefined
-	editingTitle.value = ''
+  // 清除编辑状态
+  editingConversationId.value = undefined
+  editingTitle.value = ''
 }
 
 onMounted(() => {
-	scrollToBottom()
+  scrollToBottom()
 })
 
 //杂项
 const getUserInfos = ref<{
-	avatar: string
-	userName: string
+  avatar: string
+  userName: string
 }>(Local.get('userInfo') || {})
 
 const canSendMessage = computed(() => {
-	return !inputMessage.value.trim() || loadingModels.value || loadConversations.value || loadingMessage.value
+  return !inputMessage.value.trim() || loadingModels.value || loadConversations.value || loadingMessage.value
 })
 
 const router = useRouter()
@@ -482,1040 +504,1040 @@ const activeTab = ref('history')
 
 // 收藏消息功能
 const favoriteMessageIdx = ref(-1)
-const { loading: loadingFavoriteMessage, doLoading: toggleFavorite } = useLoading(async (messageIndex: number) => {
-	favoriteMessageIdx.value = messageIndex
-	const data = messages.value[messageIndex]
-	const active = activeConversationId.value
-	if (active === undefined) {
-		return
-	}
-	const success = assist.session.message
-		.bookmark(active, data.id, !(data.like ?? false))
-		.then(() => true)
-		.catch(() => false)
-		.finally(() => (favoriteMessageIdx.value = -1))
-	if (!success) {
-		return
-	}
-
-	messages.value[messageIndex] = {
-		...data,
-		like: !(data.like ?? false),
-	}
+const {loading: loadingFavoriteMessage, doLoading: toggleFavorite} = useLoading(async (messageIndex: number) => {
+  favoriteMessageIdx.value = messageIndex
+  const data = messages.value[messageIndex]
+  //@ts-ignore
+  const active = activeConversationId.value === -1 ? data["session_id"] : (activeConversationId.value ?? undefined)
+  if (active === undefined) {
+    return
+  }
+  const success = assist.session.message
+      .bookmark(active, data.id, !(data.like ?? false))
+      .then(() => true)
+      .catch(() => false)
+      .finally(() => (favoriteMessageIdx.value = -1))
+  if (!success) {
+    return
+  }
+
+  messages.value[messageIndex] = {
+    ...data,
+    like: !(data.like ?? false),
+  }
 })
+
 </script>
 
 <template>
-	<el-container class="chat-container">
-		<!-- 左侧会话列表 -->
-		<el-aside width="300px" class="chat-sidebar">
-			<div class="sidebar-header">
-				<h3>对话历史</h3>
-				<el-dropdown v-model="showSettingsPanel" trigger="click" placement="bottom-end">
-					<el-button round :icon="EleSetting" size="small">
-						<el-icon class="el-icon--right">
-							<ArrowDown />
-						</el-icon>
-					</el-button>
-					<template #dropdown>
-						<el-dropdown-menu>
-							<el-dropdown-item class="settings-item">
-								<div class="settings-row">
-									<span class="settings-label">显示工具调用</span>
-									<el-switch v-model="showToolCalls" size="small" />
-								</div>
-							</el-dropdown-item>
-							<el-dropdown-item @click="redirectToModelManager">
-								<span class="settings-label">模型管理</span>
-							</el-dropdown-item>
-						</el-dropdown-menu>
-					</template>
-				</el-dropdown>
-			</div>
-			<el-scrollbar class="conversation-list" v-loading="loadConversations">
-				<div
-					v-for="conv in conversations"
-					:key="conv.session_id"
-					@click="editingConversationId !== conv.session_id ? selectConversation(conv.session_id) : () => {}"
-					:class="['conversation-item', { active: activeConversationId === conv.session_id, editing: editingConversationId === conv.session_id }]"
-				>
-					<!-- 非编辑状态 -->
-					<div v-if="editingConversationId !== conv.session_id" class="conversation-content">
-						<span class="conversation-title">{{ conv.title }}</span>
-					</div>
-
-					<!-- 编辑状态 -->
-					<div v-else class="conversation-edit-content">
-						<el-input
-							v-model="editingTitle"
-							size="small"
-							@keydown.enter="confirmEdit(conv.session_id)"
-							@keydown.esc="cancelEdit"
-							class="edit-input"
-						/>
-					</div>
-
-					<!-- 操作按钮 -->
-					<div class="conversation-actions">
-						<!-- 非编辑状态的按钮 -->
-						<template v-if="editingConversationId !== conv.session_id">
-							<el-button
-								type="primary"
-								size="small"
-								:icon="Edit"
-								@click.stop="editSummary(conv.session_id)"
-								class="action-btn edit-btn"
-								title="编辑摘要"
-								plain
-								circle
-							/>
-							<el-button
-								type="danger"
-								size="small"
-								:icon="Delete"
-								:loading="loadingDeleteConversation && currentDeletingConversation == conv.session_id"
-								@click.stop="deleteConversation(conv.session_id)"
-								class="action-btn delete-btn"
-								title="删除对话"
-								plain
-								circle
-								:disabled="isConversationActive"
-							/>
-						</template>
-
-						<!-- 编辑状态的按钮 -->
-						<template v-else>
-							<el-button
-								type="success"
-								size="small"
-								:icon="Check"
-								:loading="loadingConfirmEdit"
-								@click.stop="confirmEdit(conv.session_id)"
-								class="action-btn confirm-btn"
-								title="确认修改"
-								plain
-								circle
-							/>
-							<el-button
-								type="info"
-								size="small"
-								@click.stop="cancelEdit(conv.session_id)"
-								class="action-btn cancel-btn"
-								title="取消编辑"
-								plain
-								circle
-							>
-								<el-icon>
-									<Close />
-								</el-icon>
-							</el-button>
-						</template>
-					</div>
-				</div>
-			</el-scrollbar>
-			<el-button
-				type="primary"
-				size="large"
-				class="create-conversation-btn"
-				@click="createConversationAndSetItActive"
-				:loading="creatingConversation"
-				:disabled="isConversationActive"
-				>创建对话
-			</el-button>
-		</el-aside>
-
-		<!-- 右侧聊天区域 -->
-		<el-main class="chat-main">
-			<!-- 消息展示区域 -->
-			<div class="messages-container" ref="messagesContainer" v-loading="loadingMessage">
-				<div v-if="messages.length !== 0">
-					<div v-for="(message, idx) in messages" :key="message.id" :class="['message-wrapper', message.role]">
-						<!-- AI消息 -->
-						<div v-if="message.role === 'assistant'" class="ai-message-container">
-							<el-avatar class="message-avatar" :icon="ChatDotRound" />
-							<div class="ai-message-content">
-								<div class="message-bubble ai-bubble">
-									<Markdown v-if="message.render_content !== ''" :content="message.render_content" :plugins="plugins" class="markdown-content" />
-									<div v-else class="loading-container">
-										<div class="loading-dots">
-											<span class="dot"></span>
-											<span class="dot"></span>
-											<span class="dot"></span>
-										</div>
-										<span class="loading-text">AI正在思考中...</span>
-									</div>
-								</div>
-								<div class="ai-message-actions">
-									<el-button
-										:loading="favoriteMessageIdx == idx && loadingFavoriteMessage"
-										@click="toggleFavorite(idx)"
-										class="favorite-btn"
-										:class="{ 'favorited': message.like ?? false }"
-										:disabled="isConversationActive"
-										text
-										circle
-									>
-										<el-icon>
-											<svg viewBox="0 0 1024 1024" width="16" height="16">
-												<path
-													:fill="(message.like ?? false) ? '#ff4757' : 'currentColor'"
-													d="M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.5-42.4-84-55.5-32.5-13.5-66.9-20.3-102.4-20.3-49.3 0-97.4 13.5-139.2 39-10 6.1-19.5 12.8-28.5 20.1-9-7.3-18.5-14-28.5-20.1-41.8-25.5-89.9-39-139.2-39-35.5 0-69.9 6.8-102.4 20.3-31.4 13-59.7 31.7-84 55.5-24.4 23.9-43.5 51.7-56.9 82.8-13.9 32.3-21 66.6-21 101.9 0 33.3 6.8 68 20.3 103.3 11.3 29.5 27.5 60.1 48.2 91 32.8 48.9 77.9 99.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.7 24 6.7 34.5 0l23.7-15.2c3.9-2.5 95.7-61.6 188.6-147.3 56-51.7 101.1-102.7 133.9-151.6 20.7-30.9 37-61.5 48.2-91 13.5-35.3 20.3-70 20.3-103.3.1-35.3-7-69.6-20.9-101.9z"
-												/>
-											</svg>
-										</el-icon>
-									</el-button>
-								</div>
-							</div>
-						</div>
-
-						<!-- 用户消息 -->
-						<div v-if="message.role === 'user'" class="user-message-container">
-							<div class="user-message-content">
-								<div class="message-bubble user-bubble">
-									{{ message.render_content }}
-								</div>
-								<div class="user-message-actions">
-									<el-button
-										type="primary"
-										size="small"
-										@click="replaceMessage(messages.indexOf(message))"
-										class="retry-btn"
-										plain
-										:disabled="isConversationActive"
-									>
-										重试
-									</el-button>
-								</div>
-							</div>
-							<el-avatar class="message-avatar" :src="getUserInfos.avatar" :icon="User" />
-						</div>
-					</div>
-				</div>
-
-				<!-- 空状态页面 -->
-
-				<div v-else class="empty-content">
-					<!-- 主图标 -->
-					<div class="empty-icon">
-						<el-icon :size="80" color="#d1d5db">
-							<ChatDotRound />
-						</el-icon>
-					</div>
-
-					<!-- 标题和描述 -->
-					<div class="empty-text">
-						<h2 class="empty-title">开始新的对话</h2>
-					</div>
-					<!-- 示例问题 -->
-					<div class="example-questions">
-						<h4>试试这些问题:</h4>
-						<div class="question-tags">
-							<el-tag class="question-tag" @click="inputMessage = '帮我查看设备运行状态和告警信息'" type="info">
-								帮我查看设备运行状态和告警信息
-							</el-tag>
-							<el-tag class="question-tag" @click="inputMessage = '分析用户权限配置和角色分配情况'" type="success">
-								分析用户权限配置和角色分配情况
-							</el-tag>
-							<el-tag class="question-tag" @click="inputMessage = '检查系统性能和在线用户统计'" type="warning"> 检查系统性能和在线用户统计 </el-tag>
-						</div>
-					</div>
-				</div>
-
-				<div class="messages-spacer"></div>
-			</div>
-
-			<div class="input-container">
-				<!-- 工具和模型选择行 -->
-				<div class="selection-row">
-					<!-- 工具选择 -->
-					<div class="tool-selector">
-						<el-cascader
-							v-model="selectedTool"
-							:options="toolOptions"
-							:show-all-levels="false"
-							:props="{ multiple: true }"
-							collapse-tags
-							collapse-tags-tooltip
-							clearable
-							size="small"
-							style="width: 200px"
-						/>
-					</div>
-
-					<!-- 模型选择 -->
-					<div class="model-selector">
-						<el-select :loading="loadingModels" v-model="selectedModel" placeholder="选择模型" size="small" style="width: 200px">
-							<el-option v-for="item in modelOptions" :key="item.id" :value="item.id" :label="item.modelName" />
-						</el-select>
-					</div>
-
-					<!-- 提示词输入框 -->
-					<div class="prompt-selector">
-						<el-input v-model="prompt" placeholder="点击设置提示词..." size="small" style="width: 200px" readonly @click="openPromptDialog = true" />
-					</div>
-				</div>
-
-				<!-- 输入框和按钮行 -->
-				<div class="input-row">
-					<!-- 输入框 -->
-					<div class="message-input-wrapper">
-						<el-input
-							v-model="inputMessage"
-							type="textarea"
-							placeholder="请输入您的问题..."
-							:rows="2"
-							resize="none"
-							@keydown.enter.ctrl="sendMessage"
-							@keydown.enter.meta="sendMessage"
-							:disabled="isConversationActive"
-						/>
-					</div>
-
-					<!-- 按钮组 -->
-					<div class="button-group">
-						<el-button
-							v-show="messages.length !== 0"
-							type="warning"
-							size="small"
-							@click="clearMessage"
-							style="margin-left: 12px"
-							:loading="loadingClearMessage"
-						>
-							清空
-						</el-button>
-						<el-button
-							v-if="!isConversationActive"
-							type="primary"
-							size="small"
-							@click="sendMessage"
-							@keyup.ctrl.enter="sendMessage"
-							:disabled="canSendMessage"
-						>
-							发送
-						</el-button>
-						<el-button v-else type="danger" size="small" @click="stopConversation"> 终止</el-button>
-					</div>
-				</div>
-			</div>
-		</el-main>
-
-		<!-- 提示词设置对话框 -->
-		<el-dialog
-			v-model="openPromptDialog"
-			title="设置提示词"
-			width="600px"
-			:before-close="
+  <el-container class="chat-container">
+    <!-- 左侧会话列表 -->
+    <el-aside width="300px" class="chat-sidebar">
+      <div class="sidebar-header">
+        <h3>对话历史</h3>
+        <el-dropdown v-model="showSettingsPanel" trigger="click" placement="bottom-end">
+          <el-button round :icon="EleSetting" size="small">
+            <el-icon class="el-icon--right">
+              <ArrowDown/>
+            </el-icon>
+          </el-button>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item class="settings-item">
+                <div class="settings-row">
+                  <span class="settings-label">显示工具调用</span>
+                  <el-switch v-model="showToolCalls" size="small"/>
+                </div>
+              </el-dropdown-item>
+              <el-dropdown-item @click="redirectToModelManager">
+                <span class="settings-label">模型管理</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <el-scrollbar class="conversation-list" v-loading="loadConversations">
+        <div
+            v-for="conv in conversations"
+            :key="conv.session_id"
+            @click="editingConversationId !== conv.session_id ? selectConversation(conv.session_id) : () => {}"
+            :class="['conversation-item', { active: activeConversationId === conv.session_id, editing: editingConversationId === conv.session_id }]"
+        >
+          <!-- 非编辑状态 -->
+          <div v-if="editingConversationId !== conv.session_id" class="conversation-content">
+            <span class="conversation-title">{{ conv.title }}</span>
+          </div>
+
+          <!-- 编辑状态 -->
+          <div v-else class="conversation-edit-content">
+            <el-input
+                v-model="editingTitle"
+                size="small"
+                @keydown.enter="confirmEdit(conv.session_id)"
+                @keydown.esc="cancelEdit"
+                class="edit-input"
+            />
+          </div>
+
+          <!-- 操作按钮 -->
+          <div class="conversation-actions" v-if="conv.session_id !== -1">
+            <!-- 非编辑状态的按钮 -->
+            <template v-if="editingConversationId !== conv.session_id">
+              <el-button
+                  type="primary"
+                  size="small"
+                  :icon="Edit"
+                  @click.stop="editSummary(conv.session_id)"
+                  class="action-btn edit-btn"
+                  title="编辑摘要"
+                  plain
+                  circle
+              />
+              <el-button
+                  type="danger"
+                  size="small"
+                  :icon="Delete"
+                  :loading="loadingDeleteConversation && currentDeletingConversation == conv.session_id"
+                  @click.stop="deleteConversation(conv.session_id)"
+                  class="action-btn delete-btn"
+                  title="删除对话"
+                  plain
+                  circle
+                  :disabled="isConversationActive"
+              />
+            </template>
+
+            <!-- 编辑状态的按钮 -->
+            <template v-else>
+              <el-button
+                  type="success"
+                  size="small"
+                  :icon="Check"
+                  :loading="loadingConfirmEdit"
+                  @click.stop="confirmEdit(conv.session_id)"
+                  class="action-btn confirm-btn"
+                  title="确认修改"
+                  plain
+                  circle
+              />
+              <el-button
+                  type="info"
+                  size="small"
+                  @click.stop="cancelEdit(conv.session_id)"
+                  class="action-btn cancel-btn"
+                  title="取消编辑"
+                  plain
+                  circle
+              >
+                <el-icon>
+                  <Close/>
+                </el-icon>
+              </el-button>
+            </template>
+          </div>
+        </div>
+      </el-scrollbar>
+      <el-button
+          type="primary"
+          size="large"
+          class="create-conversation-btn"
+          @click="createConversationAndSetItActive"
+          :loading="creatingConversation"
+          :disabled="isConversationActive"
+      >创建对话
+      </el-button>
+    </el-aside>
+
+    <!-- 右侧聊天区域 -->
+    <el-main class="chat-main">
+      <!-- 消息展示区域 -->
+      <div class="messages-container" ref="messagesContainer" v-loading="loadingMessage">
+        <div v-if="messages.length !== 0">
+          <div v-for="(message, idx) in messages" :key="message.id" :class="['message-wrapper', message.role]">
+            <!-- AI消息 -->
+            <div v-if="message.role === 'assistant'" class="ai-message-container">
+              <el-avatar class="message-avatar" :icon="ChatDotRound"/>
+              <div class="ai-message-content">
+                <div class="message-bubble ai-bubble">
+                  <Markdown v-if="message.render_content !== ''" :content="message.render_content" :plugins="plugins"
+                            class="markdown-content"/>
+                  <div v-else class="loading-container">
+                    <div class="loading-dots">
+                      <span class="dot"></span>
+                      <span class="dot"></span>
+                      <span class="dot"></span>
+                    </div>
+                    <span class="loading-text">AI正在思考中...</span>
+                  </div>
+                </div>
+                <div class="ai-message-actions">
+                  <el-button
+                      :loading="favoriteMessageIdx === idx && loadingFavoriteMessage"
+                      @click="toggleFavorite(idx)"
+                      class="favorite-btn"
+                      :class="{ 'favorited': message.like ?? false }"
+                      :disabled="isConversationActive"
+                      :icon="(message.like ?? false) ? StarFilled : Star"
+                      plain
+                      circle
+                  >
+                  </el-button>
+                </div>
+              </div>
+            </div>
+
+            <!-- 用户消息 -->
+            <div v-if="message.role === 'user'" class="user-message-container">
+              <div class="user-message-content">
+                <div class="message-bubble user-bubble">
+                  {{ message.render_content }}
+                </div>
+                <div class="user-message-actions">
+                  <el-button
+                      type="primary"
+                      size="small"
+                      @click="replaceMessage(messages.indexOf(message))"
+                      class="retry-btn"
+                      plain
+                      :disabled="isConversationActive"
+                  >
+                    重试
+                  </el-button>
+                </div>
+              </div>
+              <el-avatar class="message-avatar" :src="getUserInfos.avatar" :icon="User"/>
+            </div>
+          </div>
+        </div>
+
+        <!-- 空状态页面 -->
+
+        <div v-else class="empty-content">
+          <!-- 主图标 -->
+          <div class="empty-icon">
+            <el-icon :size="80" color="#d1d5db">
+              <ChatDotRound/>
+            </el-icon>
+          </div>
+
+          <!-- 标题和描述 -->
+          <div class="empty-text">
+            <h2 class="empty-title">开始新的对话</h2>
+          </div>
+          <!-- 示例问题 -->
+          <div class="example-questions">
+            <h4>试试这些问题:</h4>
+            <div class="question-tags">
+              <el-tag class="question-tag" @click="inputMessage = '帮我查看设备运行状态和告警信息'" type="info">
+                帮我查看设备运行状态和告警信息
+              </el-tag>
+              <el-tag class="question-tag" @click="inputMessage = '分析用户权限配置和角色分配情况'" type="success">
+                分析用户权限配置和角色分配情况
+              </el-tag>
+              <el-tag class="question-tag" @click="inputMessage = '检查系统性能和在线用户统计'" type="warning">
+                检查系统性能和在线用户统计
+              </el-tag>
+            </div>
+          </div>
+        </div>
+
+        <div class="messages-spacer"></div>
+      </div>
+
+      <div class="input-container"  v-show="activeConversationId !== -1">
+        <!-- 工具和模型选择行 -->
+        <div class="selection-row">
+          <!-- 工具选择 -->
+          <div class="tool-selector">
+            <el-cascader
+                v-model="selectedTool"
+                :options="toolOptions"
+                :show-all-levels="false"
+                :props="{ multiple: true }"
+                collapse-tags
+                collapse-tags-tooltip
+                clearable
+                size="small"
+                style="width: 200px"
+            />
+          </div>
+
+          <!-- 模型选择 -->
+          <div class="model-selector">
+            <el-select :loading="loadingModels" v-model="selectedModel" placeholder="选择模型" size="small"
+                       style="width: 200px">
+              <el-option v-for="item in modelOptions" :key="item.id" :value="item.id" :label="item.modelName"/>
+            </el-select>
+          </div>
+
+          <!-- 提示词输入框 -->
+          <div class="prompt-selector">
+            <el-input v-model="prompt" placeholder="点击设置提示词..." size="small" style="width: 200px" readonly
+                      @click="openPromptDialog = true"/>
+          </div>
+        </div>
+
+        <!-- 输入框和按钮行 -->
+        <div class="input-row">
+          <!-- 输入框 -->
+          <div class="message-input-wrapper">
+            <el-input
+                v-model="inputMessage"
+                type="textarea"
+                placeholder="请输入您的问题..."
+                :rows="2"
+                resize="none"
+                @keydown.enter.ctrl="sendMessage"
+                @keydown.enter.meta="sendMessage"
+                :disabled="isConversationActive"
+            />
+          </div>
+
+          <!-- 按钮组 -->
+          <div class="button-group">
+            <el-button
+                v-show="messages.length !== 0"
+                type="warning"
+                size="small"
+                @click="clearMessage"
+                style="margin-left: 12px"
+                :loading="loadingClearMessage"
+            >
+              清空
+            </el-button>
+            <el-button
+                v-if="!isConversationActive"
+                type="primary"
+                size="small"
+                @click="sendMessage"
+                @keyup.ctrl.enter="sendMessage"
+                :disabled="canSendMessage"
+            >
+              发送
+            </el-button>
+            <el-button v-else type="danger" size="small" @click="stopConversation"> 终止</el-button>
+          </div>
+        </div>
+      </div>
+    </el-main>
+
+    <!-- 提示词设置对话框 -->
+    <el-dialog
+        v-model="openPromptDialog"
+        title="设置提示词"
+        width="600px"
+        :before-close="
 				() => {
 					openPromptDialog = false
 				}
 			"
-		>
-			<el-input v-model="prompt" type="textarea" placeholder="请输入提示词..." :rows="8" resize="none" />
-			<template #footer>
-				<div class="dialog-footer">
-					<el-button @click="openPromptDialog = false">取消</el-button>
-					<el-button type="primary" @click="openPromptDialog = false">确定</el-button>
-				</div>
-			</template>
-		</el-dialog>
-	</el-container>
+    >
+      <el-input v-model="prompt" type="textarea" placeholder="请输入提示词..." :rows="8" resize="none"/>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="openPromptDialog = false">取消</el-button>
+          <el-button type="primary" @click="openPromptDialog = false">确定</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </el-container>
 </template>
 
 <style scoped lang="scss">
 :deep(.el-icon) {
-	margin-right: 0 !important;
+  margin-right: 0 !important;
 }
 
 .chat-container {
-	height: 100%;
-	background: var(--el-bg-color-page);
+  height: 100%;
+  background: var(--el-bg-color-page);
 }
 
 .create-conversation-btn {
-	margin: 16px;
+  margin: 16px;
 }
 
 /* 左侧边栏样式 */
 .chat-sidebar {
-	background: var(--el-bg-color);
-	border-right: 1px solid var(--el-border-color-light);
-	display: flex;
-	flex-direction: column;
+  background: var(--el-bg-color);
+  border-right: 1px solid var(--el-border-color-light);
+  display: flex;
+  flex-direction: column;
 }
 
 .sidebar-header {
-	padding: 20px;
-	border-bottom: 1px solid var(--el-border-color-light);
-	display: flex;
-	justify-content: space-between;
-	align-items: center;
+  padding: 20px;
+  border-bottom: 1px solid var(--el-border-color-light);
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
 
-	h3 {
-		margin: 0;
-		color: var(--el-text-color-primary);
-		font-size: 16px;
-		font-weight: 600;
-	}
+  h3 {
+    margin: 0;
+    color: var(--el-text-color-primary);
+    font-size: 16px;
+    font-weight: 600;
+  }
 }
 
 /* 设置面板样式 */
 :deep(.el-dropdown-menu) {
-	.settings-item {
-		padding: 0;
+  .settings-item {
+    padding: 0;
 
-		&:hover {
-			background: none;
-		}
-	}
+    &:hover {
+      background: none;
+    }
+  }
 
-	.settings-row {
-		display: flex;
-		align-items: center;
-		justify-content: space-between;
-		padding: 8px 16px;
-		min-width: 160px;
+  .settings-row {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 8px 16px;
+    min-width: 160px;
 
-		&:hover {
-			background: var(--el-fill-color-light);
-		}
-	}
+    &:hover {
+      background: var(--el-fill-color-light);
+    }
+  }
 
-	.settings-label {
-		font-size: 14px;
-		color: var(--el-text-color-primary);
-		flex: 1;
-	}
+  .settings-label {
+    font-size: 14px;
+    color: var(--el-text-color-primary);
+    flex: 1;
+  }
 }
 
 .conversation-list {
-	flex: 1;
-	padding: 10px;
+  flex: 1;
+  padding: 10px;
 }
 
 .conversation-item {
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	padding: 12px 16px;
-	margin-bottom: 8px;
-	border-radius: 8px;
-	transition: all 0.2s;
-	color: var(--el-text-color-regular);
-	border: 1px solid transparent;
-	position: relative;
-	cursor: pointer;
-
-	&:hover {
-		background: var(--el-fill-color-light);
-		color: var(--el-text-color-primary);
-
-		.conversation-actions {
-			opacity: 1;
-		}
-	}
-
-	&.active {
-		background: var(--el-color-primary-light-9);
-		color: var(--el-color-primary);
-		border-color: var(--el-color-primary-light-7);
-
-		.conversation-actions {
-			opacity: 1;
-		}
-	}
-
-	&.editing {
-		background: var(--el-color-warning-light-9);
-		border-color: var(--el-color-warning-light-7);
-
-		.conversation-actions {
-			opacity: 1;
-		}
-	}
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 16px;
+  margin-bottom: 8px;
+  border-radius: 8px;
+  transition: all 0.2s;
+  color: var(--el-text-color-regular);
+  border: 1px solid transparent;
+  position: relative;
+  cursor: pointer;
+
+  &:hover {
+    background: var(--el-fill-color-light);
+    color: var(--el-text-color-primary);
+
+    .conversation-actions {
+      opacity: 1;
+    }
+  }
+
+  &.active {
+    background: var(--el-color-primary-light-9);
+    color: var(--el-color-primary);
+    border-color: var(--el-color-primary-light-7);
+
+    .conversation-actions {
+      opacity: 1;
+    }
+  }
+
+  &.editing {
+    background: var(--el-color-warning-light-9);
+    border-color: var(--el-color-warning-light-7);
+
+    .conversation-actions {
+      opacity: 1;
+    }
+  }
 }
 
 .conversation-content {
-	flex: 1;
-	min-width: 0;
-	cursor: pointer;
+  flex: 1;
+  min-width: 0;
+  cursor: pointer;
 }
 
 .conversation-edit-content {
-	flex: 1;
-	min-width: 0;
+  flex: 1;
+  min-width: 0;
 }
 
 .edit-input {
-	width: 100%;
+  width: 100%;
 
-	:deep(.el-input__inner) {
-		font-size: 14px;
-		padding: 4px 8px;
-		height: 28px;
-		line-height: 20px;
-	}
+  :deep(.el-input__inner) {
+    font-size: 14px;
+    padding: 4px 8px;
+    height: 28px;
+    line-height: 20px;
+  }
 }
 
 .conversation-title {
-	display: block;
-	overflow: hidden;
-	text-overflow: ellipsis;
-	white-space: nowrap;
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
 .conversation-actions {
-	display: flex;
-	align-items: center;
-	gap: 4px;
-	opacity: 0;
-	transition: opacity 0.2s;
-	flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  opacity: 0;
+  transition: opacity 0.2s;
+  flex-shrink: 0;
 }
 
 .action-btn {
-	width: 28px !important;
-	height: 28px !important;
-	padding: 0 !important;
-	margin: 0 2px;
+  width: 28px !important;
+  height: 28px !important;
+  padding: 0 !important;
+  margin: 0 2px;
 }
 
 /* 右侧聊天区域样式 */
 .chat-main {
-	display: flex;
-	flex-direction: column;
-	padding: 0;
-	background: var(--el-bg-color-page);
+  display: flex;
+  flex-direction: column;
+  padding: 0;
+  background: var(--el-bg-color-page);
 
-	position: relative;
+  position: relative;
 }
 
 .messages-container {
-	flex: 1;
-	padding: 20px;
-	overflow-y: auto;
+  flex: 1;
+  padding: 20px;
+  overflow-y: auto;
 }
 
 .messages-spacer {
-	height: 160px;
+  height: 160px;
 }
 
 .message-wrapper {
-	margin-bottom: 20px;
+  margin-bottom: 20px;
 
-	&:last-child {
-		margin-bottom: 0;
-	}
+  &:last-child {
+    margin-bottom: 0;
+  }
 }
 
 /* AI消息样式 */
 .ai-message-container {
-	display: flex;
-	align-items: flex-start;
-	gap: 12px;
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
 }
 
 .ai-message-content {
-	display: flex;
-	flex-direction: column;
-	align-items: flex-start;
-	gap: 8px;
-	width: 70%;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  gap: 8px;
+  width: 70%;
 }
 
 .ai-message-actions {
-	display: flex;
-	justify-content: flex-start;
-	transition: opacity 0.2s ease;
+  display: flex;
+  justify-content: flex-start;
+  transition: opacity 0.2s ease;
 }
 
 .favorite-btn {
-	width: 32px !important;
-	height: 32px !important;
-	padding: 0 !important;
-	border: none !important;
-	background: transparent !important;
-	color: var(--el-text-color-regular);
-	transition: all 0.2s ease;
+  width: 32px !important;
+  height: 32px !important;
+  padding: 0 !important;
+  border: none !important;
+  background: transparent !important;
+  color: var(--el-text-color-regular);
+  transition: all 0.2s ease;
 
-	&:hover {
-		background: var(--el-fill-color-light) !important;
-	}
+  &:hover {
+    background: var(--el-fill-color-light) !important;
+  }
 
-	&.favorited {
-		color: #ff4757;
-	}
+  &.favorited {
+    color: #ff4757;
+  }
 
-	.el-icon {
-		font-size: 16px;
-	}
+  .el-icon {
+    font-size: 16px;
+  }
 }
 
 .ai-bubble {
-	background: var(--el-fill-color-light);
-	color: var(--el-text-color-primary);
-	position: relative;
-	max-width: 100%;
-	border: 1px solid var(--el-border-color-lighter);
-
-	&::before {
-		content: '';
-		position: absolute;
-		left: -8px;
-		top: 12px;
-		width: 0;
-		height: 0;
-		border-right: 8px solid var(--el-fill-color-light);
-		border-top: 8px solid transparent;
-		border-bottom: 8px solid transparent;
-	}
+  background: var(--el-fill-color-light);
+  color: var(--el-text-color-primary);
+  position: relative;
+  max-width: 100%;
+  border: 1px solid var(--el-border-color-lighter);
+
+  &::before {
+    content: '';
+    position: absolute;
+    left: -8px;
+    top: 12px;
+    width: 0;
+    height: 0;
+    border-right: 8px solid var(--el-fill-color-light);
+    border-top: 8px solid transparent;
+    border-bottom: 8px solid transparent;
+  }
 }
 
 /* 用户消息样式 */
 .user-message-container {
-	display: flex;
-	align-items: flex-start;
-	gap: 12px;
-	justify-content: flex-end;
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+  justify-content: flex-end;
 }
 
 .user-message-content {
-	display: flex;
-	flex-direction: column;
-	align-items: flex-end;
-	gap: 8px;
-	width: 70%;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+  gap: 8px;
+  width: 70%;
 }
 
 .user-message-actions {
-	display: flex;
-	justify-content: flex-end;
-	opacity: 0;
-	transition: opacity 0.2s ease;
+  display: flex;
+  justify-content: flex-end;
+  opacity: 0;
+  transition: opacity 0.2s ease;
 }
 
 .user-message-container:hover .user-message-actions {
-	opacity: 1;
+  opacity: 1;
 }
 
 .retry-btn {
-	font-size: 12px;
-	padding: 4px 12px;
-	height: 24px;
-	border-radius: 12px;
+  font-size: 12px;
+  padding: 4px 12px;
+  height: 24px;
+  border-radius: 12px;
 }
 
 .user-bubble {
-	background: var(--el-color-primary);
-	color: white;
-	position: relative;
-	max-width: 100%;
-
-	&::after {
-		content: '';
-		position: absolute;
-		right: -8px;
-		top: 12px;
-		width: 0;
-		height: 0;
-		border-left: 8px solid var(--el-color-primary);
-		border-top: 8px solid transparent;
-		border-bottom: 8px solid transparent;
-	}
+  background: var(--el-color-primary);
+  color: white;
+  position: relative;
+  max-width: 100%;
+
+  &::after {
+    content: '';
+    position: absolute;
+    right: -8px;
+    top: 12px;
+    width: 0;
+    height: 0;
+    border-left: 8px solid var(--el-color-primary);
+    border-top: 8px solid transparent;
+    border-bottom: 8px solid transparent;
+  }
 }
 
 /* 消息气泡通用样式 */
 .message-bubble {
-	padding: 12px 16px;
-	border-radius: 12px;
-	line-height: 1.5;
-	word-wrap: break-word;
-	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+  padding: 12px 16px;
+  border-radius: 12px;
+  line-height: 1.5;
+  word-wrap: break-word;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
 }
 
 .message-avatar {
-	flex-shrink: 0;
-	margin-top: 2px;
+  flex-shrink: 0;
+  margin-top: 2px;
 }
 
 /* 加载动画样式 */
 .loading-container {
-	display: flex;
-	align-items: center;
-	gap: 12px;
-	padding: 8px 0;
-	color: var(--el-text-color-secondary);
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  padding: 8px 0;
+  color: var(--el-text-color-secondary);
 }
 
 .loading-dots {
-	display: flex;
-	gap: 4px;
+  display: flex;
+  gap: 4px;
 }
 
 .dot {
-	width: 6px;
-	height: 6px;
-	border-radius: 50%;
-	background-color: var(--el-color-primary);
-	animation: loading-bounce 1.4s ease-in-out infinite both;
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  background-color: var(--el-color-primary);
+  animation: loading-bounce 1.4s ease-in-out infinite both;
 }
 
 .dot:nth-child(1) {
-	animation-delay: -0.32s;
+  animation-delay: -0.32s;
 }
 
 .dot:nth-child(2) {
-	animation-delay: -0.16s;
+  animation-delay: -0.16s;
 }
 
 .dot:nth-child(3) {
-	animation-delay: 0s;
+  animation-delay: 0s;
 }
 
 @keyframes loading-bounce {
-	0%,
-	80%,
-	100% {
-		transform: scale(0.8);
-		opacity: 0.5;
-	}
-	40% {
-		transform: scale(1);
-		opacity: 1;
-	}
+  0%,
+  80%,
+  100% {
+    transform: scale(0.8);
+    opacity: 0.5;
+  }
+  40% {
+    transform: scale(1);
+    opacity: 1;
+  }
 }
 
 .loading-text {
-	font-size: 14px;
-	font-weight: 500;
+  font-size: 14px;
+  font-weight: 500;
 }
 
 .tools-button {
-	margin-bottom: 10px;
-	color: var(--el-text-color-regular);
+  margin-bottom: 10px;
+  color: var(--el-text-color-regular);
 }
 
 .message-input {
-	margin-bottom: 12px;
+  margin-bottom: 12px;
 
-	:deep(.el-textarea__inner) {
-		border-radius: 8px;
-		resize: none;
-	}
+  :deep(.el-textarea__inner) {
+    border-radius: 8px;
+    resize: none;
+  }
 }
 
 .input-actions {
-	display: flex;
-	justify-content: flex-end;
-	gap: 12px;
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
 }
 
 /* Markdown 内容样式优化 - 接入Element Plus颜色系统 */
 :deep(.markdown-content) {
-	/* 标题样式 */
-	h1,
-	h2,
-	h3,
-	h4,
-	h5,
-	h6 {
-		margin-top: 24px;
-		margin-bottom: 16px;
-		font-weight: 600;
-		line-height: 1.25;
-		color: var(--el-text-color-primary);
-	}
-
-	h1 {
-		font-size: 2em;
-		border-bottom: 1px solid var(--el-border-color-light);
-		padding-bottom: 8px;
-	}
-
-	h2 {
-		font-size: 1.5em;
-		border-bottom: 1px solid var(--el-border-color-lighter);
-		padding-bottom: 6px;
-	}
-
-	h3 {
-		font-size: 1.25em;
-	}
-
-	/* 段落样式 */
-	p {
-		margin-bottom: 16px;
-		line-height: 1.6;
-		color: var(--el-text-color-regular);
-	}
-
-	/* 代码块样式 */
-	pre {
-		background-color: var(--el-fill-color-light);
-		border: 1px solid var(--el-border-color-light);
-		border-radius: 6px;
-		padding: 16px;
-		overflow: auto;
-		margin: 16px 0;
-		color: var(--el-text-color-primary);
-	}
-
-	/* 行内代码样式 */
-	code {
-		background-color: var(--el-fill-color-light);
-		color: var(--el-color-danger);
-		padding: 2px 4px;
-		border-radius: 3px;
-		font-size: 85%;
-		border: 1px solid var(--el-border-color-lighter);
-	}
-
-	/* 引用块样式 */
-	blockquote {
-		border-left: 4px solid var(--el-color-primary);
-		padding-left: 16px;
-		margin: 16px 0;
-		color: var(--el-text-color-secondary);
-		background-color: var(--el-fill-color-extra-light);
-		padding: 12px 16px;
-		border-radius: 4px;
-	}
-
-	/* 列表样式 */
-	ul,
-	ol {
-		margin: 16px 0;
-		padding-left: 32px;
-		color: var(--el-text-color-regular);
-	}
-
-	li {
-		margin: 4px 0;
-		line-height: 1.6;
-	}
-
-	/* 表格样式 */
-	table {
-		border-collapse: collapse;
-		margin: 16px 0;
-		width: 100%;
-		border: 1px solid var(--el-border-color-light);
-	}
-
-	th,
-	td {
-		border: 1px solid var(--el-border-color-light);
-		padding: 8px 12px;
-		text-align: left;
-	}
-
-	th {
-		background-color: var(--el-fill-color-light);
-		font-weight: 600;
-		color: var(--el-text-color-primary);
-	}
-
-	td {
-		color: var(--el-text-color-regular);
-	}
-
-	/* 分割线样式 */
-	hr {
-		border: none;
-		border-top: 1px solid var(--el-border-color-light);
-		margin: 24px 0;
-	}
-
-	/* 链接样式 */
-	a {
-		color: var(--el-color-primary);
-		text-decoration: none;
-
-		&:hover {
-			color: var(--el-color-primary-light-3);
-			text-decoration: underline;
-		}
-	}
-
-	/* 强调文本样式 */
-	strong {
-		font-weight: 600;
-		color: var(--el-text-color-primary);
-	}
-
-	em {
-		font-style: italic;
-		color: var(--el-text-color-regular);
-	}
+  /* 标题样式 */
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
+    margin-top: 24px;
+    margin-bottom: 16px;
+    font-weight: 600;
+    line-height: 1.25;
+    color: var(--el-text-color-primary);
+  }
+
+  h1 {
+    font-size: 2em;
+    border-bottom: 1px solid var(--el-border-color-light);
+    padding-bottom: 8px;
+  }
+
+  h2 {
+    font-size: 1.5em;
+    border-bottom: 1px solid var(--el-border-color-lighter);
+    padding-bottom: 6px;
+  }
+
+  h3 {
+    font-size: 1.25em;
+  }
+
+  /* 段落样式 */
+  p {
+    margin-bottom: 16px;
+    line-height: 1.6;
+    color: var(--el-text-color-regular);
+  }
+
+  /* 代码块样式 */
+  pre {
+    background-color: var(--el-fill-color-light);
+    border: 1px solid var(--el-border-color-light);
+    border-radius: 6px;
+    padding: 16px;
+    overflow: auto;
+    margin: 16px 0;
+    color: var(--el-text-color-primary);
+  }
+
+  /* 行内代码样式 */
+  code {
+    background-color: var(--el-fill-color-light);
+    color: var(--el-color-danger);
+    padding: 2px 4px;
+    border-radius: 3px;
+    font-size: 85%;
+    border: 1px solid var(--el-border-color-lighter);
+  }
+
+  /* 引用块样式 */
+  blockquote {
+    border-left: 4px solid var(--el-color-primary);
+    padding-left: 16px;
+    margin: 16px 0;
+    color: var(--el-text-color-secondary);
+    background-color: var(--el-fill-color-extra-light);
+    padding: 12px 16px;
+    border-radius: 4px;
+  }
+
+  /* 列表样式 */
+  ul,
+  ol {
+    margin: 16px 0;
+    padding-left: 32px;
+    color: var(--el-text-color-regular);
+  }
+
+  li {
+    margin: 4px 0;
+    line-height: 1.6;
+  }
+
+  /* 表格样式 */
+  table {
+    border-collapse: collapse;
+    margin: 16px 0;
+    width: 100%;
+    border: 1px solid var(--el-border-color-light);
+  }
+
+  th,
+  td {
+    border: 1px solid var(--el-border-color-light);
+    padding: 8px 12px;
+    text-align: left;
+  }
+
+  th {
+    background-color: var(--el-fill-color-light);
+    font-weight: 600;
+    color: var(--el-text-color-primary);
+  }
+
+  td {
+    color: var(--el-text-color-regular);
+  }
+
+  /* 分割线样式 */
+  hr {
+    border: none;
+    border-top: 1px solid var(--el-border-color-light);
+    margin: 24px 0;
+  }
+
+  /* 链接样式 */
+  a {
+    color: var(--el-color-primary);
+    text-decoration: none;
+
+    &:hover {
+      color: var(--el-color-primary-light-3);
+      text-decoration: underline;
+    }
+  }
+
+  /* 强调文本样式 */
+  strong {
+    font-weight: 600;
+    color: var(--el-text-color-primary);
+  }
+
+  em {
+    font-style: italic;
+    color: var(--el-text-color-regular);
+  }
 }
 
 .input-container {
-	position: absolute;
-	width: 90%;
-	left: 50%;
-	bottom: 25px;
-	transform: translate(-50%, 0);
-	background: var(--el-bg-color);
-	border: 1px solid var(--el-border-color-light);
-	border-radius: 12px;
-	padding: 16px;
-	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-	display: flex;
-	flex-direction: column;
-	gap: 12px;
+  position: absolute;
+  width: 90%;
+  left: 50%;
+  bottom: 25px;
+  transform: translate(-50%, 0);
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-light);
+  border-radius: 12px;
+  padding: 16px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
 }
 
 .selection-row {
-	display: flex;
-	align-items: center;
-	gap: 12px;
-	flex-wrap: wrap;
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  flex-wrap: wrap;
 }
 
 .tool-selector,
 .model-selector,
 .prompt-selector {
-	flex-shrink: 0;
+  flex-shrink: 0;
 }
 
 .input-row {
-	display: flex;
-	align-items: flex-end;
-	gap: 12px;
+  display: flex;
+  align-items: flex-end;
+  gap: 12px;
 }
 
 .message-input-wrapper {
-	flex: 1;
-	min-width: 0;
+  flex: 1;
+  min-width: 0;
 }
 
 .message-input-wrapper :deep(.el-textarea__inner) {
-	border-radius: 8px;
-	resize: none;
-	font-size: 14px;
-	line-height: 1.4;
+  border-radius: 8px;
+  resize: none;
+  font-size: 14px;
+  line-height: 1.4;
 }
 
 .button-group {
-	display: flex;
-	flex-direction: column;
-	gap: 6px;
-	flex-shrink: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+  flex-shrink: 0;
 }
 
 .button-group .el-button {
-	min-width: 60px;
+  min-width: 60px;
 }
 
 /* 提示词输入框样式 */
 .prompt-selector .el-input {
-	cursor: pointer;
+  cursor: pointer;
 }
 
 .prompt-selector .el-input__inner {
-	cursor: pointer;
+  cursor: pointer;
 }
 
 /* 对话框样式 */
 .dialog-footer {
-	text-align: right;
+  text-align: right;
 }
 
 /* 响应式调整 */
 /* 空状态页面样式 */
 .empty-content {
-	text-align: center;
-	max-width: 600px;
-	width: 100%;
+  text-align: center;
+  max-width: 600px;
+  width: 100%;
 
-	position: absolute;
-	left: 50%;
-	top: 50%;
-	transform: translate(-50%, -50%);
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
 }
 
 .empty-icon {
-	margin-bottom: 24px;
-	opacity: 0.6;
+  margin-bottom: 24px;
+  opacity: 0.6;
 }
 
 .empty-text {
-	margin-bottom: 40px;
+  margin-bottom: 40px;
 }
 
 .empty-title {
-	font-size: 28px;
-	font-weight: 600;
-	color: var(--el-text-color-primary);
-	margin: 0 0 12px 0;
+  font-size: 28px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+  margin: 0 0 12px 0;
 }
 
 .empty-description {
-	font-size: 16px;
-	color: var(--el-text-color-regular);
-	margin: 0;
-	line-height: 1.5;
+  font-size: 16px;
+  color: var(--el-text-color-regular);
+  margin: 0;
+  line-height: 1.5;
 }
 
 .quick-start {
-	display: flex;
-	justify-content: center;
-	gap: 40px;
-	margin-bottom: 40px;
-	flex-wrap: wrap;
+  display: flex;
+  justify-content: center;
+  gap: 40px;
+  margin-bottom: 40px;
+  flex-wrap: wrap;
 }
 
 .quick-start-item {
-	display: flex;
-	align-items: center;
-	gap: 12px;
-	color: var(--el-text-color-regular);
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  color: var(--el-text-color-regular);
 }
 
 .step-number {
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	width: 24px;
-	height: 24px;
-	background: var(--el-color-primary);
-	color: white;
-	border-radius: 50%;
-	font-size: 12px;
-	font-weight: 600;
-	flex-shrink: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  background: var(--el-color-primary);
+  color: white;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: 600;
+  flex-shrink: 0;
 }
 
 .step-text {
-	font-size: 14px;
-	font-weight: 500;
+  font-size: 14px;
+  font-weight: 500;
 }
 
 .example-questions {
-	h4 {
-		font-size: 16px;
-		font-weight: 600;
-		color: var(--el-text-color-primary);
-		margin: 0 0 16px 0;
-	}
+  h4 {
+    font-size: 16px;
+    font-weight: 600;
+    color: var(--el-text-color-primary);
+    margin: 0 0 16px 0;
+  }
 }
 
 .question-tags {
-	display: flex;
-	flex-wrap: wrap;
-	gap: 12px;
-	justify-content: center;
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+  justify-content: center;
 }
 
 .question-tag {
-	cursor: pointer;
-	transition: all 0.2s ease;
-	font-size: 13px;
-	padding: 8px 16px;
-	border-radius: 20px;
-
-	&:hover {
-		transform: translateY(-1px);
-		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
-	}
+  cursor: pointer;
+  transition: all 0.2s ease;
+  font-size: 13px;
+  padding: 8px 16px;
+  border-radius: 20px;
+
+  &:hover {
+    transform: translateY(-1px);
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+  }
 }
 </style>