feat: 对话功能开发
This commit is contained in:
@@ -8,6 +8,19 @@
|
||||
</div>
|
||||
<div v-for="msg in messages" :key="msg.id" class="bubble-wrap" :class="msg.role">
|
||||
<div class="bubble">
|
||||
<!-- 推理过程(仅助手消息显示) -->
|
||||
<div v-if="msg.parts?.reasoning?.length > 0" class="reasoning-section">
|
||||
<div class="reasoning-header" @click="msg.showReasoning = !msg.showReasoning">
|
||||
<el-icon :size="14"><ArrowRight v-if="!msg.showReasoning" /><ArrowDown v-else /></el-icon>
|
||||
<span>思考过程</span>
|
||||
</div>
|
||||
<div v-show="msg.showReasoning" class="reasoning-content">
|
||||
<div v-for="(r, idx) in msg.parts.reasoning" :key="idx" class="reasoning-item">
|
||||
{{ r.text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 文本内容 -->
|
||||
<pre class="bubble-text">{{ msg.text }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,8 +46,9 @@
|
||||
import { ref, computed, onUnmounted, nextTick, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { ChatDotRound } from '@element-plus/icons-vue';
|
||||
import { createEventSource, listMessagesAction } from '@/http/api.js';
|
||||
import { ChatDotRound, ArrowRight, ArrowDown } from '@element-plus/icons-vue';
|
||||
import { createEventSource } from '@/http/api.js';
|
||||
import axios from 'axios';
|
||||
|
||||
const route = useRoute();
|
||||
const isSending = ref(false);
|
||||
@@ -52,7 +66,11 @@ const routeSessionId = computed(() => route.query.sessionId);
|
||||
async function loadHistoryMessages(sessionId) {
|
||||
if (!sessionId) return;
|
||||
try {
|
||||
const messagesData = await listMessagesAction(sessionId);
|
||||
const baseUrl = window.__opencodeBaseUrl || 'http://127.0.0.1:4096';
|
||||
const response = await axios.get(`${baseUrl}/session/${sessionId}/message`);
|
||||
const messagesData = response.data || [];
|
||||
console.log('[loadHistoryMessages] 原始消息数据:', messagesData);
|
||||
|
||||
// 清空当前消息
|
||||
messages.value = [];
|
||||
assistantMessageIds.clear();
|
||||
@@ -62,17 +80,38 @@ async function loadHistoryMessages(sessionId) {
|
||||
const { info, parts } = item;
|
||||
if (!info || !parts) return;
|
||||
|
||||
// 提取文本内容
|
||||
const text = parts
|
||||
.filter((part) => part.type === 'text')
|
||||
.map((part) => part.text)
|
||||
.join('');
|
||||
// 按 type 分类存储 parts
|
||||
const partsByType = {
|
||||
text: [],
|
||||
reasoning: [],
|
||||
'step-start': [],
|
||||
'step-finish': [],
|
||||
// 可以在这里添加更多 type
|
||||
};
|
||||
|
||||
if (text) {
|
||||
parts.forEach((part) => {
|
||||
if (partsByType.hasOwnProperty(part.type)) {
|
||||
partsByType[part.type].push(part);
|
||||
} else {
|
||||
// 未识别的 type 统一放到 others
|
||||
if (!partsByType.others) partsByType.others = [];
|
||||
partsByType.others.push(part);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`[loadHistoryMessages] 消息 ${info.id} 的 parts 分类:`, partsByType);
|
||||
|
||||
// 提取文本内容(用于展示)
|
||||
const text = partsByType.text.map((part) => part.text).join('');
|
||||
|
||||
if (text || info.role === 'assistant') {
|
||||
messages.value.push({
|
||||
id: info.id,
|
||||
role: info.role,
|
||||
text: text,
|
||||
parts: partsByType, // 存储分类后的 parts,方便后续按 type 渲染
|
||||
rawParts: parts, // 保留原始 parts
|
||||
showReasoning: false, // 默认折叠推理过程
|
||||
});
|
||||
|
||||
// 记录 assistant 消息 ID
|
||||
@@ -82,6 +121,7 @@ async function loadHistoryMessages(sessionId) {
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[loadHistoryMessages] 处理后的消息列表:', messages.value);
|
||||
scrollToBottom();
|
||||
} catch (err) {
|
||||
console.error('加载历史消息失败:', err);
|
||||
@@ -265,6 +305,43 @@ onUnmounted(() => {
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.reasoning-section {
|
||||
margin-bottom: 8px;
|
||||
border-radius: 6px;
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.reasoning-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.reasoning-header:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.reasoning-content {
|
||||
padding: 0 10px 8px;
|
||||
}
|
||||
|
||||
.reasoning-item {
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
line-height: 1.5;
|
||||
padding: 4px 0;
|
||||
border-left: 2px solid #ddd;
|
||||
padding-left: 8px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
Reference in New Issue
Block a user