瀏覽代碼

格式化代码,统一格式

wzg 5 月之前
父節點
當前提交
ae0960785c

+ 2 - 1
.prettierrc

@@ -2,5 +2,6 @@
   "tabWidth": 2,
   "useTabs": false,
   "singleQuote": true,
-  "semi": false
+  "semi": false,
+  "trailingComma": "none"
 }

+ 2 - 1
package.json

@@ -12,7 +12,8 @@
     "zip": "wxt zip",
     "zip:firefox": "wxt zip -b firefox",
     "compile": "vue-tsc --noEmit",
-    "postinstall": "wxt prepare"
+    "postinstall": "wxt prepare",
+    "prettier": "prettier --write '**/*.{js,jsx,ts,tsx,css,scss,html,json}'"
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",

+ 1 - 1
postcss.config.js

@@ -3,4 +3,4 @@ export default {
     tailwindcss: {},
     autoprefixer: {}
   }
-}
+}

+ 1 - 1
shims-vue.d.ts

@@ -2,4 +2,4 @@ declare module '*.vue' {
   import { DefineComponent } from 'vue'
   const component: DefineComponent<{}, {}, any>
   export default component
-}
+}

+ 47 - 38
src/entrypoints/background.js

@@ -1,13 +1,10 @@
 export default defineBackground(() => {
-
   let currentTabId
   // Executed when background is loaded
   chrome.sidePanel
     .setPanelBehavior({ openPanelOnActionClick: true })
     .catch((error) => console.error(error))
   chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
-
-
     if (message.type === 'PAGE_INFO') {
       // 转发到侧边栏
       chrome.runtime.sendMessage({
@@ -23,17 +20,21 @@ export default defineBackground(() => {
 
         if (tabs.length === 0) return // 确保有活动标签页
         const tabId = tabs[0].id // 获取当前活动的 tabId
-        chrome.tabs.sendMessage(tabId, { type: 'GET_TAG_ACTION', data: message.data }, (response) => {
-          if (chrome.runtime.lastError) {
-            console.error('消息发送失败:', chrome.runtime.lastError.message)
-          } else {
-            console.log('收到 content script 响应:', response)
-            console.log(response, 998)
+        chrome.tabs.sendMessage(
+          tabId,
+          { type: 'GET_TAG_ACTION', data: message.data },
+          (response) => {
+            if (chrome.runtime.lastError) {
+              console.error('消息发送失败:', chrome.runtime.lastError.message)
+            } else {
+              console.log('收到 content script 响应:', response)
+              console.log(response, 998)
 
-            sendResponse(response)
+              sendResponse(response)
+            }
+            return true
           }
-          return true
-        })
+        )
       })
       return true
     }
@@ -41,15 +42,18 @@ export default defineBackground(() => {
       chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
         if (tabs.length === 0) return // 确保有活动标签页
         const tabId = tabs[0].id // 获取当前活动的 tabId
-        chrome.tabs.sendMessage(tabId, { type: 'GET_PAGE_FORM', data: 'Hello from background!' }, (response) => {
-          if (chrome.runtime.lastError) {
-            console.error('消息发送失败:', chrome.runtime.lastError.message)
-          } else {
-            console.log('收到 content script 响应:', response)
-            sendResponse(response)
+        chrome.tabs.sendMessage(
+          tabId,
+          { type: 'GET_PAGE_FORM', data: 'Hello from background!' },
+          (response) => {
+            if (chrome.runtime.lastError) {
+              console.error('消息发送失败:', chrome.runtime.lastError.message)
+            } else {
+              console.log('收到 content script 响应:', response)
+              sendResponse(response)
+            }
           }
-
-        })
+        )
       })
       return true
     }
@@ -57,15 +61,18 @@ export default defineBackground(() => {
       chrome.tabs.query({ active: true }, (tabs) => {
         if (tabs.length === 0) return // 确保有活动标签页
         const tabId = tabs[0].id // 获取当前活动的 tabId
-        chrome.tabs.sendMessage(tabId, { type: 'INPUT_FORM', data: message.data }, (response) => {
-          if (chrome.runtime.lastError) {
-            console.error('消息发送失败:', chrome.runtime.lastError.message)
-          } else {
-            console.log('收到 content script 响应:', response)
-            sendResponse(response.data)
+        chrome.tabs.sendMessage(
+          tabId,
+          { type: 'INPUT_FORM', data: message.data },
+          (response) => {
+            if (chrome.runtime.lastError) {
+              console.error('消息发送失败:', chrome.runtime.lastError.message)
+            } else {
+              console.log('收到 content script 响应:', response)
+              sendResponse(response.data)
+            }
           }
-
-        })
+        )
       })
       return true
     }
@@ -73,17 +80,20 @@ export default defineBackground(() => {
       chrome.tabs.query({ active: true }, (tabs) => {
         if (tabs.length === 0) return // 确保有活动标签页
         const tabId = tabs[0].id // 获取当前活动的 tabId
-        chrome.tabs.sendMessage(tabId, { type: 'GET_PAGE_INFO', data: 'Hello from background!' }, (response) => {
-          if (chrome.runtime.lastError) {
-            console.error('消息发送失败:', chrome.runtime.lastError.message)
-          } else {
-            console.log(response, 777)
+        chrome.tabs.sendMessage(
+          tabId,
+          { type: 'GET_PAGE_INFO', data: 'Hello from background!' },
+          (response) => {
+            if (chrome.runtime.lastError) {
+              console.error('消息发送失败:', chrome.runtime.lastError.message)
+            } else {
+              console.log(response, 777)
 
-            console.log('收到 content script 响应:', response)
-            sendResponse(response.data)
+              console.log('收到 content script 响应:', response)
+              sendResponse(response.data)
+            }
           }
-
-        })
+        )
       })
       return true
     }
@@ -111,7 +121,6 @@ export default defineBackground(() => {
       currentTabId = tab.id
     })
   })
-
 })
 
 // Allows users to open the side panel by clicking on the action toolbar icon

+ 142 - 103
src/entrypoints/content.js

@@ -10,93 +10,92 @@ export default defineContentScript({
     window.onload = () => {
       chrome.runtime.sendMessage(getPageInfo())
       console.log(document.title)
-
     }
     let form = null
     let formChildren = []
     let excelDataA = {}
-    chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
-      if (message.type === 'GET_PAGE_INFO') {
-        sendResponse({
-          data: getPageInfo()
-        })
-      }
-      let dom = null
-      if (message.type === 'GET_TAG_ACTION') {
-        const data = message.data
-        if (0) {
-          dom = document.getElementById(data.id)
-        } else {
-          dom = [...document.getElementsByTagName(data.tag.toLowerCase())]
-            // .filter(_ => _.className.includes(data.class))
-            .filter(_ => _.innerText.includes(data.text))[0]
+    chrome.runtime.onMessage.addListener(
+      async (message, sender, sendResponse) => {
+        if (message.type === 'GET_PAGE_INFO') {
+          sendResponse({
+            data: getPageInfo()
+          })
         }
-        console.log(dom)
-        if (!dom) {
-          sendResponse({ data: '未找到元素,请重试', statue: 'error' })
-          return
+        let dom = null
+        if (message.type === 'GET_TAG_ACTION') {
+          const data = message.data
+          if (0) {
+            dom = document.getElementById(data.id)
+          } else {
+            dom = [...document.getElementsByTagName(data.tag.toLowerCase())]
+              // .filter(_ => _.className.includes(data.class))
+              .filter((_) => _.innerText.includes(data.text))[0]
+          }
+          console.log(dom)
+          if (!dom) {
+            sendResponse({ data: '未找到元素,请重试', statue: 'error' })
+            return
+          }
+          // 添加红色边框,500ms后移除边框,然后点击
+          const originalBorder = dom.style.border
+          dom.style.border = '2px solid red'
+          setTimeout(() => {
+            dom.style.border = originalBorder
+            dom.click()
+          }, 1000)
+          sendResponse({ data: '完成' })
+          return true
         }
-        // 添加红色边框,500ms后移除边框,然后点击
-        const originalBorder = dom.style.border
-        dom.style.border = '2px solid red'
-        setTimeout(() => {
-          dom.style.border = originalBorder
-          dom.click()
-        }, 1000)
-        sendResponse({ data: '完成' })
-        return true
-      }
 
-      if (message.type === 'GET_PAGE_FORM') {
+        if (message.type === 'GET_PAGE_FORM') {
+          const len = document.querySelectorAll('form').length
+          if (document.title === '智能招采' && len === 1) {
+            sendResponse({
+              status: 'error',
+              message: '没有找到表单'
+            })
+            return
+          }
 
-        const len = document.querySelectorAll('form').length
-        if (document.title === '智能招采' && len === 1) {
+          if (len) {
+            const forms = document.querySelectorAll('form')
+            if (len > 1) {
+              form = forms[len - 1]
+            } else form = forms[0]
+            form.querySelectorAll('svg').forEach((el) => el.remove())
+            formChildren = [...form.elements]
+          }
+          // if (!form && document.querySelector("input")) {
+          //     const arr = []
+          //     const inputs = document.querySelectorAll("input")
+          //     formChildren = [...inputs]
+
+          //     for (const element of inputs) {
+          //         arr.push(element.outerHTML)
+          //     }
+          //     form = { outerHTML: JSON.stringify(arr) }
+          // }
+          if (!form) {
+            sendResponse({
+              status: 'error',
+              message: '没有找到表单'
+            })
+            return
+          }
           sendResponse({
-            status: 'error',
-            message: '没有找到表单'
+            status: 'ok',
+            data: form.outerHTML
           })
-          return
         }
-
-        if (len) {
-          const forms = document.querySelectorAll('form')
-          if (len > 1) {
-            form = forms[len - 1]
-          } else form = forms[0]
-          form.querySelectorAll('svg')
-            .forEach((el) => el.remove())
-          formChildren = [...form.elements]
-        }
-        // if (!form && document.querySelector("input")) {
-        //     const arr = []
-        //     const inputs = document.querySelectorAll("input")
-        //     formChildren = [...inputs]
-
-        //     for (const element of inputs) {
-        //         arr.push(element.outerHTML)
-        //     }
-        //     form = { outerHTML: JSON.stringify(arr) }
-        // }
-        if (!form) {
-          sendResponse({
-            status: 'error',
-            message: '没有找到表单'
-          })
-          return
+        if (message.type === 'INPUT_FORM') {
+          const { formData, excelData } = message.data
+          excelDataA = excelData
+          console.log(formData, excelDataA)
+          await handleFillInput(formData, 0)
         }
-        sendResponse({
-          status: 'ok',
-          data: form.outerHTML
-        })
-      }
-      if (message.type === 'INPUT_FORM') {
-        const { formData, excelData } = message.data
-        excelDataA = excelData
-        console.log(formData, excelDataA)
-        await handleFillInput(formData, 0)
+        return true
       }
-      return true
-    })
+    )
 
     function getPageInfo() {
       const favIconUrl = getFavicon()
@@ -139,11 +138,19 @@ export default defineContentScript({
       for (let i = 0; i < data.length; i++) {
         const item = data[i]
         if (item.findBy === 'id') {
-          const input = formChildren.find(child => child.id === item.findByValue)
-          if (item.type === 'text' || item.type === 'textarea' || item.type === 'number') {
+          const input = formChildren.find(
+            (child) => child.id === item.findByValue
+          )
+          if (
+            item.type === 'text' ||
+            item.type === 'textarea' ||
+            item.type === 'number'
+          ) {
             if (!input) {
               if (item.label) {
-                const label = [...form.getElementsByTagName('label')].find(label => label.innerText.includes(item.label))
+                const label = [...form.getElementsByTagName('label')].find(
+                  (label) => label.innerText.includes(item.label)
+                )
                 if (label) {
                   const input = findLabelForInput(label)
                   if (input) {
@@ -158,35 +165,48 @@ export default defineContentScript({
           }
           if (item.type === 'radio' || item.type === 'checkbox') {
             if (item.label) {
-
-              const label = [...form.getElementsByTagName('label')].find(label => label.innerText.includes(item.label))
+              const label = [...form.getElementsByTagName('label')].find(
+                (label) => label.innerText.includes(item.label)
+              )
               if (label) {
                 const span = findLabelForSpan(label)
-                span.forEach(span => {
-                  span.innerText === excelDataA[item.excelColumn] && span.click()
+                span.forEach((span) => {
+                  span.innerText === excelDataA[item.excelColumn] &&
+                    span.click()
                 })
               }
             }
           }
 
           if (item.type === 'date') {
-
-            const input = formChildren.find(child => child.id === item.findByValue)
+            const input = formChildren.find(
+              (child) => child.id === item.findByValue
+            )
             if (excelDataA[item.excelColumn]) {
-
-              await simulateCompleteUserAction(input, input, formatDate(excelDataA[item.excelColumn]), formatDate(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)
+          const input = formChildren.find(
+            (child) => child.placeholder === item.findByValue
+          )
           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))
+          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') {
@@ -194,10 +214,13 @@ export default defineContentScript({
 
             if (label) {
               const span = findLabelForSpan(label)
-              span.forEach(span => {
+              span.forEach((span) => {
                 console.log(span.innerText)
                 if (span.innerText) {
-                  if (span.innerText.includes(excelDataA[item.excelColumn]) || excelDataA[item.excelColumn].includes(span.innerText)) {
+                  if (
+                    span.innerText.includes(excelDataA[item.excelColumn]) ||
+                    excelDataA[item.excelColumn].includes(span.innerText)
+                  ) {
                     console.log(span)
                     span.click()
                   }
@@ -211,11 +234,23 @@ export default defineContentScript({
             console.log(input, excelDataA[item.excelColumn])
 
             if (input) {
-              await simulateCompleteUserAction(input, input, formatDate(excelDataA[item.excelColumn]), formatDate(excelDataA[item.excelColumn]))
+              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' || item.type === 'textarea') {
+          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)
@@ -302,7 +337,12 @@ export default defineContentScript({
       }
       return null
     }
-    const simulateCompleteUserAction = async (clickElement, inputElement, inputText, tdTitle) => {
+    const simulateCompleteUserAction = async (
+      clickElement,
+      inputElement,
+      inputText,
+      tdTitle
+    ) => {
       // 1. 模拟鼠标弹起事件
       const simulateMouseUp = (element) => {
         const mouseUpEvent = new MouseEvent('mouseup', {
@@ -322,7 +362,7 @@ export default defineContentScript({
       const simulateTyping = async (element, text, delay = 50) => {
         element.focus()
         for (let char of text) {
-          await new Promise(resolve => setTimeout(resolve, delay))
+          await new Promise((resolve) => setTimeout(resolve, delay))
 
           // 按键按下
           const keydownEvent = new KeyboardEvent('keydown', {
@@ -399,27 +439,27 @@ export default defineContentScript({
 
         // 1. 触发鼠标弹起
         simulateMouseUp(clickElement)
-        await new Promise(resolve => setTimeout(resolve, 100))
+        await new Promise((resolve) => setTimeout(resolve, 100))
 
         // 2. 模拟键盘输入
         await simulateTyping(inputElement, inputText)
-        await new Promise(resolve => setTimeout(resolve, 200))
+        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    // 允许事件被取消
+          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))
+        await new Promise((resolve) => setTimeout(resolve, 500))
         return true
       } catch (error) {
         throw error
@@ -437,6 +477,5 @@ export default defineContentScript({
 
       return `${year}-${month}-${day}`
     }
-
   }
 })

+ 20 - 18
src/entrypoints/options/index.html

@@ -1,21 +1,23 @@
 <!doctype html>
 <html lang="en">
-<head>
-  <meta charset="UTF-8" />
-  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <title>Default Popup Title</title>
-  <meta name="manifest.type" content="browser_action" />
-</head>
-<body>
-<div id="app"></div>
-<script>
-  let timer = null
-  chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
-    // The callback for runtime.onMessage must return falsy if we're not sending a response
-    if (message.type === 'updateSidePanel') sendResponse(111)
-    return
-  })
-</script>
-<script type="module" src="./main.ts"></script>
-</body>
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Default Popup Title</title>
+    <meta name="manifest.type" content="browser_action" />
+  </head>
+  <body>
+    <div id="app"></div>
+    <script>
+      let timer = null
+      chrome.runtime.onMessage.addListener(
+        async (message, sender, sendResponse) => {
+          // The callback for runtime.onMessage must return falsy if we're not sending a response
+          if (message.type === 'updateSidePanel') sendResponse(111)
+          return
+        }
+      )
+    </script>
+    <script type="module" src="./main.ts"></script>
+  </body>
 </html>

+ 60 - 60
src/entrypoints/options/style.css

@@ -1,101 +1,101 @@
 :root {
-    font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-    line-height: 1.5;
-    font-weight: 400;
-
-    color-scheme: light dark;
-    color: rgba(255, 255, 255, 0.87);
-    background-color: #242424;
-
-    font-synthesis: none;
-    text-rendering: optimizeLegibility;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-    -webkit-text-size-adjust: 100%;
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
+
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-text-size-adjust: 100%;
 }
 
 * {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
 }
 
 a {
-    font-weight: 500;
-    color: #646cff;
-    text-decoration: inherit;
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
 }
 
 a:hover {
-    color: #535bf2;
+  color: #535bf2;
 }
 
 a {
-    font-weight: 500;
-    color: #646cff;
-    text-decoration: inherit;
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
 }
 
 a:hover {
-    color: #535bf2;
+  color: #535bf2;
 }
 
 body {
-    margin: 0;
-    display: flex;
-    place-items: center;
-    min-width: 320px;
-    min-height: 100vh;
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
 }
 
 h1 {
-    font-size: 3.2em;
-    line-height: 1.1;
+  font-size: 3.2em;
+  line-height: 1.1;
 }
 
 button {
-    border-radius: 8px;
-    border: 1px solid transparent;
-    padding: 0.6em 1.2em;
-    font-size: 1em;
-    font-weight: 500;
-    font-family: inherit;
-    background-color: #1a1a1a;
-    cursor: pointer;
-    transition: border-color 0.25s;
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
 }
 
 button:hover {
-    border-color: #646cff;
+  border-color: #646cff;
 }
 
 button:focus,
 button:focus-visible {
-    outline: 4px auto -webkit-focus-ring-color;
+  outline: 4px auto -webkit-focus-ring-color;
 }
 
 .card {
-    padding: 2em;
+  padding: 2em;
 }
 
 #app {
-    margin: auto;
-    max-width: 1280px;
-    width: 100%;
-    height: 100vh;
+  margin: auto;
+  max-width: 1280px;
+  width: 100%;
+  height: 100vh;
 }
 
 @media (prefers-color-scheme: light) {
-    :root {
-        color: #213547;
-        background-color: #ffffff;
-    }
-
-    a:hover {
-        color: #747bff;
-    }
-
-    button {
-        background-color: #f9f9f9;
-    }
-}
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+
+  a:hover {
+    color: #747bff;
+  }
+
+  button {
+    background-color: #f9f9f9;
+  }
+}

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

@@ -10,7 +10,7 @@
   display: flex;
   flex-direction: column;
   border: 1px solid #dcdfe6;
-  background-color: #F0F0F0;
+  background-color: #f0f0f0;
   //background-color: #ffffff;
 }
 
@@ -64,7 +64,7 @@
 .content {
   padding: 12px;
   min-height: 40px;
-  background-color: #F0F4F8;
+  background-color: #f0f4f8;
   border-radius: 12px;
   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
   word-break: break-all;
@@ -132,7 +132,6 @@
   color: #409eff;
 }
 
-
 @keyframes scrollTitle {
   0% {
     transform: translateX(0);
@@ -193,7 +192,6 @@
   }
 
   @keyframes pulse {
-
     0%,
     100% {
       transform: scale(0.8);
@@ -207,7 +205,6 @@
   }
 }
 
-
 .input-area {
   padding: 8px 10px;
   color: black;
@@ -250,7 +247,6 @@
       flex-direction: column;
     }
 
-
     .title-scroller {
       display: inline-block;
       white-space: nowrap;
@@ -277,7 +273,6 @@
       border-radius: 50%;
       z-index: 1;
     }
-
   }
 
   .card-content:hover {
@@ -320,4 +315,4 @@
 .input-area :deep(.el-textarea__inner:focus) {
   border-color: #409eff;
   box-shadow: none;
-}
+}

+ 1 - 2
src/entrypoints/sidepanel/hook/useAutoResizeTextarea.ts

@@ -1,7 +1,6 @@
 import { ref, watch, onMounted } from 'vue'
 
 export function useAutoResizeTextarea(textareaRef: any, text: any) {
-
   const adjustHeight = () => {
     const textarea = textareaRef.value.ref
     textarea.style.height = 'auto' // 重置高度
@@ -20,4 +19,4 @@ export function useAutoResizeTextarea(textareaRef: any, text: any) {
   onMounted(() => {
     adjustHeight() // 初始化时调整高度
   })
-}
+}

+ 85 - 78
src/entrypoints/sidepanel/hook/useIndexedDB.ts

@@ -1,18 +1,18 @@
 import { ref, readonly } from 'vue'
 
 interface IndexedDBStoreConfig {
-  name: string;
-  keyPath: string;
+  name: string
+  keyPath: string
   indexes?: Array<{
     name: string
     keyPath: string | string[]
     options?: IDBIndexParameters
-  }>;
+  }>
 }
 
 interface IndexedDBConfig {
-  dbName: string;
-  version?: number;
+  dbName: string
+  version?: number
 }
 
 // 全局存储Store配置
@@ -47,25 +47,19 @@ export function useIndexedDB(config: IndexedDBConfig) {
       const request = indexedDB.open(dbName.value, currentVersion.value)
 
       request.onupgradeneeded = (event: IDBVersionChangeEvent) => {
-
         const db = (event.target as IDBOpenDBRequest).result
 
         // 从注册中心获取所有Store配置
         const stores = Array.from(storeRegistry.values())
 
-        stores.forEach(storeConfig => {
+        stores.forEach((storeConfig) => {
           if (!db.objectStoreNames.contains(storeConfig.name)) {
-            const objectStore = db.createObjectStore(
-              storeConfig.name,
-              { keyPath: storeConfig.keyPath }
-            )
-
-            storeConfig.indexes?.forEach(index => {
-              objectStore.createIndex(
-                index.name,
-                index.keyPath,
-                index.options
-              )
+            const objectStore = db.createObjectStore(storeConfig.name, {
+              keyPath: storeConfig.keyPath
+            })
+
+            storeConfig.indexes?.forEach((index) => {
+              objectStore.createIndex(index.name, index.keyPath, index.options)
             })
           }
         })
@@ -132,70 +126,83 @@ export function useIndexedDB(config: IndexedDBConfig) {
         }
 
         // 执行操作
-        operation(objectStore).then((result) => {
-          // 操作成功,返回结果
-          resolve(result)
-        }).catch((error) => {
-          // 操作失败,拒绝 Promise
-          reject(error)
-        })
+        operation(objectStore)
+          .then((result) => {
+            // 操作成功,返回结果
+            resolve(result)
+          })
+          .catch((error) => {
+            // 操作失败,拒绝 Promise
+            reject(error)
+          })
       })
     }
 
-
     return {
-      add: (data: T) => executeWithTransaction(
-        'readwrite',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.add(data)
-          request.onsuccess = () => resolve(request.result as IDBValidKey)
-          request.onerror = () => reject(request.error)
-        })
-      ),
-
-      get: (key: IDBValidKey) => executeWithTransaction(
-        'readonly',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.get(key)
-          request.onsuccess = () => resolve(request.result as T | undefined)
-          request.onerror = () => reject(request.error)
-        })
-      ),
-
-      update: (data: T) => executeWithTransaction(
-        'readwrite',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.put(data)
-          request.onsuccess = () => resolve(request.result as IDBValidKey)
-          request.onerror = () => reject(request.error)
-        })
-      ),
-
-      delete: (key: IDBValidKey) => executeWithTransaction(
-        'readwrite',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.delete(key)
-          request.onsuccess = () => resolve(undefined)
-          request.onerror = () => reject(request.error)
-        })
-      ),
-
-      getAll: () => executeWithTransaction(
-        'readonly',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.getAll()
-          request.onsuccess = () => resolve(request.result as T[])
-          request.onerror = () => reject(request.error)
-        })
-      ),
-      clearAll: () => executeWithTransaction(
-        'readwrite',
-        (store) => new Promise((resolve, reject) => {
-          const request = store.clear()
-          request.onsuccess = () => resolve(true)
-          request.onerror = () => reject(request.error)
-        })
-      )
+      add: (data: T) =>
+        executeWithTransaction(
+          'readwrite',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.add(data)
+              request.onsuccess = () => resolve(request.result as IDBValidKey)
+              request.onerror = () => reject(request.error)
+            })
+        ),
+
+      get: (key: IDBValidKey) =>
+        executeWithTransaction(
+          'readonly',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.get(key)
+              request.onsuccess = () => resolve(request.result as T | undefined)
+              request.onerror = () => reject(request.error)
+            })
+        ),
+
+      update: (data: T) =>
+        executeWithTransaction(
+          'readwrite',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.put(data)
+              request.onsuccess = () => resolve(request.result as IDBValidKey)
+              request.onerror = () => reject(request.error)
+            })
+        ),
+
+      delete: (key: IDBValidKey) =>
+        executeWithTransaction(
+          'readwrite',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.delete(key)
+              request.onsuccess = () => resolve(undefined)
+              request.onerror = () => reject(request.error)
+            })
+        ),
+
+      getAll: () =>
+        executeWithTransaction(
+          'readonly',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.getAll()
+              request.onsuccess = () => resolve(request.result as T[])
+              request.onerror = () => reject(request.error)
+            })
+        ),
+      clearAll: () =>
+        executeWithTransaction(
+          'readwrite',
+          (store) =>
+            new Promise((resolve, reject) => {
+              const request = store.clear()
+              request.onsuccess = () => resolve(true)
+              request.onerror = () => reject(request.error)
+            })
+        )
     }
   }
 

+ 72 - 48
src/entrypoints/sidepanel/hook/useMsg.ts

@@ -3,7 +3,11 @@ 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 {
+  getFormKey,
+  buildObjPrompt,
+  getFileSummaryPrompt
+} from '@/utils/ai-service'
 import { ElMessage } from 'element-plus'
 import { getPageInfo } from '../utils/index.js'
 import { mockData, mockData2 } from '../mock'
@@ -78,16 +82,17 @@ export function useMsg(scrollbar?: any) {
   // 发送消息
   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
-      })
+      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 {
@@ -119,20 +124,32 @@ export function useMsg(scrollbar?: any) {
     str = input
     console.log(str)
     const pageInfo = await getPageInfo()
-    await new Promise((res: any) => setTimeout(() => {
-      res()
-    }, 2000))
+    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] })
+        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 })
+      ElMessage({
+        message: '未找到标签,请重试',
+        type: 'error',
+        duration: 4 * 1000,
+        grouping: true
+      })
       obj.content = '未找到标签,请重试'
       type.value = ''
       return
@@ -141,42 +158,49 @@ export function useMsg(scrollbar?: any) {
   }
 
   async function handleClick(res: any, msgObj: any) {
-    await new Promise(resolve => setTimeout(resolve, 2000))
+    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)
+    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 (type.value === '2') {
-            await new Promise((resolve, reject) => {
-              setTimeout(() => {
-                resolve(1)
-              }, 2000)
-            })
-            msgObj.content = `请上传数据`
-            ElMessage({ message: '请上传数据', type: 'success', duration: 4 * 1000, grouping: true })
+          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 {
-            msgObj.content = `执行完毕`
+            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
       }
-      return true
-    })
-
+    )
   }
 
   /**
@@ -210,12 +234,12 @@ export function useMsg(scrollbar?: any) {
       })
     } else {
       history = messages.value
-        .filter((item: any) => item.addToHistory).slice(-20)
+        .filter((item: any) => item.addToHistory)
+        .slice(-20)
         .map((item: any) => ({
           role: item.isSelf ? 'user' : 'system',
           content: item.rawContent
         }))
-
     }
 
     messages.value.push(obj)

+ 10 - 10
src/entrypoints/sidepanel/index.html

@@ -1,13 +1,13 @@
 <!doctype html>
 <html lang="en">
-<head>
-  <meta charset="UTF-8" />
-  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <title>Default Popup Title</title>
-  <meta name="manifest.type" content="browser_action" />
-</head>
-<body>
-<div id="app"></div>
-<script type="module" src="./main.ts"></script>
-</body>
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Default Popup Title</title>
+    <meta name="manifest.type" content="browser_action" />
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="./main.ts"></script>
+  </body>
 </html>

+ 1 - 0
src/entrypoints/sidepanel/main.ts

@@ -6,6 +6,7 @@ import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 import locale from 'element-plus/es/locale/lang/zh-cn' // 中文语言
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import store from '@/store/index'
 
 const app = createApp(App)
 app.use(ElementPlus, { locale: locale, size: 'small' })

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

@@ -16,7 +16,6 @@ export const options = [
     ]
   }
 
-
   // {
   //     value: 'DeepSeek-R1',
   //     label: 'DeepSeek-R1',
@@ -44,7 +43,8 @@ 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>',
+    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: '是'
@@ -53,7 +53,8 @@ export const mockData = [
     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>',
+    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: '否'
@@ -62,7 +63,8 @@ export const mockData = [
     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>',
+    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: '是'
@@ -74,7 +76,8 @@ 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>',
+    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: '是'
@@ -83,7 +86,8 @@ export const mockData2 = [
     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>',
+    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: '否'
@@ -92,7 +96,8 @@ export const mockData2 = [
     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>',
+    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: '是'

+ 42 - 41
src/entrypoints/sidepanel/style.css

@@ -3,80 +3,81 @@
 @tailwind utilities;
 
 :root {
-    font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-    line-height: 1.5;
-    font-weight: 400;
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
 
-    color-scheme: light dark;
-    color: rgba(255, 255, 255, 0.87);
-    background-color: #242424;
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
 
-    font-synthesis: none;
-    text-rendering: optimizeLegibility;
-    -webkit-font-smoothing: antialiased;
-    -moz-osx-font-smoothing: grayscale;
-    -webkit-text-size-adjust: 100%;
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-text-size-adjust: 100%;
 }
 
 * {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
 }
 
-html, body {
-    height: 100%;
-    min-width: 320px;
-    min-height: 100%;
-    /*background-color: #ffffff;*/
-    background-color: #F6F6F8;
-    font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
+html,
+body {
+  height: 100%;
+  min-width: 320px;
+  min-height: 100%;
+  /*background-color: #ffffff;*/
+  background-color: #f6f6f8;
+  font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
 }
 
 #app {
-    margin: auto;
-    max-width: 1280px;
-    width: 100%;
-    height: 100%;
-    padding: 4px;
+  margin: auto;
+  max-width: 1280px;
+  width: 100%;
+  height: 100%;
+  padding: 4px;
 }
 
 /* 自定义滚动条样式 */
 ::-webkit-scrollbar {
-    width: 8px;
-    height: 8px;
+  width: 8px;
+  height: 8px;
 }
 
 ::-webkit-scrollbar-track {
-    background: #f1f1f1;
-    border-radius: 4px;
+  background: #f1f1f1;
+  border-radius: 4px;
 }
 
 ::-webkit-scrollbar-thumb {
-    background: #888;
-    border-radius: 4px;
+  background: #888;
+  border-radius: 4px;
 }
 
 ::-webkit-scrollbar-thumb:hover {
-    background: #555;
+  background: #555;
 }
 
 /* 为代码块滚动条设置暗色主题 */
 pre::-webkit-scrollbar-track {
-    background: #343842;
+  background: #343842;
 }
 
 pre::-webkit-scrollbar-thumb {
-    background: #666;
+  background: #666;
 }
 
 pre::-webkit-scrollbar-thumb:hover {
-    background: #888;
+  background: #888;
 }
 
 @media (prefers-color-scheme: light) {
-    :root {
-        color: #213547;
-        background-color: #ffffff;
-    }
-}
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+}

+ 12 - 9
src/entrypoints/sidepanel/utils/index.js

@@ -2,16 +2,19 @@ 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)
+    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) => {

+ 1 - 1
src/store/index.ts

@@ -2,4 +2,4 @@ import { createPinia } from 'pinia'
 
 const store = createPinia()
 
-export default store
+export default store

+ 90 - 70
src/store/modules/indexedDB.ts

@@ -1,18 +1,18 @@
 import { defineStore } from 'pinia'
 
 interface IndexedDBStoreConfig {
-  name: string;
-  keyPath: string;
+  name: string
+  keyPath: string
   indexes?: Array<{
     name: string
     keyPath: string | string[]
     options?: IDBIndexParameters
-  }>;
+  }>
 }
 
 interface IndexedDBConfig {
-  dbName: string;
-  version?: number;
+  dbName: string
+  version?: number
 }
 
 export const useIndexedDBStore = defineStore('indexedDB', {
@@ -54,10 +54,9 @@ export const useIndexedDBStore = defineStore('indexedDB', {
           const stores = Array.from(this.storeRegistry.values())
           stores.forEach((storeConfig: any) => {
             if (!db.objectStoreNames.contains(storeConfig.name)) {
-              const objectStore = db.createObjectStore(
-                storeConfig.name,
-                { keyPath: storeConfig.keyPath }
-              )
+              const objectStore = db.createObjectStore(storeConfig.name, {
+                keyPath: storeConfig.keyPath
+              })
               storeConfig.indexes?.forEach((index: any) => {
                 objectStore.createIndex(
                   index.name,
@@ -104,9 +103,16 @@ export const useIndexedDBStore = defineStore('indexedDB', {
         }
       }
 
-      const executeWithTransaction = async (mode: IDBTransactionMode, operation: any) => {
+      const executeWithTransaction = async (
+        mode: IDBTransactionMode,
+        operation: any
+      ) => {
         await verifyStore()
-        if (!this.db) await this.openDB({ dbName: this.dbName, version: this.currentVersion })
+        if (!this.db)
+          await this.openDB({
+            dbName: this.dbName,
+            version: this.currentVersion
+          })
 
         return new Promise((resolve, reject) => {
           // this.db;
@@ -124,69 +130,83 @@ export const useIndexedDBStore = defineStore('indexedDB', {
           }
 
           // 执行操作
-          operation(objectStore).then((result: any) => {
-            // 操作成功,返回结果
-            resolve(result)
-          }).catch((error: any) => {
-            // 操作失败,拒绝 Promise
-            reject(error)
-          })
+          operation(objectStore)
+            .then((result: any) => {
+              // 操作成功,返回结果
+              resolve(result)
+            })
+            .catch((error: any) => {
+              // 操作失败,拒绝 Promise
+              reject(error)
+            })
         })
       }
 
       return {
-        add: (data: any) => executeWithTransaction(
-          'readwrite',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.add(data)
-            request.onsuccess = () => resolve(request.result as IDBValidKey)
-            request.onerror = () => reject(request.error)
-          })
-        ),
-
-        get: (key: IDBValidKey) => executeWithTransaction(
-          'readonly',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.get(key)
-            request.onsuccess = () => resolve(request.result as any)
-            request.onerror = () => reject(request.error)
-          })
-        ),
-
-        update: (data: any) => executeWithTransaction(
-          'readwrite',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.put(data)
-            request.onsuccess = () => resolve(request.result as IDBValidKey)
-            request.onerror = () => reject(request.error)
-          })
-        ),
-
-        delete: (key: IDBValidKey) => executeWithTransaction(
-          'readwrite',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.delete(key)
-            request.onsuccess = () => resolve(undefined)
-            request.onerror = () => reject(request.error)
-          })
-        ),
-
-        getAll: () => executeWithTransaction(
-          'readonly',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.getAll()
-            request.onsuccess = () => resolve(request.result as any[])
-            request.onerror = () => reject(request.error)
-          })
-        ),
-        clearAll: () => executeWithTransaction(
-          'readwrite',
-          (store: any) => new Promise((resolve, reject) => {
-            const request = store.clear()
-            request.onsuccess = () => resolve(true)
-            request.onerror = () => reject(request.error)
-          })
-        )
+        add: (data: any) =>
+          executeWithTransaction(
+            'readwrite',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.add(data)
+                request.onsuccess = () => resolve(request.result as IDBValidKey)
+                request.onerror = () => reject(request.error)
+              })
+          ),
+
+        get: (key: IDBValidKey) =>
+          executeWithTransaction(
+            'readonly',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.get(key)
+                request.onsuccess = () => resolve(request.result as any)
+                request.onerror = () => reject(request.error)
+              })
+          ),
+
+        update: (data: any) =>
+          executeWithTransaction(
+            'readwrite',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.put(data)
+                request.onsuccess = () => resolve(request.result as IDBValidKey)
+                request.onerror = () => reject(request.error)
+              })
+          ),
+
+        delete: (key: IDBValidKey) =>
+          executeWithTransaction(
+            'readwrite',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.delete(key)
+                request.onsuccess = () => resolve(undefined)
+                request.onerror = () => reject(request.error)
+              })
+          ),
+
+        getAll: () =>
+          executeWithTransaction(
+            'readonly',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.getAll()
+                request.onsuccess = () => resolve(request.result as any[])
+                request.onerror = () => reject(request.error)
+              })
+          ),
+        clearAll: () =>
+          executeWithTransaction(
+            'readwrite',
+            (store: any) =>
+              new Promise((resolve, reject) => {
+                const request = store.clear()
+                request.onsuccess = () => resolve(true)
+                request.onerror = () => reject(request.error)
+              })
+          )
       }
     }
   }

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

@@ -1,13 +1,11 @@
 import { defineStore } from 'pinia'
 import { useIndexedDBStore } from './indexedDB'
 
-
 export const useMsgStore = defineStore('msg', {
   state: () => ({
     msgUuid: <string>'',
     messages: <any[]>[],
     pageInfoList: []
-
   }),
   actions: {
     addMsg(msg: any) {
@@ -24,6 +22,5 @@ export const useMsgStore = defineStore('msg', {
       const { useStore } = useIndexedDBStore()
       useStore(this.msgUuid).add(msg)
     }
-
   }
-})
+})

+ 33 - 33
src/utils/ai-service.js

@@ -2,24 +2,21 @@ import OpenAI from 'openai'
 import hljs from 'highlight.js'
 import 'highlight.js/styles/atom-one-dark.css' // 导入一个暗色主题样式
 
-export const openai = new OpenAI(
-  {
-    // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",
-    apiKey: 'sk-e9855234f47346049809ce23ed3ebe3f',
-    baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
-    dangerouslyAllowBrowser: true
-  }
-)
+export const openai = new OpenAI({
+  // 若没有配置环境变量,请用百炼API Key将下行替换为:apiKey: "sk-xxx",
+  apiKey: 'sk-e9855234f47346049809ce23ed3ebe3f',
+  baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
+  dangerouslyAllowBrowser: true
+})
 
 export async function getFileContent(data) {
   try {
     const res = await fetch('http://180.76.147.97:18899/upload_file', {
       method: 'post',
       body: data
-    }).then(res => res.json())
+    }).then((res) => res.json())
     return res
-  } catch (error) {
-  }
+  } catch (error) {}
 }
 
 export async function getFormKey(data) {
@@ -27,10 +24,9 @@ export async function getFormKey(data) {
     const res = await fetch('http://180.76.147.97:18899/uie', {
       method: 'post',
       body: JSON.stringify(data)
-    }).then(res => res.json())
+    }).then((res) => res.json())
     return res
