Bladeren bron

合并 chd 分支

wzg 5 maanden geleden
bovenliggende
commit
4b72eb79de

+ 184 - 171
src/entrypoints/content.js

@@ -172,254 +172,267 @@ export default defineContentScript({
                         if (excelDataA[item.excelColumn]) {
 
                             await simulateCompleteUserAction(input, input, formatDate(excelDataA[item.excelColumn]), formatDate(excelDataA[item.excelColumn]))
-            }
-          }
-        }
+                        }
+                    }
+                }
                 if (item.findBy === 'placeholder') {
                     const input = formChildren.find(child => child.placeholder === item.findByValue)
-          if (input) {
+                    if (input) {
                         simulateUserInput(input, excelDataA[item.excelColumn])
-          }
-        }
+                    }
+                }
                 if (item.findBy === 'label') {
                     if (!excelDataA[item.excelColumn]) continue
                     const label = [...form.getElementsByTagName('label')].find(label => label.innerText.includes(item.findByValue) || item.findByValue.includes(label.innerText))
                     console.log(label)
                     if (!label) continue
                     if (item.type === 'radio' || item.type === 'checkbox') {
-            console.log(excelDataA[item.excelColumn]);
-
-            if (label) {
+                        console.log(excelDataA[item.excelColumn]);
+                        
+                        if (label) {
                             const span = findLabelForSpan(label)
-              span.forEach(span => {
-                console.log(span.innerText);
-                if (span.innerText) {
-                  if (span.innerText.includes(excelDataA[item.excelColumn]) || excelDataA[item.excelColumn].includes(span.innerText)) {
-                    console.log(span);
+                            span.forEach(span => {
+                                console.log(span.innerText);
+                                if (span.innerText) {
+                                    if (span.innerText.includes(excelDataA[item.excelColumn]) || excelDataA[item.excelColumn].includes(span.innerText)) {
+                                        console.log(span);
                                         span.click()
-                  }
-                }
+                                    }     
+                                }
                             })
-            }
-          }
+                        }
+                    }
 
                     if (item.type === 'date') {
                         const input = findLabelForInput(label)
-            console.log(input, excelDataA[item.excelColumn]);
-
-            if (input) {
+                        console.log(input, excelDataA[item.excelColumn]);
+                        
+                        if (input) {
                             await simulateCompleteUserAction(input, input, formatDate(excelDataA[item.excelColumn]), formatDate(excelDataA[item.excelColumn]))
-            }
-          }
+                        }
+                    }
 
-                    if (item.type === 'input' || item.type === 'text' || item.type === 'number' || item.type === 'tel' || item.type === 'email') {
-                        const input = findLabelForInput(label)
-            if (input) {
-              if (input.value) continue;
-              console.log(excelDataA[item.excelColumn]);
+                    if (item.type === 'input' || item.type === 'text' || item.type === 'number' || item.type === 'tel' || item.type === 'email' || item.type === 'textarea') {
+                        let input
+                        if (item.type === 'textarea') {
+                             input = findLabelForTextarea(label)
+                        } else input = findLabelForInput(label)
+                        if (input) {
+                            if (input.value) continue
+                            console.log(excelDataA[item.excelColumn]);
 
                             await simulateUserInput(input, excelDataA[item.excelColumn])
-            }
-          }
+                        }
+                    }
                     if (item.type === 'select') {
                         const input = findLabelForInput(label)
-            if (input) {
+                        if (input) {
                             await simulateUserInput(input, excelDataA[item.excelColumn])
+                        }
+                    }
+                }
             }
-          }
-        }
-      }
         }
-    const simulateUserInput = async (element, value) => {
-      // 设置值
-      // if (element.tagName.toLowerCase() === 'textarea') {
-      //   element.focus()
-      //   element.value = value
-      //   element.blur()
-      //   return
-      // }
-      element.value = value;
-
-      // 创建并触发 input 事件
+        const simulateUserInput = async (element, value) => {
+            // 设置值
+            if (element.tagName.toLowerCase() === 'textarea') {
+              element.focus()
+              element.value = value
+              element.blur()
+              return
+            }
+            element.value = value;
+
+            // 创建并触发 input 事件
             const inputEvent = new Event('input', { bubbles: true });
-      element.dispatchEvent(inputEvent);
+            element.dispatchEvent(inputEvent);
 
-      // 创建并触发 change 事件
+            // 创建并触发 change 事件
             const changeEvent = new Event('change', { bubbles: true });
-      element.dispatchEvent(changeEvent);
+            element.dispatchEvent(changeEvent);
 
-      // 可选:如果表单使用了 React/Vue 等框架,可能还需要触发以下事件
+            // 可选:如果表单使用了 React/Vue 等框架,可能还需要触发以下事件
             element.dispatchEvent(new Event('blur'));
             element.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
         }
-    const findLabelForInput = (label) => {
+        const findLabelForInput = (label) => {
             let p = label.parentElement
-      for (let i = 0; i < 10; i++) {
+            for (let i = 0; i < 5; i++) {
                 const input = p.getElementsByTagName('input')
-        if (input.length > 0) {
+                if (input.length > 0) {
                     return input[0]
-        }
+                }
                 p = p.parentElement
-      }
+            }
             return null
         }
-    const findLabelForTag = (label, tag) => {
+        const findLabelForTag = (label, tag) => {
             let p = label.parentElement
-      for (let i = 0; i < 10; i++) {
+            for (let i = 0; i < 10; i++) {
                 const input = p.getElementsByTagName('input')
-        if (input.length > 0) {
+                if (input.length > 0) {
                     return input[0]
-        }
+                }
                 p = p.parentElement
-      }
+            }
             return null
         }
-    const findLabelForSpan = (label) => {
+        const findLabelForSpan = (label) => {
             let p = label.parentElement
-      for (let i = 0; i < 10; i++) {
+            for (let i = 0; i < 10; i++) {
                 const span = p.getElementsByTagName('span')
-        if (span.length > 0) {
+                if (span.length > 0) {
                     return [...span]
+                }
+                p = p.parentElement
+            }
+            return null
         }
+        const findLabelForTextarea = (label) => {
+            let p = label.parentElement
+            for (let i = 0; i < 10; i++) {
+                const span = p.getElementsByTagName('textarea')
+                if (span.length > 0) {
+                    return [...span]
+                }
                 p = p.parentElement
-      }
+            }
             return null
         }
-    const simulateCompleteUserAction = async (clickElement, inputElement, inputText, tdTitle) => {
-      // 1. 模拟鼠标弹起事件
-      const simulateMouseUp = (element) => {
+        const simulateCompleteUserAction = async (clickElement, inputElement, inputText, tdTitle) => {
+            // 1. 模拟鼠标弹起事件
+            const simulateMouseUp = (element) => {
                 const mouseUpEvent = new MouseEvent('mouseup', {
-          bubbles: true,
-          cancelable: true,
-          view: window,
-          button: 0,
-          buttons: 0,
-          clientX: 0,
-          clientY: 0,
-          detail: 1
-        });
-        element.dispatchEvent(mouseUpEvent);
-      };
-      // 123456qq?
-      // 2. 模拟键盘输入
-      const simulateTyping = async (element, text, delay = 50) => {
-        element.focus();
-        for (let char of text) {
-          await new Promise(resolve => setTimeout(resolve, delay));
-
-          // 按键按下
+                    bubbles: true,
+                    cancelable: true,
+                    view: window,
+                    button: 0,
+                    buttons: 0,
+                    clientX: 0,
+                    clientY: 0,
+                    detail: 1
+                });
+                element.dispatchEvent(mouseUpEvent);
+            };
+            // 123456qq?
+            // 2. 模拟键盘输入
+            const simulateTyping = async (element, text, delay = 50) => {
+                element.focus();
+                for (let char of text) {
+                    await new Promise(resolve => setTimeout(resolve, delay));
+
+                    // 按键按下
                     const keydownEvent = new KeyboardEvent('keydown', {
-            key: char,
-            code: `Key${char.toUpperCase()}`,
-            bubbles: true,
-            cancelable: true
-          });
-          element.dispatchEvent(keydownEvent);
+                        key: char,
+                        code: `Key${char.toUpperCase()}`,
+                        bubbles: true,
+                        cancelable: true
+                    });
+                    element.dispatchEvent(keydownEvent);
 
-          // 更新输入值
-          element.value += char;
+                    // 更新输入值
+                    element.value += char;
 
-          // 触发输入事件
+                    // 触发输入事件
                     const inputEvent = new InputEvent('input', {
-            bubbles: true,
-            cancelable: true,
-            data: char,
+                        bubbles: true,
+                        cancelable: true,
+                        data: char,
                         inputType: 'insertText'
-          });
-          element.dispatchEvent(inputEvent);
+                    });
+                    element.dispatchEvent(inputEvent);
 
-          // 按键弹起
+                    // 按键弹起
                     const keyupEvent = new KeyboardEvent('keyup', {
-            key: char,
-            code: `Key${char.toUpperCase()}`,
-            bubbles: true,
-            cancelable: true
-          });
-          element.dispatchEvent(keyupEvent);
-        }
+                        key: char,
+                        code: `Key${char.toUpperCase()}`,
+                        bubbles: true,
+                        cancelable: true
+                    });
+                    element.dispatchEvent(keyupEvent);
+                }
 
-        // 触发change事件
+                // 触发change事件
                 element.dispatchEvent(new Event('change', { bubbles: true }));
-      };
-
-      // 3. 查找td元素
-      const findTdByTitle = (title, timeout = 5000) => {
-        return new Promise((resolve, reject) => {
-          const startTime = Date.now();
-
-          const find = () => {
-            const td = document.querySelector(`td[title="${title}"]`);
-            if (td) {
-              resolve(td);
-              return;
-            }
+            };
+
+            // 3. 查找td元素
+            const findTdByTitle = (title, timeout = 5000) => {
+                return new Promise((resolve, reject) => {
+                    const startTime = Date.now();
+
+                    const find = () => {
+                        const td = document.querySelector(`td[title="${title}"]`);
+                        if (td) {
+                            resolve(td);
+                            return;
+                        }
 
-            if (Date.now() - startTime > timeout) {
-              reject(new Error(`未找到title为"${title}"的td元素`));
-              return;
-            }
+                        if (Date.now() - startTime > timeout) {
+                            reject(new Error(`未找到title为"${title}"的td元素`));
+                            return;
+                        }
 
-            requestAnimationFrame(find);
-          };
+                        requestAnimationFrame(find);
+                    };
 
-          find();
-        });
-      };
+                    find();
+                });
+            };
 
