Pārlūkot izejas kodu

Merge branch 'master' of http://gitlab.smartai.com/zhupei/smartai2dian0

张涛 1 gadu atpakaļ
vecāks
revīzija
3615ec017d
33 mainītis faili ar 1229 papildinājumiem un 195 dzēšanām
  1. 34 0
      .gitlab-ci.yml
  2. 9 0
      package-lock.json
  3. 1 0
      package.json
  4. 16 0
      src/apis/intelligent.js
  5. 15 0
      src/apis/login.js
  6. 43 1
      src/apis/rgflow-dify.js
  7. 92 13
      src/modules/conversation-chat/DialogInput.vue
  8. 48 10
      src/modules/conversation-chat/DialogItem.vue
  9. 3 24
      src/modules/conversation-chat/DialogList.vue
  10. 71 0
      src/modules/conversation-chat/dialog-content/DialogCode.vue
  11. 10 0
      src/modules/conversation-chat/dialog-content/DialogContent.vue
  12. 118 0
      src/modules/conversation-chat/dialog-input/InputActions.vue
  13. 7 3
      src/modules/conversation-chat/dialog-item/DialogActions.vue
  14. 42 7
      src/modules/conversation-chat/dialog-item/DialogAnswer.vue
  15. 7 2
      src/modules/conversation-chat/dialog-item/DialogQuestion.vue
  16. 50 50
      src/modules/conversation-dialog/api-dialog.js
  17. 33 0
      src/modules/conversation-dialog/format-code-string.js
  18. 29 0
      src/modules/conversation-dialog/format-dialog-chat-finished-message.js
  19. 8 0
      src/modules/conversation-dialog/format-dialog-history-item.js
  20. 17 0
      src/modules/conversation-dialog/format-dialog-history-messages.js
  21. 239 0
      src/modules/conversation-dialog/format-dialog-receive-message.js
  22. 45 0
      src/modules/conversation-dialog/format-dialog-send-options.js
  23. 31 0
      src/modules/conversation-dialog/format-dialog-temp-loading-message.js
  24. 0 0
      src/modules/conversation-dialog/use-websocket-chat.js
  25. 17 5
      src/modules/dify/MessageBody.vue
  26. 3 1
      src/modules/dify/TopSetting.vue
  27. 23 3
      src/modules/intelligent/MessageBody.vue
  28. 18 6
      src/modules/intelligent/TopSetting.vue
  29. 59 33
      src/modules/intelligent/rgflow/AgentConfig.vue
  30. 8 6
      src/modules/login/FormItem.vue
  31. 125 31
      src/views/conversation/ConversationChatContainer.vue
  32. 1 0
      src/views/manage/CardItem.vue
  33. 7 0
      vite.config.mjs

+ 34 - 0
.gitlab-ci.yml

@@ -0,0 +1,34 @@
+image: node:latest
+
+stages:
+  - build
+  - deploy
+
+cache:
+  paths:
+    - node_modules/
+
+build_job:
+  stage: build
+  script:
+    - node -v
+    - npm -v
+    - npm config set registry https://registry.npmmirror.com
+    - npm install --legacy-peer-deps
+    - npm run build
+    - ls
+    - ls dist
+    - echo '所有目录内容'
+  artifacts:
+    paths:
+      - dist
+
+deploy_job:
+  stage: deploy
+  dependencies:
+    - build_job
+  script:
+    - rsync -e "ssh -p 22" -av --delete --no-o --no-g --progress dist basic@192.168.20.119:/data/disk3/rag/smart2
+
+  environment:
+    name: production

+ 9 - 0
package-lock.json

@@ -12,6 +12,7 @@
         "@arco-design/web-vue": "2.56.3",
         "axios": "1.7.8",
         "dayjs": "1.11.13",
+        "highlight.js": "11.10.0",
         "js-cookie": "3.0.5",
         "mitt": "3.0.1",
         "pinia": "2.2.8",
@@ -2725,6 +2726,14 @@
         "url": "https://opencollective.com/unified"
       }
     },
+    "node_modules/highlight.js": {
+      "version": "11.10.0",
+      "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.10.0.tgz",
+      "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
     "node_modules/hookable": {
       "version": "5.5.3",
       "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",

+ 1 - 0
package.json

@@ -26,6 +26,7 @@
     "@arco-design/web-vue": "2.56.3",
     "axios": "1.7.8",
     "dayjs": "1.11.13",
+    "highlight.js": "11.10.0",
     "js-cookie": "3.0.5",
     "mitt": "3.0.1",
     "pinia": "2.2.8",

+ 16 - 0
src/apis/intelligent.js

@@ -92,3 +92,19 @@ export function addLabelToDialog(labelIdList, objectId) {
     }
   });
 }
+
+/**
+ * @description 获取智能体详情
+ * @param {string} id 智能体ID
+ * @param {string} status 智能体状态 1-上线 2-下线
+ * */
+export function changeDialogStatus(id, status) {
+  return request({
+    url: `/api/dialog/status`,
+    method: 'put',
+    data: {
+      id,
+      status
+    }
+  });
+}

+ 15 - 0
src/apis/login.js

@@ -15,6 +15,14 @@ export function register(data) {
     data
   });
 }
+// 注册完成同步数据
+
+export function asycData(userFlag) {
+  return request({
+    url: `/api/auth/v2/sync?userFlag=${userFlag}`,
+    method: 'get'
+  });
+}
 // 获取用户信息
 export function getInfo() {
   return request({
@@ -29,3 +37,10 @@ export function generateRoutes() {
     method: 'get'
   });
 }
+// token转化
+export function tokenExchange() {
+  return request({
+    url: `/api/auth/token`,
+    method: 'get'
+  });
+}

+ 43 - 1
src/apis/rgflow-dify.js

@@ -1,7 +1,7 @@
 import request from '../base/ajax';
 
 /**
- * 创建Dify智能体,
+ * @description 创建Dify智能体,
  * @param {Object} data 包含创建Dify所需参数的对象
  * @param {String} data.mode 模式,可选值:'advanced-chat'、'agent-chat'、'workflow'
  * */
@@ -16,3 +16,45 @@ export function createDify(data) {
     data: initData
   });
 }
+
+// 创建rgflow智能体
+export function createRgFlow(data) {
+  return request({
+    url: `/rgflow/v1/dialog/set`,
+    method: 'post',
+    data
+  });
+}
+
+// 查询知识库列表
+export function queryKbList() {
+  return request({
+    url: `/rgflow/v1/kb/list`,
+    method: 'get'
+  });
+}
+// 头像上传
+// export function kbdocumentupload(params) {
+//   const config = {
+//     headers: {
+//       'Content-Type': 'application/x-www-form-urlencoded'
+//       // token: token,
+//     }
+//   };
+//   return axios.post('/api/v1/document/upload', params, config);
+// }
+// 查询模型列表
+export function queryModelList() {
+  return request({
+    url: `/rgflow/v1/llm/list`,
+    method: 'get'
+  });
+}
+
+// 获取某个智能体的数据
+export function getRagFlowData(id) {
+  return request({
+    url: `/rgflow/v1/dialog/get?dialog_id=${id}`,
+    method: 'get'
+  });
+}

+ 92 - 13
src/modules/conversation-chat/DialogInput.vue

@@ -1,26 +1,40 @@
 <template>
   <div class="dialog-input">
     <div>
