kagg886 пре 2 месеци
родитељ
комит
ca7572d928
3 измењених фајлова са 89 додато и 15 уклоњено
  1. 7 0
      src/api/assist/index.ts
  2. 3 1
      src/api/assist/type.ts
  3. 79 14
      src/views/assistant/index.vue

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

@@ -45,6 +45,13 @@ export default {
 			list: (params: SessionMessagesListParams) => get('/system/lmsessions/messages/list', params),
 			// 保存会话消息
 			save: (data: SessionMessagesSaveReq) => post('/system/lmsessions/messages/save', data),
+
+			bookmark: (sessionId: number, messageId: number, like: boolean) =>
+				put('/system/lmsessions/messages/like', {
+					sessionId,
+					messageId,
+					like,
+				}),
 		},
 	},
 

+ 3 - 1
src/api/assist/type.ts

@@ -24,11 +24,13 @@ export type Message = {
 	content: string
 	timestamp: number
 
+
 	//仅role为tool时需要
 	name?: string,
-	//仅role为tool时需要
 	tool_call_id?: string
+
 	//仅role为assistant时需要
+	like?: boolean
 	tool_calls?: FunctionCall[]
 }
 

+ 79 - 14
src/views/assistant/index.vue

@@ -231,7 +231,6 @@ const chatInternal = (rtn: Message, context: Message[] = messages.value) => {
 					rtn.content += resp.message
 					break
 				case 'toolres': {
-
 					if (showToolCalls.value) {
 						rtn.render_content += `
 \`\`\`tools-loading
@@ -477,6 +476,30 @@ const redirectToModelManager = () => router.push('manage/model')
 // 设置面板相关状态
 const showSettingsPanel = ref(false)
 const showToolCalls = ref(false)
+
+// 收藏消息功能
+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),
+	}
+})
 </script>
 
 <template>
@@ -603,19 +626,26 @@ const showToolCalls = ref(false)
 			<!-- 消息展示区域 -->
 			<div class="messages-container" ref="messagesContainer" v-loading="loadingMessage">
 				<div v-if="messages.length !== 0">
-					<div v-for="message in messages" :key="message.id" :class="['message-wrapper', message.role]">
+					<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="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 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>
-									<span class="loading-text">AI正在思考中...</span>
+								</div>
+								<div class="ai-message-actions">
+									<el-button :loading="favoriteMessageIdx == idx && loadingFavoriteMessage" type="primary" size="small" @click="toggleFavorite(idx)" class="favorite-btn" plain :disabled="isConversationActive">
+										{{(message.like ?? false) ? '取消收藏' : '收藏'}}
+									</el-button>
 								</div>
 							</div>
 						</div>
@@ -725,7 +755,16 @@ const showToolCalls = ref(false)
 
 					<!-- 按钮组 -->
 					<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-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"
@@ -957,12 +996,37 @@ const showToolCalls = ref(false)
 	gap: 12px;
 }
 
+.ai-message-content {
+	display: flex;
+	flex-direction: column;
+	align-items: flex-start;
+	gap: 8px;
+	width: 70%;
+}
+
+.ai-message-actions {
+	display: flex;
+	justify-content: flex-start;
+	opacity: 0;
+	transition: opacity 0.2s ease;
+}
+
+.ai-message-container:hover .ai-message-actions {
+	opacity: 1;
+}
+
+.favorite-btn {
+	font-size: 12px;
+	padding: 4px 12px;
+	height: 24px;
+	border-radius: 12px;
+}
+
 .ai-bubble {
 	background: var(--el-fill-color-light);
 	color: var(--el-text-color-primary);
 	position: relative;
-	min-width: 70%;
-	max-width: 90%;
+	max-width: 100%;
 	border: 1px solid var(--el-border-color-lighter);
 
 	&::before {
@@ -991,6 +1055,7 @@ const showToolCalls = ref(false)
 	flex-direction: column;
 	align-items: flex-end;
 	gap: 8px;
+	width: 70%;
 }
 
 .user-message-actions {
@@ -1015,7 +1080,7 @@ const showToolCalls = ref(false)
 	background: var(--el-color-primary);
 	color: white;
 	position: relative;
-	max-width: 70%;
+	max-width: 100%;
 
 	&::after {
 		content: '';