-      // 4. 模拟点击事件
-      const simulateClick = (element) => {
+            // 4. 模拟点击事件
+            const simulateClick = (element) => {
                 const clickEvent = new MouseEvent('click', {
-          bubbles: true,
-          cancelable: true,
-          view: window,
-          detail: 1
-        });
-        element.dispatchEvent(clickEvent);
-      };
+                    bubbles: true,
+                    cancelable: true,
+                    view: window,
+                    detail: 1
+                });
+                element.dispatchEvent(clickEvent);
+            };
 
-      try {
-        // 执行操作序列
+            try {
+                // 执行操作序列
 
-        // 1. 触发鼠标弹起
-        simulateMouseUp(clickElement);
-        await new Promise(resolve => setTimeout(resolve, 100));
+                // 1. 触发鼠标弹起
+                simulateMouseUp(clickElement);
+                await new Promise(resolve => setTimeout(resolve, 100));
 
-        // 2. 模拟键盘输入
-        await simulateTyping(inputElement, inputText);
-        await new Promise(resolve => setTimeout(resolve, 200));
+                // 2. 模拟键盘输入
+                await simulateTyping(inputElement, inputText);
+                await new Promise(resolve => setTimeout(resolve, 200));
 
-        // 3. 查找并点击td元素
-        // const tdElement = await findTdByTitle(tdTitle);
+                // 3. 查找并点击td元素
+                // const tdElement = await findTdByTitle(tdTitle);
 
                 const enterKeyEvent = new KeyboardEvent('keydown', {
                     key: 'Enter',       // 事件键名
                     code: 'Enter',      // 物理按键编码
-          keyCode: 13,        // 传统键码(Enter键为13)
-          which: 13,          // 同keyCode
-          bubbles: true,      // 允许事件冒泡
-          cancelable: true    // 允许事件被取消
-        });
-        setTimeout(() => {
-          inputElement.dispatchEvent(enterKeyEvent);
+                    keyCode: 13,        // 传统键码(Enter键为13)
+                    which: 13,          // 同keyCode
+                    bubbles: true,      // 允许事件冒泡
+                    cancelable: true    // 允许事件被取消
+                });
+                setTimeout(() => {
+                    inputElement.dispatchEvent(enterKeyEvent);
                 }, 0)
-        await new Promise(resolve => setTimeout(resolve, 500));
-        return true;
-      } catch (error) {
-        throw error;
-      }
-    };
-
-    function formatDate(date) {
-      // 直接创建北京时间的日期对象
-      const d = new Date(date);
-
-      // 获取年、月、日
-      const year = d.getFullYear();
+                await new Promise(resolve => setTimeout(resolve, 500));
+                return true;
+            } catch (error) {
+                throw error;
+            }
+        };
+        function formatDate(date) {
+            // 直接创建北京时间的日期对象
+            const d = new Date(date);
+
+            // 获取年、月、日
+            const year = d.getFullYear();
             const month = String(d.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要加1
             const day = String(d.getDate()).padStart(2, '0');
 
-      return `${year}-${month}-${day}`;
-    }
+            return `${year}-${month}-${day}`;
+        }
 
     },
 });

+ 144 - 516
src/entrypoints/sidepanel/Chat.vue

@@ -16,7 +16,13 @@
                 <span class="dot"></span>
               </span>
             </div>
-            <div v-else class="content">{{ message.content }}</div>
+            <div v-else class="content">{{ message.content }}
+              <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
+                <span class="dot"></span>
+                <span class="dot"></span>
+                <span class="dot"></span>
+              </span>
+            </div>
             <div class="timestamp ">{{ message.timestamp }}
               <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
             </div>
@@ -52,7 +58,7 @@
 
           <div class="card-btn">
             <el-tooltip content="总结当前页面" placement="top">
-              <el-button round @click="handleCardButtonClick" :disabled="!taklToHtml">总结</el-button>
+              <el-button round @click="handleSummary" :disabled="!taklToHtml">总结</el-button>
             </el-tooltip>
 
             <el-tooltip content="选择后,在输入框描述填表流程" placement="top">
@@ -81,18 +87,25 @@
 </template>
 
 <script setup>
-import {ref, onMounted, nextTick, inject, useTemplateRef} from 'vue'
-import {ElScrollbar, ElAvatar, ElInput, ElButton} from 'element-plus'
+import { ref, onMounted, nextTick, inject, useTemplateRef } from "vue";
+import { ElScrollbar, ElAvatar, ElInput, ElButton } from "element-plus";
 import moment from "moment";
-import { buildExcelUnderstandingPrompt ,getFileContent} from '@/utils/ai-service.js'
-import * as XLSX from "xlsx";
-import {ElMessage} from 'element-plus';
-import {useMsg} from '@/entrypoints/sidepanel/hook/useMsg.ts';
+import {
+  buildExcelUnderstandingPrompt,
+  getFileSummaryPrompt,
+  getSummaryPrompt,
+  getFileContent,
+  buildObjPrompt
+} from "@/utils/ai-service.js";
+import { storeToRefs } from "pinia";
+import { ElMessage } from "element-plus";
+import { useMsg } from "@/entrypoints/sidepanel/hook/useMsg.ts";
 import Tools from "@/entrypoints/sidepanel/component/tools.vue";
-import historyComponent from '@/entrypoints/sidepanel/component/historyComponent.vue';
-import {useSummary} from '@/entrypoints/sidepanel/hook/useSummary.ts'
-import {mockData, startMsg, mockData2} from "@/entrypoints/sidepanel/mock"
-import {useAutoResizeTextarea} from '@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts';
+import historyComponent from "@/entrypoints/sidepanel/component/historyComponent.vue";
+import { mockData, startMsg, mockData2 } from "@/entrypoints/sidepanel/mock";
+import { useAutoResizeTextarea } from "@/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts";
+import { getPageInfo, getXlsxValue, handleInput } from "./utils/index.js";
+import { useMsgStore } from "@/store/modules/msg.ts";
 
 // 滚动条引用
 const scrollbar = ref(null);
@@ -101,28 +114,47 @@ const isShowPage = ref(false);
 const tareRef = useTemplateRef("textareaRef");
 const drawerRef = useTemplateRef("historyComponentRef");
 // 获取父组件提供的 Hook 实例
