Pārlūkot izejas kodu

feat: 完善小数绘图的功能

1.
拆分图片上传组件。优化图片上传组件的展示。使得在小数绘图功能周期内图片上传组件一只显示
2. 新增Question组件,对于用户的提问内容,单独处理
3. 图生文功能中,客户端临时增加所选图片的展示
zhupei 1 gadu atpakaļ
vecāks
revīzija
2fc3afe236

+ 15 - 1
src/components/DialogWrap.vue

@@ -35,7 +35,14 @@
           </div>
 
           <div class="dialog-footer" v-if="item?.question">
-            <p style="margin-top: 0">{{ item.question }}</p>
+            <!-- <p style="margin-top: 0" data-question-content="------">{{ item.question }}</p> -->
+            <Question 
+              :question="item.question"                 
+              :index="key" 
+              :chat_id="dialogId" 
+              :chat_type="type" 
+              :agentId="agentId"
+              />
           </div>
           <!-- 如果是代码则不显示头像 -->
           <div class="dialog-header" v-if="item?.capacity">
@@ -262,16 +269,20 @@ import initTempHistory, {getChatIdFromUrl} from './answer-effect/temp-history';
 
 import { getDocumentUrl } from '../ajax/document';
 
+import Question from './dialog-wrap/Question.vue';
 import Suggestion from './dialog-wrap/Suggestion.vue';
 
 import Answer from './dialog-wrap/Answer.vue';
 
+import {huituSetTempImageIndex} from './dialog-wrap/temp-image'
+
 export default defineComponent({
   components: {
     DialogFeedback,
 
     Suggestion,
 
+    Question,
     Answer
   },
   props: [
@@ -821,6 +832,9 @@ export default defineComponent({
               }
 
               setTimeout(() => {
+                // 记录此时页面中的问题记录
+                huituSetTempImageIndex(dialogList.length);
+
                 helper.write('sendData', {}); 
               });
             }

+ 194 - 147
src/components/PictureCards.vue

@@ -1,6 +1,6 @@
 <template>
-  <main>
-    <section class="xiaoshuhuitu-sections">
+  
+    <!-- <section class="xiaoshuhuitu-sections">
       <div v-for="(item, key) in data" :key="key" :style="{ background: item.background }">
         <img :src="getAssetURL(item.img)" />
         <div :style="{ color: item.color }">
@@ -8,123 +8,122 @@
           <span>{{ item.content }}</span>
         </div>
       </div>
-    </section>
+    </section> -->
+
+<footer class="picture-upload-footer">
+  <a-popover :popup-visible="showUpload" position="tl" @popup-visible-change="() => {
+    showDeleteTooltip = false;
+  }">
+    <div class="text-hide">
+      <div class="upload-img-container" @click="() => {
+        showUpload = true;
+      }
+        ">
+        <img src="@/assets/document/6.svg" class="img-normal" v-if="!showUpload" />
+        <img src="@/assets/document/7.svg" class="img-hover" v-if="!showUpload" />
+        <img src="@/assets/document/8.svg" class="img-active" v-if="showUpload" />
+      </div>
 
-    <footer class="picture-upload-footer">
-      <a-popover :popup-visible="showUpload" position="tl" @popup-visible-change="() => {
-        showDeleteTooltip = false;
-      }">
-        <div class="text-hide">
+      <p>已选择 {{ fileList.length }} 个图片</p>
+    </div>
+
+    <template #content>
+      <div :class="{ 'upload-wrap': true, 'upload-wrap-narrow': uploadFile }">
+        <header>
+          <p>{{ uploadFile ? "上传图片" : "上传图片" }}</p>
           <div @click="() => {
-            showUpload = true;
+            showUpload = false;
           }
             ">
-            <img src="@/assets/document/6.svg" class="img-normal" v-if="!showUpload" />
-            <img src="@/assets/document/7.svg" class="img-hover" v-if="!showUpload" />
-            <img src="@/assets/document/8.svg" class="img-active" v-if="showUpload" />
+            <icon-close size="large" />
           </div>
