소스 검색

perf: 完善项目说明、文档

1. 调整部分组件的目录结构
2. 完善项目说明
3. 新增外链配置文件
zhupei 1 년 전
부모
커밋
f62fd80474

+ 26 - 1
README.md

@@ -24,7 +24,32 @@ $ npm run build
 
 1. `dist`目录需要在`git`进行追踪,不必添加到`.gitignore`文件中。
 2. 安装项目依赖使用`npm install --legacy-peer-deps`,仅使用`npm install`命令可能无法启动项目。
-3. 配置文件位于`src/config/settings.json`中,`RAGFlowUrl`需要配置,当前值为`http://192.168.20.119:11080`。如果不配置,则会引起`文档引用`效果出错,图片和 PDF 文档将无法显示。
+3. 配置文件`src/ajax/url-external-config.js`中的`REFFERENCE_ASSETS_FILE_URL`需要配置,当前值为`http://192.168.20.119:11080`。如果不配置,则会引起`文档引用`效果出错,图片和 PDF 文档将无法显示。
+4. 项目启动之前,可能需要做一些配置,详情请见下方`关于配置文件`章节
+
+## 关于配置文件
+
+项目里一同有三个配置文件,如下:
+
+- src/ajax/url-external-config.js
+- src/utils/config.js
+- src/config/settings.json
+
+### src/ajax/url-external-config.js
+
+此文件中配置了一些`外部链接`,这些外部链接支撑了项目里的部分功能。例如`iframe`弹窗,例如 PDF 文档和图片的展示
+
+如果项目里有用到外部链接,请在此文件中进行配置。
+
+### src/utils/config.js
+
+此文件配置了项目的一些`基本信息`,例如项目的`名称`,项目的`介绍`,左侧边栏展示的导航项目,图标等。
+
+### src/config/settings.json
+
+此文件配置了一下项目的可用功能,例如项目`主题`、`菜单`是否可以隐藏,是否显示 `footer` 等。
+
+文档待完善。
 
 ## 相关资源
 

+ 3 - 3
src/ajax/document.js

@@ -1,10 +1,10 @@
-import config from '../config/settings.json';
+import { REFFERENCE_ASSETS_FILE_URL } from './url-external-config';
 
 /**
- * 切记 配置文件中的配置项 RAGFlowUrl 不能为空
+ * 切记 配置文件中的配置项 REFFERENCE_ASSETS_FILE_URL 不能为空
  * 否则,文档引用效果将会异常,无法看到图片和文档
  */
-const server_url = config.RAGFlowUrl;
+const server_url = REFFERENCE_ASSETS_FILE_URL;
 
 /**
  * 获取一个文档的链接

+ 18 - 0
src/ajax/url-external-config.js

@@ -0,0 +1,18 @@
+/**
+ * 文档出卷 -> 出卷 -> iframe弹窗地址
+ */
+export const IFRAME_URL_CHUJUAN = 'http://192.168.20.119:8501'
+
+/**
+ * 小数绘图 -> ComfyUI -> iframe弹窗地址
+ */
+export const IFRAME_URL_COMFYUI = 'http://192.168.20.119:8188'
+
+/**
+ * 引用的静态资源地址
+ * PDF文档和图片链接需要使用此地址
+ * 例如:
+ * http://192.168.20.119:11080/v1/document/image/e2ade7feaae211ef88b50242ac130006-d66a28e5d91eea31e32d6e42474834cd
+ * http://192.168.20.119:11080/v1/document/get/041cc868aaef11efa2170242ac130006
+ */
+export const REFFERENCE_ASSETS_FILE_URL = "http://192.168.20.119:11080"

+ 89 - 18
src/components/DialogWrap.vue

@@ -130,7 +130,8 @@
                 :chat_id="dialogId" 
                 :chat_type="type" 
                 :agentId="agentId"
-                :special_refference="item.fullRefference"
+                :special_4_refference="item.fullRefference"
+                :special_4_image="item.fullImageUrls"
               />
             </p>
           </div>
