feat: 对话逻辑调整、历史对话接口联调

This commit is contained in:
rd
2025-08-22 18:28:09 +08:00
parent 0d5cb7ba38
commit 063ce3df5e
13 changed files with 221 additions and 71 deletions

68
src/api/all/chat.ts Normal file
View File

@ -0,0 +1,68 @@
import Http from '@/api';
import axios from 'axios';
import { glsWithCatch } from '@/utils/stroage';
import { useEnterpriseStore } from '@/stores/modules/enterprise';
// 历史记录-列表
export const getAgentHistory = (id: string) => {
return Http.get(`/v1/multi-agent/history/${id}`);
};
// 历史记录-更新标题
export const postUpdateSessionTitle = (data: any) => {
return Http.post('/v1/multi-agent/edit-session-title', data);
};
// 历史记录-置顶/取消置顶
export const postUpdateSessionSort = (data: any) => {
return Http.post('/v1/multi-agent/edit-session-sort', data);
};
// 历史记录-删除
export const deleteHistoryItem = (id: string) => {
return Http.delete(`/v1/multi-agent/del-session/${id}`);
};
export const baseUrl = 'http://192.168.40.41:8001';
const getHeaders = () => {
const store = useEnterpriseStore();
return {
Authorization: glsWithCatch('accessToken'),
'enterprise-id': store.enterpriseInfo?.id,
Accept: 'application/json',
'Content-Type': 'application/json',
};
};
/**
* 获取智能体信息
*/
export const getAgentInfo = async () => {
const { data } = await axios.get(`${baseUrl}/api/agent/info`, {
headers: getHeaders(),
});
return data;
};
/**
* 指令输入
*/
export const getInputAgent = async (params: {}) => {
const { data } = await axios.get(`${baseUrl}/api/agent/input`, {
params,
headers: { ...getHeaders(), Accept: 'text/event-stream' },
});
return data;
};
/**
* 生成会话id
*/
export const createSession = async () => {
const { data } = await axios.get(`${baseUrl}/api/agent/create_session`, {
headers: getHeaders(),
});
return data;
};

View File