-
-          <p>已选择 {{ fileList.length }} 个图片</p>
-        </div>
-
-        <template #content>
-          <div :class="{ 'upload-wrap': true, 'upload-wrap-narrow': uploadFile }">
-            <header>
-              <p>{{ uploadFile ? "上传图片" : "上传图片" }}</p>
-              <div @click="() => {
-                showUpload = false;
-              }
-                ">
-                <icon-close size="large" />
-              </div>
-            </header>
-
-            <div class="xiaoshuhuitu-image-upload">
-              <section>
-                <a-upload 
-                  draggable 
-                  list-type="picture"
-                  :multiple="false" 
-                  :limit="1" 
-                  :show-file-list="false"
-                  @before-upload="onBeforeUpload" 
-                  @exceed-limit="onExceedLimit" 
-                  @before-remove="onBeforeRemove"
-                  :custom-request="customRequest" 
-                  :show-upload-button="{ showOnExceedLimit: false }"
-                  accept=".png,.jpg"
-                >
-                  <template #upload-button>
-                    <div class="upload-main">
-                      <p>拖拽图片到这里,或<span>点此上传</span></p>
-                      <span>
-                        支持上传 .png, .jpg,大小不超过10M
-                      </span>
+        </header>
+
+        <div class="xiaoshuhuitu-image-upload">
+          <section>
+            <a-upload 
+              draggable 
+              list-type="picture"
+              :multiple="false" 
+              :limit="1" 
+              :show-file-list="false"
+              @before-upload="onBeforeUpload" 
+              @exceed-limit="onExceedLimit" 
+              @before-remove="onBeforeRemove"
+              :custom-request="customRequest" 
+              :show-upload-button="{ showOnExceedLimit: false }"
+              accept=".png,.jpg"
+            >
+              <template #upload-button>
+                <div class="upload-main">
+                  <p>拖拽图片到这里,或<span>点此上传</span></p>
+                  <span>
+                    支持上传 .png, .jpg,大小不超过10M
+                  </span>
+                </div>
+              </template>
+            </a-upload>
+          </section>
+
+          <div v-if="tableData.length > 0">
+            <p>
+              <span>已选择图片列表</span>
+              <span>共{{ tableData.length }}个</span>
+            </p>
+            <div>
+              <a-table :pagination="false" row-key="name" :columns="columns" :data="tableData"
+                :row-selection="rowSelection" v-model:selectedKeys="selectedKeys">
+                <template #methods="{ record }">
+                  <a-tooltip content="下载">
+                    <div class="method-button" @click="downloadDocument(record)">
+                      <icon-cloud-download />
                     </div>
-                  </template>
-                </a-upload>
-              </section>
-
-              <div v-if="tableData.length > 0">
-                <p>
-                  <span>已选择图片列表</span>
-                  <span>共{{ tableData.length }}个</span>
-                </p>
-                <div>
-                  <a-table :pagination="false" row-key="name" :columns="columns" :data="tableData"
-                    :row-selection="rowSelection" v-model:selectedKeys="selectedKeys">
-                    <template #methods="{ record }">
-                      <a-tooltip content="下载">
-                        <div class="method-button" @click="downloadDocument(record)">
-                          <icon-cloud-download />
-                        </div>
-                      </a-tooltip>
-                      <a-popover :popup-visible="showDeleteTooltip && deleteId === record.key">
-                        <a-tooltip content="删除">
-                          <div class="method-button" @click="handleDeleteButton(record.key)">
-                            <icon-delete />
-                          </div>
-                        </a-tooltip>
-                        <template #content>
+                  </a-tooltip>
+                  <a-popover :popup-visible="showDeleteTooltip && deleteId === record.key">
+                    <a-tooltip content="删除">
+                      <div class="method-button" @click="handleDeleteButton(record.key)">
+                        <icon-delete />
+                      </div>
+                    </a-tooltip>
+                    <template #content>
+                      <div>
+                        <p>删除后无法恢复,您是否删除?</p>
+                        <div class="delete-buttons">
                           <div>
-                            <p>删除后无法恢复,您是否删除?</p>
-                            <div class="delete-buttons">
-                              <div>
-                                <a-button size="mini" type="primary" style="margin-right: 10px"
-                                  @click="deleteDocument">确认</a-button>
-                                <a-button size="mini" type="outline" @click="() => {
-                                  showDeleteTooltip = false;
-                                }
-                                  ">取消</a-button>
-                              </div>
-                            </div>
+                            <a-button size="mini" type="primary" style="margin-right: 10px"
+                              @click="deleteDocument">确认</a-button>
+                            <a-button size="mini" type="outline" @click="() => {
+                              showDeleteTooltip = false;
+                            }
+                              ">取消</a-button>
                           </div>
-                        </template>
-                      </a-popover>
+                        </div>
+                      </div>
                     </template>
-                  </a-table>
-                </div>
-              </div>
+                  </a-popover>
+                </template>
+              </a-table>
             </div>
           </div>
+        </div>
+      </div>
 