-  } catch (error) {
-  }
+  } catch (error) {}
 }
 
 export async function hepl(data) {
@@ -38,11 +34,9 @@ export async function hepl(data) {
     const res = await fetch(' http://180.76.147.97:18899/test', {
       method: 'post',
       body: JSON.stringify(data)
-    }).then(res => res.json())
+    }).then((res) => res.json())
     return res
-  } catch (error) {
-
-  }
+  } catch (error) {}
 }
 
 export async function sendMessage(message) {
@@ -51,7 +45,7 @@ export async function sendMessage(message) {
     const controller = new AbortController()
     const a = +new Date()
     const response = await openai.chat.completions.create({
-      model: 'qwen-plus',  //模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
+      model: 'qwen-plus', //模型列表:https://help.aliyun.com/zh/model-studio/getting-started/models
       messages: message,
       stream: true
     })
@@ -60,15 +54,16 @@ export async function sendMessage(message) {
     console.error('API call failed:', error)
     return '[FILE]'
   } finally {
-
   }
 }
 
 function formatMessages(currentMessage, Summary, html) {
-  const messages = [{
-    role: 'user',
-    content: currentMessage
-  }]
+  const messages = [
+    {
+      role: 'user',
+      content: currentMessage
+    }
+  ]
   // const messages = this.context.filter(msg => msg.role === 'user').map((msg) => ({
   //     role: msg.role,
   //     content: msg.content,
@@ -81,10 +76,11 @@ function formatMessages(currentMessage, Summary, html) {
   // }
   // messages.push()
   if (Summary) return messages
-  if (html) messages.unshift({
-    role: 'user',
-    content: `页面主要内容${html}`
-  })
+  if (html)
+    messages.unshift({
+      role: 'user',
+      content: `页面主要内容${html}`
+    })
   return messages
 }
 
@@ -166,7 +162,11 @@ export function formatMessage(text) {
         return `<pre style="background-color: #282c34; padding: 10px; border-radius: 5px; overflow-x: auto;"><code class="hljs">${highlightedCode}</code></pre>`
       })
       // 处理行内代码
-      .replace(/`([^`]+)`/g, (match, code) => `<code style="background-color: #f0f0f0; padding: 2px 4px; border-radius: 3px;">${escapeHtml(code)}</code>`)
+      .replace(
+        /`([^`]+)`/g,
+        (match, code) =>
+          `<code style="background-color: #f0f0f0; padding: 2px 4px; border-radius: 3px;">${escapeHtml(code)}</code>`
+      )
       // 处理标题 (h1 ~ h6)
       .replace(/^#{1,6}\s+(.+)$/gm, (match, content) => {
         const level = match.trim().split('#').length - 1
@@ -181,7 +181,10 @@ export function formatMessage(text) {
       // 处理斜体
       .replace(/\*(.*?)\*/g, '<em>$1</em>')
       // 处理链接
-      .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
+      .replace(
+        /\[([^\]]+)\]\(([^)]+)\)/g,
+        '<a href="$2" target="_blank">$1</a>'
+      )
       // 处理无序列表:先将每行列表项转换为 <li> 标签
       .replace(/^[*-]\s+(.+)$/gm, '<li>$1</li>')
       // 然后将连续的 <li> 包裹在 <ul> 中
@@ -202,7 +205,6 @@ export function buildExcelUnderstandingPrompt(data, fileName, pageInfo) {
   //     return "这是一个空的Excel文件,请检查文件内容。";
   // }
 
-
   // const headers = data[0];
   // const rows = data.slice(1);
   // data[0].forEach((header, i) => {
@@ -229,8 +231,6 @@ ${pageInfo}
 
 // 5. 如果表单项有label标签,同时返回label, 通过label字段告诉我label元素的文本
 export function buildObjPrompt(obj, pageInfo) {
-
-
   return `我将向你展示一个对象和一个form表单。请帮我理解这些数据:
 
 对象:${JSON.stringify(obj)}

+ 13 - 7
src/utils/dynamicTime.js

@@ -14,12 +14,18 @@ const dd = [
 export function useTime(format = 'HH:mm:ss') {
   const interval = ref(null)
   const globalTime = ref(null)
-  onMounted(() => interval.value = setInterval(() => {
-    const l = dd.find(e => {
-      return e.value === moment(new Date()).format('dddd')
-    }).label
-    globalTime.value = moment(new Date()).format('YYYY年MM月DD日 ') + l + moment(new Date()).format(format)
-  }, 1000))
+  onMounted(
+    () =>
+      (interval.value = setInterval(() => {
+        const l = dd.find((e) => {
+          return e.value === moment(new Date()).format('dddd')
+        }).label
+        globalTime.value =
+          moment(new Date()).format('YYYY年MM月DD日 ') +
+          l +
+          moment(new Date()).format(format)
+      }, 1000))
+  )
   onUnmounted(() => clearInterval(interval.value))
   return globalTime
-}
+}

+ 4 - 4
src/utils/errorCode.js

@@ -1,6 +1,6 @@
 export default {
-  '401': '认证失败,无法访问系统资源',
-  '403': '当前操作没有权限',
-  '404': '访问资源不存在',
-  'default': '系统未知错误,请反馈给管理员'
+  401: '认证失败,无法访问系统资源',
+  403: '当前操作没有权限',
+  404: '访问资源不存在',
+  default: '系统未知错误,请反馈给管理员'
 }

+ 48 - 23
src/utils/page-analyzer.js

@@ -12,9 +12,7 @@ export default class PageAnalyzer {
     try {
       // 检查Readability是否可用
       if (typeof Readability === 'undefined') {
-        console.warn(
-          'Readability not loaded, falling back to basic extraction'
-        )
+        console.warn('Readability not loaded, falling back to basic extraction')
         return this.fallbackAnalysis(form)
       }
 
@@ -73,36 +71,38 @@ export default class PageAnalyzer {
       // content
       //     .querySelectorAll("script, style, iframe, nav, header, footer,svg")
       //     .forEach((el) => el.remove());
-
-
     }
-
-
   }
-};
+}
 
 export function cleanPage(body) {
-
   // 移除所有行内样式
   const elementsWithInlineStyle = body.querySelectorAll('[style]')
-  elementsWithInlineStyle.forEach(element => {
+  elementsWithInlineStyle.forEach((element) => {
     element.removeAttribute('style')
   })
 
   // 移除所有注释节点
   const comments = []
-  const walk = document.createTreeWalker(body, NodeFilter.SHOW_COMMENT, null, false)
+  const walk = document.createTreeWalker(
+    body,
+    NodeFilter.SHOW_COMMENT,
+    null,
+    false
+  )
   while (walk.nextNode()) {
     comments.push(walk.currentNode)
   }
-  comments.forEach(comment => comment.remove())
+  comments.forEach((comment) => comment.remove())
 
   // 移除所有SVG图标
-  body.querySelectorAll('script, style, iframe, nav, header, footer,svg').forEach(svg => svg.remove())
+  body
+    .querySelectorAll('script, style, iframe, nav, header, footer,svg')
+    .forEach((svg) => svg.remove())
 
   // 移除所有图片的src属性
   const images = body.querySelectorAll('img')
-  images.forEach(img => {
+  images.forEach((img) => {
     img.removeAttribute('src')
   })
 
@@ -114,12 +114,36 @@ export function cleanPage(body) {
   // iframes.forEach(iframe => iframe.remove());
 
   const ads = body.querySelectorAll('.ad, .advertisement, .ads')
-  ads.forEach(ad => ad.remove())
+  ads.forEach((ad) => ad.remove())
 
   const regex = /\s+/g
 
   // 定义标准 HTML 属性集合 排除id class style href src target
-  const standardAttributes = new Set(['alt', 'title', 'type', 'value', 'name', 'placeholder', 'disabled', 'checked', 'selected', 'readonly', 'required', 'maxlength', 'min', 'max', 'step', 'pattern', 'autocomplete', 'autofocus', 'multiple', 'rows', 'cols', 'rel', 'aria-*'])
+  const standardAttributes = new Set([
+    'alt',
+    'title',
+    'type',
+    'value',
+    'name',
+    'placeholder',
+    'disabled',
+    'checked',
+    'selected',
+    'readonly',
+    'required',
+    'maxlength',
+    'min',
+    'max',
+    'step',
+    'pattern',
+    'autocomplete',
+    'autofocus',
+    'multiple',
+    'rows',
+    'cols',
+    'rel',
+    'aria-*'
+  ])
 
   // 创建一个临时容器
   const temp = document.createElement('div')
@@ -128,7 +152,7 @@ export function cleanPage(body) {
   // 递归删除空标签
   function removeEmpty(element) {
     const children = Array.from(element.children)
-    children.forEach(child => {
+    children.forEach((child) => {
       removeEmpty(child) // 递归处理子元素
     })
 
@@ -143,18 +167,21 @@ export function cleanPage(body) {
 
   // 删除 <link> 标签
   const linkTags = temp.querySelectorAll('link')
-  linkTags.forEach(link => {
+  linkTags.forEach((link) => {
     link.remove()
   })
 
   // 遍历所有元素
   const elements = temp.querySelectorAll('*')
-  elements.forEach(element => {
+  elements.forEach((element) => {
     // 获取所有属性
     const attributes = Array.from(element.attributes)
-    attributes.forEach(attr => {
+    attributes.forEach((attr) => {
       // 如果属性不是标准属性,则移除
-      if (!standardAttributes.has(attr.name) && !attr.name.startsWith('aria-')) {
+      if (
+        !standardAttributes.has(attr.name) &&
+        !attr.name.startsWith('aria-')
+      ) {
         element.removeAttribute(attr.name)
       }
     })
@@ -170,5 +197,3 @@ export function cleanPage(body) {
   // content.outerHTML.trim().replace(/\s+/g, " ");
   return cleanedHtml
 }
-
-

+ 3 - 2
src/utils/rem.js

@@ -1,7 +1,8 @@
 /** 设置 rem 函数 */
 function setRem() {
   console.log(222223)
-  const htmlWidth = document.documentElement.clientWidth || document.body.clientWidth
+  const htmlWidth =
+    document.documentElement.clientWidth || document.body.clientWidth
   const htmlDom = document.getElementsByTagName('html')[0]
   console.log(htmlWidth, htmlDom.style.fontSize)
   htmlDom.style.fontSize = htmlWidth / 100 + 'px'
@@ -9,6 +10,6 @@ function setRem() {
 
 setRem()
 
-window.onresize = function() {
+window.onresize = function () {
   setRem()
 }

+ 66 - 54
src/utils/request.js

@@ -12,70 +12,77 @@ const service = axios.create({
 })
 let cancel
 // request拦截器
-service.interceptors.request.use(config => {
-  // console.log(config)
-  if (cancel) cancel('取消了')
-  if (config.cancel) {
-    config.cancelToken = new CancelToken((c) => { //c是一个函数,调用c就可以关闭本次请求
-      cancel = c
-    })
-  }
-  // // 是否需要设置 token
-  // const isToken = (config.headers || {}).isToken === false
-  // // 是否需要防止数据重复提交
-  // const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
-  let token = sessionStorage.getItem('token')
-  if (token) {
-    config.headers['Authorization'] = 'Bearer ' + token // 让每个请求携带自定义token 请根据实际情况自行修改
+service.interceptors.request.use(
+  (config) => {
+    // console.log(config)
+    if (cancel) cancel('取消了')
+    if (config.cancel) {
+      config.cancelToken = new CancelToken((c) => {
+        //c是一个函数,调用c就可以关闭本次请求
+        cancel = c
+      })
+    }
+    // // 是否需要设置 token
+    // const isToken = (config.headers || {}).isToken === false
+    // // 是否需要防止数据重复提交
+    // const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
+    let token = sessionStorage.getItem('token')
+    if (token) {
+      config.headers['Authorization'] = 'Bearer ' + token // 让每个请求携带自定义token 请根据实际情况自行修改
+    }
+    // get请求映射params参数
+    // if (config.method === 'get' && config.params) {
+    //   let url = config.url + '?' + tansParams(config.params);
+    //   url = url.slice(0, -1);
+    //   config.params = {};
+    //   config.url = url;
+    // }
+    // if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
+    //   const requestObj = {
+    //     url: config.url,
+    //     data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
+    //     time: new Date().getTime()
+    //   }
+    //   const sessionObj = cache.session.getJSON('sessionObj')
+    //   if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
+    //     cache.session.setJSON('sessionObj', requestObj)
+    //   } else {
+    //     const s_url = sessionObj.url;                // 请求地址
+    //     const s_data = sessionObj.data;              // 请求数据
+    //     const s_time = sessionObj.time;              // 请求时间
+    //     const interval = 1000;                       // 间隔时间(ms),小于此时间视为重复提交
+    //     if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
+    //       const message = '数据正在处理,请勿重复提交';
+    //       console.warn(`[${s_url}]: ` + message)
+    //       return Promise.reject(new Error(message))
+    //     } else {
+    //       cache.session.setJSON('sessionObj', requestObj)
+    //     }
+    //   }
+    // }
+    return config
+  },
+  (error) => {
+    Promise.reject(error)
   }
-  // get请求映射params参数
-  // if (config.method === 'get' && config.params) {
-  //   let url = config.url + '?' + tansParams(config.params);
-  //   url = url.slice(0, -1);
-  //   config.params = {};
-  //   config.url = url;
-  // }
-  // if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
-  //   const requestObj = {
-  //     url: config.url,
-  //     data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
-  //     time: new Date().getTime()
-  //   }
-  //   const sessionObj = cache.session.getJSON('sessionObj')
-  //   if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
-  //     cache.session.setJSON('sessionObj', requestObj)
-  //   } else {
-  //     const s_url = sessionObj.url;                // 请求地址
-  //     const s_data = sessionObj.data;              // 请求数据
-  //     const s_time = sessionObj.time;              // 请求时间
-  //     const interval = 1000;                       // 间隔时间(ms),小于此时间视为重复提交
-  //     if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
-  //       const message = '数据正在处理,请勿重复提交';
-  //       console.warn(`[${s_url}]: ` + message)
-  //       return Promise.reject(new Error(message))
-  //     } else {
-  //       cache.session.setJSON('sessionObj', requestObj)
-  //     }
-  //   }
-  // }
-  return config
-}, error => {
-  Promise.reject(error)
-})
+)
 
 // 响应拦截器
-service.interceptors.response.use(res => {
+service.interceptors.response.use(
+  (res) => {
     // 未设置状态码则默认成功状态
     const code = res.data.code || 200
     // 获取错误信息
     const msg = errorCode[code] || res.data.msg || errorCode['default']
     // 二进制数据则直接返回
-    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
+    if (
+      res.request.responseType === 'blob' ||
+      res.request.responseType === 'arraybuffer'
+    ) {
       let filename = res.headers['content-disposition'].split('filename=')[1]
       return { fileName: filename, data: res.data }
     }
     if (code === 401) {
-
       // if (!isRelogin.show) {
       //   isRelogin.show = true;
       //   ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
@@ -101,7 +108,7 @@ service.interceptors.response.use(res => {
       return Promise.resolve(res.data)
     }
   },
-  error => {
+  (error) => {
     if (isCancel(error)) {
       // console.log('用户取消')
     } else {
@@ -113,7 +120,12 @@ service.interceptors.response.use(res => {
       } else if (message.includes('Request failed with status code')) {
         message = '系统接口' + message.substr(message.length - 3) + '异常'
       }
-      ElMessage({ message: message, type: 'error', duration: 5 * 1000, grouping: true })
+      ElMessage({
+        message: message,
+        type: 'error',
+        duration: 5 * 1000,
+        grouping: true
+      })
       return Promise.reject(error)
     }
   }

+ 0 - 1
tailwind.config.js

@@ -6,4 +6,3 @@ export default {
   },
   plugins: []
 }
-

+ 8 - 8
wxt.config.ts

@@ -5,9 +5,9 @@ export default defineConfig({
   extensionApi: 'chrome',
   srcDir: 'src',
   manifest: {
-    'name': '派维斯智能体助手',
-    'version': '0.1.7',
-    'permissions': [
+    name: '派维斯智能体助手',
+    version: '0.1.7',
+    permissions: [
       'storage',
       'history',
       'tabs',
@@ -16,13 +16,13 @@ export default defineConfig({
       'webRequest',
       'sidePanel'
     ],
-    'content_security_policy': {
-      'extension_pages': 'script-src \'self\'; object-src \'self\';'
+    content_security_policy: {
+      extension_pages: "script-src 'self'; object-src 'self';"
     },
-    'action': {
-      'default_title': '派维斯智能体助手'
+    action: {
+      default_title: '派维斯智能体助手'
     },
-    'options_page': 'options.html'
+    options_page: 'options.html'
   },
   modules: ['@wxt-dev/module-vue']
 })