New file |
| | |
| | | <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, nextTick } from "vue"; |
| | | import { Message } from '@arco-design/web-vue'; |
| | | import { useAppStore } from "@/store"; |
| | | import { getAuthorization } from "@/utils/auth"; |
| | | import { EventSourceParserStream } from 'eventsource-parser/stream'; |
| | | import { agentResetApi, agentSetApi, getAgentSessionDetailsApi } from "@/api/agentSession"; |
| | | const props = defineProps({ |
| | | modalObj: Object, |
| | | }); |
| | | |
| | | // const emit = defineEmits(['addSession']); |
| | | |
| | | const sessionDetailList = ref([]); //根据会话id出来的会话详情 |
| | | const sessionList = ref([]); //会话列表 |
| | | const modalObj = reactive({ }); |
| | | 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 agentObj = reactive({}); |
| | | const agentList = ref([]); |
| | | const selectValue = ref(''); |
| | | const sectionList = ref({}); |
| | | |
| | | const appStore = useAppStore(); |
| | | const theme = computed(() => { |
| | | return appStore.theme; |
| | | }); |
| | | |
| | | |
| | | |
| | | // 初始化页面 |
| | | const initPage = async () => { |
| | | agentSet(); |
| | | agentReset(); |
| | | agentCompletion(); |
| | | queryAgentSessionDetail(agentObj.id); |
| | | }; |
| | | |
| | | // 调用set方法 |
| | | const agentSet = async () => { |
| | | const res = await agentSetApi({ |
| | | id: agentObj.id, |
| | | title: agentObj.title, |
| | | dsl: agentObj.dsl, |
| | | }); |
| | | if (res.code === 200) { |
| | | // Message.success('修改成功'); |
| | | } |
| | | } |
| | | // 调用reset方法 |
| | | const agentReset = async () => { |
| | | const res = await agentResetApi({ |
| | | id: agentObj.id, |
| | | }); |
| | | if (res.code === 200) { |
| | | // Message.success('修改成功'); |
| | | } |
| | | } |
| | | // 调用completion方法 |
| | | const agentCompletion = async () => { |
| | | const response = await fetch( |
| | | '/api/v1/canvas/completion', |
| | | { |
| | | method: 'POST', |
| | | headers: { |
| | | 'Authorization': getAuthorization(), |
| | | 'Content-Type': 'application/json', |
| | | }, |
| | | body: JSON.stringify({ |
| | | id: agentObj.id |
| | | }), |
| | | } |
| | | ); |
| | | |
| | | 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 = ''; |
| | | queryAgentSessionDetail(agentObj.id); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | chatDis.value = false; |
| | | loading.value = false; |
| | | inputMsg.value = ''; |
| | | } |
| | | // 调用get方法 |
| | | |
| | | const queryAgentSessionDetail = async (id) => { |
| | | const { code, data } = await getAgentSessionDetailsApi(id); |
| | | if (code == 0) { |
| | | console.log(data,'会话详情'); |
| | | sessionDetailList.value = data.dsl.messages; |
| | | // refreshScroll(); //刷新滚动条位置 |
| | | } |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | 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/v1/canvas/completion', |
| | | { |
| | | 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 = ''; |
| | | queryAgentSessionDetail(agentObj.id); |
| | | 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'; |
| | | }; |
| | | |
| | | |
| | | |
| | | //文字动态输出 |
| | | 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 scrollbar = ref(null); |
| | | const refreshScroll = () => { |
| | | nextTick(() => { |
| | | const container = document.getElementById('home'); |
| | | scrollbar.value.scrollTop(container.scrollHeight); |
| | | }); |
| | | }; |
| | | |
| | | |
| | | onMounted(() => { |
| | | |
| | | }); |
| | | |
| | | watch( |
| | | () => props.modalObj, |
| | | (newVal, oldVal) => { |
| | | console.log(newVal,'监听变化'); |
| | | Object.assign(agentObj, newVal); |
| | | //调用agent初始化方法 |
| | | if(JSON.stringify(newVal) != '{}'){ |
| | | initPage(); |
| | | } |
| | | },{ |
| | | immediate: true, |
| | | deep: true |
| | | } |
| | | ); |
| | | </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> |