-          <div class="bottom-buttons">
-            <div>
-              <a-button type="outline" style="margin-right: 10px" @click="selectFiles(false)">取消</a-button>
-              <a-button :loading="buttonLoading" type="primary" @click="selectFiles(true, 'paper')">去{{
-                uploadFile ? '提问'
-                  : '提问'
-              }}({{ fileList.length
-                }})</a-button>
-            </div>
-          </div>
-        </template>
-      </a-popover>
+      <div class="bottom-buttons">
+        <div>
+          <a-button type="outline" style="margin-right: 10px" @click="selectFiles(false)">取消</a-button>
+          <a-button :loading="buttonLoading" type="primary" @click="selectFiles(true, 'paper')">去{{
+            uploadFile ? '提问'
+              : '提问'
+          }}({{ fileList.length
+            }})</a-button>
+        </div>
+      </div>
+    </template>
+  </a-popover>
 
-      <span class="placeholder-width-5px"></span>
-      <BtnIframeComfyui />
-    </footer>
-  </main>
+  <span class="placeholder-width-5px"></span>
+  <BtnIframeComfyui />
+</footer>
 </template>
 <script>
 import dayjs from "dayjs";
@@ -136,8 +135,10 @@ import BtnIframeComfyui from './btn-iframe-comfyui/BtnIframeComfyui.vue';
 
 import ImgEditBtn from './image-editor/ImgEditBtn.vue';
 