-const {registerStore, useStore} = inject('indexedDBHook');
+const { registerStore, useStore } = inject("indexedDBHook");
+const { pageInfoList, messages, msgUuid } = storeToRefs(useMsgStore());
 const {
-  msgUuid,
-  messages,
-  inputMessage,
-  indexTemp,
   taklToHtml,
-  pageInfo,
   sendLoading,
   type,
-  addMessage,
-  sendRequese,
-  getPageInfo,
   streamRes,
-  handleInput,
-  getFileValue
-} = useMsg(scrollbar, xlsxData, fetchDataAndProcess);
-const { handleCardButtonClick } = useSummary(addMessage, sendRequese);
+  getFormKeyAndValue,
+  handleSend
+} = useMsg(scrollbar);
+const inputMessage = ref("");
+const pageInfo = ref("");
+
+const addMessage = (msg, raw) => {
+  if (!msg) return;
+  const newMessage = reactive({
+    id: messages.value.length + 1,
+    username: "我",
+    rawContent: raw ?? msg,
+    content: msg,
+    timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+    isSelf: true,
+    avatar: "https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png",
+    addToHistory: !taklToHtml.value
+  });
+  messages.value.push(newMessage);
+  useStore(msgUuid.value).add(newMessage);
+  nextTick(() => {
+    scrollbar.value?.setScrollTop(99999);
+  });
+  return newMessage;
+};
+const handleSummary = async () => {
+  const res = await getPageInfo();
+  addMessage("总结页面", getSummaryPrompt(res.content));
+  handleSend();
+};
 
 function handelIntelligentFillingClick() {
-  inputMessage.value = '/智能填表 '
-  type.value = '2'
+  inputMessage.value = "/智能填表 ";
+  type.value = "2";
 }
 
 function handleCurrentData(e) {
@@ -134,17 +166,18 @@ function handleCurrentData(e) {
   // 添加indexDB Store配置
   msgUuid.value = e;
   useStore(e).getAll().then((res) => {
-    messages.value = res
+    messages.value = res;
     nextTick(() => {
-      scrollbar.value?.setScrollTop(99999)
-    })
-  })
+      scrollbar.value?.setScrollTop(99999);
+    });
+  });
 }
 
 async function readClick() {
   isShowPage.value = true;
   taklToHtml.value = true;
   const tempPageInfo = await getPageInfo();
+  pageInfo.value = tempPageInfo;
   pageInfoList.value.push(tempPageInfo);
 }
 
@@ -154,28 +187,31 @@ function deletePageInfo(i) {
     isShowPage.value = false;
     taklToHtml.value = false;
   }
+
 }
 
 function addNewDialogue() {
   messages.value = [];
-  msgUuid.value = 'D' + Date.now().toString();
+  msgUuid.value = "D" + Date.now().toString();
   registerStore({
     name: msgUuid.value,
-    keyPath: 'id',
-  })
+    keyPath: "id"
+  });
   messages.value.push(startMsg);
   useStore(msgUuid.value).add(startMsg);
 }
 
 async function handleAsk() {
   if (sendLoading.value) return;
-  addMessage(inputMessage.value.trim(), true);
-  inputMessage.value = '';
+  addMessage(inputMessage.value.trim());
+  handleSend(inputMessage.value.trim());
+  inputMessage.value = "";
+  // streamRes(taklToHtml)
 }
 
 function handleCapture() {
   ElMessage({
-    message: '开发中...',
+    message: "开发中...",
     grouping: true,
     showClose: true
   });
@@ -185,518 +221,110 @@ function hisRecords() {
   drawerRef.value.drawer = true;
 }
 
-// let formMap = []
-let formInfo = []
-const flag = ref(false)  //true调用算法接口
-
-const handleUpload = (file) => {
-  if (type.value === '2') {
+const handleUpload = async (file) => {
+  if (type.value === "2") {
     chrome.runtime.sendMessage({
-    type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM',
+      type: "FROM_SIDE_PANEL_TO_GET_PAGE_FORM"
     }, async (response) => {
       if (chrome.runtime.lastError) {
         console.error("消息发送错误:", chrome.runtime.lastError);
       } else {
-      addMessage(`已上传文件:${file.name}`, false)
-      const fileExtension = file.name.split('.').pop().toLowerCase();
-      if (response.status === 'error') return ElMessage({message:response.message,type: 'error', duration: 4 * 1000, grouping: true})
-      formInfo = response.data
-      if (fileExtension === 'xlsx') {
-          const reader = new FileReader();
-          reader.readAsArrayBuffer(file);
-          reader.onload = async (e) => {
-            const data = new Uint8Array(e.target.result);
-            const workbook = XLSX.read(data, {
-              type: "array",
-              cellDates: false,
-              cellNF: true,
-              cellText: true,
-            dateNF: 'yyyy-mm-dd'
-            });
-
-            // 修复日期处理
-            const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
-
-            // 转换为JSON数据
-            const readData = XLSX.utils.sheet_to_json(firstSheet, {
-              header: 1,
-              raw: false,
-              defval: "",
-            dateNF: 'yyyy-mm-dd'
-            });
-            console.log(readData, 58);
-
-            readData[0].forEach((header, i) => {
-              // if (!xlsxData.value[header]) xlsxData.value[header] = []
-            xlsxData.value[header] = readData[1][i]
-          })
-          if (type.value === '2') {
-            await streamRes(buildExcelUnderstandingPrompt(readData, file?.name, response), false)
-            }
-          };
+        const fileExtension = file.name.split(".").pop().toLowerCase();
+        if (response.status === "error") return ElMessage({
+          message: response.message,
+          type: "error",
+          duration: 4 * 1000,
+          grouping: true
+        });
+        if (fileExtension === "xlsx") {
+          const readData = await getXlsxValue(file);
+          readData[0].forEach((header, i) => {
+            // if (!xlsxData.value[header]) xlsxData.value[header] = []
+            xlsxData.value[header] = readData[1][i];
+          });
+          addMessage(`已上传文件:${file.name}`, buildExcelUnderstandingPrompt(readData[0], file?.name, response.data));
+          const a = await streamRes();
+          handleInput(xlsxData.value, JSON.parse(a.split("json")[1].split("```")[0]));
         } else {
-        await getFileValue(file, response.data)
-
-          // const keys = Object.keys(res)
-          // const values = Object.values(res)
-          // readData[0].forEach((header, i) => {
-          //   if (!xlsxData.value[header]) xlsxData.value[header] = []
-          //   xlsxData.value[header].push(readData[1][i])
-          // })
-          // console.log(res);
+          const { data, msg } = await getFileValue(file);
+          const res2 = await getFormKeyAndValue(data, response.data);
+          xlsxData.value = res2.data;
+          msg.rawContent = buildObjPrompt(res2.data, response.data);
+          console.log();
+
+          const a = await streamRes();
+          handleInput(xlsxData.value, JSON.parse(a.split("json")[1].split("```")[0]));
+          console.log(xlsxData.value);
+          console.log(type.value);
         }
-
       }
-    return true
+      return true;
     });
-
   }
-  if (type.value === '') {
-
-  }
-}
-let str = ''
-async function fetchDataAndProcess(input, obj) {
-  if (input.startsWith('/智能填表')) {
-    input = input.split('/智能填表')[1]
-  }
-  str = input
-  console.log(str);
-  const pageInfo = await getPageInfo();
-  await new Promise(res => setTimeout(() => {
-    res()
-  }, 2000))
-  // const res = await hepl({
-  //   input_data: input,
-  //   body: pageInfo.content.mainContent
-  // })
-  console.log(pageInfo.title);
-
-  const res = await new Promise((resolve, reject) => {
-    setTimeout(() => {
-      resolve({ data:pageInfo.title === '智能招采' ?  mockData[indexTemp.value] :mockData2[indexTemp.value] })
-    }, 1000)
-  })
-  if (!res.data.tag || res.data.tag === 'undefined') {
-    ElMessage({ message: '未找到标签,请重试', type: 'error', duration: 4 * 1000, grouping: true })
-    obj.content = '未找到标签,请重试'
-    type.value = ''
-    return
+  if (type.value === "") {
+    const fileVaue = await getFileValue(file);
+    // streamRes()
   }
-  await handleClick(res.data, obj);
-}
-
-
-async function handleClick(res, msgObj) {
-  await new Promise(resolve => setTimeout(resolve, 2000))
-  msgObj.content = `点击${res.tag}元素`
-  chrome.runtime.sendMessage({
-    type: 'FROM_SIDE_PANEL_TO_ACTION',
-    data: res
-  }, async ({ data, status }) => {
-    if (chrome.runtime.lastError) {
-      console.error("消息发送错误:", chrome.runtime.lastError);
-      rej(chrome.runtime.lastError)
-    } else {
-      if (status === 'error') {
-        msgObj.content = data
-        type.value = ''
-        return
-      }
-      if (res.next === '是') {
-        const arr = str.split(',')
-        arr.shift()
-        str = arr.join(',')
-        indexTemp.value++
-        fetchDataAndProcess(str, msgObj)
-      } else {
-        if (type.value === '2') {
-          await new Promise((resolve, reject) => {
-            setTimeout(() => {
-      resolve()
-    }, 2000)
-  })
-          msgObj.content = `请上传数据`
-          ElMessage({ message: '请上传数据', type: 'success', duration: 4 * 1000, grouping: true })
-        } else {
-          msgObj.content = `执行完毕`
-        }
-      }
-    }
-    return true
-  });
-
-}
-
-const isHoveringTitle = ref(false)
-
+};
+let str = "";
+
+async function getFileValue(file) {
+  const msg = addMessage(`文件上传中`);
+  sendLoading.value = true;
+  let formData = new FormData();
+  formData.append("file", file);
+  const res = await getFileContent(formData);
+  sendLoading.value = false;
+  msg.content = `已上传文件:${file.name}`;
+  msg.rawContent = res.data;
+  return {
+    data: res.data,
+    msg
+  };
+}
+}
+const isHoveringTitle = ref(false);
 // 组件挂载时滚动到底部
 onMounted(() => {
   useAutoResizeTextarea(tareRef, inputMessage);
   chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
-    if (message.type === 'TO_SIDE_PANEL_PAGE_INFO') {
-      pageInfo.value = message.data
-      console.log(pageInfo.value);
-
+    if (message.type === "TO_SIDE_PANEL_PAGE_INFO") {
+      pageInfo.value = message.data;
       // 转发到 content.js
       chrome.tabs.sendMessage(sender.tab.id, {
-        type: 'TO_CONTENT_SCRIPT',
+        type: "TO_CONTENT_SCRIPT",
         data: message.data
       });
     }
-
-    if (message.type === 'TO_SIDE_PANEL_PAGE_CHANGE') {
-      pageInfo.value = message.data
+    if (message.type === "TO_SIDE_PANEL_PAGE_CHANGE") {
+      pageInfo.value = message.data;
     }
   });
 
   // 添加标题悬停事件监听
   nextTick(() => {
-    const titleWrapper = document.querySelector('.title-wrapper')
+    const titleWrapper = document.querySelector(".title-wrapper");
     if (titleWrapper) {
-      titleWrapper.addEventListener('mouseenter', () => {
-        isHoveringTitle.value = true
-      })
-      titleWrapper.addEventListener('mouseleave', () => {
-        isHoveringTitle.value = false
-      })
+      titleWrapper.addEventListener("mouseenter", () => {
+        isHoveringTitle.value = true;
+      });
+      titleWrapper.addEventListener("mouseleave", () => {
+        isHoveringTitle.value = false;
+      });
     }
-    scrollbar.value?.setScrollTop(99999)
-  })
-
+    scrollbar.value?.setScrollTop(99999);
+  });
   // 添加indexDB Store配置
-  msgUuid.value = 'D' + Date.now().toString();
+  msgUuid.value = "D" + Date.now().toString();
   registerStore({
     name: msgUuid.value,
-    keyPath: 'id',
-  })
+    keyPath: "id"
+  });
   messages.value.push(startMsg);
   useStore(msgUuid.value).add(startMsg);
-})
+});
 </script>
 
 <style lang="scss" scoped>