-      <a-textarea placeholder="输入文本内容" allow-clear />
-    </div>
-    <div>
-      <div>
-        <div>联网查询</div>
-      </div>
-
-      <div>
-        <div v-if="enable_frequent_question">常用问题定义</div>
-        <div v-if="enable_file_upload">文件上传</div>
-        <div v-if="enable_voice_recognition">语音识别</div>
-        <div @click="handleSubmit">发送</div>
-      </div>
+      <a-textarea
+        placeholder="输入文本内容"
+        :model-value="input_text_value"
+        allow-clear
+        :auto-size="{
+          minRows: 1,
+          maxRows: 4
+        }"
+        @input="handleTextInput"
+        @keyup="handleTextKeyUp"
+      />
     </div>
 
+    <InputActions
+      :disabled="disabled"
+      :enable_file_upload="enable_file_upload"
+      :enable_frequent_question="enable_frequent_question"
+      :enable_voice_recognition="enable_voice_recognition"
+      :file_upload_type="file_upload_type"
+      :file_upload_limit="file_upload_limit"
+      @fileUpload="handleFileUpload"
+      @questionDefine="handleQuestionDefine"
+      @voiceRecognize="handleVoiceRecognize"
+      @submit="handleSubmit"
+    />
+
     <div>文件上传预览区: 当点击文件上传,选择某个文件后,此处需要展示进行预览。</div>
   </div>
 </template>
 
 <script setup>