@ -72,7 +72,6 @@ export class Request {
const { response } = err;
const status = response?.status;
let errMessage = response?.data?.message ?? err.message;
switch (status) {
case HttpStatusCode.InternalServerError:
errMessage = '系统繁忙,请稍后再试或联系管理员。';

View File

@ -71,7 +71,7 @@ export default defineComponent({
};
const onMenuItemClick = ({ menuInfo, item }) => {
const { key } = menuInfo;
emit('menuClick', menuInfo);
emit('menuClick', { menuInfo, item });
switch (key) {
case 'rename':

View File

@ -1,16 +1,16 @@
<script lang="jsx">
import { Input } from 'ant-design-vue';
import { handleUserHome } from '@/utils/user.ts';
import { useSharedDataStore } from '@/stores/modules/share-data';
import { useChatStore } from '@/stores/modules/chat';
export default {
setup(props, { emit, expose }) {
const sharedDataStore = useSharedDataStore();
const chatStore = useChatStore();
const keyWord = ref('');
const handleSearch = () => {
sharedDataStore.setRouteParams({ keyWord: keyWord.value });
handleUserHome();
chatStore.setSearchValue(keyWord.value);
chatStore.onCreateSession();
keyWord.value = '';
};
return () => (

View File

@ -0,0 +1,46 @@
import { defineStore } from 'pinia';
import { createSession } from '@/api/all/chat';
import { handleUserHome } from '@/utils/user';
interface ChatState {
searchValue?: string;
agentInfo?: agentInfo;
}
type agentInfo = {
agent_id?: string;
name?: string;
description?: string;
};
export const useChatStore = defineStore('chat', {
state: (): ChatState => ({
searchValue: '',
agentInfo: {},
}),
actions: {
setSearchValue(searchValue: string) {
this.searchValue = searchValue;
},
clearSearchValue() {
this.searchValue = '';
},
setAgentInfo(agentInfo: agentInfo) {
this.agentInfo = agentInfo;
},
clearAgentInfo() {
this.agentInfo = {};
},
clearAllAgentInfo() {
this.agentInfo = {};
this.searchValue = '';
},
async onCreateSession() {
const { code, data } = await createSession();
if (code === 200) {
handleUserHome({
conversationId: data.session_id,
});
}
},
},
});

View File

@ -8,3 +8,4 @@
export * from './app';
export * from './tab-bar';
export * from './user';
export * from './chat';

View File

@ -1,20 +0,0 @@
import { defineStore } from 'pinia';
interface SharedDataState {
routeParams: Record<string, any> | null;
}
export const useSharedDataStore = defineStore('sharedData', {
state: (): SharedDataState => ({
routeParams: null,
}),
actions: {
setRouteParams(params: Record<string, any>) {
this.routeParams = params;
},
clearRouteParams() {
this.routeParams = null;
},
},
});

View File

@ -4,11 +4,13 @@ import { BubbleList } from '@/components/xt-chat/xt-bubble';
import SenderInput from '../sender-input/index.vue';
import { Typography } from 'ant-design-vue';
import RightView from './rightView.vue';
import type { Ref } from 'vue';
import { useRoute } from 'vue-router';
import markdownit from 'markdown-it';
import { useClipboard } from '@vueuse/core';
import { genRandomId } from '@/utils/tools';
import { useChatStore } from '@/stores/modules/chat';
import type { BubbleListProps } from '@/components/xt-chat/xt-bubble/types';
const QUESTION_ROLE = 'question';
@ -16,6 +18,8 @@ const ANSWER_ROLE = 'text';
export default {
setup(props, { emit, expose }) {
const chatStore = useChatStore();
const route = useRoute();
const { copy } = useClipboard();
@ -187,6 +191,20 @@ export default {
},
};
const initSse = () => {
console.log('initSse', { agentInfo: chatStore.agentInfo, searchValue: chatStore.searchValue });
};
const closeSse = () => {
console.log('closeSse');
};
onMounted(() => {
initSse();
});
onUnmounted(() => {
closeSse();
});
return () => (
<div class="conversation-detail-wrap w-full h-full flex">
<section class="flex-1 flex flex-col pt-20px justify-center relative px-16px">
@ -203,7 +221,7 @@ export default {
v-model={searchValue.value}
onSubmit={handleSubmit}
onCancel={handleCancel}
data-ne='123'
data-ne="123"
/>
<p class="cts !color-#939499 text-12px !lh-20px my-4px">内容由AI生成仅供参考</p>
</div>

View File

@ -3,25 +3,21 @@ import { message } from 'ant-design-vue';
import ExpandableTags from '@/components/expandable-tags/index.vue';
import SenderInput from '../sender-input/index.vue';
import { useSharedDataStore } from '@/stores/modules/share-data';
import { useRouter } from 'vue-router';
import { handleUserHome } from '@/utils/user';
import { useChatStore } from '@/stores/modules/chat';
export default {
setup(props, { emit, expose }) {
const router = useRouter();
const senderRef = ref(null);
const searchValue = ref('');
const sharedDataStore = useSharedDataStore();
const chatStore = useChatStore();
const handleSubmit = () => {
handleSearch();
};
const handleSearch = () => {
console.log('searchValue.value', searchValue.value);
// const conversationId = searchValue.value;
// handleUserHome({conversationId})
const handleSearch = async () => {
chatStore.setSearchValue(searchValue.value);
chatStore.onCreateSession();
};
const tagList = [
@ -48,13 +44,7 @@ export default {
};
onMounted(() => {
const params = sharedDataStore.routeParams;
if (params) {
searchValue.value = params.keyWord;
sharedDataStore.clearRouteParams();
handleSearch();
}
chatStore.clearSearchValue();
});
return () => (

View File

@ -15,7 +15,7 @@
<script setup>
import { ref } from 'vue';
import { deleteTask, deleteBatchTasks } from '@/api/all/common';
import { deleteHistoryItem } from '@/api/all/chat';
import icon1 from '@/assets/img/media-account/icon-warn-1.png';
const emits = defineEmits(['update', 'close', 'batchUpdate']);
@ -32,6 +32,7 @@ function onClose() {
}
const open = (record) => {
console.log({record})
const { id = null } = record;
chatId.value = id;
@ -39,7 +40,7 @@ const open = (record) => {
};
async function onDelete() {
const { code } = await deleteTask(chatId.value);
const { code } = await deleteHistoryItem(chatId.value);
if (code === 200) {
AMessage.success('删除成功');
emits('delete', chatId.value);

View File

@ -1,41 +1,64 @@
<script lang="jsx">
import { Drawer } from 'ant-design-vue';
import { Drawer, message } from 'ant-design-vue';
import TextoverTips from '@/components/text-over-tips';
import Conversations from '@/components/xt-chat/xt-conversations';
import SvgIcon from '@/components/svg-icon';
import { Button, Flex, Input } from 'ant-design-vue';
import DeleteChatModal from './delete-chat-modal.vue';
import NoData from '@/components/no-data/index.vue';
import { handleUserHome } from '@/utils/user';
import { useRouter } from 'vue-router';
import { getAgentHistory, postUpdateSessionTitle, postUpdateSessionSort } from '@/api/all/chat';
import { useChatStore } from '@/stores/modules/chat';
export default {
setup(props, { emit, expose }) {
const router = useRouter();
const chatStore = useChatStore();
const open = ref(false);
const dataSource = ref([]);
const activeKey = ref('');
const deleteChatModalRef = ref(null);
const showDrawer = () => {
const showDrawer = (id) => {
getData();
open.value = true;
};
const getData = () => {
dataSource.value = Array.from({ length: 4 }).map((conversation, index) => ({
id: `${index + 1}`,
key: `item${index + 1}`,
label: `Conversation Item ${index + 1}Conversation Item 1`,
}));
const getData = async () => {
const { code, data } = await getAgentHistory(chatStore.agentInfo.agent_id);
if (code === 200) {
dataSource.value = data.map((item) => ({
...item,
key: item.id,
label: item.title,
}));
}
};
const onPin = async (item) => {
const { id, sort } = item;
// sort大于0就是已置顶
const is_top = sort > 0 ? 0 : 1;
const { code } = await postUpdateSessionSort({
id,
is_top,
agent_id: chatStore.agentInfo.agent_id,
});
if (code === 200) {
message.success(is_top === 0 ? '取消置顶成功' : '置顶成功');
getData();
}
};
const onClose = () => {
activeKey.value = '';
dataSource.value = [];
open.value = false;
};
const handleMenuClick = (menuInfo) => {
const { item, key } = menuInfo;
switch (key) {
const handleMenuClick = async ({ menuInfo, item }) => {
switch (menuInfo.key) {
case 'pin':
console.log('置顶');
onPin(item);
break;
case 'rename':
item.editing = true;
@ -45,8 +68,15 @@ export default {
break;
}
};
const handleRename = (item) => {
console.log('handleRename', item);
const handleRename = async (item) => {
const { id, label } = item;
const { code } = await postUpdateSessionTitle({
id,
title: label,
});
if (code === 200) {
message.success('重命名成功');
}
};
const handleActiveChange = (item) => {
const { id } = item;
@ -60,18 +90,23 @@ export default {
return () => (
<Drawer width={240} rootClassName="ct-history-conversation-drawer" v-model:open={open.value} onClose={onClose}>
<header class="header h-40px px-12px flex justify-between items-center">
<span class="s1">历史对话</span>
<span class="text-16px font-400 color-#211F24 font-family-medium">历史对话</span>
<icon-close size={16} class="color-#211F24 cursor-pointer" onClick={onClose} />
</header>
<section class="flex-1 overflow-y-auto content p-12px">
<Conversations
v-model={activeKey.value}
dataSource={dataSource.value}
onMenuClick={handleMenuClick}
onRename={handleRename}
onActiveChange={handleActiveChange}
/>
</section>
{dataSource.value.length === 0 ? (
<NoData text="暂无历史对话" />
) : (
<section class="flex-1 overflow-y-auto content p-12px">
<Conversations
v-model={activeKey.value}
dataSource={dataSource.value}
onMenuClick={handleMenuClick}
onRename={handleRename}
onActiveChange={handleActiveChange}
/>
</section>
)}
<DeleteChatModal ref={deleteChatModalRef} />
</Drawer>
);

View File

@ -18,7 +18,6 @@
left: 0;
width: 100%;
height: 1px;
margin: 0 16px;
background: #f2f3f5;
}
}

View File

@ -1,6 +1,8 @@
<script lang="tsx">
import { useRoute } from 'vue-router';
import { ref, computed } from 'vue';
import { getAgentInfo } from '@/api/all/chat';
import { useChatStore } from '@/stores/modules/chat';
import HistoryConversationDrawer from './components/history-conversation-drawer/index.vue';
import ConversationDetail from './components/conversation-detail/index.vue';
@ -9,13 +11,24 @@ import ConversationCreate from './components/created/index.vue';
export default {
setup(props, { emit, expose }) {
const route = useRoute();
const chatStore = useChatStore();
const historyConversationDrawerRef = ref(null);
const conversationId = computed(() => {
return route.params.conversationId;
});
const getAgentData = async () => {
const { code, data } = await getAgentInfo();
if (code === 200) {
chatStore.setAgentInfo(data);
}
};
onMounted(() => {
getAgentData();
});
return () => (
<div class="chat-wrap rounded-12px w-full h-full">
{conversationId.value ? <ConversationDetail /> : <ConversationCreate />}