src/api/agentSession.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/dmx/IntelligentAgent/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/components/addSession.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/components/agentSession.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/index.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/views/sessionManager/index1.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/api/agentSession.ts
New file @@ -0,0 +1,47 @@ import axios from 'axios'; export interface ISessionListResult { code: number; msg: string; data: any; } // 会话列表 // export function sessionListApi(dialog_id) { // return axios.get<ISessionListResult>( // "/api/conversation/list?modeltype=localragflow&dialogid=" + dialog_id // ); // } // 删除会话 // export function deleteSessionApi(conversation_ids: string[]) { // return axios.post<ISessionListResult>( // '/api/conversation/del?modeltype=localragflow', // { conversation_ids } // ); // } // 新增会话 // export function addSessionApi(params: any) { // return axios.get<ISessionListResult>( // '/api/getConId/kdwithai?platform=localragflow', // { params } // ); // } // 聊天 // export function chatApi(data: { conversation_id: string; messages: string }) { // return axios.post<ISessionListResult>( // '/api/tech/cloudminds/query?modeltype=localragflow', // data // ); // } // 获取会话详情 export function getAgentSessionDetailsApi(id: string) { return axios.get( '/api/v1/canvas/get/' + id, {} ); } // 获取智能助手列表 // export function getDialogListApi() { // return axios.get<ISessionListResult>('/api/dialog/list'); // } src/views/dmx/IntelligentAgent/index.vue
@@ -132,7 +132,7 @@ > <icon-calendar-clock /> <span style="font-size: 12px;margin-left: 10px"> {{ parseTime(item.create_date) }} {{ moment(item.create_date).format('YYYY-MM-DD HH:mm:ss') }} </span> </div> <!-- <div--> @@ -215,6 +215,7 @@ import AgentConfig from '@/views/dmx/IntelligentAgent/components/agentConfig.vue'; import logo from '../../../assets/images/model.png'; import { documentHeight, parseTime } from "@/utils"; import moment from "moment"; // console.log(documentHeight,'高度'); let count = 5; const activeKey = ref(1); src/views/sessionManager/components/addSession.vue
@@ -85,7 +85,7 @@ > <template #title> 新增会话 </template> <a-form ref="formRef" :model="conversation" :rules="rules"> <a-form-item label="助手关联:" field="dialog_id" @submit="handleSubmit"> <a-form-item label="助手关联:" field="dialog_id" > <a-select style="width: 80%" v-model="conversation.dialog_id" src/views/sessionManager/components/agentSession.vue
New file @@ -0,0 +1,377 @@ <template> <!-- 内容--> <a-scrollbar ref="scrollbar" id="home" class="chat-list" style=" width: 90%; overflow: auto; height: calc(100vh - 380px); margin: 0px auto 20px; " > <div class="chat-item" v-for="sessionDetail in sessionDetailList"> <a-comment v-if="sessionDetail.role === 'user'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-user.jpg" alt="本地图片" /> </template> <template #content> <div :class="{ chartUserText: theme === 'light' }" >{{ sessionDetail.content }} </div> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'assistant'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-card :class="{ chatItemAnswer: theme === 'light' }"> <div :class="{ light: theme === 'light' }" >{{ sessionDetail.content }} </div> </a-card> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'last'"> <template #avatar> <img class="icon-user-jpg" src="../../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-textarea readonly auto-size v-model="displayedText" :class="{ chatItemAnswer: theme === 'light' }" > </a-textarea> </template> </a-comment> </div> <!-- <div class="chartStart" v-if="isStart" @click="startChat" >停止生成</div > <div class="chartStart v-else" @click="stopChat">重新生成</div> --> </a-scrollbar> <div class="chat_bottom"> <div class="center-bottom"> <a-textarea v-model="inputMsg" @keydown.shift.enter="handleShiftEnter" @keydown.enter="sendMessage" placeholder="输入您想了解的内容,Shift+Enter换行,Enter发送" allow-clear show-word-limit :disabled="chatDis" :class="{ textItemAnswer: theme === 'dark' }" :auto-size="{ minRows: 12, maxRows: 5, }" /> <div class="btn-send"> <!-- <icon-send size="32" /> --> <a-button :disabled="chatDis" @click="sentClick" type="primary" style="border-radius: 24px" :loading="loading" size="large" >发送</a-button > </div> </div> </div> </template> <script setup lang="ts"> import { defineProps, ref, watch, defineEmits, onMounted, reactive, computed } from "vue"; import { Message } from '@arco-design/web-vue'; import { useAppStore } from "@/store"; import { getAuthorization } from "@/utils/auth"; import { EventSourceParserStream } from 'eventsource-parser/stream'; // const props = defineProps({ // modalObj: Object, // dialogId: String, // }); // const emit = defineEmits(['addSession']); const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ add: false }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const dialogObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/tech/cloudminds/query?modeltype=localragflow', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; querySessionDetail(sectionList.value); break; } } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } } } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; onMounted(() => { }); // watch( // () => props.dialogId, // (newVal, oldVal) => { // // } // ); </script> <style scoped lang="scss"> .dark { color: gray !important; } .container { .chatItemAnswer { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; } } .textItemAnswer { background-color: #373739; } .center { box-sizing: border-box; height: calc(100vh - 200px); position: relative; .center-title { line-height: 60px; font-size: 25px; font-family: 黑体; color: deepskyblue; } .center-content { font-size: 14px; color: gray; } .center-question { margin-top: 20px; display: flex; justify-content: space-between; .center-question-left { margin-top: 5px; margin-left: 20px; } .center-question-right { margin-right: 20px; } } .center-list { margin-top: 10px; .item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 50px; } } :deep(.arco-textarea-wrapper) { padding-top: 5px; } } } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .icon-user-jpg { border: 1px solid #d9d9d9; } } } </style> src/views/sessionManager/index.vue
@@ -50,10 +50,10 @@ :class="{ isLeftActive: activeSessionId === session.id }" > <div class="text" :class="{ time: theme === 'dark' }" >{{ session.name }} >{{ session.name }} </div> <div class="time" >{{ >{{ moment(new Date(session.create_time)).format( 'YYYY-MM-DD HH:mm:ss' ) @@ -63,7 +63,8 @@ </a-scrollbar> </a-card> </a-col> <a-col :span="18"> <!-- 智能体会话--> <a-col :span="18" v-show="agentType == '1'"> <a-card class="center"> <!-- <div v-if="sessionDetailList.length === 0" @@ -163,7 +164,7 @@ </template> <template #content> <div :class="{ chartUserText: theme === 'light' }" >{{ sessionDetail.content }} >{{ sessionDetail.content }} </div> </template> </a-comment> @@ -178,7 +179,7 @@ <template #content> <a-card :class="{ chatItemAnswer: theme === 'light' }"> <div :class="{ light: theme === 'light' }" >{{ sessionDetail.content }} >{{ sessionDetail.content }} </div> </a-card> </template> @@ -242,13 +243,22 @@ style="border-radius: 24px" :loading="loading" size="large" >发送</a-button >发送</a-button > </div> </div> </div> </a-card> </a-col> <a-col :span="18" v-show="agentType == '2'"> <a-card class="center"> <agentSession ></agentSession> </a-card> </a-col> <!-- <a-col :span="5"> <a-card class="right"> <div class="right-top"> @@ -313,442 +323,522 @@ </div> </template> <script setup lang="ts"> import { IconClose, IconSearch, IconTiktokColor, } from '@arco-design/web-vue/es/icon'; import { useAppStore, useUserStore } from '@/store'; import { computed, nextTick, onMounted, watch, reactive, ref } from 'vue'; import { IconClose, IconSearch, IconTiktokColor, } from '@arco-design/web-vue/es/icon'; import { useAppStore, useUserStore } from '@/store'; import { computed, nextTick, onMounted, watch, reactive, ref } from 'vue'; import { Message } from '@arco-design/web-vue'; import { EventSourceParserStream } from 'eventsource-parser/stream'; import moment from 'moment'; import AddSession from '@/views/sessionManager/components/addSession.vue'; import { chatApi, getDialogListApi, getSessionDetailsApi, sessionListApi, } from '@/api/session'; import { getAuthorization } from '@/utils/auth'; import { Message } from '@arco-design/web-vue'; import { EventSourceParserStream } from 'eventsource-parser/stream'; import moment from 'moment'; import AddSession from '@/views/sessionManager/components/addSession.vue'; import agentSession from '@/views/sessionManager/components/agentSession.vue'; import { chatApi, getDialogListApi, getSessionDetailsApi, sessionListApi, } from '@/api/session'; import { getAuthorization } from '@/utils/auth'; import { queryCanvasList } from "@/api/Agent"; import { getAgentSessionDetailsApi } from "@/api/agentSession"; const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ add: false }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ add: false }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const agentType = ref('1'); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const selectValue = ref(''); const sectionList = ref({}); const DialogList = async () => { const { code, data } = await getDialogListApi(); if (code === 200) { if (data) { selectValue.value = data[0].id; dialogs.value = data; querySessionList(); } const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const dialogObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const DialogList = async () => { const { code, data } = await getDialogListApi(); if (code === 200) { if (data) { selectValue.value = data[0].id; dialogs.value = data.map((item) => { return { ...item, type: 1,//智能体 } }); console.log(data, 'dialogs'); queryCanvas(); // querySessionList(); } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; const dialogChange = (val) => { dialogId.value = val; } }; const queryCanvas = async (params = {}) => { try { const { data } = await queryCanvasList(params); console.log(data, 'agent'); agentList.value = data.map((item) => { return { ...item, name: item.title, type: 2,//agent } }); // 合并数组 dialogs.value = dialogs.value.concat(agentList.value); // 判断当前是智能体或agent // console.log(val, 'val'); if(dialogs.value.length>0){ dialogChange(dialogs.value[0].id); } } catch (err) { // you can report use errorHandler or other } finally { } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; const dialogChange = (val) => { // 判断当前是智能体或agent // console.log(val, 'val'); dialogId.value = val; dialogs.value.forEach((item) => { if (item.id === val) { Object.assign(dialogObj, item) } }) console.log(dialogObj.type, 'dialogObj'); if (dialogObj.type == 1) { agentType.value = 1; querySessionList(); }; const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } } else { agentType.value = 2; queryAgentSessionList() } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/tech/cloudminds/query?modeltype=localragflow', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), // querySessionList(); }; const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/tech/cloudminds/query?modeltype=localragflow', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; querySessionDetail(sectionList.value); break; } if (done) { console.info('done'); displayedText.value = ''; querySessionDetail(sectionList.value); break; } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } } }; const querySessionDetail = async (session) => { sectionList.value = session; activeSessionId.value = session.id; const { code, data } = await getSessionDetailsApi(session.id); if (code === 200) { sessionDetailList.value = data.message; refreshScroll(); //刷新滚动条位置 } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; // 查询会话列表 const querySessionList = async () => { const { code, data } = await sessionListApi(selectValue.value); if (code === 200) { sessionList.value = data; activeSessionId.value = data[0].id; //默认选择第一个 querySessionDetail(data[0]); } else { Message.warning('查询失败'); } }; //新增会话之后刷新会话列表 const addSession = () => { querySessionList(); }; onMounted(() => { DialogList(); } }; const querySessionDetail = async (session) => { sectionList.value = session; activeSessionId.value = session.id; const { code, data } = await getSessionDetailsApi(session.id); if (code === 200) { sessionDetailList.value = data.message; refreshScroll(); //刷新滚动条位置 } }; const queryAgentSessionDetail = async (id) => { const { code, data } = await getAgentSessionDetailsApi(id); if (code === 200) { // sessionDetailList.value = data.message; // refreshScroll(); //刷新滚动条位置 } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; // 查询会话列表 const querySessionList = async () => { const { code, data } = await sessionListApi(selectValue.value); if (code === 200) { sessionList.value = data; activeSessionId.value = data[0].id; //默认选择第一个 querySessionDetail(data[0]); } else { Message.warning('查询失败'); } }; const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); //文字动态输出 const startDisplayStr = () => { if (timer) { clearTimeout(timer!); } const res = streamStr.value; // 将数组中的字符串拼接起来 if (currIndex.value < res.length) { displayedText.value += res[currIndex.value]; currIndex.value++; setTimeout(startDisplayStr, 100); refreshScroll(); } else { clearTimeout(timer!); timer = null; } }; // 查询会话列表 const queryAgentSessionList = async () => { // const { code, data } = await sessionListApi(selectValue.value); // if (code === 200) { // // } else { // Message.warning('查询失败'); // } queryAgentSessionDetail(selectValue.value); }; //新增会话之后刷新会话列表 const addSession = () => { querySessionList(); }; onMounted(() => { DialogList(); }); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); //文字动态输出 const startDisplayStr = () => { if (timer) { clearTimeout(timer!); } const res = streamStr.value; // 将数组中的字符串拼接起来 if (currIndex.value < res.length) { displayedText.value += res[currIndex.value]; currIndex.value++; setTimeout(startDisplayStr, 100); refreshScroll(); } else { clearTimeout(timer!); timer = null; } }; </script> <style scoped lang="scss"> .isLeftActive { background-color: #ededf5; } .left-list-item { margin-bottom: 2px; } .left-list-item:hover { background-color: #ededf5; } .isLeftActive { background-color: #ededf5; } .left-list-item { margin-bottom: 2px; } .left-list-item:hover { background-color: #ededf5; } .dark { color: gray !important; } .dark { color: gray !important; } .container { .top-title { line-height: 32px; font-size: 16px; color: #333; .container { .top-title { line-height: 32px; font-size: 16px; color: #333; } .left-select { :deep(.arco-select-view-single) { border-radius: 5px; } .left-select { :deep(.arco-select-view-single) { border-radius: 5px; } } } .center, .right { box-sizing: border-box; height: calc(100vh - 200px); } .center, .right { box-sizing: border-box; height: calc(100vh - 200px); } .left { /* height: calc(100vh - 160px); overflow-y: auto; overflow-x: hidden;*/ border: 0px; .left { /* height: calc(100vh - 160px); overflow-y: auto; overflow-x: hidden;*/ border: 0px; .left-list { .item { cursor: pointer; .left-list { .item { cursor: pointer; .text, .time { line-height: 30px; } .text, .time { line-height: 30px; } .text { color: black; padding-left: 10px; } .text { color: black; padding-left: 10px; } .time { color: gray; font-size: 12px; padding-left: 10px; } .time { color: gray; font-size: 12px; padding-left: 10px; } } } .card-btn-1 { margin: 8px 15px; width: 100%; border-radius: 5px; } } .card-btn-1 { margin: 8px 15px; width: 100%; border-radius: 5px; } .card-btn-2 { margin: 10px 10px; } .chatItemAnswer { .card-btn-2 { margin: 10px 10px; } .chatItemAnswer { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; } } .textItemAnswer { background-color: #373739; } .textItemAnswer { background-color: #373739; } .center { position: relative; .center-title { line-height: 60px; font-size: 25px; font-family: 黑体; color: deepskyblue; } .center { position: relative; .center-content { font-size: 14px; color: gray; } .center-title { line-height: 60px; font-size: 25px; font-family: 黑体; color: deepskyblue; .center-question { margin-top: 20px; display: flex; justify-content: space-between; .center-question-left { margin-top: 5px; margin-left: 20px; } .center-content { font-size: 14px; color: gray; .center-question-right { margin-right: 20px; } } .center-list { margin-top: 10px; .center-question { margin-top: 20px; display: flex; justify-content: space-between; .center-question-left { margin-top: 5px; margin-left: 20px; } .center-question-right { margin-right: 20px; } } .center-list { .item { border-radius: 10px; margin-top: 10px; .item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 60px; } } flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { padding-top: 5px; border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 60px; } } :deep(.arco-textarea-wrapper) { padding-top: 5px; } } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .icon-user-jpg { border: 1px solid #d9d9d9; } } .right { .right-top { display: flex; justify-content: space-between; .right-title { font-size: 25px; color: black; } .icon-user-jpg { border: 1px solid #d9d9d9; .right-btn { position: relative; left: 20px; top: 0px; } } .right { .right-top { display: flex; justify-content: space-between; .right-title { font-size: 25px; color: black; } .right-btn { position: relative; left: 20px; top: 0px; } } .right-tag { margin-top: 20px; display: flex; justify-content: space-between; flex-wrap: wrap; :deep(.arco-btn-primary) { margin-bottom: 10px !important; &:hover { background-color: #e9f3ff; color: rgb(22, 93, 255); } } } .right-list { .right-item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; .right-tag { margin-top: 20px; display: flex; justify-content: space-between; flex-wrap: wrap; :deep(.arco-btn-primary) { margin-bottom: 10px !important; &:hover { background-color: #e9f3ff; color: rgb(22, 93, 255); } } } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } .right-list { .right-item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } } } </style> src/views/sessionManager/index1.vue
New file @@ -0,0 +1,844 @@ <template> <div class="container"> <AddSession :modalObj="modalObj" @addSession="addSession" :dialogId="dialogId" ></AddSession> <a-card class="top-title">AI会话记录</a-card> <a-row :gutter="[5, 5]" style="margin-top: 3px"> <a-col :span="6"> <a-card class="left-select"> <a-select v-model="selectValue" :options="dialogs" :field-names="fieldNames" @change="dialogChange" > </a-select> </a-card> <a-card style="height: 50px"> <template #cover> <div style="display: flex; justify-content: space-between"> <a-button type="primary" shape="round" class="card-btn-1" @click="modalObj.add = true" > +新建会话 </a-button> <!-- <a-button type="text" shape="circle" class="card-btn-2"> <icon-search /> </a-button> --> </div> </template> </a-card> <a-card class="left"> <a-scrollbar class="left-list" style=" height: calc(100vh - 350px); overflow-y: auto; overflow-x: hidden; " > <div class="item left-list-item" v-for="session in sessionList" @click="querySessionDetail(session)" :class="{ isLeftActive: activeSessionId === session.id }" > <div class="text" :class="{ time: theme === 'dark' }" >{{ session.name }} </div> <div class="time" >{{ moment(new Date(session.create_time)).format( 'YYYY-MM-DD HH:mm:ss' ) }} </div> </div> </a-scrollbar> </a-card> </a-col> <!-- 智能体会话--> <a-col :span="18" v-show="agentType == '1'"> <a-card class="center"> <!-- <div v-if="sessionDetailList.length === 0" style=" width: 90%; overflow: auto; height: 65vh; margin: 0px auto 20px; " > <div class="center-title">智能问答</div> <div class="center-content"> 我可以理解和学习人类的语言,具备多轮对话的能力,现在和我开始交流吧~ </div> <div class="center-question"> <div class="center-question-left">试一试这样问我</div> <div class="center-question-right"> <a-button type="primary">换一换</a-button> </div> </div> <a-row justify="space-around" class="center-list"> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> <a-col :span="7" class="item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </a-col> </a-row> </div> --> <a-scrollbar ref="scrollbar" id="home" class="chat-list" style=" width: 90%; overflow: auto; height: calc(100vh - 380px); margin: 0px auto 20px; " > <div class="chat-item" v-for="sessionDetail in sessionDetailList"> <a-comment v-if="sessionDetail.role === 'user'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-user.jpg" alt="本地图片" /> </template> <template #content> <div :class="{ chartUserText: theme === 'light' }" >{{ sessionDetail.content }} </div> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'assistant'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-card :class="{ chatItemAnswer: theme === 'light' }"> <div :class="{ light: theme === 'light' }" >{{ sessionDetail.content }} </div> </a-card> </template> </a-comment> <a-comment v-else-if="sessionDetail.role === 'last'"> <template #avatar> <img class="icon-user-jpg" src="../../assets/images/icon-chart.png" alt="本地图片" /> </template> <template #content> <a-textarea readonly auto-size v-model="displayedText" :class="{ chatItemAnswer: theme === 'light' }" > </a-textarea> </template> </a-comment> </div> <!-- <div class="chartStart" v-if="isStart" @click="startChat" >停止生成</div > <div class="chartStart v-else" @click="stopChat">重新生成</div> --> </a-scrollbar> <div class="chat_bottom"> <div class="center-bottom"> <!-- <a-textarea v-model="inputMsg" @keydown.shift.enter="sendMessage" style="height: 180px" placeholder="输入您想了解的内容,Shift+Enter发送" :max-length="500" allow-clear show-word-limit > </a-textarea> --> <a-textarea v-model="inputMsg" @keydown.shift.enter="handleShiftEnter" @keydown.enter="sendMessage" placeholder="输入您想了解的内容,Shift+Enter换行,Enter发送" allow-clear show-word-limit :disabled="chatDis" :class="{ textItemAnswer: theme === 'dark' }" :auto-size="{ minRows: 12, maxRows: 5, }" /> <div class="btn-send"> <!-- <icon-send size="32" /> --> <a-button :disabled="chatDis" @click="sentClick" type="primary" style="border-radius: 24px" :loading="loading" size="large" >发送</a-button > </div> </div> </div> </a-card> </a-col> <a-col :span="18" v-show="agentType == '2'"> <a-card class="center"> <agentSession ></agentSession> </a-card> </a-col> <!-- <a-col :span="5"> <a-card class="right"> <div class="right-top"> <div class="right-title">数智库</div> <div class="right-btn"> <a-button type="outline" shape="circle" style="border: none"> <icon-search /> </a-button> <a-button type="outline" shape="circle" style="border: none; margin-left: -10px" > <icon-close /> </a-button> </div> </div> <div class="right-tag"> <a-button type="primary" size="mini" class="btn">全部 </a-button> <a-button type="outline" size="mini" class="btn" >文档创作 </a-button> <a-button type="outline" size="mini" class="btn" >知识学习 </a-button> <a-button type="outline" size="mini" class="btn" >效率提升 </a-button> </div> <div class="right-list"> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> <div class="right-item"> <div class="item-title"> <IconTiktokColor></IconTiktokColor> 抖音带货脚本 </div> <div class="item-content" :class="{ dark: theme === 'dark' }"> 编写引人注目且具有说服力的、适用于产品的... </div> </div> </div> </a-card> </a-col> --> </a-row> </div> </template> <script setup lang="ts"> import { IconClose, IconSearch, IconTiktokColor, } from '@arco-design/web-vue/es/icon'; import { useAppStore, useUserStore } from '@/store'; import { computed, nextTick, onMounted, watch, reactive, ref } from 'vue'; import { Message } from '@arco-design/web-vue'; import { EventSourceParserStream } from 'eventsource-parser/stream'; import moment from 'moment'; import AddSession from '@/views/sessionManager/components/addSession.vue'; import agentSession from '@/views/sessionManager/components/agentSession.vue'; import { chatApi, getDialogListApi, getSessionDetailsApi, sessionListApi, } from '@/api/session'; import { getAuthorization } from '@/utils/auth'; import { queryCanvasList } from "@/api/Agent"; import { getAgentSessionDetailsApi } from "@/api/agentSession"; const sessionDetailList = ref([]); //根据会话id出来的会话详情 const sessionList = ref([]); //会话列表 const modalObj = reactive({ add: false }); const dialogId = ref(''); const chatDis = ref(false); const loading = ref(false); const agentType = ref('1'); const currIndex = ref(0); const displayedText = ref(''); // 正在显示的文字 let timer: number | null = null; const streamStr = ref(''); const inputMsg = ref(''); const activeSessionId = ref(''); const fieldNames = { value: 'id', label: 'name' }; const dialogs = ref([]); const dialogObj = reactive({}); const agentList = ref([]); const selectValue = ref(''); const sectionList = ref({}); const DialogList = async () => { const { code, data } = await getDialogListApi(); if (code === 200) { if (data) { selectValue.value = data[0].id; dialogs.value = data.map((item) => { return { ...item, type: 1,//智能体 } }); console.log(data, 'dialogs'); queryCanvas(); // querySessionList(); } } }; const queryCanvas = async (params = {}) => { try { const { data } = await queryCanvasList(params); console.log(data, 'agent'); agentList.value = data.map((item) => { return { ...item, name: item.title, type: 2,//agent } }); // 合并数组 dialogs.value = dialogs.value.concat(agentList.value); // 判断当前是智能体或agent // console.log(val, 'val'); if(dialogs.value.length>0){ dialogChange(dialogs.value[0].id); } } catch (err) { // you can report use errorHandler or other } finally { } }; const handleShiftEnter = (event) => { event.preventDefault(); inputMsg.value += '\n'; }; const dialogChange = (val) => { // 判断当前是智能体或agent // console.log(val, 'val'); dialogId.value = val; dialogs.value.forEach((item) => { if (item.id === val) { Object.assign(dialogObj, item) } }) console.log(dialogObj.type, 'dialogObj'); if (dialogObj.type == 1) { agentType.value = 1; querySessionList(); } else { agentType.value = 2; queryAgentSessionList() } // querySessionList(); }; const sentClick = () => { sendMessage('click'); }; const sendMessage = async (event) => { if (event.keyCode == 13 || event === 'click') { if (!event.shiftKey) { //只有enter没有shift,或进行你的其他逻辑 if (event !== 'click') { event.preventDefault(); // 阻止默认行为,即不换行 } chatDis.value = true; loading.value = true; if (!activeSessionId.value) { Message.warning('请选择会话'); chatDis.value = false; loading.value = false; return; } // if (displayedText.value) { // querySessionList(); // } if (inputMsg.value) { sessionDetailList.value.push({ content: inputMsg.value, role: 'user', }); sessionDetailList.value.push({ role: 'last' }); refreshScroll(); const response = await fetch( '/api/tech/cloudminds/query?modeltype=localragflow', { method: 'POST', headers: { 'Authorization': getAuthorization(), 'Content-Type': 'application/json', }, body: JSON.stringify({ conversation_id: activeSessionId.value, messages: inputMsg.value, }), } ); const reader = response?.body ?.pipeThrough(new TextDecoderStream()) .pipeThrough(new EventSourceParserStream()) .getReader(); currIndex.value = 0; while (true) { const x = await reader?.read(); if (x) { const { done, value } = x; console.log(x, 999); try { const val = JSON.parse(value?.data || ''); const d = val?.data; if (typeof d !== 'boolean') { console.info('data:', d); streamStr.value = d.content; startDisplayStr(); } } catch (e) { console.warn(e); } if (done) { console.info('done'); displayedText.value = ''; querySessionDetail(sectionList.value); break; } } } chatDis.value = false; loading.value = false; inputMsg.value = ''; } else { Message.warning('消息不能为空'); chatDis.value = false; loading.value = false; } } } }; const querySessionDetail = async (session) => { sectionList.value = session; activeSessionId.value = session.id; const { code, data } = await getSessionDetailsApi(session.id); if (code === 200) { sessionDetailList.value = data.message; refreshScroll(); //刷新滚动条位置 } }; const queryAgentSessionDetail = async (id) => { const { code, data } = await getAgentSessionDetailsApi(id); if (code === 200) { // sessionDetailList.value = data.message; // refreshScroll(); //刷新滚动条位置 } }; const scrollbar = ref(null); const refreshScroll = () => { nextTick(() => { const container = document.getElementById('home'); scrollbar.value.scrollTop(container.scrollHeight); }); }; // 查询会话列表 const querySessionList = async () => { const { code, data } = await sessionListApi(selectValue.value); if (code === 200) { sessionList.value = data; activeSessionId.value = data[0].id; //默认选择第一个 querySessionDetail(data[0]); } else { Message.warning('查询失败'); } }; // 查询会话列表 const queryAgentSessionList = async () => { // const { code, data } = await sessionListApi(selectValue.value); // if (code === 200) { // // } else { // Message.warning('查询失败'); // } queryAgentSessionDetail(selectValue.value); }; //新增会话之后刷新会话列表 const addSession = () => { querySessionList(); }; onMounted(() => { DialogList(); }); const appStore = useAppStore(); const theme = computed(() => { return appStore.theme; }); //文字动态输出 const startDisplayStr = () => { if (timer) { clearTimeout(timer!); } const res = streamStr.value; // 将数组中的字符串拼接起来 if (currIndex.value < res.length) { displayedText.value += res[currIndex.value]; currIndex.value++; setTimeout(startDisplayStr, 100); refreshScroll(); } else { clearTimeout(timer!); timer = null; } }; </script> <style scoped lang="scss"> .isLeftActive { background-color: #ededf5; } .left-list-item { margin-bottom: 2px; } .left-list-item:hover { background-color: #ededf5; } .dark { color: gray !important; } .container { .top-title { line-height: 32px; font-size: 16px; color: #333; } .left-select { :deep(.arco-select-view-single) { border-radius: 5px; } } .center, .right { box-sizing: border-box; height: calc(100vh - 200px); } .left { /* height: calc(100vh - 160px); overflow-y: auto; overflow-x: hidden;*/ border: 0px; .left-list { .item { cursor: pointer; .text, .time { line-height: 30px; } .text { color: black; padding-left: 10px; } .time { color: gray; font-size: 12px; padding-left: 10px; } } } } .card-btn-1 { margin: 8px 15px; width: 100%; border-radius: 5px; } .card-btn-2 { margin: 10px 10px; } .chatItemAnswer { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; .light { box-sizing: border-box; background: #f1f1f1; border-radius: 14px; } } .textItemAnswer { background-color: #373739; } .center { position: relative; .center-title { line-height: 60px; font-size: 25px; font-family: 黑体; color: deepskyblue; } .center-content { font-size: 14px; color: gray; } .center-question { margin-top: 20px; display: flex; justify-content: space-between; .center-question-left { margin-top: 5px; margin-left: 20px; } .center-question-right { margin-right: 20px; } } .center-list { margin-top: 10px; .item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-content { color: #666; } .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } .chartStart { color: #4955f5; cursor: pointer; font-family: PingFangSC-Medium; font-size: 12px; font-weight: 500; } .chat_bottom { display: flex; align-items: center; .center-bottom { // position: absolute; // width: 90%; // bottom: 20px; // left: 5%; background: #fff; border: 1px solid #00000014; border-radius: 24px; display: flex; flex: 1 1; flex-direction: column; overflow: hidden; position: relative; // padding-top:10px; :deep(.arco-textarea-wrapper) { border-radius: 24px; } .btn-send { position: absolute !important; right: 10px; bottom: 10px; z-index: 10; } :deep(.arco-btn-size-large) { height: 28px; width: 50px; } } :deep(.arco-textarea-wrapper) { padding-top: 5px; } } } .chat-item { padding: 10px 0; .chartUserText { font-weight: 600; font-size: 14px; color: #333; margin-top: 4px; } .icon-user-jpg { border: 1px solid #d9d9d9; } } .right { .right-top { display: flex; justify-content: space-between; .right-title { font-size: 25px; color: black; } .right-btn { position: relative; left: 20px; top: 0px; } } .right-tag { margin-top: 20px; display: flex; justify-content: space-between; flex-wrap: wrap; :deep(.arco-btn-primary) { margin-bottom: 10px !important; &:hover { background-color: #e9f3ff; color: rgb(22, 93, 255); } } } .right-list { .right-item { border-radius: 10px; margin-top: 10px; padding: 10px; min-height: 120px; background-color: #e9f3ff; .item-title { text-align: center; line-height: 40px; font-size: 20px; font-family: 黑体; color: #333; } } } } } </style>