|
@@ -1,1040 +0,0 @@
|
|
|
-<template>
|
|
|
|
|
- <div class="conversation-chat-container">
|
|
|
|
|
- <div class="conversation-chat-main" ref="chat_scrren_container">
|
|
|
|
|
- <DialogHeader class="conversation-chat-header" :agent_info="CHAT_AGENT_INFOMATION" :style="style_header" />
|
|
|
|
|
-
|
|
|
|
|
- <div class="conversation-chat-content" :style="style_body">
|
|
|
|
|
- <div class="conversation-chat-main-container">
|
|
|
|
|
- <template v-if="show_welcome">
|
|
|
|
|
- <!-- 有会话ID 获取历史记录,显示加载中状态 -->
|
|
|
|
|
- <div v-if="session_id" class="conversation-chat-area history-list-loading">
|
|
|
|
|
- <div class="scroll-left"></div>
|
|
|
|
|
- <div class="history-list-loading-container"><a-spin /></div>
|
|
|
|
|
- <div class="scroll-right"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <!-- 无会话ID,直接对话,此时可现实欢迎消息:欢迎使用报表合并、欢迎使用文档智能、欢迎使用小数绘图 -->
|
|
|
|
|
- <DialogWelcome v-if="!session_id" class="conversation-chat-area" :agent_info="CHAT_AGENT_INFOMATION" />
|
|
|
|
|
- </template>
|
|
|
|
|
-
|
|
|
|
|
- <DialogList
|
|
|
|
|
- v-if="!show_welcome"
|
|
|
|
|
- ref="list_scroller"
|
|
|
|
|
- class="conversation-chat-area"
|
|
|
|
|
- :height="list_height"
|
|
|
|
|
- :width="chat_width / 2"
|
|
|
|
|
- :agent_id="agent_id"
|
|
|
|
|
- :list="chat_display_list"
|
|
|
|
|
- :suggestion="chat_suggestion"
|
|
|
|
|
- :answer_action_list="answer_action_list"
|
|
|
|
|
- @share="handleChatShare"
|
|
|
|
|
- @submit="handleSuggestSubmit"
|
|
|
|
|
- >
|
|
|
|
|
- <template v-slot:left>
|
|
|
|
|
- <div class="scroll-left"></div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template v-slot:right>
|
|
|
|
|
- <div class="scroll-right"></div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </DialogList>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <DialogInput
|
|
|
|
|
- ref="dialog_input"
|
|
|
|
|
- class="conversation-chat-input"
|
|
|
|
|
- :style="input_style"
|
|
|
|
|
- :disabled="input_disabled"
|
|
|
|
|
- :enable_text_input="enable_text_input"
|
|
|
|
|
- :enable_file_upload="enable_file_upload"
|
|
|
|
|
- :file_accept="file_accept"
|
|
|
|
|
- :width="chat_width / 2"
|
|
|
|
|
- @sizeChange="handleInputSizeChange"
|
|
|
|
|
- @submit="handleInputSubmit"
|
|
|
|
|
- >
|
|
|
|
|
- <template v-if="show_input_others_opts" v-slot:top>
|
|
|
|
|
- <DialogInputTop
|
|
|
|
|
- :menu_id="current_menu_id"
|
|
|
|
|
- :agent_info="CHAT_AGENT_INFOMATION"
|
|
|
|
|
- :user_input_form="conversation_params.user_input_form"
|
|
|
|
|
- ref="dialog_input_top"
|
|
|
|
|
- />
|
|
|
|
|
- </template>
|
|
|
|
|
-
|
|
|
|
|
- <template v-slot:left>
|
|
|
|
|
- <div class="scroll-left"></div>
|
|
|
|
|
- </template>
|
|
|
|
|
- <template v-slot:right>
|
|
|
|
|
- <div class="scroll-right"></div>
|
|
|
|
|
- </template>
|
|
|
|
|
- </DialogInput>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <DialogFooter class="conversation-chat-footer" :style="style_footer" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script setup>
|
|
|
|
|
-import { ref, onMounted, onUnmounted, computed, nextTick } from 'vue';
|
|
|
|
|
-import { useI18n } from 'vue-i18n';
|
|
|
|
|
-// import { Message } from '@arco-design/web-vue';
|
|
|
|
|
-import { Message } from '../../3rd-libs/arco-vue-libs';
|
|
|
|
|
-
|
|
|
|
|
-import { jsonStringify } from '../../utils/utils';
|
|
|
|
|
-
|
|
|
|
|
-import DialogHeader from '../../modules/conversation-chat/DialogHeader.vue';
|
|
|
|
|
-import DialogWelcome from '../../modules/conversation-chat/DialogWelcome.vue';
|
|
|
|
|
-import DialogList from '../../modules/conversation-chat/DialogList.vue';
|
|
|
|
|
-import DialogInput from '../../modules/conversation-chat/DialogInput.vue';
|
|
|
|
|
-// import DialogInputAttachedAttrs from '../../modules/conversation-chat/DialogInputAttachedAttrs.vue';
|
|
|
|
|
-import DialogInputTop from '../../modules/conversation-chat/DialogInputTop.vue';
|
|
|
|
|
-import DialogFooter from '../../modules/conversation-chat/DialogFooter.vue';
|
|
|
|
|
-
|
|
|
|
|
-import useWebSocketChat from '../../modules/conversation-dialog/use-websocket-chat';
|
|
|
|
|
-import { getHistoryLogs, getDialogChatId, uploadFileByAgentInfo } from '../../modules/conversation-dialog/api-dialog';
|
|
|
|
|
-
|
|
|
|
|
-import formatDialogSendOptions from '../../modules/conversation-dialog/format-dialog-send-options';
|
|
|
|
|
-
|
|
|
|
|
-import formatDialogHistory from '../../modules/conversation-dialog/format-dialog-history';
|
|
|
|
|
-
|
|
|
|
|
-import formatDialogQuestionByClient from '../../modules/conversation-dialog/format-dialog-question-by-client';
|
|
|
|
|
-import formatDialogAnswerForLoading from '../../modules/conversation-dialog/format-dialog-answer-for-loading';
|
|
|
|
|
-import formatDialogAnswerForException from '../../modules/conversation-dialog/format-dialog-answer-for-exception';
|
|
|
|
|
-import formatDialogAnswerFromWebsocketReceiveMessage from '../../modules/conversation-dialog/format-dialog-answer-from-websocket_receive_message';
|
|
|
|
|
-
|
|
|
|
|
-import useEleSizeV1 from '../../utils/use-ele-size-v1';
|
|
|
|
|
-import useInputClear from './use-input-clear';
|
|
|
|
|
-import useInputGetOptions from './use-input-get-options';
|
|
|
|
|
-import useScroll2Bottom from './use-scroll-bottom';
|
|
|
|
|
-
|
|
|
|
|
-const props = defineProps({
|
|
|
|
|
- /**
|
|
|
|
|
- * 智能体的ID
|
|
|
|
|
- * 此参数必须传递
|
|
|
|
|
- */
|
|
|
|
|
- agent_id: String,
|
|
|
|
|
- /**
|
|
|
|
|
- * 智能体的一些关键信息
|
|
|
|
|
- */
|
|
|
|
|
- CHAT_AGENT_INFOMATION: Object,
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 会话ID
|
|
|
|
|
- * 如果有此参数,则意味着获取该对话的记录内容
|
|
|
|
|
- */
|
|
|
|
|
- session_id: String,
|
|
|
|
|
- /**
|
|
|
|
|
- * 动态表单数据
|
|
|
|
|
- * 根据智能体的agent_id 获取到动态表单数据
|
|
|
|
|
- * */
|
|
|
|
|
- conversation_params: Object
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const i18n = useI18n();
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * input的一些操作方法
|
|
|
|
|
- */
|
|
|
|
|
-const inputClearFn1 = useInputClear('dialog_input'); // 主表单清空
|
|
|
|
|
-const inputClearFn2 = useInputClear('dialog_input_top'); // 顶部附加表单的数据
|
|
|
|
|
-const getInputTopOptions = useInputGetOptions('dialog_input_top'); // 获取附加表单参数的方法
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话区域滚动到底部
|
|
|
|
|
- */
|
|
|
|
|
-const scroll2Bottom = useScroll2Bottom('list_scroller');
|
|
|
|
|
-const scroll2ListBottom = () => {
|
|
|
|
|
- nextTick(() => {
|
|
|
|
|
- scroll2Bottom();
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 整个对话区域的一些尺寸定义
|
|
|
|
|
- */
|
|
|
|
|
-const header_height = 30;
|
|
|
|
|
-const footer_height = 30;
|
|
|
|
|
-const input_height = ref(100);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 获取到对话区域的宽度和高度
|
|
|
|
|
- */
|
|
|
|
|
-const { width: chat_width, height: chat_height } = useEleSizeV1('chat_scrren_container');
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话列表区域的高度
|
|
|
|
|
- */
|
|
|
|
|
-const list_height = computed(() => {
|
|
|
|
|
- return chat_height.value - header_height - footer_height - input_height.value - 20;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const input_style = computed(() => {
|
|
|
|
|
- return {
|
|
|
|
|
- // height: input_height + 'px'
|
|
|
|
|
- };
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-const style_header = computed(() => {
|
|
|
|
|
- return { height: header_height + 'px' };
|
|
|
|
|
-});
|
|
|
|
|
-const style_body = computed(() => {
|
|
|
|
|
- return { height: chat_height - 30 - 30 + 'px' };
|
|
|
|
|
-});
|
|
|
|
|
-const style_footer = computed(() => {
|
|
|
|
|
- return { height: footer_height + 'px' };
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// ------------------------------------------------------------------
|
|
|
|
|
-// ------------------------------------------------------------------
|
|
|
|
|
-// ------------------------------------------------------------------
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话数据列表
|
|
|
|
|
- */
|
|
|
|
|
-const chat_log_list = ref([]); // 历史对话的数据列表
|
|
|
|
|
-const chat_new_list = ref([]); // 新对话的数据列表
|
|
|
|
|
-const chat_suggestion = ref([]); // 对话推荐项目
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 当前对话使用的ID
|
|
|
|
|
- * 当用户有输入后,点击提交,此时创建新对话,新对话的ID为此ID
|
|
|
|
|
- * 之后用户可以一直使用此ID进行对话。
|
|
|
|
|
- */
|
|
|
|
|
-const chat_round_id = ref(null);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话发起的次数记录
|
|
|
|
|
- * 用户每发起一次会话,此值会加1
|
|
|
|
|
- */
|
|
|
|
|
-const chat_round_sent_count = ref(0);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 新对话,客户端的提问数据
|
|
|
|
|
- * 用以临时保存客户端的提问数据
|
|
|
|
|
- * 每当用户确认进行提问时,将新提问赋给此值
|
|
|
|
|
- * 一旦对话消息通过websocket发送到服务器,此值可清空
|
|
|
|
|
- */
|
|
|
|
|
-const chat_new_question = ref({});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 界面展示的对话列表
|
|
|
|
|
- * 当用户从历史会话进入时,历史会话记录有数据,当用户基于历史会话进行对话时,新会话也有数据,
|
|
|
|
|
- * 当用户从agent进入时,仅有新会话,没有历史会话
|
|
|
|
|
- *
|
|
|
|
|
- * 页面渲染的会话列表是 历史会话 + 新会话数据
|
|
|
|
|
- */
|
|
|
|
|
-const chat_display_list = computed(() => {
|
|
|
|
|
- return [].concat(chat_log_list.value, chat_new_list.value);
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 是否禁用输入组件
|
|
|
|
|
- */
|
|
|
|
|
-const input_disabled = ref(false);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 菜单ID编号
|
|
|
|
|
- * 1 报表合并
|
|
|
|
|
- * 2 知识问答
|
|
|
|
|
- * 3 文档智能
|
|
|
|
|
- * 4 智能问答
|
|
|
|
|
- * 5 智能数据
|
|
|
|
|
- * 6 小数绘图
|
|
|
|
|
- * 7 文档出卷
|
|
|
|
|
- * 8 出题组卷
|
|
|
|
|
- * 9 文档报告
|
|
|
|
|
- */
|
|
|
|
|
-const current_menu_id = computed(() => {
|
|
|
|
|
- const agent = props.CHAT_AGENT_INFOMATION.agent;
|
|
|
|
|
- const menu_id = agent.menuId / 1;
|
|
|
|
|
- return menu_id;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 是否显示欢迎页
|
|
|
|
|
- * 当用户进入对话页面,还未正式开始对话时
|
|
|
|
|
- * 对话区域会显示欢迎内容
|
|
|
|
|
- * 当用户开始对话时,欢迎内容消失,对话列表开始展示
|
|
|
|
|
- */
|
|
|
|
|
-const show_welcome = ref(true);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 表单区域:是否显示附加参数表单
|
|
|
|
|
- *
|
|
|
|
|
- * 根据动态表单返回的数据中user_input_form中是否有数据来控制是否显示附加参数表单
|
|
|
|
|
- */
|
|
|
|
|
-const show_input_others_opts = computed(() => {
|
|
|
|
|
- if (
|
|
|
|
|
- props.conversation_params &&
|
|
|
|
|
- props.conversation_params.user_input_form &&
|
|
|
|
|
- props.conversation_params.user_input_form.length
|
|
|
|
|
- ) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- return false;
|
|
|
|
|
-
|
|
|
|
|
- // const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- // /**
|
|
|
|
|
- // * 7 文档出卷
|
|
|
|
|
- // * 9 文档报告
|
|
|
|
|
- // */
|
|
|
|
|
- // if (menu_id === 7 || menu_id === 9) {
|
|
|
|
|
- // return true;
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // return false;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 是否支持文本输入
|
|
|
|
|
- * 返回 true,输入区域将不存在文本输入框
|
|
|
|
|
- * 返回 false,输入区域将可以进行文本输入
|
|
|
|
|
- *
|
|
|
|
|
- *通过conversation_params.retriever_resource.enabled来控制是否支持文本输入
|
|
|
|
|
- */
|
|
|
|
|
-const enable_text_input = computed(() => {
|
|
|
|
|
- let enabled = false;
|
|
|
|
|
- if (props.conversation_params && props.conversation_params.retriever_resource) {
|
|
|
|
|
- enabled = props.conversation_params.retriever_resource.enabled;
|
|
|
|
|
- console.log('enable_text_input', enabled);
|
|
|
|
|
- }
|
|
|
|
|
- return enabled;
|
|
|
|
|
- // const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- // /**
|
|
|
|
|
- // * 1 报表合并:上传几个文档,进行合并 允许 .xlsx
|
|
|
|
|
- // * 无需输入
|
|
|
|
|
- // */
|
|
|
|
|
- // if (menu_id === 1) {
|
|
|
|
|
- // return false;
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- // // if (menu_id === 7) {
|
|
|
|
|
- // // return false;
|
|
|
|
|
- // // }
|
|
|
|
|
-
|
|
|
|
|
- // return true;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话文本是否必须
|
|
|
|
|
- * 此值如果为 true,则在发起对话之前,会进行 文本的存在性检查
|
|
|
|
|
- * 此值如果为 false,则在发起对话之前,不会进行 文件存在性检查
|
|
|
|
|
- */
|
|
|
|
|
-const is_chat_text_required = computed(() => {
|
|
|
|
|
- const is_enable = enable_text_input.value;
|
|
|
|
|
- if (!is_enable) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return true;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 默认的文本值
|
|
|
|
|
- */
|
|
|
|
|
-const default_text_value = computed(() => {
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 某些情况下,无需输入文本
|
|
|
|
|
- * 但是在对话部分,需要展示用户的提问,此时可以给一个默认的文本值
|
|
|
|
|
- * 例如在报表合并情况下,仅需要上传文件,不需要输入文字
|
|
|
|
|
- */
|
|
|
|
|
- if (menu_id === 1) {
|
|
|
|
|
- return '合并Excel';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return '';
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 表单区域:是否允许文件上传
|
|
|
|
|
- * 返回 ture,输入区域将会有文件上传按钮
|
|
|
|
|
- * 返回 false,输入区域将没有文件上传按钮
|
|
|
|
|
- *
|
|
|
|
|
- * 根据 conversation_params.file_upload.enabled来控制是否允许文件上传
|
|
|
|
|
- */
|
|
|
|
|
-const enable_file_upload = computed(() => {
|
|
|
|
|
- let enabled = false;
|
|
|
|
|
- if (props.conversation_params && props.conversation_params.file_upload) {
|
|
|
|
|
- enabled = props.conversation_params.file_upload.enabled;
|
|
|
|
|
- }
|
|
|
|
|
- return enabled;
|
|
|
|
|
-
|
|
|
|
|
- // const menu_id = current_menu_id.value;
|
|
|
|
|
- // /**
|
|
|
|
|
- // * 1 报表合并:上传几个文档,进行合并 允许 .xlsx
|
|
|
|
|
- // * 3 文档智能:上传文档,让智能体进行解析 允许: .pdf/.docx
|
|
|
|
|
- // * 5 智能数据:上传Excel,生成饼状图 允许 .xlsx
|
|
|
|
|
- // * 6 小数绘图:上传图片,描述图片的内容 允许 .png/jpg/jpeg
|
|
|
|
|
- // * 9 文档报告: 允许 .docx
|
|
|
|
|
- // */
|
|
|
|
|
- // if (menu_id === 1 || menu_id === 3 || menu_id === 5 || menu_id === 6 || menu_id === 9) {
|
|
|
|
|
- // return true;
|
|
|
|
|
- // }
|
|
|
|
|
- // // 文档出卷
|
|
|
|
|
- // if (menu_id === 7) {
|
|
|
|
|
- // return true;
|
|
|
|
|
- // }
|
|
|
|
|
- // return false;
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 对话文件是否必须上传
|
|
|
|
|
- * 此值如果返回 true,则 在对话时,会进行文件检查
|
|
|
|
|
- * 此值如果返回 false,则 在对话时,不会检查文件的存在性
|
|
|
|
|
- */
|
|
|
|
|
-const is_chat_file_required = computed(() => {
|
|
|
|
|
- const is_enable = enable_file_upload.value;
|
|
|
|
|
- if (!is_enable) {
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- // 报表合并: 文件必须
|
|
|
|
|
- if (menu_id === 1) {
|
|
|
|
|
- // 当第一次会话时,要求必须上传文件
|
|
|
|
|
- // 非第一次,不做要求
|
|
|
|
|
- if (chat_round_sent_count.value === 0) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 文档出卷:文件必须上传
|
|
|
|
|
- if (menu_id === 7) {
|
|
|
|
|
- // 当第一次对话时,必须上传文件,
|
|
|
|
|
- // 非第一次,不做要求
|
|
|
|
|
- if (chat_round_sent_count.value === 0) {
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return false;
|
|
|
|
|
-});
|
|
|
|
|
-/**
|
|
|
|
|
- * 此值在 enable_file_upload === true 的前提下有效
|
|
|
|
|
- * 表单区域:文件上传允许的文件类型
|
|
|
|
|
- * 对应表单参数的 accept
|
|
|
|
|
- */
|
|
|
|
|
-const file_accept = computed(() => {
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 1 报表合并:上传几个文档,进行合并 允许 .xlsx
|
|
|
|
|
- * 3 文档智能:上传文档,让智能体进行解析 允许: .pdf/.docx
|
|
|
|
|
- * 5 智能数据:上传Excel,生成饼状图 允许 .xlsx
|
|
|
|
|
- * 6 小数绘图:上传图片,描述图片的内容 允许 .png/jpg/jpeg
|
|
|
|
|
- * 9 文档报告: 允许 .docx
|
|
|
|
|
- */
|
|
|
|
|
- if (menu_id === 1) {
|
|
|
|
|
- return '.xlsx';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (menu_id === 3) {
|
|
|
|
|
- /**
|
|
|
|
|
- * 2025-2-8 16:50
|
|
|
|
|
- * 王永新和赵源在测试deepseek时发现 文档智能 能上传的文件类型有限
|
|
|
|
|
- * 要求将accept调整为和dify的 文档 类型一致
|
|
|
|
|
- */
|
|
|
|
|
- // return '.pdf,.docx';
|
|
|
|
|
- return '.txt,.md,.markdown,.pdf,.html,.xlsx,.xls,.docx,.csv,.eml,.msg,.pptx,.ppt,.xml,.epub';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 智能数据
|
|
|
|
|
- if (menu_id === 5) {
|
|
|
|
|
- return '.xlsx,.xls,.csv';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (menu_id === 6) {
|
|
|
|
|
- return '.jpg,.jpeg,.png';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (menu_id === 9) {
|
|
|
|
|
- return '.docx';
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return '*';
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 答案可用的操作按钮
|
|
|
|
|
- * 可用的值有 [] 或者 ['copy']
|
|
|
|
|
- * 后续可能有新值添加
|
|
|
|
|
- */
|
|
|
|
|
-const answer_action_list = computed(() => {
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
-
|
|
|
|
|
- // 报表合并无操作按钮
|
|
|
|
|
- if (menu_id === 1) {
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return ['copy'];
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 内容分享操作
|
|
|
|
|
- * @param chat_id
|
|
|
|
|
- */
|
|
|
|
|
-const handleChatShare = (chat_id) => {};
|
|
|
|
|
-/**
|
|
|
|
|
- * 在聊天界面点击推荐项
|
|
|
|
|
- * 继续对话
|
|
|
|
|
- */
|
|
|
|
|
-const handleSuggestSubmit = (suggest) => {};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 处理页面中的错误消息
|
|
|
|
|
- * @param msg
|
|
|
|
|
- */
|
|
|
|
|
-const handleErrorMessage = (msg) => {
|
|
|
|
|
- Message.error({ content: msg, position: 'top' });
|
|
|
|
|
-};
|
|
|
|
|
-// ---------------------------------------------
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 异常处理
|
|
|
|
|
- * 异常处理过程是一个会话模拟的过程
|
|
|
|
|
- * 先在界面上显示加载动画,然后紧接着显示错误提示
|
|
|
|
|
- * 让用户感知到智能体有处理数据的过程
|
|
|
|
|
- * 所以在异常处理阶段,有一些需要处理的关键点,比如欢迎界面的显示(如果在显示,则需要隐藏,否则对话显示不了)
|
|
|
|
|
- * @param exceptionInfo
|
|
|
|
|
- */
|
|
|
|
|
-const handleChatRequestionException = (exceptionInfo) => {
|
|
|
|
|
- // 已结束
|
|
|
|
|
- const answerData = formatDialogAnswerForException(props.CHAT_AGENT_INFOMATION, exceptionInfo);
|
|
|
|
|
-
|
|
|
|
|
- // 原来的最后一项弹出
|
|
|
|
|
- chat_new_list.value.pop();
|
|
|
|
|
- // 添加新的最后一项
|
|
|
|
|
- chat_new_list.value.push(answerData);
|
|
|
|
|
-
|
|
|
|
|
- // 对话已结束:文件上传、文本输入区域放开使用
|
|
|
|
|
- input_disabled.value = false;
|
|
|
|
|
- // 对话区域滚动到底部
|
|
|
|
|
- scroll2ListBottom();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 输入区域的尺寸发生了变化
|
|
|
|
|
- * 整个对话区域的尺寸也需要依据此变化而变化
|
|
|
|
|
- * @param size
|
|
|
|
|
- */
|
|
|
|
|
-const handleInputSizeChange = (size) => {
|
|
|
|
|
- const input_width = size.width;
|
|
|
|
|
- // const input_height = size.height;
|
|
|
|
|
-
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- input_height.value = size.height;
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 输入已经确认要提交了
|
|
|
|
|
- *
|
|
|
|
|
- * 1. 创建会话 get-chat-id
|
|
|
|
|
- * 2. 文件上传 如果有文件,则需要文件上传,没有文件,则不需要文件上传
|
|
|
|
|
- * 3. 连接socket,socket 发送文本与文件数据内容
|
|
|
|
|
- * 4. 接收 socket 返回的数据项
|
|
|
|
|
- */
|
|
|
|
|
-const handleInputSubmit = async (options) => {
|
|
|
|
|
- let ask_text = options.text; // 文本
|
|
|
|
|
- const ask_files = options.files; // 文件列表
|
|
|
|
|
-
|
|
|
|
|
- // 用户选定的文件数量
|
|
|
|
|
- const file_total = ask_files.length;
|
|
|
|
|
-
|
|
|
|
|
- const is_text_required = is_chat_text_required.value;
|
|
|
|
|
- const is_file_required = is_chat_file_required.value;
|
|
|
|
|
-
|
|
|
|
|
- // 检查对话文字是否必须
|
|
|
|
|
- if (is_text_required) {
|
|
|
|
|
- if (!ask_text) {
|
|
|
|
|
- const msg = i18n.t('conversation.please_input_text');
|
|
|
|
|
-
|
|
|
|
|
- handleErrorMessage(msg);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ask_text = default_text_value.value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 检查对话文件是否必须
|
|
|
|
|
- if (is_file_required) {
|
|
|
|
|
- if (file_total === 0) {
|
|
|
|
|
- const msg = i18n.t('conversation.please_upload_file');
|
|
|
|
|
-
|
|
|
|
|
- handleErrorMessage(msg);
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------消息发送前的准备工作
|
|
|
|
|
- // 保存附加参数数据,由于下方有一个 inputClearFn2() 方法的执行
|
|
|
|
|
- // 会将所有数据清空,故附件参数需要在清理之前保存
|
|
|
|
|
- const other_options = getInputTopOptions();
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 当新消息发送时
|
|
|
|
|
- * 意味着新一轮对话的开始
|
|
|
|
|
- * 此时应该将之前一轮对话的信息进行清空
|
|
|
|
|
- *
|
|
|
|
|
- * 整理客户端提问的数据
|
|
|
|
|
- * 组装Question数据
|
|
|
|
|
- */
|
|
|
|
|
- const client_question = formatDialogQuestionByClient(ask_text, ask_files);
|
|
|
|
|
- // 保存用户的提问信息
|
|
|
|
|
- // 当消息正式发出后,将提问数据放入列表中
|
|
|
|
|
- chat_new_question.value = client_question;
|
|
|
|
|
- // 消息即将发送,记录用户的提问数据
|
|
|
|
|
- chat_new_list.value.push(chat_new_question.value);
|
|
|
|
|
- // 客户端答案数据(loading状态),客户端模拟的答案数据,依据此数据展示加载中的动画
|
|
|
|
|
- const answerLoadingMsg = formatDialogAnswerForLoading(props.CHAT_AGENT_INFOMATION);
|
|
|
|
|
- chat_new_list.value.push(answerLoadingMsg);
|
|
|
|
|
- /**
|
|
|
|
|
- * 会话已确认发送
|
|
|
|
|
- * 隐藏欢迎内容,显示对话内容
|
|
|
|
|
- */
|
|
|
|
|
- show_welcome.value = false;
|
|
|
|
|
- // 清空文本数据和文件数据,避免下一次会话冲突
|
|
|
|
|
- // 清空表单里的数据
|
|
|
|
|
- inputClearFn1();
|
|
|
|
|
- inputClearFn2();
|
|
|
|
|
- // 清空可能存在的推荐项目
|
|
|
|
|
- chat_suggestion.value = [];
|
|
|
|
|
- // 清空已保存的提问数据(提问数据已经推入数组,此处无需继续保存)
|
|
|
|
|
- chat_new_question.value = null;
|
|
|
|
|
- // 文件上传、文本输入区域禁用
|
|
|
|
|
- input_disabled.value = true;
|
|
|
|
|
- // 对话区域滚动到底部
|
|
|
|
|
- scroll2ListBottom();
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
- // --------------------------------------------
|
|
|
|
|
-
|
|
|
|
|
- const agent_id = props.agent_id;
|
|
|
|
|
-
|
|
|
|
|
- // 对话分为三个步骤
|
|
|
|
|
- // 1. 创建会话,如果已经创建,则不需要再次创建
|
|
|
|
|
- // 2. 上传文件,如果不存在文件,则无需文件上传
|
|
|
|
|
- // 3. 连接websocket
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 创建会话 get chat id
|
|
|
|
|
- * 判断对话是否已经创建
|
|
|
|
|
- * 如果对话已经创建,则使用已经创建的对话
|
|
|
|
|
- * 否则,创建新对话
|
|
|
|
|
- */
|
|
|
|
|
- if (chat_round_id.value === null) {
|
|
|
|
|
- // 未创建chat,开始创建
|
|
|
|
|
- try {
|
|
|
|
|
- const chatResult = await getDialogChatId(agent_id);
|
|
|
|
|
-
|
|
|
|
|
- if (chatResult.code / 1 === 200) {
|
|
|
|
|
- // 成功
|
|
|
|
|
- const res_data = chatResult.data;
|
|
|
|
|
-
|
|
|
|
|
- chat_round_id.value = res_data.chat_id;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 失败
|
|
|
|
|
- // 处理异常:直接在界面上显示
|
|
|
|
|
- handleChatRequestionException({
|
|
|
|
|
- message: `code: ${chatResult.code} message: ${chatResult.msg}`
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.log('err-nfsdnakiuwirea7', err);
|
|
|
|
|
-
|
|
|
|
|
- // 处理错误
|
|
|
|
|
- handleChatRequestionException({
|
|
|
|
|
- message: err.message
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 已创建chat,使用已创建的会话ID,进入下一步流程
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 文件上传后的信息
|
|
|
|
|
- let files_response = null;
|
|
|
|
|
- /**
|
|
|
|
|
- * 如果存在文件数据,则进行文件上传
|
|
|
|
|
- */
|
|
|
|
|
- if (file_total !== 0) {
|
|
|
|
|
- // 进行文件上传,获取文件信息
|
|
|
|
|
- const chat_id = chat_round_id.value;
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
- const uploadResult = await uploadFileByAgentInfo(agent_id, chat_id, ask_files, menu_id);
|
|
|
|
|
-
|
|
|
|
|
- if (uploadResult.code / 1 === 200) {
|
|
|
|
|
- // 文件上传成功
|
|
|
|
|
- files_response = uploadResult.data;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 文件上传异常
|
|
|
|
|
- handleChatRequestionException({
|
|
|
|
|
- message: `code: ${uploadResult.code} \n\n message: ${uploadResult.msg}`
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.log('err-fhskafhakfjslkfjl', err);
|
|
|
|
|
-
|
|
|
|
|
- handleChatRequestionException({
|
|
|
|
|
- message: err.message
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- // TODO: 重新组装sse参数,调用sse接口问答
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 组装参数
|
|
|
|
|
- * 组装socket参数: 文本和文件
|
|
|
|
|
- * 此方法会依据当前使用的agent,组装好传递给websocket的 基础的文本和文件的参数
|
|
|
|
|
- */
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
- const msg_obj = formatDialogSendOptions(ask_text, files_response, menu_id);
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 文本和文件的数据已经组装完成
|
|
|
|
|
- * 在某些情况下,仅有文本和文件还不够,可能还有其他控制参数
|
|
|
|
|
- * 在这种情况下,可以通过 DialogInputTop 获取到附加参数
|
|
|
|
|
- * 添加附加参数
|
|
|
|
|
- * 附加参数应该是 一个 键值对 对象,最终和和已有的 文本参数和文件参数 合并成 一个 新的键值对 对象 发送给websocket
|
|
|
|
|
- */
|
|
|
|
|
- // 获取附加表单的数据项目
|
|
|
|
|
- if (other_options && typeof other_options === 'object') {
|
|
|
|
|
- for (let key in other_options) {
|
|
|
|
|
- msg_obj[key] = other_options[key];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 所有参数已经正式组装完成,将对象格式转换为字符串
|
|
|
|
|
- */
|
|
|
|
|
- const msg_str = jsonStringify(msg_obj);
|
|
|
|
|
- // 发送websocket消息
|
|
|
|
|
- handleWSSend(chat_round_id.value, msg_str);
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * ------------------------------------------------------------
|
|
|
|
|
- * websocket定义以及初始化
|
|
|
|
|
- */
|
|
|
|
|
-const wsTrigger = {
|
|
|
|
|
- /**
|
|
|
|
|
- * 当websocket消息回来后
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
- onMessage: handleWSMessage,
|
|
|
|
|
- /**
|
|
|
|
|
- * 当websocket消息出错时
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
- onError: handleWSError,
|
|
|
|
|
- /**
|
|
|
|
|
- * 当websocket消息关闭时
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
- onClose: handleWSClose
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 初始化websocket相关的一些方法
|
|
|
|
|
- */
|
|
|
|
|
-const { initWSConnection, sendWSMessage, getWSInstance, closeWSConnect } = useWebSocketChat(wsTrigger);
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 发送websocket消息
|
|
|
|
|
- * @param chat_id - 对话记录的ID
|
|
|
|
|
- * @param value
|
|
|
|
|
- */
|
|
|
|
|
-function handleWSSend(chat_id, value) {
|
|
|
|
|
- /**
|
|
|
|
|
- * -----------------------------------------------------------
|
|
|
|
|
- * -----------------------------------------------------------
|
|
|
|
|
- * -----------------------------------------------------------
|
|
|
|
|
- * websocket消息正式发送,发送步骤:
|
|
|
|
|
- * 1. 初始化链接
|
|
|
|
|
- * 2. 发送消息
|
|
|
|
|
- */
|
|
|
|
|
-
|
|
|
|
|
- // 初始化websocket连接
|
|
|
|
|
- const menu_id = current_menu_id.value;
|
|
|
|
|
- initWSConnection(props.agent_id, chat_id, menu_id);
|
|
|
|
|
- // 发送socket连接
|
|
|
|
|
- sendWSMessage(value);
|
|
|
|
|
- // 统计会话发起的次数
|
|
|
|
|
- chat_round_sent_count.value = chat_round_sent_count.value + 1;
|
|
|
|
|
- /**
|
|
|
|
|
- * websocket消息发送流程完成
|
|
|
|
|
- * ------------------------------------------------------------
|
|
|
|
|
- * ------------------------------------------------------------
|
|
|
|
|
- * ------------------------------------------------------------
|
|
|
|
|
- */
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 接收websocket消息
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
-function handleWSMessage(e) {
|
|
|
|
|
- const res_data = e.data;
|
|
|
|
|
-
|
|
|
|
|
- // 获取当前对话中最后一个对话数据
|
|
|
|
|
- // 最后一个对话数据为当前正在进行的对话
|
|
|
|
|
- const last_msg = chat_new_list.value[chat_new_list.value.length - 1];
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 整理返回的数据
|
|
|
|
|
- */
|
|
|
|
|
- const currentChat = formatDialogAnswerFromWebsocketReceiveMessage(res_data, last_msg, props.CHAT_AGENT_INFOMATION);
|
|
|
|
|
- const chat_status = currentChat.status;
|
|
|
|
|
- const chat_message = currentChat.message;
|
|
|
|
|
-
|
|
|
|
|
- // 处理对话数据
|
|
|
|
|
- const handleChatData = () => {
|
|
|
|
|
- const chat_data = currentChat.data; // 对话相关的数据项
|
|
|
|
|
-
|
|
|
|
|
- // 原来的最后一项弹出
|
|
|
|
|
- chat_new_list.value.pop();
|
|
|
|
|
- // 添加新的最后一项
|
|
|
|
|
- chat_new_list.value.push(chat_data);
|
|
|
|
|
-
|
|
|
|
|
- // 滚动到底部
|
|
|
|
|
- scroll2ListBottom();
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- if (chat_status === 0) {
|
|
|
|
|
- // 未开始: 此状态不存在,无需处理
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (chat_status === 1) {
|
|
|
|
|
- // 此轮对话仍然在进行中
|
|
|
|
|
- handleChatData();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (chat_status === 2) {
|
|
|
|
|
- // 此轮对话已结束
|
|
|
|
|
- handleChatData();
|
|
|
|
|
-
|
|
|
|
|
- // 对话已结束:检查是否存在推荐数据
|
|
|
|
|
- const chat_suggest = currentChat.suggest; // 对话相关的推荐数据
|
|
|
|
|
- if (chat_suggest) {
|
|
|
|
|
- chat_suggestion.value = chat_suggest;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 对话已结束:文件上传、文本输入区域放开使用
|
|
|
|
|
- input_disabled.value = false;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (chat_status === 3) {
|
|
|
|
|
- // 此轮对话出错了
|
|
|
|
|
- // TODO: 错误处理
|
|
|
|
|
-
|
|
|
|
|
- // 此轮对话已结束
|
|
|
|
|
- handleChatData();
|
|
|
|
|
- // 对话已结束:文件上传、文本输入区域放开使用
|
|
|
|
|
- input_disabled.value = false;
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 处理websocket错误
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
-function handleWSError(e) {
|
|
|
|
|
- console.log('socket-error-8jfdksagjsfdjaklnfsdajkl', e);
|
|
|
|
|
- handleChatRequestionException({
|
|
|
|
|
- message: `对话无法继续:WebSocket Error: ${e.target.url}`
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 处理websocket关闭事件
|
|
|
|
|
- * @param e
|
|
|
|
|
- */
|
|
|
|
|
-function handleWSClose(e) {}
|
|
|
|
|
-
|
|
|
|
|
-onMounted(() => {
|
|
|
|
|
- const agent_id = props.agent_id;
|
|
|
|
|
- const session_id = props.session_id;
|
|
|
|
|
-
|
|
|
|
|
- // 组件挂载
|
|
|
|
|
- if (session_id) {
|
|
|
|
|
- // 有历史记录:获取历史记录内容进行展示
|
|
|
|
|
- getHistoryLogs(agent_id, session_id)
|
|
|
|
|
- .then((res) => {
|
|
|
|
|
- if (res.code / 1 === 200) {
|
|
|
|
|
- // 成功
|
|
|
|
|
- const session_list = res.data;
|
|
|
|
|
-
|
|
|
|
|
- // 将列表数据进行转化
|
|
|
|
|
- const list = formatDialogHistory(session_list, props.CHAT_AGENT_INFOMATION);
|
|
|
|
|
-
|
|
|
|
|
- // 如果历史记录不为空
|
|
|
|
|
- // 则需要展示对话内容
|
|
|
|
|
- // 此时欢迎内容就不需要展示
|
|
|
|
|
- if (list.length !== 0) {
|
|
|
|
|
- show_welcome.value = false;
|
|
|
|
|
- // 滚动到底部
|
|
|
|
|
- scroll2ListBottom();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- chat_log_list.value = list;
|
|
|
|
|
- } else {
|
|
|
|
|
- // 异常
|
|
|
|
|
- // TODO:处理异常
|
|
|
|
|
- show_welcome.value = false;
|
|
|
|
|
- // handleChatRequestionException({
|
|
|
|
|
- // message: res.msg
|
|
|
|
|
- // });
|
|
|
|
|
-
|
|
|
|
|
- // 使用消息提示,而非界面错误提示
|
|
|
|
|
- handleErrorMessage('历史会话异常:' + res.msg);
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .catch((err) => {
|
|
|
|
|
- console.log('err-mfsjakfjsdkalfjs', err);
|
|
|
|
|
-
|
|
|
|
|
- show_welcome.value = false;
|
|
|
|
|
- // handleChatRequestionException({
|
|
|
|
|
- // message: err.message
|
|
|
|
|
- // });
|
|
|
|
|
-
|
|
|
|
|
- handleErrorMessage('历史会话错误:' + err.message);
|
|
|
|
|
- });
|
|
|
|
|
- } else {
|
|
|
|
|
- // 无历史记录:等待创建会话内容(用户开始输入、提交完成),列表区域显示欢迎界面
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-
|
|
|
|
|
-// ---------------------------------------------------------
|
|
|
|
|
-onUnmounted(() => {
|
|
|
|
|
- // 组件卸载,关闭组件内的websocket连接
|
|
|
|
|
- closeWSConnect();
|
|
|
|
|
-});
|
|
|
|
|
-</script>
|
|
|
|
|
-
|
|
|
|
|
-<style lang="css" scoped>
|
|
|
|
|
-.conversation-chat-container {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
-
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- margin-right: auto;
|
|
|
|
|
-
|
|
|
|
|
- height: calc(100% - 52px);
|
|
|
|
|
-
|
|
|
|
|
- padding-top: 52px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.conversation-chat-left {
|
|
|
|
|
- width: 25%;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-right {
|
|
|
|
|
- width: 25%;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-main {
|
|
|
|
|
- flex-grow: 1;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- /* max-width: 960px; */
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- margin-right: auto;
|
|
|
|
|
-
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
-
|
|
|
|
|
- /* background-color: #aaa; */
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-content {
|
|
|
|
|
- flex-grow: 1;
|
|
|
|
|
-
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
-
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
-
|
|
|
|
|
- padding-top: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-main-container {
|
|
|
|
|
- /* height: 80%; */
|
|
|
|
|
- flex-grow: 1;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-area {
|
|
|
|
|
- flex-grow: 1;
|
|
|
|
|
- overflow: auto;
|
|
|
|
|
- /* max-width: 960px; */
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.history-list-loading {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- min-height: 300px;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-}
|
|
|
|
|
-.history-list-loading-container {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-input-attached {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- margin-right: auto;
|
|
|
|
|
-
|
|
|
|
|
- /* margin-top: 5px; */
|
|
|
|
|
- margin-bottom: -10px;
|
|
|
|
|
- padding: 5px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
-
|
|
|
|
|
- display: flex;
|
|
|
|
|
-}
|
|
|
|
|
-.conversation-chat-input {
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-
|
|
|
|
|
- width: 100%;
|
|
|
|
|
-
|
|
|
|
|
- margin-left: auto;
|
|
|
|
|
- margin-right: auto;
|
|
|
|
|
-
|
|
|
|
|
- margin-top: 5px;
|
|
|
|
|
- margin-bottom: 5px;
|
|
|
|
|
- padding: 5px;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
-
|
|
|
|
|
- display: flex;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.scroll-left {
|
|
|
|
|
- width: 25%;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.scroll-right {
|
|
|
|
|
- width: 25%;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-</style>
|
|
|