Skip to content

Commit 4057409

Browse files
committed
将 refs 移动到侧边栏
1 parent d56df04 commit 4057409

6 files changed

Lines changed: 689 additions & 307 deletions

File tree

web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"less": "^4.1.3",
2727
"marked": "^13.0.2",
2828
"marked-highlight": "^2.1.4",
29-
"md-editor-v3": "^4.16.7",
29+
"md-editor-v3": "^4.21.3",
3030
"pinia": "^2.0.32",
3131
"vue": "^3.5.13",
3232
"vue-router": "^4.1.6"

web/src/components/ChatComponent.vue

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,15 @@
8181
</div>
8282
<div class="chat-box" :class="{ 'wide-screen': meta.wideScreen, 'font-smaller': meta.fontSize === 'smaller', 'font-larger': meta.fontSize === 'larger' }">
8383
<MessageComponent
84-
v-for="message in conv.messages"
84+
v-for="(message, index) in conv.messages"
8585
:message="message"
8686
:key="message.id"
8787
:is-processing="isStreaming"
88-
:show-refs="true"
88+
:show-refs="['copy', 'regenerate', 'subGraph', 'webSearch', 'knowledgeBase']"
89+
:is-latest-message="isLatestMessage(index)"
8990
@retry="retryMessage(message.id)"
9091
@retryStoppedMessage="retryStoppedMessage(message.id)"
92+
@openRefs="handleOpenRefs"
9193
>
9294
</MessageComponent>
9395
</div>
@@ -142,6 +144,13 @@
142144
<p class="note">请注意辨别内容的可靠性 By {{ configStore.config?.model_provider }}: {{ configStore.config?.model_name }}</p>
143145
</div>
144146
</div>
147+
<!-- 添加全局Refs侧边栏 -->
148+
<RefsSidebar
149+
ref="refsSidebarRef"
150+
:visible="refsSidebarVisible"
151+
:latestRefs="currentRefs"
152+
@update:visible="refsSidebarVisible = $event"
153+
/>
145154
</div>
146155
</template>
147156

