From 1da0bc255179f25b5227bec27833cf486cb6cb90 Mon Sep 17 00:00:00 2001
From: yinbangzhong <zhongbangyin@126.com>
Date: 星期二, 10 九月 2024 19:41:40 +0800
Subject: [PATCH] agent会话

---
 src/views/dmx/IntelligentAgent/components/agentConfig.vue |    4 
 src/api/agentSession.ts                                   |    8 +
 yarn.lock                                                 |    2 
 src/views/dmx/knowledgeLib/components/excel.vue           |    3 
 src/views/sessionManager/components/agentSession.vue      |   90 ++++++--------
 src/views/sessionManager/index.vue                        |  140 ++++++-----------------
 src/views/sessionManager/components/updataFile.vue        |   34 -----
 src/views/dmx/knowledgeLib/components/txt.vue             |   33 ++++-
 src/main.ts                                               |    1 
 9 files changed, 121 insertions(+), 194 deletions(-)

diff --git a/src/api/agentSession.ts b/src/api/agentSession.ts
index fad49ac..ef8e05a 100644
--- a/src/api/agentSession.ts
+++ b/src/api/agentSession.ts
@@ -38,6 +38,14 @@
   );
 }
 
+export function updateAgentConversation(data) {
+  return axios.post(
+    '/api/v1/canvas/update_agent_conversation',
+    data
+  );
+}
+
+
 // 鑾峰彇浼氳瘽璇︽儏
 export function getAgentSessionDetailsApi(id: string) {
   return axios.get(
diff --git a/src/main.ts b/src/main.ts
index ec48a6c..f4745fd 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,7 +6,6 @@
 import store from './store';
 import i18n from './locale';
 import directive from './directive';
-import './mock';
 import App from './App.vue';
 
 
diff --git a/src/views/dmx/IntelligentAgent/components/agentConfig.vue b/src/views/dmx/IntelligentAgent/components/agentConfig.vue
index 9ff5baf..18b227b 100644
--- a/src/views/dmx/IntelligentAgent/components/agentConfig.vue
+++ b/src/views/dmx/IntelligentAgent/components/agentConfig.vue
@@ -575,7 +575,8 @@
       }
 
       if (!max_tokens.value) {
-        formNew.llm_setting.max_tokens = '';
+        //formNew.llm_setting.max_tokens = '';
+        delete formNew.llm_setting.max_tokens;
       }
 
       setLoading(true);
@@ -607,6 +608,7 @@
       // console.log(props.formData,'浼犲叆鏁版嵁');
       if (props.formData.llm_setting.max_tokens == '') {
         max_tokens.value = false;
+        delete props.formData.llm_setting.max_tokens;
       } else {
         max_tokens.value = true;
       }
diff --git a/src/views/dmx/knowledgeLib/components/excel.vue b/src/views/dmx/knowledgeLib/components/excel.vue
index 8735164..ca95456 100644
--- a/src/views/dmx/knowledgeLib/components/excel.vue
+++ b/src/views/dmx/knowledgeLib/components/excel.vue
@@ -8,6 +8,7 @@
 </template>
 
 <script setup>
+
   //	鑾峰彇鐖剁粍浠朵紶閫掔殑璧勬簮url
   const props = defineProps({
     previewSrc: {
@@ -18,7 +19,7 @@
   });
   //寮曞叆VueOfficeExcel缁勪欢鐩稿叧
   import VueOfficeExcel from '@vue-office/excel';
-  import '@vue-office/excel/lib/index.css';
+
 
   const comStyle = {
     width: '100%',
diff --git a/src/views/dmx/knowledgeLib/components/txt.vue b/src/views/dmx/knowledgeLib/components/txt.vue
index 0110ad9..391d664 100644
--- a/src/views/dmx/knowledgeLib/components/txt.vue
+++ b/src/views/dmx/knowledgeLib/components/txt.vue
@@ -1,6 +1,6 @@
 <template>
   <div>
-    <pre v-html="content"></pre>
+    <pre v-text="content"></pre>
   </div>
 </template>
 
@@ -8,6 +8,7 @@
 import { ref, watch } from "vue";
 
 let content = ref("");
+
 //	鑾峰彇鐖剁粍浠朵紶閫掔殑璧勬簮url
 const props = defineProps({
   previewSrc: {
@@ -17,14 +18,30 @@
   }
 });
 
-const getContent = (url) => {
-  fetch(url)
-    .then((res) => res.text())
-    .then((data) => {
-      content.value = data;
-    });
+//鐩戞帶灞炴�reviewSrc鐨勫彉鍖栵紝濡傛灉鍙戠敓鍙樺寲锛岄噸鏂拌幏鍙栧唴瀹�
+watch(
+  () => props.previewSrc,
+  () => {
+    getContent();
+  }
+);
+
+const getContent = () => {
+  content.value="";
+  fetch(props.previewSrc, {
+    responseType: "arraybuffer"
+  }).then(
+    (response) => {
+      return response.arrayBuffer();
+    }
+  ).then(
+    (data) => {
+      const decoder = new TextDecoder('gbk');
+      content.value = decoder.decode(data);
+    }
+  );
 };
-getContent(props.previewSrc);
+getContent();
 
 
 const comStyle = {
diff --git a/src/views/sessionManager/components/agentSession.vue b/src/views/sessionManager/components/agentSession.vue
index 2e65936..484ad76 100644
--- a/src/views/sessionManager/components/agentSession.vue
+++ b/src/views/sessionManager/components/agentSession.vue
@@ -70,11 +70,7 @@
           />
         </template>
         <template #content>
-          <!--          <a-card :class="{ chatItemAnswer: theme === 'light' }">-->
-          <!--            <div :class="{ light: theme === 'light' }"-->
-          <!--            >{{ sessionDetail.content }}-->
-          <!--            </div>-->
-          <!--          </a-card>-->
+
           <a-textarea
             readonly
             auto-size
@@ -86,18 +82,7 @@
             style="border: none"
           >
           </a-textarea>
-          <!-- <div class="prompt">
-            <ul>
-              <li class="prompt-item" @click="copyText('43234')">
-                <span>
-                  344343klsdjkjksdjkjksdjk灏卞紑濮嬪ぇ瀹剁湅鏁版嵁搴撶櫥璁板崱鍙楁墦鍑诲嚡鎾掔櫥璁板崱鍙楁墦鍑诲嚡鎾掔櫥璁板崱鍙楁墦鍑诲嚡鎾掔殑43</span
-                >
-                <span style="margin-left: 20px">
-                  <icon-right />
-                </span>
-              </li>
-            </ul>
-          </div> -->
+
         </template>
         <template #actions>
           <span
@@ -189,19 +174,7 @@
           <icon-send size="32" style="color: #0960bd" />
         </a-button>
       </div>
-      <!--      <div class="btn-send">-->
-      <!--        &lt;!&ndash; <icon-send size="32" /> &ndash;&gt;-->
-      <!--        <a-button-->
-      <!--          :disabled="chatDis"-->
-      <!--          @click="sentClick"-->
-      <!--          type="text"-->
-      <!--          style="border-radius: 24px"-->
-      <!--          :loading="loading"-->
-      <!--        >-->
-      <!--          <icon-send size="32" style="color: #0960bd;"/>-->
-      <!--        </a-button-->
-      <!--        >-->
-      <!--      </div>-->
+
     </div>
     <a-modal
       v-model:visible="visible"
@@ -250,8 +223,8 @@
   import {
     agentResetApi,
     agentSetApi,
-    getAgentSessionDetailsApi,
-  } from '@/api/agentSession';
+    getAgentSessionDetailsApi, updateAgentConversation
+  } from "@/api/agentSession";
   import EventBus from '@/utils/EventBus';
   import useClipboard from 'vue-clipboard3';
   import { addSessionApi, getSessionDetailsApi } from '@/api/session';
@@ -282,7 +255,7 @@
   const selectValue = ref('');
   const sectionList = ref({});
   let chatObj = reactive({});
-  const isStopChat = ref(false);
+  let isStopChat = false;
   const appStore = useAppStore();
   const theme = computed(() => {
     return appStore.theme;
@@ -295,6 +268,8 @@
   let isHistory = ref(false); //鏄惁鏄巻鍙茶褰�
   let dsl = reactive({});
   const chatDataMeg = reactive({});
+
+  let toStop = false;
 
   const rules = {
     name: [
@@ -377,7 +352,9 @@
         'Content-Type': 'application/json',
       },
       body: JSON.stringify({
+        converson_id: conversation_id.value,
         id: agentObj.id,
+        dsl:agentObj.dsl,
       }),
     });
 
@@ -420,7 +397,11 @@
     if (code == 0) {
       console.log(data, 'agent浼氳瘽璇︽儏');
       Object.assign(chatObj, data);
-      sessionDetailList.value = data.dsl.messages;
+      // sessionDetailList.value = data.dsl.messages;
+      await updateAgentConversation({
+        converson_id: conversation_id.value,
+        dsl:data.dsl,
+      })
       agentTitle.value = `${data.title}` || '鏈懡鍚嶄細璇�';
       from.name = `${data.title}` || '鏈懡鍚嶄細璇�';
       refreshScroll(); //鍒锋柊婊氬姩鏉′綅缃�
@@ -498,7 +479,11 @@
     }
   };
 
+  let message_stop = [];
+  let message_id = "";
   const startChat = async (valMsg) => {
+    isStopChat = false;
+    displayedText.value = '';
     sessionDetailList.value.push({
       content: valMsg,
       role: 'user',
@@ -507,6 +492,7 @@
     refreshScroll();
     let chatStr = {
       id: agentObj.id,
+      converson_id: conversation_id.value,
       message: valMsg,
     };
     if (isHistory.value) {
@@ -527,6 +513,11 @@
       .getReader();
     currIndex.value = 0;
     while (true) {
+      if (isStopChat) {
+        message_stop.push(message_id);
+        setChatDataMeg(chatDataMeg);
+        break;
+      }
       const x = await reader?.read();
       if (x) {
         const { done, value } = x;
@@ -534,6 +525,16 @@
         try {
           const val = JSON.parse(value?.data || '');
           const d = val?.data;
+          if (message_id != d.message_id) {
+            message_id = d.message_id;
+            message_stop = [];
+          } else {
+            //message_stop涓煡鎵緈essage_id
+            const index = message_stop.findIndex(item => item === message_id);
+            if (index > -1) {
+              break;
+            }
+          }
           if (typeof d !== 'boolean') {
             console.info('data:', d);
             streamStr.value = d.content;
@@ -543,15 +544,10 @@
           console.warn(e);
         }
         if (done) {
-          console.info('done');
-          displayedText.value = '';
-          if (isStopChat.value) {
-            isStopChat.value = false;
-            setChatDataMeg(chatDataMeg);
-          } else {
-            queryAgentSessionDetail(agentObj.id);
-            EventBus.emit('queryAppUsageList');
-          }
+          console.info("done");
+          displayedText.value = "";
+          queryAgentSessionDetail(agentObj.id);
+          EventBus.emit("queryAppUsageList");
           break;
         }
       }
@@ -561,13 +557,9 @@
   };
 
   const stopChat = async () => {
-    // const { code, data } = await stopChatApi(agentObj.id);
-    // if (code === 200) {
-    //   Message.success("宸插仠姝�");
-    // }
     loading.value = false;
     chatDis.value = false;
-    isStopChat.value = true;
+    isStopChat = true;
     console.log('stopChat');
     console.log(displayedText.value, 'displayedText');
     console.log(sessionDetailList.value, 'sessionDetailList');
@@ -656,7 +648,7 @@
   watch(
     () => props.modalObj,
     (newVal, oldVal) => {
-      // Object.assign(agentObj, newVal);
+      Object.assign(agentObj, newVal);
       //璋冪敤agent鍒濆鍖栨柟娉�
       if (JSON.stringify(newVal) != '{}') {
         // initPage();
diff --git a/src/views/sessionManager/components/updataFile.vue b/src/views/sessionManager/components/updataFile.vue
index 11ac03a..6e5f47b 100644
--- a/src/views/sessionManager/components/updataFile.vue
+++ b/src/views/sessionManager/components/updataFile.vue
@@ -19,6 +19,7 @@
         :auto-upload="false"
         ref="uploadRef"
         @change="onChange"
+        :show-retry-button="false"
         multiple
         :limit="1"
       />
@@ -31,36 +32,6 @@
         "
         class="upload-wrap"
       >
-<!--        <div>-->
-<!--          <span>瑙f瀽鏂规硶: </span>-->
-<!--          <a-radio-group-->
-<!--            v-model="parser_id"-->
-<!--            style="width: 400px"-->
-<!--            @change="parserChange"-->
-<!--          >-->
-<!--            <a-popover v-for="item in filterData" :key="item.value">-->
-<!--              <a-radio :value="item.value" style="margin-right: 10px">{{-->
-<!--                item.name-->
-<!--              }}</a-radio>-->
-
-<!--              <template #content>-->
-<!--                <p>{{ item.parser_config_str }}</p>-->
-<!--              </template>-->
-<!--            </a-popover>-->
-<!--          </a-radio-group>-->
-<!--        </div>-->
-<!--        <a-select-->
-<!--          :style="{ width: '100px', margin: '0 10px' }"-->
-<!--          :default-value="['閫氱敤', '澶氭ā鎬�']"-->
-<!--          v-model="parser_configs"-->
-<!--          multiple-->
-<!--          size="small"-->
-<!--        >-->
-<!--          <a-option v-for="item in parser_ids" :key="item.value">{{-->
-<!--            item.name-->
-<!--          }}</a-option>-->
-<!--        </a-select>-->
-<!--        <p class="config-text">鏇村閰嶇疆</p>-->
         <a-button
           :loading="onFileSelectedLoading"
           @click="upDataFile"
@@ -396,7 +367,8 @@
 
   watch(
     () => props.sessionId,
-    (value, oldValue) => {
+    (value) => {
+
       activeSessionId.value = value;
     }
   );
diff --git a/src/views/sessionManager/index.vue b/src/views/sessionManager/index.vue
index 00f9106..5a681db 100644
--- a/src/views/sessionManager/index.vue
+++ b/src/views/sessionManager/index.vue
@@ -121,7 +121,7 @@
                   />
                 </template>
                 <template #content>
-                  <a-card v-if="isExistTip(sessionDetail.content)">
+                  <a-card v-if="isExistTip(sessionDetail.content)" style="padding: 10px">
                     <div
                       :class="{ chatItemAnswer: theme === 'light' }"
                       :style="{
@@ -160,10 +160,13 @@
                               "
                             >
                               <img
-                                style="width: 20px; height: 20px"
+                                style="width: 15px; height: 15px"
                                 :src="tipImage"
                               />
                             </a-tooltip>
+                            <span v-if="tipIndex == item.len - 1">
+                              {{ breakContent.substring(item.index + 5) }}
+                            </span>
                           </span>
                           <span v-else-if="tipIndex == item.len - 1">
                             {{
@@ -179,7 +182,7 @@
                               "
                             >
                               <img
-                                style="width: 20px; height: 20px"
+                                style="width: 15px; height: 15px"
                                 :src="tipImage"
                               />
                             </a-tooltip>
@@ -199,7 +202,7 @@
                               "
                             >
                               <img
-                                style="width: 20px; height: 20px"
+                                style="width: 15px; height: 15px"
                                 :src="tipImage"
                               />
                             </a-tooltip>
@@ -238,12 +241,9 @@
                     </ul>
                   </div> -->
                 </template>
-                <!-- <div>{{ sessionDetail.role === 'assistant' }}</div> -->
+
                 <template #actions>
-                  <!-- <div
-                    v-for="(item, leng) in messagenList.reference"
-                    :key="leng"
-                  > -->
+
                   <div>
                     <div>
                       <template
@@ -308,12 +308,7 @@
                 </template>
 
                 <template #actions>
-                  <!-- <div class="icon-box">
-                    <div>
-                      <img :src="getIconByExtension('pdf')" alt="" />
-                    </div>
-                    <div class="icon-text"> 鏂囦欢绫诲瀷 </div>
-                  </div> -->
+
                   <div
                     class="action"
                     @click="stopChat"
@@ -329,24 +324,12 @@
                 </template>
               </a-comment>
             </div>
-            <!-- <div class="chartStart" v-if="isStart" @click="startChat"
-              >鍋滄鐢熸垚</div
-            >
-            <div class="chartStart v-else" @click="stopChat">閲嶆柊鐢熸垚</div> -->
+
           </a-scrollbar>
 
           <div class="chat_bottom">
             <div class="center-bottom">
-              <!-- <a-textarea
-              v-model="inputMsg"
-              @keydown.shift.enter="sendMessage"
-              style="height: 180px"
-              placeholder="杈撳叆鎮ㄦ兂浜嗚В鐨勫唴瀹癸紝Shift+Enter鍙戦��"
-              :max-length="500"
-              allow-clear
-              show-word-limit
-            >
-            </a-textarea> -->
+
               <a-textarea
                 v-model="inputMsg"
                 @keydown.shift.enter="handleShiftEnter"
@@ -382,12 +365,6 @@
                   :sessionId="activeSessionId"
                   @selectFileCallback="selectFileCallback"
                 ></updataFile>
-                <!-- <uploadFile
-                  ref="fileInput"
-                  :handleRemove="handleRemove"
-                  :typeXLse="false"
-                  @selectFileCallback="selectFileCallback"
-                ></uploadFile> -->
 
                 <a-button
                   :disabled="chatDis"
@@ -483,11 +460,10 @@
       </a-col>
     </a-row>
     <a-modal title=" " v-model:visible="fileVisible" :footer="false" fullscreen>
-      <!--      <docx  previewSrc="http://192.168.20.116:1080/v1/document/get/405c3efa4d8c11ef97560242ac120006"></docx>-->
       <docx v-if="documenttype == 'docx'" :previewSrc="previewSrc"></docx>
-      <excel v-if="documenttype == 'xlsx'" :previewSrc="previewSrc"></excel>
+      <excel v-if="documenttype == ('xlsx' || 'xls')" :previewSrc="previewSrc"></excel>
       <Pdf v-if="documenttype == 'pdf'" :previewSrc="previewSrc"></Pdf>
-      <txt v-if="documenttype == 'txt'" :previewSrc="previewSrc"></txt>
+      <txt  v-if="documenttype == 'txt'" :previewSrc="previewSrc"></txt>
     </a-modal>
     <a-modal
       v-model:visible="visible"
@@ -591,7 +567,7 @@
   let from = reactive({
     name: '鏈懡鍚嶄細璇�',
   });
-  const isStopChat = ref(false);
+
   const currIndex = ref(0);
   const displayedText = ref(''); // 姝e湪鏄剧ず鐨勬枃瀛�
   let timer: number | null = null;
@@ -712,7 +688,7 @@
 
   const clickHref = async (item) => {
     documenttype.value = item.doc_name.split('.').pop();
-    previewSrc.value = httpUrl + `/api/v1/document/get/${item.doc_id}`;
+    previewSrc.value = httpUrl + `/api/v1/document/get/${item.doc_id}`+"?t="+new Date().getTime();
     fileVisible.value = true;
   };
 
@@ -780,53 +756,6 @@
     activeSessionId.value = conversation_id;
   };
 
-  //涓婁紶
-  // const selectFileCallback = async (resData, file) => {
-  //   console.log(111);
-  //   try {
-  //     const formData = new FormData();
-  //     uploaditemList.value = resData;
-  //     uploaditemList.value.map((item) => {
-  //       if (item.name == file[0].file.name) {
-  //         item.onFileSelectedLoading = true;
-  //         item.textName = '涓婁紶涓�';
-  //       }
-  //       return item;
-  //     });
-  //     debugger;
-  //     if (!activeSessionId.value) {
-  //       await createSession(resData);
-  //     }
-
-  //     // onFileSelectedLoading.value = true;
-  //     // textName.value = '涓婁紶涓�';
-
-  //     formData.append('files', file[0].file);
-  //     formData.append('conversation_id', activeSessionId.value);
-
-  //     console.log(formData, 'formData');
-  //     const { data, code } = await uploadAndParse(formData);
-  //     if (code === 200) {
-  //       uploaditemList.value.map((item: any) => {
-  //         item.onFileSelectedLoading = false;
-  //         item.textName = '涓婁紶鎴愬姛';
-  //         return item;
-  //       });
-  //       getInfo(data);
-  //       // onFileSelectedLoading.value = false;
-  //       // textName.value = '涓婁紶鎴愬姛';
-  //     }
-  //   } catch (err) {
-  //     uploaditemList.value.map((item: any) => {
-  //       item.onFileSelectedLoading = false;
-  //       item.textName = '涓婁紶澶辫触';
-  //       return item;
-  //     });
-  //     // onFileSelectedLoading.value = false;
-  //     // textName.value = '涓婁紶澶辫触';
-  //     Message.error('涓婁紶澶辫触');
-  //   }
-  // };
 
   //涓婁紶鎴愬姛瑙f瀽getinfo
   const getInfo = async (id) => {
@@ -942,6 +871,7 @@
 
   // 鍙戦��
   const sentClick = () => {
+    displayedText.value="";
     sendMessage('click');
     uploaditemList.value = [];
   };
@@ -1000,11 +930,14 @@
     }
   };
 
+  let message_stop = [];
+  let message_id = "";
   const startChat = async (valMsg) => {
     chatDis.value = true;
     loading.value = true;
-    chartLoading.value = true;
     toStop = false;
+    displayedText.value = '';
+    chartLoading.value = true;
     sessionDetailList.value.push({
       content: valMsg,
       role: 'user',
@@ -1033,6 +966,7 @@
     currIndex.value = 0;
     while (true) {
       if (toStop) {
+        message_stop.push(message_id);
         displayedText.value = '';
         setChatDataMeg(chatDataMeg);
         break;
@@ -1044,6 +978,16 @@
         try {
           const val = JSON.parse(value?.data || '');
           const d = val?.data;
+          if (message_id != d.message_id) {
+            message_id = d.message_id;
+            message_stop = [];
+          } else {
+            //message_stop涓煡鎵緈essage_id
+            const index = message_stop.findIndex(item => item === message_id);
+            if (index > -1) {
+              break;
+            }
+          }
           if (typeof d !== 'boolean') {
             // console.info("data:", d);
             streamStr.value = d.content;
@@ -1057,12 +1001,8 @@
           console.info('done');
           displayedText.value = '';
           chartLoading.value = false;
-          if (isStopChat.value) {
-            setChatDataMeg(chatDataMeg);
-          } else {
-            queryNewSessionDetail(activeSessionId.value);
-            EventBus.emit('queryAppUsageList');
-          }
+          queryNewSessionDetail(activeSessionId.value);
+          EventBus.emit('queryAppUsageList');
           break;
         }
       }
@@ -1073,14 +1013,8 @@
   };
 
   const stopChat = async () => {
-    // const { code, data } = await stopChatApi(activeSessionId.value);
-    // if (code === 200) {
-    //   Message.success("宸插仠姝�");
-    //   queryNewSessionDetail(activeSessionId.value);
-    // }
     loading.value = false;
     chatDis.value = false;
-    isStopChat.value = true;
     toStop = true;
     console.log('stopChat');
     console.log(displayedText.value, 'displayedText');
@@ -1103,7 +1037,9 @@
       .concat(lastArr);
     console.log(sessionDetailList.value, 'sessionDetailList2');
     console.log(chatObj, 'chatObj瀵硅薄');
-    chatObj.message = chatObj.message.concat(lastArr);
+    if (chatObj?.message) {
+      chatObj.message = chatObj.message.concat(lastArr);
+    }
     Object.assign(chatDataMeg, {
       id: chatObj.id,
       conversation_id: chatObj.id,
@@ -1133,7 +1069,6 @@
       agentTitle.value = data.name;
       from.name = data.name;
       refreshScroll(); //鍒锋柊婊氬姩鏉′綅缃�
-      isStopChat.value = false;
     }
   };
 
@@ -1205,6 +1140,7 @@
       // fileInput.value.cancel();
     });
     EventBus.on('newChat', () => {
+      uploaditemList.value = [];
       agentType.value = '1';
       // createSession('');
       activeSessionId.value = '';
diff --git a/yarn.lock b/yarn.lock
index 59a7c00..e9fcbee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1018,7 +1018,7 @@
 
 "@vue-office/excel@^1.7.11":
   version "1.7.11"
-  resolved "https://registry.npmmirror.com/@vue-office/excel/-/excel-1.7.11.tgz"
+  resolved "https://registry.npmmirror.com/@vue-office/excel/-/excel-1.7.11.tgz#e37e5a2c212907a62c9b65e42d9df21f9c68a2cc"
   integrity sha512-LF3R9IV573Sf4qTu6Ik5Ee8UMfkrsZQ6HEQE25/2m1c0CMcHX6KanIy6Cz0b0Q+FrLH3TjIsLTm6oPcqAbDGSA==
 
 "@vue-office/pdf@^2.0.2":

--
Gitblit v1.8.0