+import {huituSetTempImageFile} from './dialog-wrap/temp-image'
+
 export default defineComponent({
-  props: ["data", "hasUpload", "hasSlider", "uploadFile", "isQuestion", "agentId", "agengtName"],
+  props: ["data", "uploadFile", "isQuestion", "agentId", "agengtName"],
   components: {
     BtnIframeComfyui,
 
@@ -201,13 +202,19 @@ export default defineComponent({
             helper.write('sendData', sendData);
           }
         } else {
+          let temp_file = null;
           fileList.value.forEach(file => {
             formData.append('file', file)
+
+            temp_file = file;
           });
 
           const data = await ajax.message.createDialog(props.agentId);
           const result = await ajax.file.uploadFile(props.agentId, data.chat_id, formData);
 
+          // 保存一张临时图片
+          huituSetTempImageFile(temp_file, props.agentId, data.chat_id);
+
           // 保存小数绘图数据
           helper.write('sendData', {
             name: '小数绘图',
@@ -247,7 +254,6 @@ export default defineComponent({
           // helper.$bus.emit("open-dialog", '合并Excel');
         }
       }
-
     };
 
     const handleDeleteButton = (id) => {
@@ -367,6 +373,7 @@ export default defineComponent({
   }
 });
 </script>
+
 <style lang="scss" scoped>
 .bottom-buttons {
   display: flex;
@@ -431,58 +438,98 @@ export default defineComponent({
   }
 }
 
-main {
-  height: 100%;
+footer.picture-upload-footer{
   display: flex;
-  flex-direction: column;
-  overflow: hidden;
+  justify-content: flex-start;
+  align-items: center;
 
-  >section {
-    overflow-y: auto;
+  .text-hide {
+    // width: 110px;
+    overflow: hidden;
     display: flex;
-    flex-wrap: wrap;
-    gap: 20px;
+    align-items: center;
+  }
 
-    &.section-full {
-      flex: 1;
+  .upload-img-container{
+    cursor: pointer;
+    // width: 110px;
+    height: 32px;
+
+    .img-normal {
+      display: block;
     }
 
-    >div {
-      width: 382px;
-      height: 120px;
-      padding: 20px 24px;
-      border-radius: 8px;
-      display: flex;
+    .img-hover {
+      display: none;
+    }
 
-      >img {
-        width: 38px;
-        height: 38px;
-        margin-right: 12px;
+    &:hover {
+      .img-normal {
+        display: none;
       }
 
-      >div {
-        font-size: 18px;
-        font-weight: bold;
-
-        >span {
-          font-size: 15px;
-          font-weight: 400;
-          color: #797d7f;
-          margin-top: 8px;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          display: -webkit-box;
-          -webkit-line-clamp: 2;
-          -webkit-box-orient: vertical;
-          line-height: 1.4;
-        }
+      .img-hover {
+        display: block;
       }
     }
-  }
 
-  footer.picture-upload-footer{
-    justify-content: flex-start;
+    >img {
+      width: 110px;
+      height: 32px;
+    }
   }
+}
+
+main {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+
+  // >section {
+  //   overflow-y: auto;
+  //   display: flex;
+  //   flex-wrap: wrap;
+  //   gap: 20px;
+
+  //   &.section-full {
+  //     flex: 1;
+  //   }
+
+  //   >div {
+  //     width: 382px;
+  //     height: 120px;
+  //     padding: 20px 24px;
+  //     border-radius: 8px;
+  //     display: flex;
+
+  //     >img {
+  //       width: 38px;
+  //       height: 38px;
+  //       margin-right: 12px;
+  //     }
+
+  //     >div {
+  //       font-size: 18px;
+  //       font-weight: bold;
+
+  //       >span {
+  //         font-size: 15px;
+  //         font-weight: 400;
+  //         color: #797d7f;
+  //         margin-top: 8px;
+  //         overflow: hidden;
+  //         text-overflow: ellipsis;
+  //         display: -webkit-box;
+  //         -webkit-line-clamp: 2;
+  //         -webkit-box-orient: vertical;
+  //         line-height: 1.4;
+  //       }
+  //     }
+  //   }
+  // }
+
+
   >footer {
     display: flex;
     justify-content: space-between;

+ 25 - 0
src/components/PictureFooter.vue

@@ -0,0 +1,25 @@
+<template>
+<footer>
+    <div class="btn-container">
+        <picture-cards :agentId="agentId"/>
+    </div>
+    
+    <MessagePicUpload />
+</footer>
+</template>
+<script setup>
+import PictureCards from './PictureCards.vue'
+import MessagePicUpload from './MessagePicUpload.vue'
+
+const props = defineProps({
+    agentId: String,
+});
+</script>
+
+<style lang="scss" scoped>
+.btn-container{
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+}
+</style>

+ 83 - 0
src/components/dialog-wrap/Question.vue

@@ -0,0 +1,83 @@
+<template>
+  <p style="margin-top: 0" :class="question_klass" ref="container">{{ question }}</p>
+</template>
+
+<script setup>
+import { ref, onBeforeUnmount, onMounted, reactive, nextTick, inject, computed, watch, useTemplateRef } from "vue";
+
+import {getTempImageInfo} from './temp-image';
+
+const props = defineProps({
+    /**
+     * 回答的文本数据
+     */
+    question: String,
+    /**
+     * 界面中对话的序号
+     */
+    index: Number,
+    /**
+     * 当前对话的ID值
+     */
+    chat_id: String,
+    /**
+     * 当前对话的类型
+     */
+    chat_type: String,
+    /**
+     * 当前对话使用的agent ID
+     */
+    agentId: String,
+});
+
+const container = useTemplateRef('container');
+
+const question_klass = computed(() => {
+    return `${props.chat_type}-${props.agentId}-question-content-${props.index}`;
+});
+
+const showTempImage = () => {
+    const dt = getTempImageInfo(props.agentId, props.index/1 + 1);
+
+    if(!dt){
+        return;
+    }
+
+    // 需要设置
+    const file = dt.file; // 图片数据
+
+    if(!file){
+        return;
+    }
+
+    var reader = new FileReader();
+    reader.onload = function(e) {
+        const div = document.createElement('div');
+        var imgElement = document.createElement("img");
+
+        imgElement.src = e.target.result;
+
+        imgElement.style = `max-width: 200px;max-height:200px;`;
+
+        div.appendChild(imgElement);
+        
+        if(container.value){
+            container.value.appendChild(div);
+        }
+    };
+
+    reader.readAsDataURL(file);
+}
+
+onMounted(() => {
+    setTimeout(() => {
+        showTempImage();
+    });
+});
+</script>
+<style>
+
+</style>
+<style scoped>
+
+</style>

+ 52 - 0
src/components/dialog-wrap/temp-image.js

@@ -0,0 +1,52 @@
+/**
+ * 临时展示图片
+ * 小数绘图功能里,用户上传一张图片后,在问题里没有显示
+ */
+
+// 临时文件
+let temp_file = null;
+let temp_index = null;
+let agent_id = null;
+let chat_id = null;
+
+/**
+ * 1. 上传文件,保存临时文件
+ * 2. 发送文本消息,临时文件与ID和序号对应
+ * 3. 提交消息,设置question中的图片
+ */
+const store = {};
+
+/**
+ * 保存临时图片文件
+ * @param {File} file 
+ */
+export const huituSetTempImageFile = (file, agentId, chatId) => {
+    temp_file = file;
+    agent_id = agentId;
+    chat_id = chatId;
+}
+
+/**
+ * 记录临时文件的序号
+ * @param {number} index 
+ */
+export const huituSetTempImageIndex = (index) => {
+    temp_index = index;
+
+    store[`${agent_id}-${index}`] = {
+        file: temp_file,
+        index: temp_index,
+        agent_id: agent_id,
+        chat_id: chat_id
+    };
+}
+
+export const getTempImageInfo = (agent_id, index) => {
+    const key = `${agent_id}-${index}`;
+
+    if (key in store) {
+        return store[key];
+    } else {
+        return null;
+    }
+}

+ 23 - 2
src/views/Home.vue

@@ -146,9 +146,12 @@
               <a-button :style="{ width: '500px' }" type="primary" @click="continueCombine">开始</a-button>
             </footer>
 
-            <footer v-if="navId === 6 && !historyMode">
+            <!-- <footer v-if="navId === 6 && !historyMode">
+              <picture-cards />
               <MessagePicUpload />
-            </footer>
+            </footer> -->
+
+            <PictureFooter v-if="navId === 6 && !historyMode" :agentId="selectId"/>
 
             <!-- <a-tooltip :content="closeOthers ? '收起' : '展开'">
               <div @click="openAll" v-if="navId !== -2">
@@ -171,7 +174,9 @@ import DialogWrap from "../components/DialogWrap.vue";
 import Slider from "../components/Slider.vue";
 import History from "../components/History.vue";
 import MessageInput from "../components/MessageInput.vue";
+import PictureCards from '@/components/PictureCards.vue'
 import MessagePicUpload from '../components/MessagePicUpload.vue'
+import PictureFooter from '../components/PictureFooter.vue';
 import DocumentDetail from "./DocumentDetail.vue";
 import DocumentCombine from "./DocumentCombine.vue";
 import Index from "./Index.vue";
@@ -199,7 +204,11 @@ export default defineComponent({
     Index,
     Answer,
     MessageInput,
+
+    PictureCards,
     MessagePicUpload,
+    PictureFooter,
+
     DialogWrap,
     Document,
     PicArticle,
@@ -320,6 +329,10 @@ export default defineComponent({
           };
         });
       }
+
+      // 每次切换功能板块时,清空sendData数据
+      // helper.write('sendData', {}); 
+      clearLocalStorageSendData();
     };
 
     /**
@@ -503,8 +516,16 @@ export default defineComponent({
       // selectId = selectId;
       historyMode.value = false;
       setReadonly(false);
+
+      // 每次新建对话时,清空sendData数据
+      // helper.write('sendData', {}); 
+      clearLocalStorageSendData();
     };
 
+    function clearLocalStorageSendData(){
+      // helper.write('sendData', {}); 
+    }
+
     return {
       SYSTEM_NAME,
       customChange,

+ 71 - 2
src/views/PicArticle.vue

@@ -1,5 +1,17 @@
 <template>
-    <picture-cards :data="data" :hasUpload="true" :hasSlider="false" :uploadImg="true" />
+<main>
+    <section class="xiaoshuhuitu-sections">
+      <div v-for="(item, key) in data" :key="key" :style="{ background: item.background }">
+        <img :src="getAssetURL(item.img)" />
+        <div :style="{ color: item.color }">
+          {{ item.title }}
+          <span>{{ item.content }}</span>
+        </div>
+      </div>
+    </section>
+
+    <!-- <picture-cards :data="data" /> -->
+</main>
 </template>
 <script>
 import PictureCards from '@/components/PictureCards.vue'
@@ -9,6 +21,8 @@ export default defineComponent({
         PictureCards
     },
     setup() {
+        const { helper, ajax } = inject("$global");
+
         const data = ref([
             {
                 background: "rgba(72, 177, 244, 0.06)",
@@ -26,8 +40,63 @@ export default defineComponent({
             }
         ]);
         return {
-            data
+            data,
+
+            getAssetURL: helper.getAssetURL,
         };
     }
 });
 </script>
+
+<style lang="scss" scoped>
+
+main {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+
+  >section {
+    overflow-y: auto;
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px;
+
+    &.section-full {
+      flex: 1;
+    }
+
+    >div {
+      width: 382px;
+      height: 120px;
+      padding: 20px 24px;
+      border-radius: 8px;
+      display: flex;
+
+      >img {
+        width: 38px;
+        height: 38px;
+        margin-right: 12px;
+      }
+
+      >div {
+        font-size: 18px;
+        font-weight: bold;
+
+        >span {
+          font-size: 15px;
+          font-weight: 400;
+          color: #797d7f;
+          margin-top: 8px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+          line-height: 1.4;
+        }
+      }
+    }
+  }
+}
+</style>