@@ -178,6 +187,7 @@ import { useConfigStore } from '@/stores/config'
178187
import { message } from 'ant-design-vue'
179188
import MessageInputComponent from '@/components/MessageInputComponent.vue'
180189
import MessageComponent from '@/components/MessageComponent.vue'
190+
import RefsSidebar from '@/components/RefsSidebar.vue'
181191
182192
const props = defineProps({
183193
conv: Object,
@@ -222,6 +232,41 @@ const meta = reactive(JSON.parse(localStorage.getItem('meta')) || {
222232
wideScreen: false,
223233
})
224234
235+
// 添加全局refs状态
236+
const refsSidebarVisible = ref(false)
237+
const currentRefs = ref({})
238+
239+
// 处理打开refs侧边栏
240+
const handleOpenRefs = ({ type, refs }) => {
241+
console.log('ChatComponent handleOpenRefs called with type:', type);
242+
console.log('Refs data structure:', JSON.stringify(refs));
243+
244+
// 先更新引用数据,确保数据在设置标签页之前已更新
245+
currentRefs.value = Object.assign({}, refs);
246+
247+
// 强制在下一个tick更新,确保数据已经被正确应用
248+
nextTick(() => {
249+
// 显示抽屉
250+
refsSidebarVisible.value = true;
251+
252+
// 再次检查引用是否正确
253+
console.log('Updated refs data:', JSON.stringify(currentRefs.value));
254+
255+
// 根据type自动选择标签页
256+
if (refsSidebarRef.value) {
257+
console.log('Setting active tab to:', type);
258+
// 延迟50毫秒设置标签页,确保抽屉已打开
259+
setTimeout(() => {
260+
refsSidebarRef.value.setActiveTab(type);
261+
}, 50);
262+
} else {
263+
console.error('refsSidebarRef is not available');
264+
}
265+
});
266+
}
267+
268+
// 添加对RefsSidebar的ref
269+
const refsSidebarRef = ref(null)
225270
226271
const consoleMsg = (msg) => console.log(msg)
227272
onClickOutside(panel, () => setTimeout(() => opts.showPanel = false, 30))
@@ -358,6 +403,11 @@ const updateMessage = (info) => {
358403
propertiesToUpdate.forEach(prop => {
359404
if (info[prop] != null && (typeof info[prop] !== 'string' || info[prop] !== '')) {
360405
msg[prop] = info[prop];
406+
407+
// 如果更新了refs,同时更新全局refs
408+
if (prop === 'refs' && info.refs) {
409+
currentRefs.value = info.refs;
410+
}
361411
}
362412
});
363413
@@ -446,6 +496,12 @@ const fetchChatResponse = (user_input, cur_res_id) => {
446496
console.log(msg)
447497
groupRefs(cur_res_id);
448498
updateMessage({showThinking: "no", id: cur_res_id});
499+
// 更新全局refs为最新消息的refs
500+
if (msg && msg.refs) {
501+
// 深拷贝refs以确保不会出现引用问题
502+
currentRefs.value = JSON.parse(JSON.stringify(msg.refs));
503+
console.log('Updated currentRefs on response completion:', currentRefs.value);
504+
}
449505
isStreaming.value = false;
450506
if (conv.value.messages.length === 2) { renameTitle(); }
451507
return;
@@ -571,6 +627,11 @@ onMounted(() => {
571627
const parsedMeta = JSON.parse(storedMeta);
572628
Object.assign(meta, parsedMeta);
573629
}
630+
631+
// 检查refsSidebarRef是否正确挂载
632+
nextTick(() => {
633+
console.log('Is refsSidebarRef mounted?', !!refsSidebarRef.value);
634+
});
574635
});
575636
576637
onUnmounted(() => {
@@ -642,6 +703,26 @@ const selectModel = (provider, name) => {
642703
configStore.setConfigValue('model_name', name)
643704
// message.success(`已切换到模型: ${name} | ${provider}`)
644705
}
706+
707+
// 判断是否是最新的助手消息
708+
const isLatestMessage = (index) => {
709+
// 找到最后一条助手消息的索引
710+
const lastAssistantMsgIndex = findLastIndex(conv.value.messages,
711+
msg => (msg.role === 'received' || msg.role === 'assistant') && msg.status === 'finished');
712+
713+
// 如果当前索引等于最后一条助手消息的索引,则为最新消息
714+
return index === lastAssistantMsgIndex;
715+
}
716+
717+
// 辅助函数:从后向前查找满足条件的元素索引
718+
const findLastIndex = (array, predicate) => {
719+
for (let i = array.length - 1; i >= 0; i--) {
720+
if (predicate(array[i])) {
721+
return i;
722+
}
723+
}
724+
return -1;
725+
}
645726
</script>
646727
647728
<style lang="less" scoped>

web/src/components/GraphContainer.vue

Lines changed: 147 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<script setup>
66
import { Graph } from "@antv/g6";
7-
import { onMounted, watch, ref } from 'vue';
7+
import { onMounted, watch, ref, onUnmounted } from 'vue';
88
99
const props = defineProps({
1010
graphData: {
@@ -16,21 +16,49 @@ const props = defineProps({
1616
1717
const container = ref(null);
1818
let graphInstance = null;
19+
let resizeObserver = null;
1920
2021
const initGraph = () => {
22+
// 确保容器大小已经稳定
23+
const width = container.value.offsetWidth;
24+
const height = container.value.offsetHeight;
25+
26+
if (width < 100) {
27+
// 如果容器宽度太小,可能是抽屉还在展开中,稍后重试
28+
setTimeout(initGraph, 100);
29+
return;
30+
}
31+
32+
// 确保容器干净,没有残留元素
33+
if (container.value) {
34+
container.value.innerHTML = '';
35+
}
36+
37+
// 确保没有已存在的实例
38+
if (graphInstance) {
39+
graphInstance.destroy();
40+
graphInstance = null;
41+
}
42+
2143
graphInstance = new Graph({
2244
container: container.value,
23-
width: container.value.offsetWidth,
24-
height: container.value.offsetHeight,
45+
width: width,
46+
height: height,
2547
autoFit: true,
2648
autoResize: true,
2749
layout: {
2850
type: 'd3-force',
2951
preventOverlap: true,
30-
kr: 20,
52+
kr: 30,
53+
linkDistance: 200,
54+
nodeStrength: -100,
3155
collide: {
32-
strength: 1.0,
56+
strength: 1.5,
57+
radius: 60,
3358
},
59+
alpha: 0.8,
60+
alphaDecay: 0.028,
61+
center: [width / 2, height / 2],
3462
},
3563
node: {
3664
type: 'circle',
@@ -56,10 +84,51 @@ const initGraph = () => {
5684
};
5785
5886
const renderGraph = () => {
87+
// 如果已有实例且容器大小发生明显变化,则销毁重建
88+
if (graphInstance && container.value) {
89+
const currentWidth = container.value.offsetWidth;
90+
const currentHeight = container.value.offsetHeight;
91+
let graphWidth = 0;
92+
let graphHeight = 0;
93+
94+
// 安全地获取图表宽高
95+
try {
96+
if (typeof graphInstance.getWidth === 'function') {
97+
graphWidth = graphInstance.getWidth();
98+
} else if (graphInstance.cfg && graphInstance.cfg.width) {
99+
graphWidth = graphInstance.cfg.width;
100+
}
101+
102+
if (typeof graphInstance.getHeight === 'function') {
103+
graphHeight = graphInstance.getHeight();
104+
} else if (graphInstance.cfg && graphInstance.cfg.height) {
105+
graphHeight = graphInstance.cfg.height;
106+
}
107+
} catch (e) {
108+
console.error('Error getting graph dimensions:', e);
109+
}
110+
111+
// 如果宽度变化超过50px,重新初始化图表
112+
if (Math.abs(currentWidth - graphWidth) > 50 || Math.abs(currentHeight - graphHeight) > 50) {
113+
graphInstance.destroy();
114+
graphInstance = null;
115+
116+
// 清理容器内容
117+
if (container.value) {
118+
container.value.innerHTML = '';
119+
}
120+
}
121+
}
122+
59123
if (!graphInstance) {
60124
initGraph();
61125
}
62126
127+
if (!graphInstance) {
128+
// 初始化可能推迟执行,此时直接返回
129+
return;
130+
}
131+
63132
const formattedData = {
64133
nodes: props.graphData.nodes.map(node => ({
65134
id: node.id,
@@ -74,14 +143,86 @@ const renderGraph = () => {
74143
75144
graphInstance.setData(formattedData);
76145
graphInstance.render();
146+
147+
// 确保图表正确居中和适应视图
148+
setTimeout(() => {
149+
if (graphInstance) {
150+
graphInstance.fitCenter();
151+
graphInstance.fitView();
152+
}
153+
}, 300);
77154
};
78155
156+
// 添加供父组件调用的刷新方法
157+
const refreshGraph = () => {
158+
// 销毁现有图表实例
159+
if (graphInstance) {
160+
graphInstance.destroy();
161+
graphInstance = null;
162+
}
163+
164+
// 清理容器内容
165+
if (container.value) {
166+
container.value.innerHTML = '';
167+
}
168+
169+
// 延迟重新渲染,确保容器大小已经稳定
170+
setTimeout(() => {
171+
renderGraph();
172+
}, 100);
173+
};
174+
175+
// 向父组件暴露方法
176+
defineExpose({
177+
refreshGraph
178+
});
179+
79180
onMounted(() => {
181+
// 使用ResizeObserver监听容器大小变化
182+
if (window.ResizeObserver) {
183+
resizeObserver = new ResizeObserver((entries) => {
184+
for (const entry of entries) {
185+
if (entry.target === container.value) {
186+
renderGraph();
187+
}
188+
}
189+
});
190+
191+
if (container.value) {
192+
resizeObserver.observe(container.value);
193+
}
194+
}
195+
80196
renderGraph();
81197
window.addEventListener('resize', renderGraph);
82198
});
83199
84-
watch(() => props.graphData, renderGraph, { deep: true });
200+
// 添加组件卸载时的清理
201+
onUnmounted(() => {
202+
window.removeEventListener('resize', renderGraph);
203+
204+
if (resizeObserver) {
205+
resizeObserver.disconnect();
206+
resizeObserver = null;
207+
}
208+
209+
if (graphInstance) {
210+
graphInstance.destroy();
211+
graphInstance = null;
212+
}
213+
214+
// 确保清理容器内容
215+
if (container.value) {
216+
container.value.innerHTML = '';
217+
}
218+
});
219+
220+
watch(() => props.graphData, (newData, oldData) => {
221+
// 强制重新渲染图表
222+
if (newData !== oldData) {
223+
refreshGraph();
224+
}
225+
}, { deep: true });
85226
</script>
86227

87228
<style scoped>

web/src/components/MessageComponent.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767

6868

6969
<div v-if="(message.role=='received' || message.role=='assistant') && message.status=='finished' && showRefs">
70-
<RefsComponent :message="message" :show-refs="showRefs" @retry="emit('retry')" />
70+
<RefsComponent :message="message" :show-refs="showRefs" :is-latest-message="isLatestMessage" @retry="emit('retry')" @openRefs="emit('openRefs', $event)" />
7171
</div>
7272
<!-- 错误消息 -->
7373
</div>
@@ -111,6 +111,11 @@ const props = defineProps({
111111
type: Boolean,
112112
default: false
113113
},
114+
// 是否为最新消息
115+
isLatestMessage: {
116+
type: Boolean,
117+
default: false
118+
}
114119
});
115120
116121
const editorRef = ref()
@@ -122,7 +127,7 @@ const statusDefination = {
122127
error: '错误'
123128
}
124129
125-
const emit = defineEmits(['retry', 'retryStoppedMessage']);
130+
const emit = defineEmits(['retry', 'retryStoppedMessage', 'openRefs']);
126131
127132
// 推理面板展开状态
128133
const reasoningActiveKey = ref(['show']);

0 commit comments

Comments
 (0)