Files
lingji-work-fe/src/components/xt-chat/chat-view/index.vue
2025-08-29 15:49:50 +08:00

197 lines
5.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="tsx">
import { message as antdMessage } from 'ant-design-vue';
import { BubbleList } from '@/components/xt-chat/xt-bubble';
import SenderInput from './components/sender-input/index.vue';
import RightView from './components/right-view/index.vue';
import { useChatStore } from '@/stores/modules/chat';
import { getConversationList } from '@/api/all/chat';
import querySSE from '@/utils/querySSE';
import useChatHandler from './useChatHandler';
import { QUESTION_ROLE, LOADING_ROLE } from './constants';
export default {
props: {
inputInfo: {
type: Object as () => CHAT.TInputInfo,
default: null,
},
conversationId: {
type: String,
default: '',
},
},
setup(props, { emit, expose }) {
const chatStore = useChatStore();
// const route = useRoute();
const rightViewRef = ref(null);
const bubbleListRef = ref<any>(null);
const sseController = ref<any>(null);
const searchValue = ref<string>('');
const handleSubmit = (message: string) => {
if (generateLoading.value) {
antdMessage.warning('停止生成后可发送');
return;
}
searchValue.value = '';
conversationList.value.push({
role: QUESTION_ROLE,
content: message,
});
initSse({ message });
};
const clearSseController = () => {
if (sseController.value) {
sseController.value.abort?.();
sseController.value = null;
}
};
const handleCancel = () => {
if (generateLoading.value) {
bubbleListRef.value?.abortTypingByKey(generateTeamRunTaskId.value);
sseController.value?.abort?.();
}
if (showRightView.value) {
rightViewRef.value?.abortTyping?.();
}
generateLoading.value = false;
antdMessage.info('取消生成');
};
const initSse = async (inputInfo: CHAT.TInputInfo): Promise<void> => {
clearSseController();
try {
const { message } = inputInfo;
generateLoading.value = true;
sseController.value = await querySSE({
method: 'POST',
handleMessage,
body: JSON.stringify({
content: message,
session_id: props.conversationId,
agent_id: chatStore.agentInfo.agent_id,
}),
});
} catch (error) {
console.error('Failed to initialize SSE:', error);
antdMessage.error('初始化连接失败');
generateLoading.value = false;
}
};
const getConversationInfo = async () => {
const { data, code } = await getConversationList({
session_id: props.conversationId,
agent_id: chatStore.agentInfo.agent_id,
});
if (code === 200) {
const remoteData = (data.list?.flat(1) ?? []).map((v: any) => ({
...v,
teamRunTaskId: v.step_run_id,
}));
conversationList.value = [...conversationList.value, ...remoteData];
}
};
const {
roles,
showRightView,
rightViewDataSource,
rightPreviewData,
generateTeamRunTaskId,
handleMessage,
conversationList,
generateLoading,
senderRef,
} = useChatHandler({
initSse,
});
watch(
() => props.inputInfo,
async (newVal) => {
if (newVal) {
const { message } = newVal;
conversationList.value.push({
role: QUESTION_ROLE,
content: message,
});
initSse(newVal);
}
},
{ deep: true },
);
watch(
() => props.conversationId,
(newVal) => {
if (newVal) {
getConversationInfo();
}
},
{ immediate: true },
);
onUnmounted(() => {
clearSseController();
});
return () => (
<div class="chat-view-wrap w-full h-full flex">
<section class="flex-1 flex flex-col pt-20px justify-center relative pl-16px pr-2px">
<div class="flex-1 overflow-hidden pb-20px">
<BubbleList
ref={bubbleListRef}
roles={roles}
items={[
...conversationList.value,
generateLoading.value ? { loading: true, role: LOADING_ROLE } : null,
].filter(Boolean)}
/>
</div>
<div class="w-full flex flex-col justify-center items-center">
<SenderInput
v-model={searchValue.value}
ref={senderRef}
class="w-600px"
placeholder="继续追问..."
loading={generateLoading.value}
onSubmit={handleSubmit}
onCancel={handleCancel}
/>
<p class="cts !color-#939499 text-12px !lh-20px my-4px">内容由AI生成仅供参考</p>
</div>
</section>
{/* 右侧展示区域 */}
{showRightView.value && (
<RightView
ref={rightViewRef}
dataSource={rightViewDataSource.value}
previewData={rightPreviewData.value}
showRightView={showRightView.value}
onClose={() => (showRightView.value = false)}
/>
)}
</div>
);
},
};
</script>
<style lang="scss" scoped>
@import './style.scss';
</style>