-.els {
-  white-space: nowrap; /* 强制文本不换行 */
-  overflow: hidden; /* 隐藏溢出内容 */
-  text-overflow: ellipsis; /* 显示省略号 */
-}
-
-.chat-container {
-  border-radius: 14px;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  border: 1px solid #dcdfe6;
-  background-color: #F0F0F0;
-  //background-color: #ffffff;
-}
-
-.message-list {
-  flex: 1;
-  padding: 16px;
-  overflow: auto;
-}
-
-.messages {
-  min-height: 100%;
-  display: flex;
-  flex-direction: column;
-}
-
-.message-item {
-  display: flex;
-  margin-bottom: 20px;
-  gap: 12px;
-  color: #333;
-  align-items: flex-start;
-  animation: fadeIn 0.3s ease-in-out;
-}
-
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-    transform: translateY(10px);
-  }
-
-  to {
-    opacity: 1;
-    transform: translateY(0);
-  }
-}
-
-.message-item.self {
-  flex-direction: row-reverse;
-}
-
-.message-content {
-  max-width: 80%;
-}
-
-.username {
-  font-size: 14px;
-  color: #606266;
-  margin-bottom: 4px;
-}
-
-.content {
-  padding: 12px;
-  min-height: 40px;
-  background-color: #F0F4F8;
-  border-radius: 12px;
-  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
-  word-break: break-all;
-  line-height: 1.5;
-  font-size: 14px;
-}
-
-.content :deep(pre) {
-  margin: 10px 0;
-  border-radius: 8px;
-}
-
-.content :deep(code) {
-  font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
-}
-
-.content :deep(p) {
-  margin: 8px 0;
-}
-
-.content :deep(ul),
-.content :deep(ol) {
-  padding-left: 20px;
-  margin: 8px 0;
-}
-
-.content :deep(blockquote) {
-  border-left: 4px solid #ddd;
-  padding-left: 10px;
-  color: #666;
-  margin: 8px 0;
-}
-
-.self .content {
-  background: #409eff;
-  color: white;
-  border-bottom-right-radius: 4px;
-}
-
-.other .content {
-  border-bottom-left-radius: 4px;
-}
-
-.timestamp {
-  display: flex;
-  justify-content: space-between;
-  font-size: 12px;
-  color: #909399;
-  margin-top: 6px;
-}
-
-.timestamp span {
-  transition: color 0.2s;
-}
-
-.timestamp span:hover {
-  color: #409eff;
-}
-
-.info-card {
-  margin: 10px;
-}
-
-.card-icon {
-  color: #409eff;
-}
-
-
-@keyframes scrollTitle {
-  0% {
-    transform: translateX(0);
-  }
-
-  100% {
-    transform: translateX(-100%);
-  }
-}
-
-.upload :deep(.el-icon) {
-  transition: color 0.3s;
-}
-
-.can-hover :deep(.el-icon:hover) {
-  color: #409eff !important;
-}
-
-.el-check-tag {
-  margin-right: 8px;
-  transition: all 0.3s;
-}
-
-.el-check-tag:hover {
-  transform: scale(1.05);
-}
-
-/* 加载中的消息样式 */
-.loading-content {
-  min-height: 40px;
-  display: flex;
-  align-items: center;
-  justify-content: flex-start;
-}
-
-.loading-indicator {
-  display: inline-block;
-  align-items: center;
-  gap: 4px;
-  margin-left: 2px;
-
-  .dot {
-    width: 4px;
-    height: 4px;
-    margin: 0 2px;
-    background-color: gray;
-    border-radius: 50%;
-    display: inline-block;
-    animation: pulse 1.5s infinite ease-in-out;
-  }
-
-  .dot:nth-child(2) {
-    animation-delay: 0.3s;
-  }
-
-  .dot:nth-child(3) {
-    animation-delay: 0.6s;
-  }
-
-  @keyframes pulse {
-
-    0%,
-    100% {
-      transform: scale(0.8);
-      opacity: 0.6;
-    }
-
-    50% {
-      transform: scale(1.2);
-      opacity: 1;
-    }
-  }
-}
-
-
-.input-area {
-  padding: 8px 10px;
-  color: black;
-  background-color: #fff;
-  border: 1px solid rgba(102, 102, 102, 0.3);
-  border-radius: 16px;
-  margin: 0 12px 12px;
-
-  .card_list {
-    display: flex;
-    flex-wrap: nowrap;
-    overflow-x: auto;
-    padding: 4px 0;
-
-    .card_width {
-      width: 124px !important;
-    }
-  }
-
-  .card-content {
-    flex-shrink: 0;
-    width: fit-content;
-    max-width: 260px;
-    height: 50px;
-    display: flex;
-    align-items: center;
-    gap: 6px;
-    padding: 8px 6px;
-    margin: 0 8px 6px 0;
-    background: #fff;
-    border-radius: 10px;
-    border: 1px solid rgba(0, 0, 0, 0.2);
-    font-size: 14px;
-    position: relative;
-
-    .title-wrapper {
-      display: flex;
-      //width: fit-content;
-      width: calc(100% - 30px);
-      flex-direction: column;
-    }
-
-
-    .title-scroller {
-      display: inline-block;
-      white-space: nowrap;
-      color: #000000;
-      font-size: 14px;
-      font-weight: 900;
-    }
-
-    .url-scroller {
-      font-size: 12px;
-      color: #909399;
-    }
-
-    .title-scroller.scroll {
-      animation: scrollTitle 10s linear infinite;
-    }
-
-    .closeIcon {
-      display: none;
-      position: absolute;
-      right: -5px;
-      top: -5px;
-      background-color: #ffffff;
-      border-radius: 50%;
-      z-index: 1;
-    }
-
-  }
-
-  .card-content:hover {
-    .closeIcon {
-      display: block;
-    }
-  }
-
-  .card-btn {
-    padding: 0 0 4px;
-
-    .op {
-      margin-right: 3px;
-    }
-  }
-
-  .chat_area_op {
-    margin-top: 3px;
-    display: flex;
-    justify-content: end;
-  }
-}
-
-.input-area:hover {
-  border-color: rgba(102, 102, 102, 0.4);
-}
-
-.input-area :deep(.el-textarea__inner) {
-  border-radius: 8px;
-  transition: border-color 0.3s;
-  resize: none;
-  box-shadow: none;
-}
-
-.input-area :deep(.el-textarea__inner) {
-  padding: 0 4px;
-  margin-top: 3px;
-}
-
-.input-area :deep(.el-textarea__inner:focus) {
-  border-color: #409eff;
-  box-shadow: none;
-}
+@import '@/entrypoints/sidepanel/css/chat.scss';
 </style>