@@ -144,7 +145,8 @@
               :chat_id="dialogId" 
               :chat_type="type" 
               :agentId="agentId"
-              :special_refference="item.fullRefference"
+              :special_4_refference="item.fullRefference"
+              :special_4_image="item.fullImageUrls"
             />
           </div>
 
@@ -323,6 +325,14 @@ export default defineComponent({
 
         // 完整的引用数据: pdf文档引用效果需要使用此数据
         fullRefference: item.reference,
+        // 合法可用的图片地址
+        fullImageUrls: (() => {
+          if(item.images){
+            return item.images;
+          }else{
+            return [];
+          }
+        })(),
 
         // 如果有文件处理完拿第一个,后续有变动再改
         file: (item.files && item.files.length > 0) ? item.files.map(item => {
@@ -456,9 +466,14 @@ export default defineComponent({
     const isLoaded = ref(false);
     const answerData = ref("");
     const answerReference = ref([]);
+
     const fullRefference = reactive({
       full_ref: {}
     });
+    const fullImageUrls = reactive({
+      url: []
+    });
+
     const imageUrl = ref(undefined);
     const answerFile = ref(undefined);
     const answerCode = ref(undefined);
@@ -471,7 +486,10 @@ export default defineComponent({
     const addAnswer = (data) => {
       data.answer = answerData.value;
       data.reference = answerReference.value;
+
       data.fullRefference = fullRefference.full_ref; // 完整的引用数据
+      data.fullImageUrls = fullImageUrls.url; // 图片地址数据
+
       data.image_url = imageUrl.value;
       data.code = answerCode.value;
       data.file = answerFile.value;
@@ -513,16 +531,18 @@ export default defineComponent({
 
       function createSocket() {
         const token = helper.read("token");
+        const p_type = props.type;
+
         // 如果是智能数据,则拿缓存里的数据开启对话
         if (helper.read("sendData") && helper.read("sendData").agent_id == 'basic_excel_talk') {
           const data = helper.read("sendData");
 
-          helper.webSocket = ajax.message.create(token, data.agent_id, data.chat_id, props.type);
+          helper.webSocket = ajax.message.create(token, data.agent_id, data.chat_id, p_type);
 
           // 创建对话之后清空缓存
           helper.write('sendData', {});
         } else {
-          helper.webSocket = ajax.message.create(token, props.agentId, props.dialogId, props.type);
+          helper.webSocket = ajax.message.create(token, props.agentId, props.dialogId, p_type);
         }
 
         // 监听socket开启事件
@@ -579,12 +599,14 @@ export default defineComponent({
         }
 
         const { type, reference, step_message, files } = res_data;
+        const msg_type = type; // 返回的消息类型
+        const chat_type = props.type; // 对话的类型
 
         let { message } = res_data;
 
         if (!loadingData.value) return;
 
-        if (type === "stream") {
+        if (msg_type === "stream") {
           // 没解析完就一直loading
           if (step_message == '文件上传完成,开始解析') {
             emit("changeParse", true);
@@ -607,7 +629,7 @@ export default defineComponent({
           }
         }
 
-        if (type === "message") {
+        if (msg_type === "message") {
           // 获取此时的聊天临时聊天记录中的序号
           const cur_index = tempHistory.getChatIndex(cur_chat_id);
 
@@ -630,7 +652,7 @@ export default defineComponent({
           }
 
           // 智能数据
-          if (props.type == 'smartData') {
+          if (chat_type == 'smartData') {
             answerData.value = '正在处理中...'
             // 以下是智能数据对话的变量
             const { image_url, excel_url, excel_name, code, sql } = JSON.parse(evt_dt);
@@ -655,8 +677,9 @@ export default defineComponent({
 
             if (sql) answerSql.value = getHTML(sql);
           }
+
           // 出题
-          if (props.type == 'question') {
+          if (chat_type == 'question') {
             const { file_url, file_name } = JSON.parse(evt_dt);
             const port = window.location.port ? `:${window.location.port}` : '';
 
@@ -677,7 +700,7 @@ export default defineComponent({
           addAnswer(loadingData.value);
         }
 
-        if (type === "close") {
+        if (msg_type === "close") {
           if (!answerData.value) {
             answerLoaded();
             answerData.value = getHTML(message);
@@ -686,6 +709,39 @@ export default defineComponent({
           stopWriting();
           answerData.value = "";
           isLoaded.value = false;
+
+          /**
+           * 在小数绘图对话结束时
+           * 存在一个字段数据
+           */
+          if(chat_type === 'huitu'){
+            // 获取此时的聊天临时聊天记录中的序号
+            const cur_index = tempHistory.getChatIndex(cur_chat_id);
+
+            message = replaceCode(message, cur_index);
+            answerData.value = getHTML(message);
+
+            // 小数绘图
+            if(res_data.message_files){
+              console.log(11);
+              const arr = [];
+
+              if(Array.isArray(res_data.message_files)){
+                console.log(12);
+
+                res_data.message_files.forEach((item, index) => {
+                  console.log(13, index);
+                  arr.push(item.url);
+                });
+
+                console.log(arr, 'arr');
+
+                fullImageUrls.url = arr;
+              }else{
+                fullImageUrls.url = arr;
+              }
+            }
+          }
         }
 
         // // 获取所有的临时历史记录
@@ -717,22 +773,37 @@ export default defineComponent({
        */
       helper.$bus.on("send-message", (value) => {
         dialogConfig.value = helper.dialogConfig;
-        const question = props.type === "report" ? value.inputs.report_name : value;
+
+        const chat_type = props.type; // 对话的类型
+
+        const question = chat_type === "report" ? value.inputs.report_name : value;
         const data = addQuestion(question);
         loadingData.value = addLoading(data);
-        let sendData =
-          props.type === "report" || props.type === "excel"
-            ? value
-            : {
-              message: value
-            };
 
-        if (props.type === 'doc') {
+        let sendData = {
+          message: value
+        }
+
+        if(chat_type === "report"){
+          sendData = value;
+        }
+
+        if(chat_type === "excel"){
+          sendData = value;
+        }
+
+          // chat_type === "report" || chat_type === "excel"
+          //   ? value
+          //   : {
+          //     message: value
+          //   };
+
+        if (chat_type === 'doc') {
           sendData.doc_ids = [helper.read('docId')];
         }
 
         // 小数绘图
-        if(props.type === 'huitu'){
+        if(chat_type === 'huitu'){
           const img_file = helper.read('sendData');
           if(img_file){
             const upload_file_id = img_file.upload_file_id;

+ 1 - 1
src/components/answer-effect/RootRefference.vue

@@ -3,7 +3,7 @@
 </template>
 
 <script setup>
-import RefferenceFlag from './RefferenceFlag.vue'
+import RefferenceFlag from '../answer-refference/RefferenceFlag.vue'
 
 const props = defineProps({
     img: String, // 图片链接

+ 23 - 3
src/components/answer-effect/apply-image-display.js

@@ -1,9 +1,11 @@
 import dynamicImageDisplay from './effect-image-display';
 
-export default function applyImageDisplay(container_klass) {
+// const img_server = 'http://192.168.20.116';
+
+export default function applyImageDisplay(container_klass, img_list) {
     let unmount = () => { };
 
-    // 处理引用数据
+    // 处理回答中的图片数据
     setTimeout(() => {
         // 无序号,渲染所有的内容
         const ele = document.querySelectorAll(`.${container_klass} .xiaoshuhuitu-image-container`);
@@ -18,7 +20,25 @@ export default function applyImageDisplay(container_klass) {
     function renderFlag(ele) {
         // 遍历找到的所有标记元素
         ele.forEach((item, idx) => {
-            const data_src = item.getAttribute('data-src');
+            let data_src = '';
+            if (img_list && Array.isArray(img_list)) {
+                const img_src = img_list[idx];
+
+                if (img_src) {
+                    // 合法的src
+                    // data_src = `${img_server}${img_src}`
+                    data_src = item.getAttribute('data-src');
+                } else {
+                    // 非法的src
+                    data_src = item.getAttribute('data-src');
+                }
+            } else {
+                // 无
+                data_src = item.getAttribute('data-src');
+            }
+
+            // const data_src = item.getAttribute('data-src');
+
             const data_alt = item.getAttribute('data-alt');
 
             unmount = dynamicImageDisplay(item, {

+ 0 - 0
src/components/answer-effect/AsideImage.vue → src/components/answer-refference/AsideImage.vue


+ 0 - 0
src/components/answer-effect/RefferenceFlag.vue → src/components/answer-refference/RefferenceFlag.vue


+ 14 - 11
src/components/btn-iframe-chujuan/BtnIframeChujuan.vue

@@ -1,14 +1,14 @@
 <template>
-  <a-button @click="handleClick" class="comfyui-btn">
-    <img class="comfyui-logo-img" :src="logo" /> 出卷
+  <a-button @click="handleClick" class="chujuan-btn">
+    <img class="chujuan-logo-img" :src="logo" /> 出卷
   </a-button>
 
-  <a-modal v-model:visible="visible" @ok="handleOk" @cancel="handleCancel" modal-class="comfyui-container-modal" body-class="comfyui-container-body" width="90%">
+  <a-modal v-model:visible="visible" @ok="handleOk" @cancel="handleCancel" modal-class="chujuan-container-modal" body-class="chujuan-container-body" width="90%">
     <template #title>
-      <img class="comfyui-logo-img" :src="logo" /> 出卷
+      <img class="chujuan-logo-img" :src="logo" /> 出卷
     </template>
     
-    <iframe :src="iframe_src" class="comfyui-iframe"></iframe>
+    <iframe :src="iframe_src" class="chujuan-iframe"></iframe>
   </a-modal>
 </template>
 
@@ -17,7 +17,10 @@ import { ref } from 'vue';
 
 import logo from '../../assets/question/1.png'
 
-const iframe_src = 'http://192.168.20.119:8501';
+import {IFRAME_URL_CHUJUAN} from '../../ajax/url-external-config';
+
+// const iframe_src = 'http://192.168.20.119:8501';
+const iframe_src = IFRAME_URL_CHUJUAN;
 
 const visible = ref(false);
 
@@ -33,11 +36,11 @@ const handleCancel = () => {
 </script>
 
 <style scoped>
-.comfyui-btn{
+.chujuan-btn{
     border-radius: 30px;
     color: #353535;
 }
-.comfyui-logo-img{
+.chujuan-logo-img{
     width: 22px;
     display: inline-block;
     margin-right: 5px;
@@ -45,14 +48,14 @@ const handleCancel = () => {
 </style>
 
 <style>
-.comfyui-container-modal{
+.chujuan-container-modal{
 
 }
-.arco-modal-body.comfyui-container-body{
+.arco-modal-body.chujuan-container-body{
     width: 100%;
     padding: 0;
 }
-.arco-modal-body.comfyui-container-body .comfyui-iframe{
+.arco-modal-body.chujuan-container-body .chujuan-iframe{
     width: 100%;
     height: 80vh;
 }

+ 3 - 1
src/components/btn-iframe-comfyui/BtnIframeComfyui.vue

@@ -15,8 +15,10 @@
 <script setup>
 import { ref } from 'vue';
 import logo from '../../assets/images/comfyui-logo.png'
+import {IFRAME_URL_COMFYUI} from '../../ajax/url-external-config';
 
-const comfyui_src = 'http://192.168.20.119:8188';
+// const comfyui_src = 'http://192.168.20.119:8188';
+const comfyui_src = IFRAME_URL_COMFYUI;
 
 const visible = ref(false);
 

+ 8 - 3
src/components/dialog-wrap/Answer.vue

@@ -36,7 +36,12 @@ const props = defineProps({
      * 特殊数据:专为文档引用效果提供的数据
      * 文档引用功能需要使用此数据实现
      */
-    special_refference: Object
+    special_4_refference: Object,
+    /**
+     * 特殊数据:专为图片效果提供的数据
+     * 例如:['https://xxx.com/ancd.jpg']
+     */
+    special_4_image: Array,
 });
 
 const getHTML = (string) => {
@@ -57,12 +62,12 @@ onMounted(() => {
     /**
      * 小数绘图的文生图 图片效果
      */
-    applyImageDisplay(container_klass);
+    applyImageDisplay(container_klass, props.special_4_image);
 
     /**
      * 文档引用效果
      */
-    applyRefferencePopover(container_klass, props.special_refference);
+    applyRefferencePopover(container_klass, props.special_4_refference);
 });
 </script>
 <style>

+ 1 - 1
src/components/image-editor/ImgEditBtn.vue

@@ -9,7 +9,7 @@
     </template>
     
     <div style="padding:20px; height: 100%;">
-      <TuiImageEditor :image_url="image_url" />
+      <TuiImageEditor :image_url="image_url"/>
     </div>
   </a-modal>
 </template>

+ 84 - 31
src/components/image-editor/TuiImageEditorVue3.vue

@@ -7,6 +7,8 @@ import ImageEditor from 'tui-image-editor';
 import 'tui-color-picker/dist/tui-color-picker.css';
 import 'tui-image-editor/dist/tui-image-editor.css';
 
+import image2Base64 from './img-2-base64';
+
 const props = defineProps({
   // includeUi: {
   //   type: Boolean,
@@ -410,41 +412,92 @@ const theme = {
   "colorpicker.title.color": "#fff",
 };
 
-onMounted(() => {
-    // let options = props.options;
-
-    // if (props.includeUi) {
-    //   options = Object.assign(includeUIOptions, props.options);
-    // }
-
-    // options.includeUI.locale = zh_local;
-    // options.includeUI.theme = theme;
-
-    const opts = {
-      includeUI: {
-        loadImage: {
-          // path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
-          // path: 'http://localhost/src/assets/slider/logo.jpg',
-          path: props.image_url,
-          name: "image",
-        },
-        initMenu: "draw", // 默认打开的菜单项
-        menuBarPosition: "bottom", // 菜单所在的位置
-        locale: zh_local, // 本地化语言为中文
-        theme: theme
-      },
-      cssMaxWidth: 1000, // canvas 最大宽度
-      cssMaxHeight: 600, // canvas 最大高度
+function imgLoaded(url){
+  return new Promise((resolve, reject) => {
+    const img = new Image();
+    img.crossOrigin = 'Anonymous';
+    img.src = url;
+    img.onload = function(){
+      // 图片加载完成
+      resolve(img);
+    }
+
+    img.onerror = function(e){
+      reject(e);
     }
+  })
+}
 
-    editorInstance = new ImageEditor(editor.value, opts);
-    // editorInstance.addImageObject(props.image_url).then(objectProps => {
-    //   // https://nhn.github.io/tui.image-editor/latest/ImageEditor#addImageObject
-    //   console.log(ojectProps.id, 'console.log(ojectProps.id);');
+onMounted(() => {
+    imgLoaded(props.image_url).then(img => {
+      const url = img.src;
+
+      const opts = {
+        includeUI: {
+          loadImage: {
+            // path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
+            // path: 'http://localhost/src/assets/slider/logo.jpg',
+            path: url,
+            // path: base64,
+            name: "image",
+          },
+          initMenu: "draw", // 默认打开的菜单项
+          menuBarPosition: "bottom", // 菜单所在的位置
+          locale: zh_local, // 本地化语言为中文
+          theme: theme
+        },
+        cssMaxWidth: 1000, // canvas 最大宽度
+        cssMaxHeight: 600, // canvas 最大高度
+      }
+
+      editorInstance = new ImageEditor(editor.value, opts);
+      // editorInstance.addImageObject(props.image_url).then(objectProps => {
+      //   // https://nhn.github.io/tui.image-editor/latest/ImageEditor#addImageObject
+      //   console.log(ojectProps.id, 'console.log(ojectProps.id);');
+      // }).catch(err => {
+      //   console.log(err, 'error-出错了');
+      // });
+      addEventListener();
+    }).catch(err => {
+      console.log(err, '出错了');
+    });
+
+
+    // image2Base64(props.image_url).then(base64 => {
+    //   console.log(base64, 'base64 成功');
+
+    //   const opts = {
+    //     includeUI: {
+    //       loadImage: {
+    //         // path: "https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c1d7a1feb60346449c1a64893888989a~tplv-k3u1fbpfcp-watermark.image",
+    //         // path: 'http://localhost/src/assets/slider/logo.jpg',
+    //         // path: props.image_url,
+    //         path: base64,
+    //         name: "image",
+    //       },
+    //       initMenu: "draw", // 默认打开的菜单项
+    //       menuBarPosition: "bottom", // 菜单所在的位置
+    //       locale: zh_local, // 本地化语言为中文
+    //       theme: theme
+    //     },
+    //     cssMaxWidth: 1000, // canvas 最大宽度
+    //     cssMaxHeight: 600, // canvas 最大高度
+    //   }
+
+    //   editorInstance = new ImageEditor(editor.value, opts);
+    //   // editorInstance.addImageObject(props.image_url).then(objectProps => {
+    //   //   // https://nhn.github.io/tui.image-editor/latest/ImageEditor#addImageObject
+    //   //   console.log(ojectProps.id, 'console.log(ojectProps.id);');
+    //   // }).catch(err => {
+    //   //   console.log(err, 'error-出错了');
+    //   // });
+    //   addEventListener();
     // }).catch(err => {
-    //   console.log(err, 'error-出错了');
+    //   console.log(err, '转base64失败');
     // });
-    addEventListener();
+    
+
+
 });
 
 onUnmounted(() => {

+ 48 - 0
src/components/image-editor/img-2-base64.js

@@ -0,0 +1,48 @@
+
+// image2Base64(' // https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg').then(base64 => {
+//     const img = new Image();
+
+//     console.log("base64-------------")
+//     console.log(base64)
+//     img.src = base64
+//     img.crossOrigin = '*'
+//     img.onload = () => resolve(img)
+//     img.onerror = () => {
+//         img.src = '/img/admin/error1.jpg';
+//     }
+// })
+
+// 导入的图片路径https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg
+
+/**
+ * 参考互联网上方法
+ * 将图片数据转换为base64
+ * @see https://blog.csdn.net/weixin_43452154/article/details/134449821
+ * @param {string} src - 图片链接
+ * @returns 
+ */
+export default function image2Base64(src) {
+    var toBase64 = new Promise(function (resolve, reject) {
+        window.URL = window.URL || window.webkitURL;
+        var xhr = new XMLHttpRequest();
+        xhr.open("get", src, true);
+        // 至关重要
+        xhr.responseType = "blob";//文件流
+        xhr.onload = function (res) {
+            if (res.currentTarget.status == 200) {
+                //得到一个blob对象
+                var blob = res.currentTarget.response;
+                // 至关重要
+                let oFileReader = new FileReader();
+                oFileReader.onloadend = function (e) {
+                    let base64 = e.target.result;//base64
+                    resolve(base64)
+                };
+                oFileReader.readAsDataURL(blob);
+            }
+        }
+        xhr.send();
+    });
+
+    return toBase64;
+}

+ 1 - 1
src/views/Home.vue

@@ -146,7 +146,7 @@
               <a-button :style="{ width: '500px' }" type="primary" @click="continueCombine">开始</a-button>
             </footer>
 
-            <footer v-if="navId === 6">
+            <footer v-if="navId === 6 && !historyMode">
               <MessagePicUpload />
             </footer>