+import { ref } from 'vue';
+import InputActions from './dialog-input/InputActions.vue';
+
 const props = defineProps({
   /**
    * 是否禁用操作
@@ -115,6 +129,11 @@ const props = defineProps({
   }
 });
 
+/**
+ * 文本域输入的内容
+ */
+const input_text_value = ref('');
+
 const emit = defineEmits(['inputTextChange', 'inputFileChange', 'submit']);
 
 const handleSubmit = () => {
@@ -125,6 +144,66 @@ const handleSubmit = () => {
 
   emit('submit');
 };
+
+/**
+ * 文件上传
+ */
+const handleFileUpload = () => {};
+/**
+ * 常用问题定义
+ */
+const handleQuestionDefine = () => {};
+/**
+ * 语音识别
+ */
+const handleVoiceRecognize = () => {};
+
+/**
+ * 移除文本中的换行符
+ */
+const removeBreakLineFlag = () => {
+  input_text_value.value = input_text_value.value.replace(/\n/, '');
+};
+
+/**
+ * 表单输入事件
+ * @param value
+ * @param event
+ */
+const handleTextInput = (value, event) => {
+  if (props.disabled) {
+    return;
+  }
+
+  input_text_value.value = value;
+
+  emit('inputTextChange', input_text_value.value);
+};
+
+/**
+ * 用户按键事件
+ * @param e
+ */
+const handleTextKeyUp = (e) => {
+  if (props.disabled) {
+    if (e.keyCode === 13) {
+      removeBreakLineFlag();
+    }
+    return;
+  }
+
+  if (e.keyCode === 13) {
+    if (e.ctrlKey) {
+      // 此时也按下Ctrl:换行处理
+    } else {
+      // 此时没有按下Ctrl
+      removeBreakLineFlag();
+
+      // 提交
+      emit('submit');
+    }
+  }
+};
 </script>
 
 <style lang="css" scoped>

+ 48 - 10
src/modules/conversation-chat/DialogItem.vue

@@ -1,25 +1,63 @@
 <template>
   <div class="dialog-item-container">
-    <DialogQuestion />
-    <DialogAnswer />
+    <DialogQuestion :question="questionInfo" />
+
+    <DialogAnswer :answer="answerInfo" @stop="handleStopChat" @retry="handleRetryChat" @select="handleSelectChat" />
   </div>
 </template>
 
 <script setup>
+import { ref, computed } from 'vue';
+
 import DialogQuestion from './dialog-item/DialogQuestion.vue';
 import DialogAnswer from './dialog-item/DialogAnswer.vue';
 
-const props = defineProps({});
+const props = defineProps({
+  dialog: Object
+});
+
+const emit = defineEmits(['stop', 'retry', 'select']);
+
+/**
+ * 客户端提问数据信息
+ */
+const questionInfo = computed(() => {
+  return {
+    content: props.dialog.question
+  };
+});
+
+/**
+ * 智能体回答相关信息
+ */
+const answerInfo = computed(() => {
+  const obj = {};
+
+  // 智能体回答的内容
+  if (props.dialog.answer) {
+    obj.content = props.dialog.answer;
+  }
+
+  return obj;
+});
+
+/**
+ * 停止答案的生成
+ */
+const handleStopChat = () => {
+  emit('stop');
+};
+
+const handleRetryChat = () => {
+  emit('retry');
+};
+
+const handleSelectChat = () => {
+  emit('select');
+};
 </script>
 
 <style lang="css" scoped>
 .dialog-item-container {
 }
-
-/* .dialog-item {
-  color: var(--color-text-1);
-
-  margin-top: 5px;
-  margin-bottom: 5px;
-} */
 </style>

+ 3 - 24
src/modules/conversation-chat/DialogList.vue

@@ -5,30 +5,9 @@
     </div>
 
     <div class="dialog-list" v-if="!show_welcome">
-      <DialogItem />
+      <DialogItem v-for="(item, index) of list" :dialog="item" :index="index" :key="item.id" />
 
-      <DialogItem />
-
-      <DialogItem />
-
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-      <DialogItem />
-
-      <DialogSuggestion :suggestion="suggestion" />
+      <DialogSuggestion v-if="suggestion.length !== 0" :suggestion="suggestion" />
     </div>
   </a-scrollbar>
 </template>
@@ -55,7 +34,7 @@ const props = defineProps({
   /**
    * 推荐项
    */
-  suggestion: Object,
+  suggestion: Array,
 
   /**
    * 是否显示欢迎页

+ 71 - 0
src/modules/conversation-chat/dialog-content/DialogCode.vue

@@ -0,0 +1,71 @@
+<template>
+  <div :class="container_klass">
+    <p class="dialog-code-footer-title">{{ cd_title }}:</p>
+    <div v-html="code" class="dialog-code-content"></div>
+  </div>
+</template>
+<script setup>
+import { onMounted, computed } from 'vue';
+
+import { applyHighLight } from '../../conversation-dialog/format-code-string';
+
+/**
+ * 此组件展示对话中的代码
+ * 例如python代码、SQL代码
+ */
+const props = defineProps({
+  type: String, // 代码类型:python/sql
+  code: String, // 代码内容
+  index: Number // 对应了序号
+});
+
+/**
+ * 容器class
+ */
+const container_klass = computed(() => {
+  return `code-display-${props.type}-${props.index}`;
+});
+
+/**
+ * 代码类型展示
+ */
+const cd_title = computed(() => {
+  if (props.type === 'python') {
+    return 'Python';
+  }
+
+  if (props.type === 'sql') {
+    return 'SQL';
+  }
+
+  return '';
+});
+
+onMounted(() => {
+  applyHighLight(`.${container_klass.value}`);
+});
+</script>
+
+<style scoped>
+.dialog-code-footer {
+  border-radius: 12px;
+  font-size: 14px;
+  background-color: #32333c;
+  color: #bcbdc3;
+  margin: 16px 0;
+  margin-left: 50px;
+}
+
+.dialog-code-footer-title {
+  font-size: 16px;
+  font-weight: 500;
+
+  width: 100%;
+  color: #3d3f3f;
+}
+
+.dialog-code-content {
+  margin-top: 5px;
+  margin-bottom: 5px;
+}
+</style>

+ 10 - 0
src/modules/conversation-chat/dialog-content/DialogContent.vue

@@ -10,6 +10,16 @@
 import { computed } from 'vue';
 
 // 接收markdown数据,进行一些转换
+const props = defineProps({
+  /**
+   * 内容类型
+   */
+  type: String,
+  /**
+   * 文本内容
+   */
+  content: String
+});
 </script>
 
 <style lang="css" scoped>

+ 118 - 0
src/modules/conversation-chat/dialog-input/InputActions.vue

@@ -0,0 +1,118 @@
+<template>
+  <div class="dialog-input-actions">
+    <div class="dialog-input-action-left">
+      <div>联网查询</div>
+    </div>
+
+    <div class="dialog-input-action-right">
+      <div v-if="enable_frequent_question" @click="handleQuestionDefine">常用问题定义</div>
+      <div v-if="enable_file_upload" @click="handleFileUpload">文件上传</div>
+      <div v-if="enable_voice_recognition" @click="handleVoiceRecognize">语音识别</div>
+      <div @click="handleSubmit">发送</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+const props = defineProps({
+  /**
+   * 是否禁用操作
+   * 默认 false
+   * 当此值为 true 时,此组件的任何功能都不可用
+   * 当对话正在进行时,此值应为 true,意味着不允许继续对话
+   * 直到一轮对话结束,或者一轮对话终止,此值才应该变为 false
+   */
+  disabled: {
+    type: Boolean,
+    default: false,
+    required: false
+  },
+  /**
+   * 是否开启文件上传功能
+   * 默认 true,即默认开启
+   * 当此值为 false 时,该组件将没有文件上传按钮
+   */
+  enable_file_upload: {
+    type: Boolean,
+    default: true,
+    required: false
+  },
+  /**
+   * 是否开启 常用问题 功能
+   * 如果此值为false,则 常用问题 定义功能不可用
+   */
+  enable_frequent_question: {
+    type: Boolean,
+    default: true,
+    required: false
+  },
+  /**
+   * 是否开启音频识别功能
+   * 如果此值为 false,则语音识别功能不可用
+   */
+  enable_voice_recognition: {
+    type: Boolean,
+    default: false,
+    required: false
+  },
+  /**
+   * 在 enable_file_upload === true 的前提下
+   * 文件上传的类型,默认是 *,不限类型
+   * 对应html属性 accept
+   * 对应 arco.design Upload accept 属性
+   * @see https://arco.design/vue/component/upload#API
+   * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#htmlattrdefaccept
+   */
+  file_upload_type: {
+    type: String,
+    default: '*',
+    required: false
+  },
+  /**
+   * 在 enable_file_upload === true 的前提下
+   * 文件上传的数量限制
+   * 默认值为0,表示不限制
+   * 对应 arco.design Upload limit 属性
+   */
+  file_upload_limit: {
+    type: Number,
+    default: 0,
+    required: false
+  }
+});
+
+const emit = defineEmits(['fileUpload', 'questionDefine', 'voiceRecognize', 'submit']);
+
+const handleQuestionDefine = () => {
+  emit('questionDefine');
+};
+
+const handleFileUpload = () => {
+  emit('fileUpload');
+};
+
+const handleVoiceRecognize = () => {
+  emit('voiceRecognize');
+};
+
+const handleSubmit = () => {
+  emit('submit');
+};
+</script>
+
+<style lang="css" scoped>
+.dialog-input-actions {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.dialog-input-action-left {
+}
+
+.dialog-input-action-right {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 7 - 3
src/modules/conversation-chat/dialog-item/DialogActions.vue

@@ -14,12 +14,14 @@
 </template>
 
 <script setup>
-const emit = defineEmits(['retry', 'share']);
+const emit = defineEmits(['copy', 'retry', 'share']);
 
 /**
  * 复制内容
  */
-const handleCopy = () => {};
+const handleCopy = () => {
+  emit('copy');
+};
 /**
  * 重试
  */
@@ -29,7 +31,9 @@ const handleRetry = () => {
 /**
  * 分享
  */
-const handleShare = () => {};
+const handleShare = () => {
+  emit('share');
+};
 /**
  * 点赞
  */

+ 42 - 7
src/modules/conversation-chat/dialog-item/DialogAnswer.vue

@@ -8,7 +8,12 @@
       <div class="dialog-content answer-content">
         <div>
           <DialogContent>
-            <DialogActions class="answer-actions-btns" />
+            <DialogActions
+              class="answer-actions-btns"
+              @copy="handleAnswerCopy"
+              @share="handleAnswerShare"
+              @retry="handleAnswerRetry"
+            />
           </DialogContent>
 
           <BtnStopChat @stop="handleStopChat" />
@@ -22,9 +27,6 @@
 /**
  * 智能体的回答内容,可能需要包括下面一些功能
  *
- * 1. 文档引用,预览
- * 2. 图片展示、编辑、预览
- * 3. 代码展示
  * 4. 回答反馈:分享、点赞、复制
  */
 import { computed } from 'vue';
@@ -32,20 +34,53 @@ import BtnSelectItem from './BtnSelectItem.vue';
 import DialogContent from '../dialog-content/DialogContent.vue';
 import DialogActions from './DialogActions.vue';
 import BtnStopChat from './BtnStopChat.vue';
+
 const props = defineProps({
-  show_select: true
+  /**
+   * 是否显示选择按钮
+   * 仿照kimi,当用户选择分享时,选择按钮会展示
+   */
+  show_select: true,
+
+  /**
+   * 答案内容数据
+   */
+  answer: Object
 });
 
+const emit = defineEmits(['stop', 'retry', 'select']);
+
 const select_btn_visible = computed(() => {
   return props.show_select === true;
 });
 
-const handleSelectChange = () => {};
+const handleSelectChange = () => {
+  emit('select');
+};
 
 /**
  * 停止答案的生成
  */
-const handleStopChat = () => {};
+const handleStopChat = () => {
+  emit('stop');
+};
+
+/**
+ * copy
+ */
+const handleAnswerCopy = () => {};
+
+/**
+ * 分享答案
+ */
+const handleAnswerShare = () => {};
+
+/**
+ * 重新生成答案
+ */
+const handleAnswerRetry = () => {
+  emit('retry');
+};
 </script>
 
 <style src="./dialog-item-common.css" />

+ 7 - 2
src/modules/conversation-chat/dialog-item/DialogQuestion.vue

@@ -5,7 +5,7 @@
     <div class="dialog-main question-main">
       <div class="dialog-content question-content">
         <div>
-          <DialogContent />
+          <DialogContent :content="question.content" />
         </div>
       </div>
 
@@ -20,7 +20,12 @@ import BtnSelectItem from './BtnSelectItem.vue';
 import DialogContent from '../dialog-content/DialogContent.vue';
 
 const props = defineProps({
-  show_select: true
+  show_select: true,
+
+  /**
+   * 用户提问数据
+   */
+  question: Object
 });
 
 const select_btn_visible = computed(() => {

+ 50 - 50
src/modules/conversation-dialog/api-dialog.js

@@ -47,10 +47,10 @@ const uploadFile = (id, chatId, file) => {
 };
 
 /**
- * 新建对话
+ * 新建对话,根据agent获取chat_id
  * @returns {Promise<Object>}
  */
-const createDialog = (agent_id) => {
+export const getDialogChatId = (agent_id) => {
   // 返回数据示例: {"code":200,"msg":"","data":{"chat_id":"44f835504943467f9a9cde261299a06f"}}
 
   return ajax.get(`/api/agent/get-chat-id/${agent_id}`);
@@ -76,51 +76,51 @@ export const getHistoryLogs = (agent_id, conversation_id) => {
 
 // ws://localhost/api/chat/ws/42e4fcdc9bea11efac300242ac160006/44f835504943467f9a9cde261299a06f?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyIiwidXNlcl9pZCI6MjEsImV4cCI6MTczNDAxMTU4NH0.tLVyD5ds3BqJ8UCZ1pnaUVBhqZ0leqXNUQhG5U2A2u0
 
-const create = (token, agentId, dialogId, type) => {
-  // const wsParam = type === 'report' ? 'report' : 'chat';
-  // const wsUrl =
-  //   type === 'excel'
-  //     ? `ws://${location.host}/api/document/ws/excel`
-  //     : `ws://${location.host}/api/${wsParam}/ws/${agentId}/${dialogId}?token=${token}`;
-
-  const wsUrl = getWSUrl(type, agentId, dialogId);
-
-  const ws = new WebSocket(wsUrl);
-  // 当 WebSocket 打开时...
-  ws.onopen = function (event) {
-    console.log('WebSocket 连接已打开');
-  };
-
-  // 当 WebSocket 关闭时...
-  ws.onclose = function (event) {
-    console.log(event.code);
-  };
-
-  // 当有错误发生时...
-  ws.onerror = function (error) {
-    console.error('WebSocket 出错: ', error);
-  };
-  return ws;
-};
-
-/**
- * 获取websocket连接的地址
- * @param {string} type - 对话类型,可选值:report/excel/chat
- * @param {string} agent_id - 对话使用的agent ID
- * @param {string} dialog_id - 对话记录的ID
- * @returns
- */
-function getWSUrl(type, agent_id, dialog_id) {
-  const host = location.host;
-  const token = kuky_Authorization.get();
-
-  if (type === 'report') {
-    return `ws://${host}/api/${type}/ws/${agent_id}/${dialog_id}?token=${token}`;
-  } else if (type === 'chat') {
-    return `ws://${host}/api/${type}/ws/${agent_id}/${dialog_id}?token=${token}`;
-  } else if (type === 'excel') {
-    return `ws://${host}/api/document/ws/excel`;
-  } else {
-    return `ws://${host}/api/chat/ws/${agent_id}/${dialog_id}?token=${token}`;
-  }
-}
+// const create = (token, agentId, dialogId, type) => {
+//   // const wsParam = type === 'report' ? 'report' : 'chat';
+//   // const wsUrl =
+//   //   type === 'excel'
+//   //     ? `ws://${location.host}/api/document/ws/excel`
+//   //     : `ws://${location.host}/api/${wsParam}/ws/${agentId}/${dialogId}?token=${token}`;
+
+//   const wsUrl = getWSUrl(type, agentId, dialogId);
+
+//   const ws = new WebSocket(wsUrl);
+//   // 当 WebSocket 打开时...
+//   ws.onopen = function (event) {
+//     console.log('WebSocket 连接已打开');
+//   };
+
+//   // 当 WebSocket 关闭时...
+//   ws.onclose = function (event) {
+//     console.log(event.code);
+//   };
+
+//   // 当有错误发生时...
+//   ws.onerror = function (error) {
+//     console.error('WebSocket 出错: ', error);
+//   };
+//   return ws;
+// };
+
+// /**
+//  * 获取websocket连接的地址
+//  * @param {string} type - 对话类型,可选值:report/excel/chat
+//  * @param {string} agent_id - 对话使用的agent ID
+//  * @param {string} dialog_id - 对话记录的ID
+//  * @returns
+//  */
+// function getWSUrl(type, agent_id, dialog_id) {
+//   const host = location.host;
+//   const token = kuky_Authorization.get();
+
+//   if (type === 'report') {
+//     return `ws://${host}/api/${type}/ws/${agent_id}/${dialog_id}?token=${token}`;
+//   } else if (type === 'chat') {
+//     return `ws://${host}/api/${type}/ws/${agent_id}/${dialog_id}?token=${token}`;
+//   } else if (type === 'excel') {
+//     return `ws://${host}/api/document/ws/excel`;
+//   } else {
+//     return `ws://${host}/api/chat/ws/${agent_id}/${dialog_id}?token=${token}`;
+//   }
+// }

+ 33 - 0
src/modules/conversation-dialog/format-code-string.js

@@ -0,0 +1,33 @@
+import hljs from 'highlight.js';
+import 'highlight.js/styles/monokai.css';
+
+/**
+ * 应用代码高亮效果
+ * @param {string} container_klass - 代码所在容器的class值
+ */
+export const applyHighLight = (container_klass) => {
+  document.querySelectorAll(`${container_klass} code.hljs`).forEach((el) => {
+    hljs.highlightElement(el);
+  });
+};
+
+/**
+ * 格式化对话返回的python代码
+ * 注意!!!此函数体内的代码格式切记注意,不要随意添加换行、空格等,否则会引起代码呈现样式异常
+ * @param {string} code_string
+ */
+export const formatCodePython = (code_string) => {
+  return `<pre><code class="hljs language-python">
+${code_string}</code></pre>`;
+};
+
+/**
+ * 格式化对话返回的sql代码
+ * 注意!!!此函数体内的代码格式切记注意,不要随意添加换行、空格等,否则会引起代码呈现样式异常
+ * @param {string} code_string
+ * @returns
+ */
+export const formatCodeSql = (code_string) => {
+  return `<pre><code class="hljs language-sql">
+${code_string}</code></pre>`;
+};

+ 29 - 0
src/modules/conversation-dialog/format-dialog-chat-finished-message.js

@@ -0,0 +1,29 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+
+import { formatCodePython, formatCodeSql } from './format-code-string';
+
+/**
+ * 当用户已发送消息,但是还未返回时
+ * 此时界面上应该显示一个Loading状态
+ */
+export default function formatDialogChatFinishedMessage(preMsg) {
+  // 本次对话的消息内容
+  const currentMsg = {
+    /**
+     * 客户端自定义字段
+     */
+    client_custom_type: '',
+    /**
+     * 会话展示时,可以通过此值,判断该如何进行内容显示
+     * 当值为0时,可能显示加载中动画
+     * 当值为1时,展示消息内容,展示 停止生成按钮
+     * 当值为2时,可能展示反馈操作按钮
+     */
+    client_custom_chat_status: 2, // 此轮会话的状态值 0 加载中 1 进行中 2 已结束
+
+    content: '', // 消息正文,markdown格式,需要进行解析
+    system: '' // 系统消息
+  };
+
+  return currentMsg;
+}

+ 8 - 0
src/modules/conversation-dialog/format-dialog-history-item.js

@@ -0,0 +1,8 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+
+/**
+ * 格式化用户的对话记录的每一项数据
+ */
+export default function formatDialogHistoryItem(session_item) {
+  return {};
+}

+ 17 - 0
src/modules/conversation-dialog/format-dialog-history-messages.js

@@ -0,0 +1,17 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+import formatDialogHistoryItem from './format-dialog-history-item';
+
+/**
+ * 格式化用户的对话记录数据
+ */
+export default function formatDialogHistoryMessages(session_list) {
+  const arr = [];
+
+  session_list.forEach((item) => {
+    const msg = formatDialogHistoryItem(item);
+
+    arr.push(msg);
+  });
+
+  return arr;
+}

+ 239 - 0
src/modules/conversation-dialog/format-dialog-receive-message.js

@@ -0,0 +1,239 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+
+import { formatCodePython, formatCodeSql } from './format-code-string';
+
+/**
+ * 格式化websocket发送消息的参数
+ * @param {string} message_str
+ * @param {object} preMsg - 前一次的消息,如果是流式消息,则需要上一次的Content进行拼接,从而形成新的消息
+ * @returns
+ */
+export default function formatDialogReceiveMessage(message_str, preMsg) {
+  const res_content = jsonParse(message_str);
+
+  console.log('收到消息::');
+  console.log(res_content);
+
+  // 复制一份原消息内容
+  const copyPreMsg = jsonParse(jsonStringify(preMsg));
+
+  /**
+   * 类别:
+   * close
+   * error
+   * message
+   * stream
+   * system
+   */
+  const resType = res_content.type;
+  const resMessage = res_content.message; // 消息内容
+  const resRefference = res_content.reference; // 引用消息
+  const resStepMessage = res_content.step_message; // 消息内容
+  const resFiles = res_content.files; // 文件
+  const resMessageFiles = res_content.message_files; // 关联的文件
+  const resDownloadUrl = res_content.download_url; // 下载链接
+  const resFileUrl = res_content.file_url; // 文件链接
+  const resFileName = res_content.file_name; // 文件名称
+  const resImageUrl = res_content.image_url; // 图片链接
+  const resExcelUrl = res_content.excel_url; // excel链接
+  const resExcelName = res_content.excel_name; // excel文件
+  const resCode = res_content.code; // 代码内容
+  const resSQL = res_content.sql; // SQL内容
+
+  // 本次对话的消息内容
+  const currentMsg = {
+    /**
+     * 对话消息类别
+     * 0 历史消息
+     * 1 新消息(本次进入页面新建的消息,但是已经结束轮次)
+     * 2 当前的消息(此刻正在进行中的消息)
+     */
+    client_custom_chat_type: 2,
+    /**
+     * 会话展示时,可以通过此值,判断该如何进行内容显示
+     * 当值为0时,可能显示加载中动画
+     * 当值为1时,展示消息内容,展示 停止生成按钮
+     * 当值为2时,可能展示反馈操作按钮
+     */
+    client_custom_chat_status: 1, // 此轮会话的状态值 0 加载中 1 进行中 2 已结束
+
+    question: res_content.question // 用户的提问内容
+
+    // answer: '', // 消息正文,markdown格式,需要进行解析
+    // system: '', // 系统消息
+
+    // file: '',
+    // image_url: '',
+    // code: '',
+    // sql: '',
+    // reference: ''
+  };
+
+  /**
+   * 将前面websocket发来的数据,合并到当前的消息中
+   */
+  for (let key in copyPreMsg) {
+    if (key === 'client_custom_chat_type' || key === 'client_custom_chat_status') {
+      continue;
+    } else {
+      currentMsg[key] = copyPreMsg[key];
+    }
+  }
+
+  // 0 未开始 1 进行中 2 已结束 3 错误
+  let chat_status = 0;
+  let chat_msg = '';
+
+  if (resType === 'message') {
+    // 消息
+    chat_status = 1;
+
+    currentMsg.answer = resMessage;
+
+    if (resImageUrl) {
+      // 智能数据可能会存在此值
+      const port = window.location.port ? `:${window.location.port}` : '';
+
+      const image_url = `http://${window.location.hostname}${port}${resImageUrl}`;
+
+      currentMsg.image_url = image_url;
+    }
+
+    if (resExcelUrl && resExcelName) {
+      // 智能数据需要用到的值
+      const port = window.location.port ? `:${window.location.port}` : '';
+
+      const file = {
+        file_type: 'excel',
+        file_url: `http://${window.location.hostname}${port}${resExcelUrl}`,
+        file_name: `${resExcelName}.xlsx` || '点击下载'
+      };
+
+      currentMsg.file = file;
+    }
+
+    if (resFileUrl && resFileName) {
+      // 出题组卷可能存在此数据
+      const port = window.location.port ? `:${window.location.port}` : '';
+
+      const file = {
+        file_type: 'word',
+        file_url: `http://${window.location.hostname}${port}${resFileUrl}`,
+        file_name: `${resFileName}.docx` || '点击下载'
+      };
+
+      currentMsg.file = file;
+    }
+
+    if (resCode) {
+      // 智能数据可能存在此字段
+      const code_formated = formatCodePython(resCode);
+
+      currentMsg.code = code_formated;
+    }
+
+    if (resSQL) {
+      // 智能数据可能存在此字段
+      const sql_formated = formatCodeSql(resSQL);
+
+      currentMsg.sql = sql_formated;
+    }
+
+    if (resDownloadUrl) {
+      // 文档报告可能存在此数据(type document_report)
+      const file_url_arr = decodeURIComponent(resDownloadUrl).split('/');
+      const file_name = file_url_arr[file_url_arr.length - 1];
+
+      const file = {
+        file_type: 'word',
+        file_url: resDownloadUrl,
+        file_name: `${file_name}.docx` || '点击下载'
+      };
+
+      currentMsg.file = file;
+    }
+
+    if (resRefference) {
+      // 文档引用
+    }
+  }
+
+  if (resType === 'stream') {
+    // 流消息
+    chat_status = 1;
+
+    // 拼接数据
+    if (preMsg && preMsg.answer) {
+      currentMsg.answer = preMsg.answer + resMessage;
+    } else {
+      currentMsg.answer = resMessage;
+    }
+
+    if (resFiles) {
+      const file_name = resFiles[0].file_name;
+      const file_info_arr = file_name.split('.');
+      const end_fix = file_info_arr[file_info_arr.length - 1];
+
+      const file = {
+        file_name: resFiles[0].file_name,
+        file_url: resFiles[0].file_url
+      };
+
+      // 理论上报表合并,只能是 excel
+      // if(chat_type === 'excel'){
+      //   file.file_type = 'excel'
+      // }
+      if (end_fix === 'xlsx') {
+        file.file_type = 'excel';
+      }
+      if (end_fix === 'docx') {
+        file.file_type = 'word';
+      }
+
+      currentMsg.file = file;
+    }
+  }
+
+  if (resType === 'system') {
+    // 系统消息
+    chat_status = 1;
+  }
+
+  if (resType === 'close') {
+    // 会话结束
+    chat_status = 2;
+    currentMsg.client_custom_chat_status = 2; // 次轮会话已结束
+    currentMsg.client_custom_chat_type = 1; // 修改消息类型
+
+    // TODO: 合并原先的内容,形成最终结果
+
+    // 如果有新消息,生成一个新消息返回,如果没有新消息,则返回原消息
+    if (resMessageFiles) {
+      // 小数绘图存在此数据
+      const arr = [];
+
+      if (Array.isArray(resMessageFiles)) {
+        resMessageFiles.forEach((item, index) => {
+          arr.push(item);
+        });
+
+        // fullImageUrls.url = arr;
+      } else {
+        // fullImageUrls.url = arr;
+      }
+
+      // arr的值类似于: ['https://xxx.com/ancd.jpg'];
+    }
+  }
+
+  if (resType === 'error') {
+    // 错误消息
+    chat_status = 3;
+  }
+
+  return {
+    status: chat_status, // 0 未开始 1 进行中 2 已结束 3 错误
+    message: chat_msg,
+    data: currentMsg
+  };
+}

+ 45 - 0
src/modules/conversation-dialog/format-dialog-send-options.js

@@ -0,0 +1,45 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+
+/**
+ * 格式化websocket发送消息的参数
+ */
+export default function formatDialogSendOptions(text, file_list) {
+  /**
+   * 组装socket参数
+   */
+  const send_opts = jsonStringify({
+    message: text
+  });
+
+  // 文档报告参数
+  // send_opts = {
+  //   title: value,
+  //   upload_files: docReport.upload_files,
+  //   workflow: docReport.workflow
+  // }
+
+  // 小数绘图:文生图
+  // send_opts = {
+  //   message: text
+  // }
+
+  // 小数绘图:图生文
+  // send_opts = {
+  //   message: text,
+  //   upload_file_id: upload_file_id
+  // }
+
+  // type report
+  // send_opts = text
+
+  // type excel
+  // send_opts = text
+
+  // type doc
+  // send_opts = {
+  //   message: text,
+  //   doc_ids: []
+  // };
+
+  return send_opts;
+}

+ 31 - 0
src/modules/conversation-dialog/format-dialog-temp-loading-message.js

@@ -0,0 +1,31 @@
+import { jsonParse, jsonStringify } from '../../utils/utils';
+
+import { formatCodePython, formatCodeSql } from './format-code-string';
+
+/**
+ * 当用户已发送消息,但是还未返回时
+ * 此时界面上应该显示一个Loading状态
+ */
+export default function formatDialogTempLoadingMessage() {
+  const currentMsg = {
+    /**
+     * 对话消息类别
+     * 0 历史消息
+     * 1 新消息(本次进入页面新建的消息,但是已经结束轮次)
+     * 2 当前的消息(此刻正在进行中的消息)
+     */
+    client_custom_chat_type: 2,
+    /**
+     * 会话展示时,可以通过此值,判断该如何进行内容显示
+     * 当值为0时,可能显示加载中动画
+     * 当值为1时,展示消息内容,展示 停止生成按钮
+     * 当值为2时,可能展示反馈操作按钮
+     */
+    client_custom_chat_status: 0, // 此轮会话的状态值 0 加载中 1 进行中 2 已结束
+
+    question: '',
+    answer: '' // 消息正文,markdown格式,需要进行解析
+  };
+
+  return currentMsg;
+}

+ 0 - 0
src/modules/conversation-chat/use-websocket-chat.js → src/modules/conversation-dialog/use-websocket-chat.js


+ 17 - 5
src/modules/dify/MessageBody.vue

@@ -19,17 +19,27 @@
 
 <script setup>
 import { ref, onMounted } from 'vue';
+import { tokenExchange } from '../../apis/login';
+import { kuky_Authorization } from '../../base/storage';
 const props = defineProps({
   id: {
     type: String,
     required: true
   }
 });
+const DIFY_TOKEN = ref('');
 const BASE_URL = 'http://192.168.20.119:28003/app';
-const defaultURL = ref(BASE_URL + '/' + props.id + '/workflow');
-console.log(defaultURL.value);
+const getToken = async () => {
+  const res = await tokenExchange();
+  if (res.code == 200) {
+    DIFY_TOKEN.value = res.data.dify_token;
+  }
+};
+const defaultURL = ref('');
 
-onMounted(() => {
+onMounted(async () => {
+  await getToken();
+  defaultURL.value = BASE_URL + '/' + props.id + `/workflow?token=${DIFY_TOKEN.value}`;
   const element = document.getElementById('dify');
   element.onload = function () {
     if (element && element.contentWindow) {
@@ -37,8 +47,10 @@ onMounted(() => {
       element.contentWindow.postMessage(
         JSON.stringify({
           type: 'insertStyle',
-          value: `body{background-color:red;font-size:50px;}`
-        })
+          value: `
+            .sticky{display:none;}`
+        }),
+        defaultURL.value
       );
     }
   };

+ 3 - 1
src/modules/dify/TopSetting.vue

@@ -1,7 +1,9 @@
 <template>
   <a-breadcrumb class="breadcrumb">
     <a-breadcrumb-item><icon-apps /></a-breadcrumb-item>
-    <a-breadcrumb-item>智能体管理</a-breadcrumb-item>
+    <a-breadcrumb-item>
+      <router-link to="/intelligent">智能体</router-link>
+    </a-breadcrumb-item>
     <a-breadcrumb-item>智能体详情</a-breadcrumb-item>
   </a-breadcrumb>
 </template>

+ 23 - 3
src/modules/intelligent/MessageBody.vue

@@ -3,7 +3,7 @@
     <CardItem v-for="item in moke_data" :key="item.id" :name="item.name" :img_src="item.img_src" :height="200">
       <template #description> {{ item.desc }} </template>
       <template #settings>
-        <a-switch v-model="item.online">
+        <a-switch v-model="item.online" @change="changeStatus(item)">
           <template #checked> {{ $t('management.online') }} </template>
           <template #unchecked> {{ $t('management.offline') }} </template>
         </a-switch>
@@ -64,7 +64,7 @@ import { getIconIntelligent, getImageIntelligent } from '../../views/manage/comm
 import LabelSelected from './LabelSelected.vue';
 import { getDialogList } from '../../apis/intelligent';
 import { bus_intelligentSearch, bus_intelligentEdit } from '../../base/event-bus';
-import { getLabelList, addLabelToDialog } from '../../apis/intelligent';
+import { getLabelList, addLabelToDialog, changeDialogStatus } from '../../apis/intelligent';
 
 const moke_data = ref([
   // {
@@ -94,7 +94,6 @@ const getDialog = async () => {
   try {
     const res = await getDialogList(pages.current, pages.pageSize, search_text.value);
     const data = res.data.rows.map((item) => {
-      console.log(item.icon, getImageIntelligent(item.icon));
       return {
         ...item,
         time: new Date(item.update_time).toLocaleString('zh-CN'),
@@ -172,6 +171,27 @@ const changeDropdownVisible = async (visible, id) => {
     }
   }
 };
+
+const changeStatus = async (item) => {
+  console.log(item);
+  try {
+    const res = await changeDialogStatus(item.id, item.status == '1' ? '0' : '1');
+    if (res.code != 200) {
+      moke_data.value.forEach((it) => {
+        if (item.id === id) {
+          it.online = item.online;
+        }
+      });
+    }
+  } catch (err) {
+    console.log(err);
+    moke_data.value.forEach((it) => {
+      if (it.id === item.id) {
+        it.online = item.online;
+      }
+    });
+  }
+};
 bus_intelligentSearch.on((text) => {
   search_text.value = text;
   moke_data.value = [];

+ 18 - 6
src/modules/intelligent/TopSetting.vue

@@ -19,8 +19,9 @@
     :target_intell_type="target_intell_type" @select_icon="selectIcon" @updateIntelligentList="updateIntelligentList">
   </ModelAddItem>
   <ModelSelectIcon v-model:show_select_icon="show_select_icon" @sendIcon="sendIcon"></ModelSelectIcon>
+  <!-- v-if="show_rg_config" -->
   <AgentConfig v-model:show_rg_config="show_rg_config" ref="editAgentKuai" :typeAngint="add_type" :formData="itemObj"
-    @queryList="queryList"></AgentConfig>
+    @queryList="queryList" @updateIntelligentList="updateIntelligentList"></AgentConfig>
 </template>
 
 <script setup>
@@ -31,6 +32,7 @@ import ModelAddItem from './ModelAddItem.vue';
 import ModelSelectIcon from './ModalSelectIcon.vue';
 import AgentConfig from './rgflow/agentConfig.vue';
 import { funcDebounce } from '../../utils/utils';
+import { getRagFlowData } from '../../apis/rgflow-dify';
 import { bus_intelligentSearch, bus_intelligentEdit } from '../../base/event-bus';
 
 const router = useRouter();
@@ -77,14 +79,10 @@ const handleSearch = funcDebounce(
 const handleCreate = () => {
   visible.value = true;
 };
-// 创建Dify智能体
+// 创建rgflow智能体
 const createRAGFLOWIntelligent = () => {
-  console.log('createRAGFLOWIntelligent');
   show_rg_config.value = true;
 };
-const closeAgentConfig = () => {
-  show_rg_config.value = false;
-};
 const addIntelligent = (data) => {
   //根据智能体类型,打开不同的配置面板data.type
   show_model_item.value = true;
@@ -102,6 +100,17 @@ const updateIntelligentList = () => {
   bus_intelligentSearch.emit('');
   target_icon.value = '';
 };
+
+const getRagFlowDataFunc = async (id) => {
+  try {
+    const res = await getRagFlowData(id);
+    Object.assign(itemObj, res.data);
+    editAgentKuai.value.editClick();
+  } catch (err) {
+    // you can report use errorHandler or other
+  } finally {
+  }
+};
 /**
  * 监听编辑智能体事件
  * @param {Object} item 编辑智能体对象
@@ -116,6 +125,9 @@ bus_intelligentEdit.on((item) => {
       // TODO: 编辑rgflow智能体
       // itemObj = item;
       show_rg_config.value = true;
+      add_type = 'edit';
+      getRagFlowDataFunc(item.id);
+
       break;
     case '4':
       // TODO: 进入Dify智能体配置

+ 59 - 33
src/modules/intelligent/rgflow/AgentConfig.vue

@@ -264,14 +264,17 @@
 
 <script setup>
 import { onMounted, onBeforeMount, reactive, ref, nextTick, onUnmounted, onBeforeUnmount, computed } from 'vue';
-// import { kbdocumentupload, queryKbList, queryModelList } from '@/api/kbList';
+// import { kbdocumentupload, queryKbList, queryModelList } from '../../../apis/rgflow-dify';
+import { queryKbList, queryModelList, createRgFlow } from '../../../apis/rgflow-dify';
+import { createDialog } from '../../../apis/intelligent';
+import { dialogType } from '../../../views/manage/common';
 import useLoading from './loading';
 import Upload from './Upload.vue';
 import { Message } from '@arco-design/web-vue';
 // import { dialogSet } from '@/api/Agent';
 
 const props = defineProps(['show_rg_config', 'typeAngint', 'formData']);
-const emit = defineEmits(['queryList', 'update:show_rg_config']);
+const emit = defineEmits(['queryList', 'update:show_rg_config', 'updateIntelligentList']);
 
 const { loading, setLoading } = useLoading(true);
 const visible = computed(() => props.show_rg_config);
@@ -433,7 +436,15 @@ const handleOpened = (el) => {
     }, 100);
   });
 };
-
+const createOwnDialog = async (data, title) => {
+  console.log(data, 'data');
+  const res = await createDialog(data);
+  if (res.code == 200) {
+    Message.success(title);
+    handleCancel();
+    emit('updateIntelligentList');
+  }
+};
 const handleBeforeOk = async (done) => {
   formRef.value.validate().then((res) => {
     // console.log('res:', res)
@@ -444,6 +455,9 @@ const handleBeforeOk = async (done) => {
   formRef2.value.validate().then((res) => {
     // console.log('res:', res)
   });
+  // TODO: 先写死知识库和模型
+  form.kb_ids = ['5e96cc5aad3111ef93990242ac130006'];
+  form.llm_id = 'qwen-2.5-72b-instruct___OpenAI-API@OpenAI-API-Compatible';
   if (form.name && form.kb_ids.length > 0 && form.prompt_config.system && form.llm_id) {
     let title = '创建成功';
     let formNew = { ...form };
@@ -478,15 +492,27 @@ const handleBeforeOk = async (done) => {
 
     setLoading(true);
     try {
-      // const data = await dialogSet(formNew);
-      // if (data.code == 0) {
-      //   Message.success(title);
-      //   handleCancel();
-      // TODO: 刷新列表
-      // EventBus.emit('queryList');
-      // } else {
-      //   Message.error(data.msg);
-      // }
+      const res = await createRgFlow(formNew);
+      if (res.data.id && res.data.id.length) {
+        const dialogData = {
+          id: res.data.id,
+          name: form.name,
+          description: '',
+          icon: form.icon,
+          dialogType: dialogType.RAGFLOW_TYPE,
+          mode: 'ragflow'
+        };
+        // TODO: 调用自己系统的新增
+        if (props.typeAngint == 'add') {
+          createOwnDialog(dialogData, title);
+        } else {
+          emit('updateIntelligentList');
+          handleCancel();
+          Message.success(title);
+        }
+      } else {
+        Message.error(data.msg);
+      }
       done(true);
       setLoading(false);
     } catch (err) {
@@ -537,35 +563,35 @@ function handleTabChange(key) {
   activeKey.value = key;
 }
 
-const queryModel = async (params) => {
+const queryModel = async () => {
   try {
-    // const data = await queryModelList(params);
-    // const arrObj = {};
-    // for (const key in data.data) {
-    //   if (Object.hasOwnProperty.call(data.data, key)) {
-    //     const element = data.data[key];
-    //     arrObj[key] = element.filter((item) => item.available === true && item.model_type === 'chat');
-    //   }
-    // }
-    // modelList.value = arrObj;
-    // rankModelList.value = {
-    //   BAAI: [data.data.BAAI[1]],
-    //   Jina: data.data.Jina,
-    //   youdao: data.data.youdao
-    // };
+    const data = await queryModelList();
+    const arrObj = {};
+    for (const key in data.data) {
+      if (Object.hasOwnProperty.call(data.data, key)) {
+        const element = data.data[key];
+        arrObj[key] = element.filter((item) => item.available === true && item.model_type === 'chat');
+      }
+    }
+    modelList.value = arrObj;
+    rankModelList.value = {
+      BAAI: [data.data.BAAI[1]],
+      Jina: data.data.Jina,
+      youdao: data.data.Youdao
+    };
   } catch (err) {
     // you can report use errorHandler or other
   } finally {
   }
 };
 
-const knowledgeData = async (params = { page: 1, page_size: 20 }) => {
+const knowledgeData = async () => {
   setLoading(true);
   try {
-    // const { data } = await queryKbList(params);
-    // nextTick(() => {
-    //   tabs.value = data;
-    // });
+    const { data } = await queryKbList();
+    nextTick(() => {
+      tabs.value = data;
+    });
   } catch (err) {
     // you can report use errorHandler or other
   } finally {
@@ -574,7 +600,7 @@ const knowledgeData = async (params = { page: 1, page_size: 20 }) => {
 };
 
 onBeforeMount(() => {
-  queryModel({});
+  queryModel();
   knowledgeData();
 });
 onMounted(() => {});

+ 8 - 6
src/modules/login/FormItem.vue

@@ -47,11 +47,11 @@
 </template>
 
 <script setup>
-import { ref, reactive, getCurrentInstance, nextTick } from 'vue';
+import { ref, reactive, getCurrentInstance } from 'vue';
 import { useRouter } from 'vue-router';
 import { useCurrentUserStore } from '../../base/store/use-current-user';
 import { getLoginFormRules } from './auth';
-import { register } from '../../apis/login';
+import { register, asycData } from '../../apis/login';
 import { useI18n } from 'vue-i18n';
 
 const router = useRouter();
@@ -101,13 +101,11 @@ const handleSubmit = (data) => {
 const toRegister = () => {
   register({ username: form.username, email: form.email, password: form.password })
     .then((res) => {
-      if (res.code == 200 && res.data.username) {
+      if (res.code == 200) {
         $message.success(res.msg);
         form_type.value = 0;
         ref_form.value.resetFields();
-        nextTick(() => {
-          form.username = res.data.username;
-        });
+        toAsycData(res.data.userFlag);
       } else {
         $message.error(res.msg);
       }
@@ -116,6 +114,10 @@ const toRegister = () => {
       loading.value = false;
     });
 };
+// 注册后同步数据
+const toAsycData = async (userFlag) => {
+  await asycData(userFlag);
+};
 const changeFormType = (e) => {
   e.preventDefault();
   ref_form.value.resetFields();

+ 125 - 31
src/views/conversation/ConversationChatContainer.vue

@@ -12,7 +12,7 @@
           class="conversation-chat-list"
           :height="list_height"
           :agent_id="agent_id"
-          :list="chat_log_list"
+          :list="chat_display_list"
           :suggestion="chat_suggestion"
           :show_welcome="show_welcome"
           @share="handleChatShare"
@@ -50,9 +50,15 @@ import DialogAsideRight from '../../modules/conversation-chat/DialogAsideRight.v
 
 import useEleSize from '../../utils/use-ele-size';
 
-import useWebSocketChat from '../../modules/conversation-chat/use-websocket-chat';
-import { getHistoryLogs } from '../../modules/conversation-dialog/api-dialog';
-import { jsonParse, jsonStringify } from '../../utils/utils';
+import useWebSocketChat from '../../modules/conversation-dialog/use-websocket-chat';
+import { getHistoryLogs, getDialogChatId } from '../../modules/conversation-dialog/api-dialog';
+
+import formatDialogHistoryMessages from '../../modules/conversation-dialog/format-dialog-history-messages';
+
+import formatDialogSendOptions from '../../modules/conversation-dialog/format-dialog-send-options';
+
+import formatDialogTempLoadingMessage from '../../modules/conversation-dialog/format-dialog-temp-loading-message';
+import formatDialogReceiveMessage from '../../modules/conversation-dialog/format-dialog-receive-message';
 
 /**
  * 1. 历史会话进入此页面,展示历史会话内容
@@ -94,8 +100,28 @@ const list_height = computed(() => {
 /**
  * 对话数据列表
  */
-const chat_log_list = ref([]);
-const chat_suggestion = ref({});
+const chat_log_list = ref([]); // 历史对话的数据列表
+const chat_new_list = ref([]); // 新对话的数据列表
+const chat_suggestion = ref({}); // 对话推荐项目
+/**
+ * 此轮对话的状态
+ * 0 未开始
+ * 1 进行中
+ * 2 已结束
+ * 3 错误
+ */
+const chat_new_status = ref(0);
+
+/**
+ * 界面展示的对话列表
+ * 当用户从历史会话进入时,历史会话记录有数据,当用户基于历史会话进行对话时,新会话也有数据,
+ * 当用户从agent进入时,仅有新会话,没有历史会话
+ *
+ * 页面渲染的会话列表是 历史会话 + 新会话数据
+ */
+const chat_display_list = computed(() => {
+  return [].concat(chat_log_list.value, chat_new_list.value);
+});
 
 /**
  * 是否显示欢迎页
@@ -103,7 +129,7 @@ const chat_suggestion = ref({});
  * 对话区域会显示欢迎内容
  * 当用户开始对话时,欢迎内容消失,对话列表开始展示
  */
-const show_welcome = ref(false);
+const show_welcome = ref(true);
 
 /**
  * 分享
@@ -133,40 +159,66 @@ const input_disabled = ref(false);
  * 输入文本发生了变化
  * @param value
  */
-const handleTextChange = (value) => {};
+const handleTextChange = (value) => {
+  input_text.value = value;
+};
 /**
  * 输入的文件发生了变化
  * @param files
  */
-const handleFileChange = (files) => {};
+const handleFileChange = (files) => {
+  input_files.value = files;
+};
 /**
  * 输入已经确认要提交了
- * 1. 文件上传
- * 2. 创建会话
- *    get-chat-id
- *    连接socket
- * 3. socket 发送文本与文件数据内容
+ *
+ * 1. 创建会话 get-chat-id
+ * 2. 文件上传
+ * 3. 连接socket,socket 发送文本与文件数据内容
  * 4. 接收 socket 返回的数据项
  */
-const handleInputSubmit = () => {
+const handleInputSubmit = async () => {
   /**
    * 创建会话 get chat id
    */
 
+  let chat_id = null;
+
+  try {
+    const chatResult = await getDialogChatId(props.agent_id);
+
+    if (chatResult.code / 1 === 200) {
+      // 成功
+      const res_data = chatResult.data;
+
+      chat_id = res_data.chat_id;
+    } else {
+      // 失败
+      // TODO: 处理错误
+
+      return;
+    }
+  } catch (err) {
+    console.log(err);
+    // TODO: 处理错误
+    return;
+  }
+
   /**
-   * 上传文件,获取文件参数
+   * 如果存在文件数据,则进行文件上传
    */
+  if (input_files.value.length !== 0) {
+    // 进行文件上传,获取文件信息
+  }
 
   /**
+   * 上传文件,获取文件参数
    * 组装socket参数
-   * 发送websocket消息
    */
+  const msg = formatDialogSendOptions(input_text.value, input_files.value);
 
-  const msg = jsonStringify({
-    message: '消息内容'
-  });
-
-  handleWSSend(msg);
+  // 发送websocket消息
+  handleWSSend(chat_id, msg);
 };
 
 /**
@@ -202,9 +254,19 @@ const { initWSConnection, sendWSMessage, getWSInstance, closeWSConnect } = useWe
  * @param value
  */
 function handleWSSend(chat_id, value) {
+  // 已发送,但是未响应,表示已经存在了新对话
+  // 只是新对话还未返回响应内容
+  // 客户端依据此数据展示加载中的动画
+  const loadingMsg = formatDialogTempLoadingMessage();
+  chat_new_list.value.push(loadingMsg);
+
   // 初始化websocket连接
   initWSConnection(props.agent_id, chat_id, props.chat_type);
-  sendWSMessage(value);
+  sendWSMessage(value); // 发送socket连接
+
+  // 清空文本数据和文件数据
+  input_text.value = '';
+  input_files.value = [];
 }
 
 /**
@@ -213,17 +275,43 @@ function handleWSSend(chat_id, value) {
  */
 function handleWSMessage(e) {
   const res_data = e.data;
-  const res_content = jsonParse(res_data);
+
+  // 获取当前对话中最后一个对话数据
+  // 最后一个对话数据为当前正在进行的对话
+  const last_msg = chat_new_list.value[chat_new_list.value.length - 1];
 
   /**
-   * 类别:
-   * close
-   * error
-   * message
-   * stream
+   * 整理返回的数据
    */
-  const res_type = res_content.type;
-  const res_message = res_content.message; // 消息内容
+  const currentChat = formatDialogReceiveMessage(res_data, last_msg);
+  const chat_status = currentChat.status;
+  const chat_message = currentChat.message;
+  const chat_data = currentChat.data;
+
+  if (chat_status === 0) {
+    // 未开始: 此状态不存在,无需处理
+  }
+
+  if (chat_status === 1) {
+    // 次轮对话仍然在进行中
+
+    // 原来的最后一项弹出
+    chat_new_list.value.pop();
+    // 添加新的最后一项
+    chat_new_list.value.push(chat_data);
+  }
+
+  if (chat_status === 2) {
+    // 此轮对话已结束:列表数据无需变化
+    // 原来的最后一项弹出
+    chat_new_list.value.pop();
+    // 添加新的最后一项
+    chat_new_list.value.push(chat_data);
+  }
+
+  if (chat_status === 3) {
+    // 对话出错了
+  }
 }
 
 /**
@@ -249,8 +337,14 @@ onMounted(() => {
         if (res.code / 1 === 200) {
           // 成功
           const session_list = res.data;
+
+          // 将列表数据进行转化
+          const list = formatDialogHistoryMessages(session_list);
+
+          chat_log_list.value = list;
         } else {
           // 异常
+          // TODO:处理异常
         }
       })
       .catch((err) => {

+ 1 - 0
src/views/manage/CardItem.vue

@@ -58,6 +58,7 @@ const props = defineProps({
 .card-des {
   display: -webkit-box;
   overflow: hidden;
+  min-height: 32px;
   margin-top: 5px;
   color: var(--color-neutral-4);
   -webkit-line-clamp: 2;

+ 7 - 0
vite.config.mjs

@@ -34,6 +34,13 @@ export default defineConfig({
         secure: false,
         ws: true,
         rewrite: (path) => path.replace(/^\/dify/, '')
+      },
+      '/rgflow': {
+        target: 'http://192.168.20.119:28002/',
+        changeOrigin: true,
+        secure: false,
+        ws: true,
+        rewrite: (path) => path.replace(/^\/rgflow/, '')
       }
     }
   },