+ 69 - 0
src/entrypoints/sidepanel/component/inputArea.vue

@@ -0,0 +1,69 @@
+<script setup>
+import { ref } from "vue";
+import { options } from "@/entrypoints/sidepanel/mock";
+import { Reading, Upload, Paperclip, Scissor, AlarmClock, CirclePlus } from "@element-plus/icons-vue";
+
+const isShowPage = ref(false)
+const taklToHtml = ref(false)
+
+const inputMessage = ref('')
+
+const emit = defineEmits(['readClick', 'uploadFile', 'handleCapture', 'addNewDialogue', 'hisRecords'])
+</script>
+
+<template>
+    <div class="input-area">
+        <div v-show="isShowPage" style="border-bottom: 1px solid #F0F0F0;">
+          <div class="card-content">
+            <img :src="pageInfo?.favIconUrl" style="width: 24px;" />
+            <div class="title-wrapper">
+              <div class="title-scroller" :class="{ 'scroll': isHoveringTitle && titleScroll }">
+                {{ pageInfo?.title }}
+              </div>
+            </div>
+            <el-icon size="16px" @click="isShowPage = false; taklToHtml = false">
+              <CircleClose />
+            </el-icon>
+          </div>
+          <div class="card-btn">
+            <el-tooltip content="总结当前页面" placement="top">
+              <el-button round @click="handleCardButtonClick" :disabled="!taklToHtml">总结</el-button>
+            </el-tooltip>
+
+            <el-tooltip content="选择后,在输入框描述填表流程" placement="top">
+              <el-button round @click="handelIntelligentFillingClick" :disabled="!taklToHtml">智能填表</el-button>
+            </el-tooltip>
+          </div>
+        </div>
+
+        <el-input ref="textareaRef" v-model="inputMessage" type="textarea" :rows="3" placeholder="输入消息..."
+                  @keyup.enter="handleAsk"/>
+        <div class="chat_area_op">
+          <el-button type="primary" link @click="handleAsk" :disabled="!inputMessage.trim() || sendLoading">
+            <el-icon size="18" :color="inputMessage.trim() ? 'black' : 'gray'">
+              <Promotion/>
+            </el-icon>
+          </el-button>
+        </div>
+      </div>
+
+</template>
+
+<style lang="scss" scoped>
+.input-area :deep(.el-textarea__inner) {
+  border-radius: 8px;
+  transition: border-color 0.3s;
+  resize: none;
+  box-shadow: none;
+}
+
+.input-area :deep(.el-textarea__inner) {
+  padding: 0 4px;
+  margin-top: 3px;
+}
+
+.input-area :deep(.el-textarea__inner:focus) {
+  border-color: #409eff;
+  box-shadow: none;
+}
+</style>

+ 323 - 0
src/entrypoints/sidepanel/css/chat.scss

@@ -0,0 +1,323 @@
+.els {
+    white-space: nowrap; /* 强制文本不换行 */
+    overflow: hidden; /* 隐藏溢出内容 */
+    text-overflow: ellipsis; /* 显示省略号 */
+}
+
+.chat-container {
+    border-radius: 14px;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    border: 1px solid #dcdfe6;
+    background-color: #F0F0F0;
+    //background-color: #ffffff;
+}
+
+.message-list {
+    flex: 1;
+    padding: 16px;
+    overflow: auto;
+}
+
+.messages {
+    min-height: 100%;
+    display: flex;
+    flex-direction: column;
+}
+
+.message-item {
+    display: flex;
+    margin-bottom: 20px;
+    gap: 12px;
+    color: #333;
+    align-items: flex-start;
+    animation: fadeIn 0.3s ease-in-out;
+}
+
+@keyframes fadeIn {
+    from {
+        opacity: 0;
+        transform: translateY(10px);
+    }
+
+    to {
+        opacity: 1;
+        transform: translateY(0);
+    }
+}
+
+.message-item.self {
+    flex-direction: row-reverse;
+}
+
+.message-content {
+    max-width: 80%;
+}
+
+.username {
+    font-size: 14px;
+    color: #606266;
+    margin-bottom: 4px;
+}
+
+.content {
+    padding: 12px;
+    min-height: 40px;
+    background-color: #F0F4F8;
+    border-radius: 12px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+    word-break: break-all;
+    line-height: 1.5;
+    font-size: 14px;
+}
+
+.content :deep(pre) {
+    margin: 10px 0;
+    border-radius: 8px;
+}
+
+.content :deep(code) {
+    font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
+}
+
+.content :deep(p) {
+    margin: 8px 0;
+}
+
+.content :deep(ul),
+.content :deep(ol) {
+    padding-left: 20px;
+    margin: 8px 0;
+}
+
+.content :deep(blockquote) {
+    border-left: 4px solid #ddd;
+    padding-left: 10px;
+    color: #666;
+    margin: 8px 0;
+}
+
+.self .content {
+    background: #409eff;
+    color: white;
+    border-bottom-right-radius: 4px;
+}
+
+.other .content {
+    border-bottom-left-radius: 4px;
+}
+
+.timestamp {
+    display: flex;
+    justify-content: space-between;
+    font-size: 12px;
+    color: #909399;
+    margin-top: 6px;
+}
+
+.timestamp span {
+    transition: color 0.2s;
+}
+
+.timestamp span:hover {
+    color: #409eff;
+}
+
+.info-card {
+    margin: 10px;
+}
+
+.card-icon {
+    color: #409eff;
+}
+
+
+@keyframes scrollTitle {
+    0% {
+        transform: translateX(0);
+    }
+
+    100% {
+        transform: translateX(-100%);
+    }
+}
+
+.upload :deep(.el-icon) {
+    transition: color 0.3s;
+}
+
+.can-hover :deep(.el-icon:hover) {
+    color: #409eff !important;
+}
+
+.el-check-tag {
+    margin-right: 8px;
+    transition: all 0.3s;
+}
+
+.el-check-tag:hover {
+    transform: scale(1.05);
+}
+
+/* 加载中的消息样式 */
+.loading-content {
+    min-height: 40px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+}
+
+.loading-indicator {
+    display: inline-block;
+    align-items: center;
+    gap: 4px;
+    margin-left: 2px;
+
+    .dot {
+        width: 4px;
+        height: 4px;
+        margin: 0 2px;
+        background-color: gray;
+        border-radius: 50%;
+        display: inline-block;
+        animation: pulse 1.5s infinite ease-in-out;
+    }
+
+    .dot:nth-child(2) {
+        animation-delay: 0.3s;
+    }
+
+    .dot:nth-child(3) {
+        animation-delay: 0.6s;
+    }
+
+    @keyframes pulse {
+
+        0%,
+        100% {
+            transform: scale(0.8);
+            opacity: 0.6;
+        }
+
+        50% {
+            transform: scale(1.2);
+            opacity: 1;
+        }
+    }
+}
+
+
+.input-area {
+    padding: 8px 10px;
+    color: black;
+    background-color: #fff;
+    border: 1px solid rgba(102, 102, 102, 0.3);
+    border-radius: 16px;
+    margin: 0 12px 12px;
+
+    .card_list {
+        display: flex;
+        flex-wrap: nowrap;
+        overflow-x: auto;
+        padding: 4px 0;
+
+        .card_width {
+            width: 124px !important;
+        }
+    }
+
+    .card-content {
+        flex-shrink: 0;
+        width: fit-content;
+        max-width: 260px;
+        height: 50px;
+        display: flex;
+        align-items: center;
+        gap: 6px;
+        padding: 8px 6px;
+        margin: 0 8px 6px 0;
+        background: #fff;
+        border-radius: 10px;
+        border: 1px solid rgba(0, 0, 0, 0.2);
+        font-size: 14px;
+        position: relative;
+
+        .title-wrapper {
+            display: flex;
+            //width: fit-content;
+            width: calc(100% - 30px);
+            flex-direction: column;
+        }
+
+
+        .title-scroller {
+            display: inline-block;
+            white-space: nowrap;
+            color: #000000;
+            font-size: 14px;
+            font-weight: 900;
+        }
+
+        .url-scroller {
+            font-size: 12px;
+            color: #909399;
+        }
+
+        .title-scroller.scroll {
+            animation: scrollTitle 10s linear infinite;
+        }
+
+        .closeIcon {
+            display: none;
+            position: absolute;
+            right: -5px;
+            top: -5px;
+            background-color: #ffffff;
+            border-radius: 50%;
+            z-index: 1;
+        }
+
+    }
+
+    .card-content:hover {
+        .closeIcon {
+            display: block;
+        }
+    }
+
+    .card-btn {
+        padding: 0 0 4px;
+
+        .op {
+            margin-right: 3px;
+        }
+    }
+
+    .chat_area_op {
+        margin-top: 3px;
+        display: flex;
+        justify-content: end;
+    }
+}
+
+.input-area:hover {
+    border-color: rgba(102, 102, 102, 0.4);
+}
+
+.input-area :deep(.el-textarea__inner) {
+    border-radius: 8px;
+    transition: border-color 0.3s;
+    resize: none;
+    box-shadow: none;
+}
+
+.input-area :deep(.el-textarea__inner) {
+    padding: 0 4px;
+    margin-top: 3px;
+}
+
+.input-area :deep(.el-textarea__inner:focus) {
+    border-color: #409eff;
+    box-shadow: none;
+}

