浏览代码

feat(sidepanel): 优化智能填表功能

- 修改了 AI模型的选择逻辑
- 优化了文件上传和处理流程
- 调整了智能填表的交互方式
wzg 5 月之前
父节点
当前提交
6a09925b56

文件差异内容过多而无法显示
+ 0 - 0
src/assets/svg/file.svg


+ 1 - 3
src/entrypoints/background.js

@@ -45,7 +45,7 @@ export default defineBackground(() => {
       return true
     }
     if (message.type === 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM') {
-      chrome.tabs.query({ active: true}, (tabs) => {
+      chrome.tabs.query({ active: true }, (tabs) => {
         if (tabs.length === 0) return // 确保有活动标签页
         const tabId = tabs[0].id // 获取当前活动的 tabId
         chrome.tabs.sendMessage(
@@ -59,8 +59,6 @@ export default defineBackground(() => {
               sendResponse(response)
             }
 
-
-
             return true
           }
         )

+ 21 - 14
src/entrypoints/content.js

@@ -1,6 +1,15 @@
 // entrypoints/content.ts
 import PageAnalyzer, { cleanPage } from '../utils/page-analyzer'
-import { formatDate, simulateCompleteUserAction, simulateUserInput, findLabelForInput, findLabelForTag, findLabelForSpan, findLabelForTextarea, getPageInfo } from '../utils/contentUtils'
+import {
+  formatDate,
+  simulateCompleteUserAction,
+  simulateUserInput,
+  findLabelForInput,
+  findLabelForTag,
+  findLabelForSpan,
+  findLabelForTextarea,
+  getPageInfo
+} from '../utils/contentUtils'
 export default defineContentScript({
   matches: ['<all_urls>'],
   main(ctx) {
@@ -23,7 +32,7 @@ export default defineContentScript({
         }
         let dom = null
         if (message.type === 'GET_TAG_ACTION') {
-          console.log(message.data);
+          console.log(message.data)
           const data = message.data
           if (0) {
             dom = document.getElementById(data.id)
@@ -57,7 +66,7 @@ export default defineContentScript({
           }
           if (forms.length === 1) {
             form = forms[0]
-            const cloneForm =  forms[0].cloneNode(true)
+            const cloneForm = forms[0].cloneNode(true)
             cloneForm.querySelectorAll('svg').forEach((el) => el.remove())
             formChildren = [...form.elements]
             sendResponse({
@@ -67,20 +76,20 @@ export default defineContentScript({
             return
           }
           sendResponse({
-            status: 'select',
+            status: 'select'
           })
           for (const item of forms) {
             item.style.border = '2px solid red'
             function handleClick(e) {
               e.stopPropagation()
-              console.log(this.outerHTML);
+              console.log(this.outerHTML)
               for (const form of forms) {
                 form.style.border = 'none'
                 form.removeEventListener('click', handleClick, true)
               }
               form = this
-              console.log(form,5855);
-              
+              console.log(form, 5855)
+
               const cloneForm = this.cloneNode(true)
               cloneForm.querySelectorAll('svg').forEach((el) => el.remove())
               formChildren = [...form.elements]
@@ -93,11 +102,9 @@ export default defineContentScript({
                 data: cloneForm.outerHTML
               })
             }
-            item.addEventListener("click", handleClick, true)
+            item.addEventListener('click', handleClick, true)
           }
-        
-        
- 
+
           // if (!form) {
           //   sendResponse({
           //     status: 'error',
@@ -131,8 +138,8 @@ export default defineContentScript({
     )
     const handleFillInput = async (data, index) => {
       console.log(data, 85888)
-      console.log(formChildren,form);
-      
+      console.log(formChildren, form)
+
       for (let i = 0; i < data.length; i++) {
         const item = data[i]
         if (item.findBy === 'id') {
@@ -149,7 +156,7 @@ export default defineContentScript({
                 const label = [...form.getElementsByTagName('label')].find(
                   (label) => label.innerText.includes(item.item)
                 )
-                console.log(item,label)
+                console.log(item, label)
                 if (label) {
                   const input = findLabelForInput(label)
                   if (input) {

+ 65 - 40
src/entrypoints/sidepanel/Chat.vue

@@ -29,7 +29,7 @@
                 <span class="dot"></span>
               </span>
             </div>
-            <div class="timestamp ">{{ message.timestamp }}
+            <div class="timestamp ">{{ moment(message.timestamp).format('MM-DD HH:mm') }}
               <span v-if="message.add" style="cursor: pointer;" @click="handleInput">填充</span>
             </div>
           </div>
@@ -42,7 +42,8 @@
            @handle-capture="handleCapture"
            @his-records="hisRecords"
            @add-new-dialogue="addNewDialogue"
-           @handle-current-change="handleCurrentChange" />
+           @handle-current-change="handleCurrentChange"
+           @handel-intelligent-filling-click="handelIntelligentFillingClick" />
 
     <div>
       <!-- 输入区域 -->
@@ -67,9 +68,11 @@
               <el-button round @click="handleSummary" :disabled="!taklToHtml">总结</el-button>
             </el-tooltip>
 
-            <el-tooltip content="选择后,在输入框描述填表流程" placement="top">
-              <el-button :class="type === '2' ? 'buttom-clicked' : ''" round @click="handelIntelligentFillingClick" :disabled="!taklToHtml">智能填表</el-button>
-            </el-tooltip>
+            <!--            <el-tooltip content="选择后,在输入框描述填表流程" placement="top">-->
+            <!--              <el-button :class="type === '2' ? 'buttom-clicked' : ''" round @click="handelIntelligentFillingClick"-->
+            <!--                         :disabled="!taklToHtml">智能填表-->
+            <!--              </el-button>-->
+            <!--            </el-tooltip>-->
           </div>
         </div>
 
@@ -96,6 +99,7 @@
 import { ref, onMounted, nextTick, inject, useTemplateRef, reactive } from 'vue'
 import { ElScrollbar, ElAvatar, ElInput, ElButton } from 'element-plus'
 import moment from 'moment'
+import fileLogo from '@/assets/svg/file.svg'
 import {
   buildExcelUnderstandingPrompt,
   getSummaryPrompt,
@@ -110,7 +114,7 @@ import Tools from '@/entrypoints/sidepanel/component/tools.vue'
 import historyComponent from '@/entrypoints/sidepanel/component/historyComponent.vue'
 import document from '@/entrypoints/sidepanel/component/document.vue'
 import pageMask from '@/entrypoints/sidepanel/component/pageMask.vue'
-import { mockData, startMsg, mockData2, options } from '@/entrypoints/sidepanel/mock'
+import { mockData, startMsg, mockData2, options, FunctionList } 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'
@@ -176,7 +180,14 @@ const handleSummary = async () => {
     pageInfoList.value.forEach((pageInfo) => {
       params.push({ role: 'system', content: `fileid://${pageInfo.fileId}` })
     })
-    params.push({ role: 'user', content: '请根据这几个文件综合分析总结,并屏蔽敏感词汇' })
+    params.push({
+      role: 'user', content: `
+    请根据这几个文件综合分析总结
+    要求:
+    1.抽取各文件的内容
+    2.每个文件的内容进行总结
+    3.所有文件进行综合的总结`
+    })
   } else {
     let tempStr = ''
     pageInfoList.value.forEach((pageInfo, index) => {
@@ -198,12 +209,20 @@ const handleSummary = async () => {
   addMessage(pageInfoList.value, '总结', 'document')
   if (requestFlowFn) {
     await requestFlowFn(params)
+    taklToHtml.value = true
+    isShowPage.value = true
+    pageInfoList.value = []
   }
 }
 
-function handelIntelligentFillingClick() {
+async function handelIntelligentFillingClick() {
+  isShowPage.value = true
+  taklToHtml.value = true
+  const tempPageInfo = await getPageInfo()
+  pageInfo.value = tempPageInfo
+  pageInfoList.value.unshift(tempPageInfo)
   inputMessage.value = '/智能填表 '
-  type.value = '2'
+  type.value = FunctionList.Intelligent_Form_filling
 }
 
 function handleCurrentChange(e) {
@@ -233,6 +252,7 @@ function handleCurrentData(e) {
 }
 
 async function readClick() {
+  type.value = FunctionList.File_Operation
   isShowPage.value = true
   taklToHtml.value = true
   if (pageInfoList.value.length >= Number(import.meta.env.VITE_MAX_FILE_NUMBER)) {
@@ -245,7 +265,6 @@ async function readClick() {
     const file = new File([blob], tempPageInfo.title + '.txt', { type: 'text/plain' })
     tempPageInfo.fileId = await modelFileUpload(file)
   }
-  pageInfo.value = tempPageInfo
   pageInfoList.value.unshift(tempPageInfo)
 }
 
@@ -277,16 +296,16 @@ async function handleAsk() {
     })
   }
   addMessage(inputMessage.value.trim())
-  if (type.value === '2') {
+  if (type.value === FunctionList.Intelligent_Form_filling) {
     const res = await fetchRes(inputMessage.value.trim())
     if (res.status === 'ok') {
       formInfo.value = res.data
-      console.log(res);
+      console.log(res)
     }
-  }
-  else streamRes(taklToHtml.value)
+  } else streamRes(taklToHtml.value)
   inputMessage.value = ''
 }
+
 function handleCapture() {
   ElMessage({
     message: '开发中...',
@@ -300,36 +319,42 @@ function hisRecords() {
 }
 
 const handleUpload = async (file) => {
-  if (AIModel.value.value === 'qwen-long') {
+  if (AIModel.value.file === true) {
     const id = await modelFileUpload(file)
-    console.log(file)
-    console.log(id)
+    pageInfoList.value.unshift({
+      fileId: id,
+      title: file.name,
+      url: 'File',
+      favIconUrl: fileLogo
+    })
+    isShowPage.value = true
+    taklToHtml.value = true
     return
   }
-  if (type.value === '2') {
+  if (type.value === FunctionList.Intelligent_Form_filling) {
     const fileExtension = file.name.split('.').pop().toLowerCase()
-        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, formInfo.value))
-          const a = await streamRes()
-          console.log(a);
-          handleInput(xlsxData.value, JSON.parse(a.split('json')[1].split('```')[0]))
-        } else {
-          const { data, msg } = await getFileValue(file)
-          const res2 = await getFormKeyAndValue(data, formInfo.value)
-          xlsxData.value = res2.data
-          msg.rawContent = buildObjPrompt(res2.data, formInfo.value)
-          const a = await streamRes()
-          handleInput(xlsxData.value, JSON.parse(a.split('json')[1].split('```')[0]))
-      }
-          type.value = ''
-        return
+    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, formInfo.value))
+      const a = await streamRes()
+      console.log(a)
+      handleInput(xlsxData.value, JSON.parse(a.split('json')[1].split('```')[0]))
+    } else {
+      const { data, msg } = await getFileValue(file)
+      const res2 = await getFormKeyAndValue(data, formInfo.value)
+      xlsxData.value = res2.data
+      msg.rawContent = buildObjPrompt(res2.data, formInfo.value)
+      const a = await streamRes()
+      handleInput(xlsxData.value, JSON.parse(a.split('json')[1].split('```')[0]))
+    }
+    type.value = FunctionList.File_Operation
+    return
   }
-  if (type.value === '') {
+  if (type.value === FunctionList.File_Operation) {
     const fileVaue = await getFileValue(file)
     // streamRes()
   }
@@ -376,5 +401,5 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-@import '@/entrypoints/sidepanel/css/chat.scss';
+@use '@/entrypoints/sidepanel/css/chat.scss';
 </style>

+ 2 - 2
src/entrypoints/sidepanel/component/tools.vue

@@ -7,7 +7,7 @@ import { useMsgStore } from '@/store/modules/msg'
 
 const { AIModel } = storeToRefs(useMsgStore())
 const selectInput = ref(null)
-const emit = defineEmits(['readClick', 'uploadFile', 'handleCapture', 'addNewDialogue', 'hisRecords', 'handleCurrentChange'])
+const emit = defineEmits(['readClick', 'uploadFile', 'handleCapture', 'addNewDialogue', 'hisRecords', 'handleCurrentChange', 'handelIntelligentFillingClick'])
 
 watchEffect(() => {
   selectInput.value = AIModel.value?.value
@@ -38,7 +38,7 @@ watchEffect(() => {
         <el-button class="tools_btn" link :icon="Scissor" @click="emit('handleCapture')" />
       </el-tooltip>
       <el-tooltip effect="dark" content="智能填表" placement="top">
-        <el-button class="tools_btn" link :icon="Edit" @click="emit('handleCapture')" />
+        <el-button class="tools_btn" link :icon="Edit" @click="emit('handelIntelligentFillingClick')" />
       </el-tooltip>
 
     </div>

+ 8 - 6
src/entrypoints/sidepanel/css/chat.scss

@@ -62,14 +62,16 @@
 }
 
 .content {
-  padding: 12px;
-  min-height: 40px;
+  padding: 6px;
   background-color: #f0f4f8;
-  border-radius: 12px;
+  border-radius: 4px;
+  //background: linear-gradient(to bottom right, #D2B48C, #E8D5B5);
+  //background: linear-gradient(to bottom, #D2B48C, #E8D5B5);
   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
   word-break: break-all;
   line-height: 1.5;
   font-size: 14px;
+  letter-spacing: 1px;
 }
 
 .content :deep(pre) {
@@ -101,7 +103,7 @@
 .self .content {
   background: #409eff;
   color: white;
-  border-bottom-right-radius: 4px;
+  //border-bottom-right-radius: 4px;
 }
 
 .other .content {
@@ -317,6 +319,6 @@
   box-shadow: none;
 }
 .buttom-clicked {
-    color: #409eff;
-    border-color: rgb(197.7, 225.9, 255);
+  color: #409eff;
+  border-color: rgb(197.7, 225.9, 255);
 }

+ 46 - 38
src/entrypoints/sidepanel/hook/useMsg.ts

@@ -14,6 +14,7 @@ import { ElMessage } from 'element-plus'
 import { getPageInfo } from '../utils/index.js'
 import { mockData, mockData2 } from '../mock'
 import { useMsgStore } from '@/store/modules/msg'
+import { FunctionList } from '../mock'
 
 export function useMsg(scrollbar?: any) {
   const { messages, msgUuid } = storeToRefs(useMsgStore())
@@ -21,7 +22,7 @@ export function useMsg(scrollbar?: any) {
   const taklToHtml = ref<any>(false)
   const sendLoading = ref(false)
   const pageInfo = ref<any>({})
-  const type = ref('')
+  const type = ref(FunctionList.File_Operation)
   const formMap = ref([])
   // 获取父组件提供的 Hook 实例
   const { useStore } = inject('indexedDBHook') as any
@@ -94,36 +95,42 @@ export function useMsg(scrollbar?: any) {
   //     fetchRes(msg)
   //   }
   // }
-  async function awaitFindForm(obj:any) {
+  async function awaitFindForm(obj: any) {
     return await new Promise((res, rej) => {
-      chrome.runtime.sendMessage({
-        type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM'
-      }, ({ status, data }) => {
-        if (status === 'error') {
-          obj.content = '当前页面未找到表单'
-          res({ status })
-        }
-        if (status === 'ok') {
-          obj.content = '请上传数据'
-          res({ status, data })
-        }
-        if (status === 'select') {
-          obj.content = '检测到左侧页面中有多个表单,请选择要填写的表单。'
-          function handle(message, sender, sendResponse)  {
-            if (message.type === 'TO_SIDE_PANEL_FORM_INFO') {
-              console.log('收到一次性消息:', message.data);
-              res({ status: 'ok', data: message.data })
-              obj.content = '请上传数据'
-              console.log(565656);
-              // 销毁监听器(确保只触发一次)
-              chrome.runtime.onMessage.removeListener(handle);
+      chrome.runtime.sendMessage(
+        {
+          type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM'
+        },
+        ({ status, data }) => {
+          if (status === 'error') {
+            obj.content = '当前页面未找到表单'
+            res({ status })
+          }
+          if (status === 'ok') {
+            obj.content = '请上传数据'
+            res({ status, data })
+          }
+          if (status === 'select') {
+            obj.content = '检测到左侧页面中有多个表单,请选择要填写的表单。'
+
+            function handle(message, sender, sendResponse) {
+              if (message.type === 'TO_SIDE_PANEL_FORM_INFO') {
+                console.log('收到一次性消息:', message.data)
+                res({ status: 'ok', data: message.data })
+                obj.content = '请上传数据'
+                console.log(565656)
+                // 销毁监听器(确保只触发一次)
+                chrome.runtime.onMessage.removeListener(handle)
+              }
             }
+
+            chrome.runtime.onMessage.addListener(handle)
           }
-         chrome.runtime.onMessage.addListener(handle);
         }
-      })
+      )
     })
   }
+
   const fetchRes = async (msg: any) => {
     indexTemp.value = 0
     sendLoading.value = true
@@ -189,7 +196,7 @@ export function useMsg(scrollbar?: any) {
         grouping: true
       })
       obj.content = '未找到标签,请重试'
-      return {status: 'error'}
+      return { status: 'error' }
     }
     await new Promise((resolve) => setTimeout(resolve, 2000))
     obj.content = `点击${res.data.tag}元素`
@@ -238,6 +245,7 @@ export function useMsg(scrollbar?: any) {
     })
     return res2
   }
+
   /**
    *
    * @param addHtml 是否添加页面信息
@@ -291,12 +299,11 @@ export function useMsg(scrollbar?: any) {
           // 实时格式化显示内容
           obj.content = formatMessage(obj.rawContent)
           // if (type.value === '2') obj.content = obj.content.replace(/item/g, '表单项').replace(/excelColumn/g, '对应数据源')
-
         }
       }
       scrollbar.value?.setScrollTop(99999)
     }
-    console.log(obj.rawContent);
+    console.log(obj.rawContent)
 
     //添加到存储历史
     useStore(msgUuid.value).add({ ...obj })
@@ -307,17 +314,18 @@ export function useMsg(scrollbar?: any) {
     })
     if (type.value === '2') {
       const arr = JSON.parse(obj.rawContent.split('json')[1].split('```')[0])
-      const newArr = arr.map(obj => {
-        return `| ${[
-          `“findBy”: \` "label",`,
-          `"findByValue" \` “${obj.findByValue}”,`,
-          `"数据来源字段" \` “${obj.excelColumn}”,`,
-          `"表单项" \` “${obj.item}"`
-        ].join(' ')} |`;
-      }).join('\n');
+      const newArr = arr
+        .map((obj) => {
+          return `| ${[
+            `“findBy”: \` "label",`,
+            `"findByValue" \` “${obj.findByValue}”,`,
+            `"数据来源字段" \` “${obj.excelColumn}”,`,
+            `"表单项" \` “${obj.item}"`
+          ].join(' ')} |`
+        })
+        .join('\n')
       obj.content = formatMessage('```json' + JSON.stringify(newArr) + '```')
-      console.log(formatMessage('```json' + JSON.stringify(newArr) + '```'));
-
+      console.log(formatMessage('```json' + JSON.stringify(newArr) + '```'))
     }
     return obj.rawContent
   }

+ 12 - 6
src/entrypoints/sidepanel/mock.ts

@@ -8,20 +8,20 @@ export const options = [
       {
         label: '通义千问-Plus',
         value: 'qwen-plus',
-        url:'https://dashscope.aliyuncs.com/compatible-mode/v1',
-        file:false
+        url: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+        file: false
       },
       {
         label: '通义千问-Max',
         value: 'qwen-max',
-        url:'https://dashscope.aliyuncs.com/compatible-mode/v1',
-        file:false
+        url: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+        file: false
       },
       {
         label: '通义千问-Long',
         value: 'qwen-long',
-        url:'https://dashscope.aliyuncs.com/compatible-mode/v1',
-        file:true
+        url: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+        file: true
       }
     ]
   }
@@ -123,3 +123,9 @@ export const startMsg = {
   isSelf: false,
   avatar: avator
 }
+
+export const FunctionList = {
+  File_Operation: '1', // 文件处理
+  Intelligent_Form_filling: '2', // 智能填表
+  Image_Manipulation: '3' // 图片处理
+}

+ 32 - 33
src/entrypoints/sidepanel/utils/ai-service.js

@@ -34,8 +34,7 @@ export async function getFileContent(data) {
       body: data
     }).then((res) => res.json())
     return res
-  } catch (error) {
-  }
+  } catch (error) {}
 }
 
 export async function getFormKey(data) {
@@ -45,8 +44,7 @@ export async function getFormKey(data) {
       body: JSON.stringify(data)
     }).then((res) => res.json())
     return res
-  } catch (error) {
-  }
+  } catch (error) {}
 }
 
 export async function hepl(data) {
@@ -56,8 +54,7 @@ export async function hepl(data) {
       body: JSON.stringify(data)
     }).then((res) => res.json())
     return res
-  } catch (error) {
-  }
+  } catch (error) {}
 }
 
 function formatMessages(currentMessage, Summary, html) {
@@ -88,25 +85,25 @@ function formatMessages(currentMessage, Summary, html) {
 }
 
 export function getSummaryPrompt(pageInfo) {
-//   return `请根据所有网页总结以下网页内容的要点:
-//
-// 页面标题:${pageInfo.title}
-// 网站:${pageInfo.siteName}
-// URL:${pageInfo.url}
-//
-// 主要内容:
-// ${pageInfo.mainContent}
-//
-// 要求:
-// 1. 用简洁清晰的语言提取3-5个核心要点
-// 2. 保持客观中立的语气
-// 3. 按重要性排序
-// 4. 如果内容是新闻,需要包含时间、地点、人物等关键信息
-// 5. 如果内容是教程,需要突出操作步骤和关键提示
-// 6. 如果内容是产品介绍,需要包含主要特点和优势
-// 7. 返回内容做好换行,以及展示样式
-//
-// 请以"以下是对该页面内容的总结:"开头,然后用要点的形式列出主要内容。`
+  //   return `请根据所有网页总结以下网页内容的要点:
+  //
+  // 页面标题:${pageInfo.title}
+  // 网站:${pageInfo.siteName}
+  // URL:${pageInfo.url}
+  //
+  // 主要内容:
+  // ${pageInfo.mainContent}
+  //
+  // 要求:
+  // 1. 用简洁清晰的语言提取3-5个核心要点
+  // 2. 保持客观中立的语气
+  // 3. 按重要性排序
+  // 4. 如果内容是新闻,需要包含时间、地点、人物等关键信息
+  // 5. 如果内容是教程,需要突出操作步骤和关键提示
+  // 6. 如果内容是产品介绍,需要包含主要特点和优势
+  // 7. 返回内容做好换行,以及展示样式
+  //
+  // 请以"以下是对该页面内容的总结:"开头,然后用要点的形式列出主要内容。`
   return `页面标题:${pageInfo.title};
           网站:${pageInfo.siteName};
           网站地址:${pageInfo.url};
@@ -236,7 +233,6 @@ ${pageInfo}
 5. 返回json格式数组,不要返回任何其他内容。`
 }
 
-
 // 5. 如果表单项有label标签,同时返回label, 通过label字段告诉我label元素的文本
 export function buildObjPrompt(obj, pageInfo) {
   return `我将向你展示一个对象和一个form表单。请帮我理解这些数据:
@@ -268,13 +264,16 @@ export async function modelFileUpload(file) {
   }
   const store = useMsgStore(pinia)
   const { openai, AIModel } = store
-  const fileObject = await openai.files.create({
-    file,
-    purpose: 'file-extract'
-  }, {
-    headers: {
-      'Content-Type': 'multipart/form-data'
+  const fileObject = await openai.files.create(
+    {
+      file,
+      purpose: 'file-extract'
+    },
+    {
+      headers: {
+        'Content-Type': 'multipart/form-data'
+      }
     }
-  })
+  )
   return fileObject.id
 }

+ 3 - 6
src/entrypoints/sidepanel/utils/index.js

@@ -1,6 +1,6 @@
 import * as XLSX from 'xlsx'
 import { ElMessage } from 'element-plus'
-export function getPageInfo (){
+export function getPageInfo() {
   return new Promise((res, rej) => {
     chrome.runtime.sendMessage(
       {
@@ -17,7 +17,7 @@ export function getPageInfo (){
     )
   })
 }
-export function handleInput (xlsxData, formMap) {
+export function handleInput(xlsxData, formMap) {
   chrome.runtime.sendMessage({
     type: 'FROM_SIDE_PANEL_TO_INPUT_FORM',
     data: {
@@ -26,7 +26,7 @@ export function handleInput (xlsxData, formMap) {
     }
   })
 }
-export function getXlsxValue (file) {
+export function getXlsxValue(file) {
   return new Promise((res, rej) => {
     try {
       const reader = new FileReader()
@@ -56,6 +56,3 @@ export function getXlsxValue (file) {
     }
   })
 }
-
-
-

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

@@ -17,7 +17,7 @@ export const useMsgStore = defineStore('msg', {
         // apiKey: import.meta.env.VITE_OPENAI_API_KEY_TONG,
         apiKey: import.meta.env.VITE_OPENAI_API_KEY_TONG,
         baseURL: this.AIModel.url,
-        timeout: 5000,
+        // timeout: 5000,
         dangerouslyAllowBrowser: true
       })
     },

+ 203 - 205
src/utils/contentUtils.js

@@ -1,233 +1,231 @@
 export 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}`
+  // 直接创建北京时间的日期对象
+  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}`
 }
-export async  function simulateCompleteUserAction  (
-    clickElement,
-    inputElement,
-    inputText,
-    tdTitle
+export async function simulateCompleteUserAction(
+  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)
+  // 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))
+
+      // 按键按下
+      const keydownEvent = new KeyboardEvent('keydown', {
+        key: char,
+        code: `Key${char.toUpperCase()}`,
+        bubbles: true,
+        cancelable: true
+      })
+      element.dispatchEvent(keydownEvent)
+
+      // 更新输入值
+      element.value += char
+
+      // 触发输入事件
+      const inputEvent = new InputEvent('input', {
+        bubbles: true,
+        cancelable: true,
+        data: char,
+        inputType: 'insertText'
+      })
+      element.dispatchEvent(inputEvent)
+
+      // 按键弹起
+      const keyupEvent = new KeyboardEvent('keyup', {
+        key: char,
+        code: `Key${char.toUpperCase()}`,
+        bubbles: true,
+        cancelable: true
+      })
+      element.dispatchEvent(keyupEvent)
     }
-    // 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)
-
-            // 更新输入值
-            element.value += char
-
-            // 触发输入事件
-            const inputEvent = new InputEvent('input', {
-                bubbles: true,
-                cancelable: true,
-                data: char,
-                inputType: 'insertText'
-            })
-            element.dispatchEvent(inputEvent)
-
-            // 按键弹起
-            const keyupEvent = new KeyboardEvent('keyup', {
-                key: char,
-                code: `Key${char.toUpperCase()}`,
-                bubbles: true,
-                cancelable: true
-            })
-            element.dispatchEvent(keyupEvent)
-        }
-
-        // 触发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
-                }
 
-                if (Date.now() - startTime > timeout) {
-                    reject(new Error(`未找到title为"${title}"的td元素`))
-                    return
-                }
+    // 触发change事件
+    element.dispatchEvent(new Event('change', { bubbles: true }))
+  }
 
-                requestAnimationFrame(find)
-            }
+  // 3. 查找td元素
+  const findTdByTitle = (title, timeout = 5000) => {
+    return new Promise((resolve, reject) => {
+      const startTime = Date.now()
 
-            find()
-        })
-    }
+      const find = () => {
+        const td = document.querySelector(`td[title="${title}"]`)
+        if (td) {
+          resolve(td)
+          return
+        }
 
-    // 4. 模拟点击事件
-    const simulateClick = (element) => {
-        const clickEvent = new MouseEvent('click', {
-            bubbles: true,
-            cancelable: true,
-            view: window,
-            detail: 1
-        })
-        element.dispatchEvent(clickEvent)
-    }
+        if (Date.now() - startTime > timeout) {
+          reject(new Error(`未找到title为"${title}"的td元素`))
+          return
+        }
 
-    try {
-        // 执行操作序列
-
-        // 1. 触发鼠标弹起
-        simulateMouseUp(clickElement)
-        await new Promise((resolve) => setTimeout(resolve, 100))
-        // 2. 模拟键盘输入
-        await simulateTyping(inputElement, inputText)
-        await new Promise((resolve) => setTimeout(resolve, 200))
-        // 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)
-        }, 0)
-        await new Promise((resolve) => setTimeout(resolve, 500))
-        return true
-    } catch (error) {
-        throw error
-    }
+        requestAnimationFrame(find)
+      }
+
+      find()
+    })
+  }
+
+  // 4. 模拟点击事件
+  const simulateClick = (element) => {
+    const clickEvent = new MouseEvent('click', {
+      bubbles: true,
+      cancelable: true,
+      view: window,
+      detail: 1
+    })
+    element.dispatchEvent(clickEvent)
+  }
+
+  try {
+    // 执行操作序列
+
+    // 1. 触发鼠标弹起
+    simulateMouseUp(clickElement)
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    // 2. 模拟键盘输入
+    await simulateTyping(inputElement, inputText)
+    await new Promise((resolve) => setTimeout(resolve, 200))
+    // 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)
+    }, 0)
+    await new Promise((resolve) => setTimeout(resolve, 500))
+    return true
+  } catch (error) {
+    throw error
+  }
 }
 export async function simulateUserInput(element, value) {
-    // 设置值
-    if (element.tagName.toLowerCase() === 'textarea') {
-        element.focus()
-        element.value = value
-        element.blur()
-        return
-    }
+  // 设置值
+  if (element.tagName.toLowerCase() === 'textarea') {
+    element.focus()
     element.value = value
-
-    // 创建并触发 input 事件
-    const inputEvent = new Event('input', { bubbles: true })
-    element.dispatchEvent(inputEvent)
-
-    // 创建并触发 change 事件
-    const changeEvent = new Event('change', { bubbles: true })
-    element.dispatchEvent(changeEvent)
-
-    // 可选:如果表单使用了 React/Vue 等框架,可能还需要触发以下事件
-    element.dispatchEvent(new Event('blur'))
-    element.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }))
+    element.blur()
+    return
+  }
+  element.value = value
+
+  // 创建并触发 input 事件
+  const inputEvent = new Event('input', { bubbles: true })
+  element.dispatchEvent(inputEvent)
+
+  // 创建并触发 change 事件
+  const changeEvent = new Event('change', { bubbles: true })
+  element.dispatchEvent(changeEvent)
+
+  // 可选:如果表单使用了 React/Vue 等框架,可能还需要触发以下事件
+  element.dispatchEvent(new Event('blur'))
+  element.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }))
 }
-export function findLabelForInput (label)  {
-    let p = label.parentElement
-    for (let i = 0; i < 5; i++) {
-        const input = p.getElementsByTagName('input')
-        if (input.length > 0) {
-            return input[0]
-        }
-        p = p.parentElement
+export function findLabelForInput(label) {
+  let p = label.parentElement
+  for (let i = 0; i < 5; i++) {
+    const input = p.getElementsByTagName('input')
+    if (input.length > 0) {
+      return input[0]
     }
-    return null
+    p = p.parentElement
+  }
+  return null
 }
 export function findLabelForTag(label, tag) {
-    let p = label.parentElement
-    for (let i = 0; i < 10; i++) {
-        const input = p.getElementsByTagName('input')
-        if (input.length > 0) {
-            return input[0]
-        }
-        p = p.parentElement
+  let p = label.parentElement
+  for (let i = 0; i < 10; i++) {
+    const input = p.getElementsByTagName('input')
+    if (input.length > 0) {
+      return input[0]
     }
-    return null
+    p = p.parentElement
+  }
+  return null
 }
 export function findLabelForSpan(label) {
-    let p = label.parentElement
-    for (let i = 0; i < 10; i++) {
-        const span = p.getElementsByTagName('span')
-        if (span.length > 0) {
-            return [...span]
-        }
-        p = p.parentElement
+  let p = label.parentElement
+  for (let i = 0; i < 10; i++) {
+    const span = p.getElementsByTagName('span')
+    if (span.length > 0) {
+      return [...span]
     }
-    return null
+    p = p.parentElement
+  }
+  return null
 }
-export function 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
+export function findLabelForTextarea(label) {
+  let p = label.parentElement
+  for (let i = 0; i < 10; i++) {
+    const span = p.getElementsByTagName('textarea')
+    if (span.length > 0) {
+      return [...span]
     }
-    return null
+    p = p.parentElement
+  }
+  return null
 }
 export function getPageInfo() {
-    const favIconUrl = getFavicon()
-    return {
-        type: 'PAGE_INFO',
-        data: {
-            favIconUrl,
-            title: document.title,
-            url: window.location.href,
-            content: window.pageAnalyzer.analyzePage()
-        }
+  const favIconUrl = getFavicon()
+  return {
+    type: 'PAGE_INFO',
+    data: {
+      favIconUrl,
+      title: document.title,
+      url: window.location.href,
+      content: window.pageAnalyzer.analyzePage()
     }
+  }
 }
 export function getFavicon() {
-    // 尝试获取动态favicon
-    const iconLinks = Array.from(
-        document.querySelectorAll('link[rel*="icon"]')
-    )
-    const favIconUrl = iconLinks
-        .sort((a, b) => {
-            // 优先使用大尺寸图标
-            const sizeA = parseInt(a.sizes?.value) || 0
-            const sizeB = parseInt(b.sizes?.value) || 0
-            return sizeB - sizeA
-        })
-        .map((link) => link.href)
-        .find(Boolean)
-
-    if (favIconUrl) return favIconUrl
-
-    // 如果没有找到,返回网站根目录的favicon.ico
-    const url = new URL(window.location.href)
-    return `${url.protocol}//${url.hostname}/favicon.ico`
+  // 尝试获取动态favicon
+  const iconLinks = Array.from(document.querySelectorAll('link[rel*="icon"]'))
+  const favIconUrl = iconLinks
+    .sort((a, b) => {
+      // 优先使用大尺寸图标
+      const sizeA = parseInt(a.sizes?.value) || 0
+      const sizeB = parseInt(b.sizes?.value) || 0
+      return sizeB - sizeA
+    })
+    .map((link) => link.href)
+    .find(Boolean)
+
+  if (favIconUrl) return favIconUrl
+
+  // 如果没有找到,返回网站根目录的favicon.ico
+  const url = new URL(window.location.href)
+  return `${url.protocol}//${url.hostname}/favicon.ico`
 }

部分文件因为文件数量过多而无法显示