+ 236 - 219
src/entrypoints/sidepanel/hook/useMsg.ts

@@ -1,241 +1,258 @@
 // 消息数组
-import {ref, reactive, nextTick, inject} from 'vue';
-import avator from '@/public/icon/32.png';
-import moment from 'moment'
-// import { getFileContent, getFormKey, buildObjPrompt } from '../../../utils/ai-service'
-import { ElMessage } from 'element-plus';
+import { ref, reactive, nextTick, inject } from "vue";
+import { storeToRefs } from "pinia";
+import avator from "@/public/icon/32.png";
+import moment from "moment";
+import { getFormKey, buildObjPrompt, getFileSummaryPrompt } from "@/utils/ai-service";
+import { ElMessage } from "element-plus";
+import { getPageInfo } from "../utils/index.js";
+import { mockData, mockData2 } from "../mock";
+import { useMsgStore } from "@/store/modules/msg";
 
 // import { sendMessage } from '@/utils/ai-service';
-export function useMsg(scrollbar?: any, xlsxData?: any, fetchDataAndProcess?: Function) {
-    const msgUuid = ref<string>();
-    const inputMessage = ref('');
-    const indexTemp = ref(0);
-    const taklToHtml = ref<any>(false);
-    const sendLoading = ref(false);
-    const pageInfo = ref<any>({});
-    const type = ref('');
-    const formMap = ref([]);
-    const messages = ref<any>([]);
-    // 获取父组件提供的 Hook 实例
-    const {useStore} = inject('indexedDBHook') as any;
+export function useMsg(scrollbar?: any) {
+  const { messages,msgUuid } = storeToRefs(useMsgStore());
+  const indexTemp = ref(0);
+  const taklToHtml = ref<any>(false);
+  const sendLoading = ref(false);
+  const pageInfo = ref<any>({});
+  const type = ref("");
+  const formMap = ref([]);
+  // 获取父组件提供的 Hook 实例
+  const { useStore } = inject("indexedDBHook") as any;
 
-
-    const getFileValue = async (file: any, form: any) => {
-        const obj = reactive({
-            id: moment(),
-            username: '用户1',
-            rawContent: '',
-            content: '解析文件中',
-            timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-            isSelf: false,
-            avatar: avator,
-            addToHistory: !taklToHtml.value
-        })
-
-        try {
-            sendLoading.value = true
-            messages.value.push(obj)
-            nextTick(() => scrollbar.value?.setScrollTop(99999))
-            let formData = new FormData();
-            formData.append("file", file);
-            const res = await getFileContent(formData)
-            console.log({
-                body: form,
-                input_data: res.data
-            });
-
-            const response = await getFormKey({
-                body: form,
-                input_data: res.data
-            })
-            xlsxData.value = response.data
-            console.log(xlsxData.value);
-            console.log(type.value);
-
-            await streamRes(buildObjPrompt(response.data, form), false)
-        } catch (error) {
-            obj.content = '解析出错'
-        } finally {
-
-        }
+  const getFileSummary = async (file: any) => {
+    const obj = reactive({
+      id: moment(),
+      username: "用户1",
+      rawContent: "",
+      content: "解析文件中",
+      timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+      isSelf: false,
+      avatar: avator,
+      addToHistory: !taklToHtml.value
+    });
+    try {
+      sendLoading.value = true;
+      messages.value.push(obj);
+      nextTick(() => scrollbar.value?.setScrollTop(99999));
+      const res = await getFileValue(file);
+      console.log(res.data, file);
+      await streamRes();
+    } catch (error) {
+      obj.content = "解析出错";
+    } finally {
+      sendLoading.value = false;
     }
+  };
+  const getFormKeyAndValue = async (file: any, form?: any) => {
+    const obj = reactive({
+      id: moment(),
+      username: "用户1",
+      rawContent: "",
+      content: "解析文件中",
+      timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+      isSelf: false,
+      avatar: avator,
+      addToHistory: !taklToHtml.value
+    });
 
-    // 发送消息
-    const addMessage = (msg: any, fetch: any) => {
-        if (!msg) return
-        const newMessage: any = {
-            id: messages.value.length + 1,
-            username: '我',
-            content: msg,
-            timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-            isSelf: true,
-            avatar: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
-            addToHistory: !taklToHtml.value
-        }
-        messages.value.push(newMessage)
-        useStore(msgUuid.value).add(newMessage)
-
-        // 滚动到底部
-        nextTick(() => {
-            scrollbar.value?.setScrollTop(99999);
-            fetch && sendRequese(msg, taklToHtml.value);
-        })
+    try {
+      sendLoading.value = true;
+      messages.value.push(obj);
+      const response = await getFormKey({
+        body: form,
+        input_data: file
+      });
+      return response;
+      xlsxData.value = response.data;
+      console.log(xlsxData.value);
+      console.log(type.value);
+      await streamRes(buildObjPrompt(response.data, form));
+    } catch (error) {
+      obj.content = "解析出错";
+    } finally {
+      sendLoading.value = false;
     }
-
-    const getPageInfo = () => {
-        return new Promise((res, rej) => {
-            chrome.runtime.sendMessage({
-                type: 'FROM_SIDE_PANEL_TO_GET_PAGE_INFO',
-            }, async (response) => {
-                if (chrome.runtime.lastError) {
-                    console.error("消息发送错误:", chrome.runtime.lastError);
-                    rej(chrome.runtime.lastError)
-                } else {
-                    pageInfo.value = response.data
-                    res(response.data)
-                }
-            });
-        })
+  };
+  // 发送消息
+  const handleSend = async (msg: any, addHtml = false) => {
+    if ((type.value === "2" && msg.startsWith("/")) || msg.startsWith("请")) {
+      if (!taklToHtml.value) return messages.value.push({
+        id: messages.value.length + 1,
+        username: "用户1",
+        content: "请打开与页面对话!",
+        rawContent: "请打开与页面对话!",
+        timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+        isSelf: false,
+        avatar: avator,
+        addToHistory: false
+      });
+      indexTemp.value = 0;
+      fetchRes(msg);
+    } else {
+      streamRes(addHtml);
     }
+  };
+  const fetchRes = async (msg: any) => {
+    sendLoading.value = true;
+    const obj: any = reactive({
+      id: messages.value.length + 1,
+      username: "用户1",
+      content: "",
+      timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+      isSelf: false,
+      avatar: avator,
+      addToHistory: !taklToHtml.value
+    });
+    messages.value.push(obj);
+    nextTick(() => scrollbar.value?.setScrollTop(99999));
+    await fetchDataAndProcess(msg, obj);
+    sendLoading.value = false;
+  };
+  let str = "";
 
-    const sendRequese = async (msg: any, addHtml = false) => {
-        const res: any = await getPageInfo()
-
-
-        if ((type.value === '2' && msg.startsWith('/')) || msg.startsWith('请')) {
-            if (!taklToHtml.value) return messages.value.push({
-                id:messages.value.length + 1,
-                username: '用户1',
-                content: '请打开与页面对话!',
-                rawContent: '请打开与页面对话!',
-                timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-                isSelf: false,
-                avatar: avator,
-                addToHistory: false
-            })
-            indexTemp.value = 0
-            fetchRes(msg)
-        }
-        else {
-            if (!addHtml) msg = getSummaryPrompt(res.content)
-            streamRes(msg, addHtml)
-        }
+  async function fetchDataAndProcess(input: any, obj: any) {
+    if (input.startsWith("/智能填表")) {
+      input = input.split("/智能填表")[1];
     }
-
-    const fetchRes = async (msg: any) => {
-        sendLoading.value = true
-        const obj: any = reactive({
-            id: messages.value.length + 1,
-            username: '用户1',
-            content: '',
-            timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-            isSelf: false,
-            avatar: avator,
-            addToHistory: !taklToHtml.value
-        })
-        messages.value.push(obj)
-        nextTick(() => scrollbar.value?.setScrollTop(99999))
-        await fetchDataAndProcess(msg, obj)
-        sendLoading.value = false
+    str = input;
+    console.log(str);
+    const pageInfo = await getPageInfo();
+    await new Promise((res: any) => setTimeout(() => {
+      res();
+    }, 2000));
+    // const res = await hepl({
+    //   input_data: input,
+    //   body: pageInfo.content.mainContent
+    // })
+    const res = await new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve({ data: pageInfo.title === "智能招采" ? mockData[indexTemp.value] : mockData2[indexTemp.value] });
+      }, 1000);
+    });
+    if (!res.data.tag || res.data.tag === "undefined") {
+      ElMessage({ message: "未找到标签,请重试", type: "error", duration: 4 * 1000, grouping: true });
+      obj.content = "未找到标签,请重试";
+      type.value = "";
+      return;
     }
+    await handleClick(res.data, obj);
+  }
 
-    const streamRes = async (msg: any, addHtml: any) => {
-        sendLoading.value = true;
-        const obj = reactive<any>({
-            id:messages.value.length + 1,
-            username: '用户1',
-            content: '',
-            rawContent: '', // 存储原始内容
-            timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-            isSelf: false,
-            avatar: avator,
-            addToHistory: !taklToHtml.value
-        });
-        let history = []
-        if (taklToHtml.value) {
-            if (addHtml) {
-                history.push({
-                    role: 'user',
-                    content: `页面主要内容${pageInfo.value.content.mainContent}`
-                })
-            }
-            history.push({
-                role: 'user',
-                content: msg
-            })
+  async function handleClick(res: any, msgObj: any) {
+    await new Promise(resolve => setTimeout(resolve, 2000));
+    msgObj.content = `点击${res.tag}元素`;
+    chrome.runtime.sendMessage({
+      type: "FROM_SIDE_PANEL_TO_ACTION",
+      data: res
+    }, async ({ data, status }) => {
+      if (chrome.runtime.lastError) {
+        console.error("消息发送错误:", chrome.runtime.lastError);
+      } else {
+        if (status === "error") {
+          msgObj.content = data;
+          return;
+        }
+        if (res.next === "是") {
+          const arr = str.split(",");
+          arr.shift();
+          str = arr.join(",");
+          indexTemp.value++;
+          fetchDataAndProcess(str, msgObj);
         } else {
-            history = messages.value
-                .filter((item: any) => item.addToHistory).slice(-20)
-                .map((item: any) => ({
-                    role: item.isSelf ? 'user' : 'system',
-                    content: item.isSelf ? item.content : item.rawContent
-                }))
+          if (type.value === "2") {
+            await new Promise((resolve, reject) => {
+              setTimeout(() => {
+                resolve(1);
+              }, 2000);
+            });
+            msgObj.content = `请上传数据`;
+            ElMessage({ message: "请上传数据", type: "success", duration: 4 * 1000, grouping: true });
+          } else {
+            msgObj.content = `执行完毕`;
+          }
         }
+      }
+      return true;
+    });
 
-        messages.value.push(obj)
-        nextTick(() => {
-            scrollbar.value?.setScrollTop(99999)
-        })
-        const iterator = await sendMessage(history, addHtml)
+  }
 
-        for await (const chunk of iterator) {
-            if (chunk) {
-                const decodedChunk = chunk.choices[0].delta.content;
-                if (decodedChunk) {
-                    // 保存原始内容
-                    obj.rawContent += decodedChunk
-                    // 实时格式化显示内容
-                    obj.content = formatMessage(obj.rawContent)
-                }
-            }
-            scrollbar.value?.setScrollTop(99999)
-        }
-        scrollbar.value?.setScrollTop(99999)
-        //添加到存储历史
-        useStore(msgUuid.value).add({...obj})
+  /**
+   *
+   * @param addHtml 是否添加页面信息
+   */
+  const streamRes = async (addHtml: any = false) => {
+    pageInfo.value = await getPageInfo();
+    sendLoading.value = true;
+    const obj = reactive<any>({
+      id: messages.value.length + 1,
+      username: "用户1",
+      content: "",
+      rawContent: "", // 存储原始内容
+      timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
+      isSelf: false,
+      avatar: avator,
+      addToHistory: !taklToHtml.value
+    });
+    let history = [];
+    if (taklToHtml.value) {
+      if (addHtml) {
+        history.push({
+          role: "user",
+          content: `页面主要内容${pageInfo.value.content.mainContent}`
+        });
+      }
+      history.push({
+        role: "user",
+        content: messages.value[messages.value.length - 1].rawContent
+      });
+    } else {
+      history = messages.value
+        .filter((item: any) => item.addToHistory).slice(-20)
+        .map((item: any) => ({
+          role: item.isSelf ? "user" : "system",
+          content: item.rawContent
+        }));
 
-        // 处理最终内容
-        if (type.value === '2') {
-            try {
-                formMap.value = JSON.parse(obj.rawContent.split('json')[1].split('```')[0])
-                handleInput()
-                type.value = ''
-            } catch (e) {
-                console.error('解析JSON失败:', e)
-            }
-        }
-        type.value = ''
-        sendLoading.value = false
-        nextTick(() => {
-            scrollbar.value?.setScrollTop(99999)
-        })
     }
 
-  const handleInput = () => {
-    chrome.runtime.sendMessage({
-      type: 'FROM_SIDE_PANEL_TO_INPUT_FORM',
-      data: {
-        excelData: xlsxData.value,
-        formData: formMap.value
-      }
+    messages.value.push(obj);
+    nextTick(() => {
+      scrollbar.value?.setScrollTop(99999);
     });
-  }
-
-    return {
-        msgUuid,
-        messages,
-        inputMessage,
-        indexTemp,
-        taklToHtml,
-        pageInfo,
-        sendLoading,
-        formMap,
-        type,
-        addMessage,
-        sendRequese,
-        getPageInfo,
-        streamRes,
-        handleInput,
-        getFileValue
+    const iterator = await sendMessage(history);
+    for await (const chunk of iterator) {
+      if (chunk) {
+        const decodedChunk = chunk.choices[0].delta.content;
+        if (decodedChunk) {
+          // 保存原始内容
+          obj.rawContent += decodedChunk;
+          // 实时格式化显示内容
+          obj.content = formatMessage(obj.rawContent);
+        }
+      }
+      scrollbar.value?.setScrollTop(99999);
     }
+    //添加到存储历史
+    useStore(msgUuid.value).add({ ...obj });
+    // 处理最终内容
+    sendLoading.value = false;
+    nextTick(() => {
+      scrollbar.value?.setScrollTop(99999);
+    });
+    return obj.rawContent;
+  };
+  return {
+    indexTemp,
+    taklToHtml,
+    sendLoading,
+    formMap,
+    type,
+    handleSend,
+    streamRes,
+    getFormKeyAndValue,
+    getFileSummary
+  };
 }

+ 1 - 1
src/entrypoints/sidepanel/hook/useSummary.ts

@@ -1,6 +1,6 @@
 export function useSummary(addMessage: Function, sendRequese: Function) {
   const handleCardButtonClick = async () => {
-    addMessage('总结当前页面', false)
+    addMessage('总结当前页面')
     await sendRequese('', false)
   }
 

+ 16 - 64
src/entrypoints/sidepanel/mock.ts

@@ -40,71 +40,23 @@ export const options = [
 ];
 
 export const mockData = [
-  {
-    action: "click",
-    class: "ant-menu-item",
-    tag: "li",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "项目建档",
-    next: "是"
-  },
-  {
-    action: "click",
-    class: "ant-menu-item",
-    tag: "button",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "新增",
-    next: "否"
-  },
-  {
-    action: "show",
-    class: "ant-menu-item",
-    tag: "button",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "请上传数据",
-    next: "是"
-  }
-];
+  { action: 'click', class: "ant-menu-item", tag: "li", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "项目建档", next: "是" },
+  { action: 'click', class: "ant-menu-item", tag: "button", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "新增", next: "否" },
+  { action: 'show', class: "ant-menu-item", tag: "button", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "请上传数据", next: "是" },
+]
 
 export const mockData2 = [
-  {
-    action: "click",
-    class: "ant-menu-item",
-    tag: "li",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "我的简历",
-    next: "是"
-  },
-  {
-    action: "click",
-    class: "ant-menu-item",
-    tag: "button",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "去录入",
-    next: "否"
-  },
-  {
-    action: "show",
-    class: "ant-menu-item",
-    tag: "button",
-    innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>",
-    id: "",
-    text: "请上传数据",
-    next: "是"
-  }
-];
+  { action: 'click', class: "ant-menu-item", tag: "li", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "我的简历", next: "是" },
+  { action: 'click', class: "ant-menu-item", tag: "button", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "去录入", next: "否" },
+  { action: 'show', class: "ant-menu-item", tag: "button", innerHTML: "<span class=\"ant-menu-item-icon\"><span role=\"img\" aria-label=\"book\" class=\"anticon anticon-book\"></span></span><span class=\"ant-menu-title-content\"><span>项目建档</span></span>", id: "", text: "请上传数据", next: "是" },
+]
 
 export const startMsg = {
-  id: 1,
-  username: "用户1",
-  content: "你好!有什么我可以帮助你的吗?",
-  rawContent: "你好!有什么我可以帮助你的吗?",
-  timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
-  isSelf: false,
-  avatar: avator
-};
+    id: 1,
+    username: '用户1',
+    content: '你好!有什么我可以帮助你的吗?',
+    rawContent: '你好!有什么我可以帮助你的吗?',
+    timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
+    isSelf: false,
+    avatar: avator
+}

+ 54 - 0
src/entrypoints/sidepanel/utils/index.js

@@ -0,0 +1,54 @@
+import * as XLSX from "xlsx";
+export const getPageInfo = () => {
+    return new Promise((res, rej) => {
+        chrome.runtime.sendMessage({
+            type: 'FROM_SIDE_PANEL_TO_GET_PAGE_INFO',
+        }, async (response) => {
+            if (chrome.runtime.lastError) {
+                console.error("消息发送错误:", chrome.runtime.lastError);
+                rej(chrome.runtime.lastError)
+            } else {
+                res(response.data)
+            }
+        });
+    })
+}
+export const handleInput = (xlsxData, formMap) => {
+    chrome.runtime.sendMessage({
+        type: 'FROM_SIDE_PANEL_TO_INPUT_FORM',
+        data: {
+            excelData: xlsxData,
+            formData: formMap
+        }
+    })
+}
+export const getXlsxValue = (file) => {
+    return new Promise((res, rej) => {
+      try {
+          const reader = new FileReader();
+          reader.readAsArrayBuffer(file)
+          reader.onload =  (e) => {
+              const data = new Uint8Array(e.target.result);
+              const workbook = XLSX.read(data, {
+                  type: "array",
+                  cellDates: false,
+                  cellNF: true,
+                  cellText: true,
+                  dateNF: 'yyyy-mm-dd'
+              });
+              // 修复日期处理
+              const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
+              // 转换为JSON数据
+              const readData = XLSX.utils.sheet_to_json(firstSheet, {
+                  header: 1,
+                  raw: false,
+                  defval: "",
+                  dateNF: 'yyyy-mm-dd'
+              });
+              res(readData)
+          }
+      } catch (error) {
+        rej()
+      }
+   })
+}

+ 1 - 3
src/store/modules/msg.ts

@@ -6,8 +6,6 @@ export const useMsgStore = defineStore("msg", {
   state: () => ({
     msgUuid: <string>"",
     messages: <any[]>[],
-    type: <string>"",
-    sendLoading: <boolean>false,
     pageInfoList: []
 
   }),
@@ -25,7 +23,7 @@ export const useMsgStore = defineStore("msg", {
     msgToDB(msg: any) {
       const { useStore } = useIndexedDBStore();
       useStore(this.msgUuid).add(msg);
-    },
+    }
 
   }
 });

+ 21 - 5
src/utils/ai-service.js

@@ -103,6 +103,23 @@ ${pageInfo.mainContent}
 
 请以"以下是对该页面内容的总结:"开头,然后用要点的形式列出主要内容。`;
 }
+
+export function getFileSummaryPrompt(file,name) {
+    return `请帮我总结以下文件内容的要点:
+
+文件名称:${name}
+
+主要内容:
+${file}
+
+要求:
+1. 用简洁清晰的语言提取3-5个核心要点
+2. 保持客观中立的语气
+3. 按重要性排序
+4. 返回内容做好换行,以及展示样式
+
+请以"以下是对该文件内容的总结:"开头,然后用要点的形式列出主要内容。`;
+}
 export function formatMessage(text) {
     if (!text) return "";
 
@@ -195,13 +212,12 @@ export function buildExcelUnderstandingPrompt(data, fileName, pageInfo) {
 ${pageInfo}
              
 要求:
-1. 请根据表单中的表单项和列标题进行匹配,一定以表单为准!
+1. 请根据表单中的表单项和列标题进行匹配,一定以表单为准,列标题只能匹配一次,没有和列标题匹配到的表单项不返回
 2. 生成表单项与列标题对应的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我excel文件中列标题的值。
-3. 根据label匹配,findBy是label,并通过findByValue给我label元素的值,根据id匹配,findBy是id,并通过findByValue给是id的值,
+3. 表单项有id根据id匹配,findBy是id,并通过findByValue告诉我id的值,没有id根据label匹配,findBy是label,并通过findByValue给我label元素的值,没有label根据placeholder匹配,findBy是placeholder,并通过findByValue告诉我placeholder的值,没有placeholder,再根据其他内容匹配
 3. 并去除没有匹配到的表单项和excel文件中没有匹配到的列,
 4. 通过type字段告诉我输入项的类型
-5. 表单项有label,根据label匹配,没有label根据placeholder匹配,没有placeholder,根据id匹配,再根据其他内容匹配
-6. 仅返回数组,不要返回任何其他内容。`
+5. 仅返回数组,不要返回任何其他内容。`
 }
 // 5. 如果表单项有label标签,同时返回label, 通过label字段告诉我label元素的文本
 export function buildObjPrompt(obj,  pageInfo) {
@@ -214,7 +230,7 @@ export function buildObjPrompt(obj,  pageInfo) {
 ${pageInfo}
              
 要求:
-1. 请根据表单中的表单项和对象进行匹配,一定以表单为准!
+1. 请根据表单中的表单项和对象进行匹配,一定以表单为准,key同时在对象和表单中出现,才能返回
 2. 表单项有label,根据label匹配,没有label根据placeholder匹配,没有placeholder,根据id匹配,再根据其他内容匹配
 3. 并根据实际可操作的表单项的所有信息与上传的对象的key进行匹配,生成表单项与key的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我对应的key值。在一个字段内返回
 4. 并去除没有匹配到的表单项和对象中没有匹配到的key,

+ 1 - 1
wxt.config.ts

@@ -6,7 +6,7 @@ export default defineConfig({
   srcDir: "src",
   manifest: {
     "name": "派维斯智能体助手",
-    "version": "0.1.6",
+    "version": "0.1.7",
     "permissions": [
       "storage",
       "history",