Explorar el Código

feat(sidepanel): 优化微信登录功能并修复相关问题

- 更新微信登录逻辑,支持通过 code 进行登录
- 优化二维码生成和过期处理机制
- 修复了一些与微信登录相关的潜在问题
- 优化了相关组件的样式和交互
chd hace 4 meses
padre
commit
ebe1060948

+ 2 - 2
.env

@@ -1,8 +1,8 @@
 VITE_OPENAI_API_KEY_TONG=sk-e9855234f47346049809ce23ed3ebe3f
 VITE_MAX_FILE_NUMBER=10
-VITE_APP_BASE_API='http://192.168.1.202:13088'
+VITE_APP_BASE_API='http://180.76.147.97:13088'
 
 # 接口地址 (WebSocket)
-VITE_API_WS_URL = 'ws://192.168.1.202:13088'
+VITE_API_WS_URL = 'ws://180.76.147.97:13088'
 # 终端ID
 VITE_CLIENT_ID = '153062e567553ec0bf1a02b8f1f563b1'

+ 2 - 3
src/api/index.js

@@ -53,9 +53,8 @@ export function putChat(data) {
 
 export function getQRcodeResult(data) {
     return request({
-        url: '/statistics/frontList',
-        method: 'put',
-        data: data
+        url: `/auth/status/${data}`,
+        method: 'get',
     })
 }
 export function getQRcode(data) {

+ 1 - 0
src/entrypoints/background.js

@@ -122,6 +122,7 @@ export default defineBackground(() => {
       })
       return true
     }
+    return true
   })
   let timer
   // chrome.tabs.onRemoved.addListener(function (...a) {

+ 9 - 9
src/entrypoints/content.js

@@ -101,6 +101,7 @@ export default defineContentScript({
             dom.click()
           }, 1000)
           sendResponse({ data: '完成' })
+          return true
         }
         if (message.type === 'GET_PAGE_FORM') {
           const forms = document.querySelectorAll('form')
@@ -176,7 +177,7 @@ export default defineContentScript({
           const { formData, excelData } = message.data
           excelDataA = excelData
           console.log(formData, excelDataA)
-          await handleFillInput(formData, 0)
+          handleFillInput(formData, 0)
         }
         return true
       }
@@ -184,7 +185,6 @@ export default defineContentScript({
     const handleFillInput = async (data, index) => {
       console.log(data, 85888)
       console.log(formChildren, form)
-
       for (let i = 0; i < data.length; i++) {
         const item = data[i]
         if (item.findBy === 'id') {
@@ -251,7 +251,7 @@ export default defineContentScript({
             simulateUserInput(input, excelDataA[item.excelColumn])
           }
         }
-        if (item.findBy === 'label') {
+        if (item.findBy === 'label') {   
           if (!excelDataA[item.excelColumn]) continue
           const label = [...form.getElementsByTagName('label')].find(
             (label) =>
@@ -282,8 +282,6 @@ export default defineContentScript({
 
           if (item.type === 'date') {
             const input = findLabelForInput(label)
-            console.log(input, excelDataA[item.excelColumn])
-
             if (input) {
               await simulateCompleteUserAction(
                 input,
@@ -304,13 +302,10 @@ export default defineContentScript({
           ) {
             let input
             if (item.type === 'textarea') {
-              input = findLabelForTextarea(label)
+              input = findLabelForTextarea(label)[0]
             } else input = findLabelForInput(label)
-            console.log(input, 7585)
-
             if (input) {
               if (input.value) continue
-              console.log(excelDataA[item.excelColumn])
               await simulateUserInput(input, excelDataA[item.excelColumn])
             }
           }
@@ -321,6 +316,11 @@ export default defineContentScript({
             }
           }
         }
+        await new Promise((res) => {
+          setTimeout(() => {
+            res()
+          }, 100);
+        })
       }
     }
   }

+ 32 - 13
src/entrypoints/sidepanel/Chat.vue

@@ -47,7 +47,7 @@
       <ScrollToBottom :target="scrollbar" ref="scrollToBottomRef" />
     </div>
 
-    <Tools :upload="type === FunctionList.File_Operation || !!formInfo" @read-click="readClick" @upload-file="(file) => createFileObj(file)" @handle-capture="handleCapture" @his-records="hisRecords"
+    <Tools :disHistory="sendLoading" :upload="type === FunctionList.File_Operation || !!formInfo" @read-click="readClick" @upload-file="(file) => createFileObj(file)" @handle-capture="handleCapture" @his-records="hisRecords"
       @add-new-dialogue="addNewDialogue" @handle-current-change="handleCurrentChange"
       @handel-intelligent-filling-click="handelIntelligentFillingClick" />
 
@@ -180,9 +180,10 @@ function handleStopAsk() {
     isShowPage.value = false
     taklToHtml.value = false
     type.value = FunctionList.File_Operation
-  controllerList.value[0].abort()
+  controllerList.value[0]?.abort()
   controllerList.value = []
   sendLoading.value = false
+  formInfo.value = null
 }
 
 function handleScroll(a) {
@@ -349,8 +350,11 @@ async function handleCurrentData(e) {
 let SystemMsg = null
 
 watchEffect(() => {
+  console.log(isShowPage.value);
+  
   if (!isShowPage.value) {
     summaryHtml.value = false
+   
     type.value = FunctionList.File_Operation
     SystemMsg = null
     inputMessage.value = ''
@@ -387,10 +391,16 @@ async function readClick() {
 function deletePageInfo(i) {
   pageInfoList.value.splice(i, 1)
   if (pageInfoList.value.length === 0) {
-    isShowPage.value = false
-    taklToHtml.value = false
-    if (type.value === FunctionList.Intelligent_Form_filling) inputMessage.value = ''
-    type.value = FunctionList.File_Operation
+    if (type.value === FunctionList.Intelligent_Form_filling) {
+      inputMessage.value = ''
+      SystemMsg.content = '智能填表流程中断'
+      SystemMsg.rawContent = '智能填表流程中断'
+      putChat({
+        ...SystemMsg,
+        content: '',
+      })
+    }
+    handleStopAsk()
   }
 }
 
@@ -415,9 +425,16 @@ const disabledBtn = computed(() => {
 })
 function closePageInfo() {
   pageInfoList.value = []
-  type.value = FunctionList.File_Operation
-  isShowPage.value = false
-  formInfo.value = null
+   if (type.value === FunctionList.Intelligent_Form_filling && SystemMsg ) {
+      SystemMsg.content = '智能填表流程中断'
+      SystemMsg.rawContent = '智能填表流程中断'
+      putChat({
+        ...SystemMsg,
+        content: '',
+      })
+    }
+    handleStopAsk()
+  
 }
 async function handleAsk(value) {
   if (sendLoading.value) return
@@ -429,11 +446,11 @@ async function handleAsk(value) {
      const [res,obj] = await fetchRes(str)
     if (res.status === 'ok') {
       console.log(obj);
-      
       formInfo.value = res.data
       SystemMsg = obj
     } else {
       type.value = FunctionList.File_Operation
+      isShowPage.value = false
     }
     return 
   }
@@ -472,10 +489,12 @@ async function createWS(msg) {
       scrollbar.value.setScrollTop(scrollbar.value.wrapRef.scrollHeight)
     }
   })
-   const wsUrl = `${import.meta.env.VITE_API_WS_URL}/webSocket/clue/${msgUuid.value}`;
+  const websocketId= uuidv4()
+   const wsUrl = `${import.meta.env.VITE_API_WS_URL}/webSocket/clue/${websocketId}`;
     const socket = new WebSocket(wsUrl);
      askQues({
-    conversationId: msgUuid.value,
+       conversationId: msgUuid.value,
+    websocketId,
     modelName: '通义千问-Max',
     question: type.value === FunctionList.File_Operation ? msg.rawContent : buildObjPrompt(xlsxData.value,formInfo.value),
     id: '699637194561691650',
@@ -565,7 +584,7 @@ const createFileObj = async (file) => {
 }
 const handleUpload = async (file) => {
     const obj = reactive({
-    title: file.name,
+    title: summaryHtml.value ?  file.name.split('.')[0] :  file.name,
     state: '上传中...',
     favIconUrl: fileLogo,
     loading: true,

+ 120 - 32
src/entrypoints/sidepanel/component/Login.vue

@@ -6,7 +6,7 @@
         </div>
         <div class="login-tabs">
             <div class="tab" :class="{ active: activeTab === 'phone' }" @click="activeTab = 'phone'">手机号登录</div>
-            <!-- <div class="tab" :class="{ active: activeTab === 'wechat' }" @click="activeTab = 'wechat'">微信登录</div> -->
+            <div class="tab" :class="{ active: activeTab === 'wechat' }" @click="activeTab = 'wechat'">微信登录</div>
         </div>
 
         <div class="login-content">
@@ -44,7 +44,7 @@
                         <!-- 使用vue-qrcode组件 -->
                         <qrcode-vue :value="qrcodeValue" :width="200" />
                         <!-- 过期遮罩层 -->
-                        <div v-if="isQrcodeExpired" class="expired-mask">
+                        <div  class="expired-mask">
                             <el-icon :size="40">
                                 <Refresh />
                             </el-icon>
@@ -52,7 +52,7 @@
                         </div>
                     </div>
                     <p class="qrcode-tip">请使用微信扫一扫登录</p>
-                    <p class="qrcode-refresh">二维码已失效?<a href="javascript:;" @click="refreshQrcode">点击刷新</a></p>
+                    <!-- <p class="qrcode-refresh">二维码已失效?<a href="javascript:;" @click="refreshQrcode">点击刷新</a></p> -->
                 </div>
             </div>
             <div class="agreement">
@@ -141,14 +141,15 @@ const loading = ref(false)
 const captchaLoading = ref(false)
 
 // 二维码值
-const qrcodeValue = ref('https://example.com/login?token=' + Date.now())
+const qrcodeValue = ref('')
 let countdown = 60
 let timer1 = null
 let timer2 = null
 // 刷新二维码
 const refreshQrcode = async () => {
+    clearTimeout(timer)
     // const result = await getQRcode()
-    qrcodeValue.value = 'https://example.com/login?token=' + Date.now()
+    getQrCode()
 }
 
 // 获取验证码
@@ -175,7 +176,6 @@ const handlePhoneLogin = async () => {
             phone: phone.value,
             captcha: captcha.value,
             authType: "PHONE",
-            clientId: "ef51c9a3e9046c4f2ea45142c8a8344a"
         })
      
     } catch (error) {
@@ -187,20 +187,16 @@ const handlePhoneLogin = async () => {
 }
 
 // 微信扫码登录处理
-const handleWechatLogin = async (token) => {
+const handleWechatLogin = async (code) => {
     try {
         loading.value = true
         // 调用user仓库的登录方法,使用微信登录类型
-        const loginResult = await userStore.login({
-            type: 'wechat',
-            // 这里可以传递微信登录相关的参数
-            wechatToken: token || qrcodeValue.value.split('token=')[1]
-        })
-
-        if (loginResult) {
-            ElMessage.success('微信登录成功')
-            // 登录成功后可以执行其他操作
-        }
+       userStore.login({
+             authType: "SOCIAL",
+             source: "wechat_mp",
+             state: state.value,
+            code,
+         })
     } catch (error) {
         console.error('微信登录失败:', error)
         ElMessage.error('微信登录失败,请稍后重试')
@@ -208,14 +204,19 @@ const handleWechatLogin = async (token) => {
         loading.value = false
     }
 }
-const checkQRcodeResult = async () => {
-    const res = await getQRcodeResult(qrcodeValue.value)
-    if (res.code === 200) {
-        await handleWechatLogin(res.data)
+const checkQRcodeResult = async (state) => {
+    const res = await getQRcodeResult(state)
+    console.log(res.data.code);
+    
+    if (!!res.data.code) {
+        await handleWechatLogin(res.data.code)
     } else {
-        setTimeout(() => {
-            checkQRcodeResult()
-        }, 5000);
+        await new Promise((res) => setTimeout(() => {
+            res()
+        }, 1000))
+        console.log(state,121212);
+            
+            checkQRcodeResult(state)
     }
 }
 // 验证手机号
@@ -230,22 +231,61 @@ const validatePhone = () => {
     }
     return true
 }
-watch(activeTab, (newTab) => {
-  if (newTab === 'wechat') {
-    getWeChatLoginCode({source:'wechat_mp'}).then(res => {
-       qrcodeValue.value = res.authorizeUrl
+let timer = null
+const state = ref('')
+function getQrCode() {
+       isQrcodeExpired.value = false
+    timer = setTimeout(() => {
+        isQrcodeExpired.value = true
+        console.log(isQrcodeExpired.value);
+        
+    }, 1000 * 4);
+      getWeChatLoginCode({ source: 'wechat_mp' }).then(res => {
+        console.log(res.data.authorizeUrl);
+        // 解析URL参数
+          const urlParams = getUrlParams(res.data.authorizeUrl);
+        qrcodeValue.value = res.data.authorizeUrl
+
+         state.value = urlParams.state
+          setTimeout(() => {
+            checkQRcodeResult(res.data.state)
+        }, 1000);
+        console.log('URL参数对象:', urlParams);
     })
+}
+
+// 添加解析URL参数的函数
+function getUrlParams(url) {
+    try {
+        // 创建URL对象
+        const urlObj = new URL(url);
+        // 获取searchParams对象
+        const params = urlObj.searchParams;
+        // 将参数转换为对象
+        const paramsObj = {};
+        for (const [key, value] of params.entries()) {
+            paramsObj[key] = value;
+        }
+        return paramsObj;
+    } catch (error) {
+        console.error('解析URL参数失败:', error);
+        return {};
+    }
+}
+watch(activeTab, (newTab) => {
+    if (newTab === 'wechat' && !qrcodeValue.value) {
+       getQrCode()
     //   qrcodeValue.value = 'https://example.com/login?token=' + Date.now()
-    //     setTimeout(() => {
-    //         checkQRcodeResult()
-    //     }, 1000);
+      
   }
 })
 
 // 组件挂载时的处理
 onMounted(() => {
 })
-
+onUnmounted(() => {
+    clearTimeout(timer)
+})
 </script>
 
 <style lang="scss" scoped>
@@ -424,11 +464,59 @@ onMounted(() => {
         background-color: #fff;
         border-radius: 12px;
         box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
+        position: relative; /* 添加相对定位 */
+        cursor: pointer; /* 添加指针样式 */
+        
         img {
             width:100%
         }
+        
+        /* 过期遮罩层样式 */
+        .expired-mask {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 100%;
+            background-color: rgba(0, 0, 0, 0.6);
+            border-radius: 12px;
+            display: flex;
+            flex-direction: column;
+            justify-content: center;
+            align-items: center;
+            color: #fff;
+            opacity: 0;
+            transition: opacity 0.3s ease;
+            pointer-events: none;
+            
+            .el-icon {
+                margin-bottom: 10px;
+                // animation: rotate 2s infinite linear;
+            }
+            
+            p {
+                font-size: 16px;
+                font-weight: 500;
+            }
+        }
+        
+        /* 当二维码过期时显示遮罩层 */
+        &.expired .expired-mask {
+            opacity: 1;
+            pointer-events: auto;
+        }
     }
 
+    /* 添加旋转动画 */
+    // @keyframes rotate {
+    //     from {
+    //         transform: rotate(0deg);
+    //     }
+    //     to {
+    //         transform: rotate(360deg);
+    //     }
+    // }
+
     .qrcode-tip {
         font-size: 16px;
         color: #333;

+ 1 - 1
src/entrypoints/sidepanel/component/Verify/Verify/VerifyPoints.vue

@@ -25,7 +25,7 @@
           style="width: 100%; height: 100%; display: block"
           @click="bindingClick ? canvasClick($event) : undefined"
         />
-
+123
         <div
           v-for="(tempPoint, index) in tempPoints"
           :key="index"

+ 1 - 1
src/entrypoints/sidepanel/component/Verify/Verify/VerifySlide.vue

@@ -1,5 +1,5 @@
 <template>
-  <div style="position: relative">
+  <div style="position: relative" v-loading="!blockBackImgBase">
     <div
       v-if="type === '2'"
       class="verify-img-out"

+ 1 - 0
src/entrypoints/sidepanel/component/formTable.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+import { login } from '@/api';
 import { onMounted, computed,ref } from 'vue'
 
 const props = defineProps({

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

@@ -20,7 +20,7 @@ const props = defineProps({
   msgUuid: {
     type: String,
     default: ''
-  }
+  },
 })
 watch(drawer, (newVal) => {
   if (newVal) {
@@ -47,7 +47,7 @@ function handleDeleteStore(e: any, item: any,name:string) {
   }
   ElMessageBox.confirm(
     '此操作无法撤销。',
-    `要删【${name}】对话吗?`,
+    `要删【${name.substring(0,20)}】对话吗?`,
     {
       confirmButtonText: '确认',
       cancelButtonText: '取消',

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

@@ -8,11 +8,11 @@
       <p class="font-black text-4xl mb-4 text-black">你好,</p>
       <p class="font-black text-3xl mb-4 text-black">我是PVS智能助手,</p>
       <p class="font-black text-2xl mb-4 text-black">我今天能帮你什么?</p>
-      <span>作为你的智能伙伴,我既能写文案、想点子,又能陪你聊天、答疑解惑。</span>
+      <span class="text-black">作为你的智能伙伴,我既能写文案、想点子,又能陪你聊天、答疑解惑。</span>
     </div>
   </div>
 </template>
 
 <style scoped lang="scss">
 
-</style>
+</style>

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

@@ -24,7 +24,7 @@ const props = defineProps({
   upload: {
     type: Boolean,
   },
-
+  disHistory:Boolean
 })
 </script>
 
@@ -56,10 +56,10 @@ const props = defineProps({
     </div>
     <div class="flex items-center">
       <el-tooltip effect="dark" content="历史记录" placement="top">
-        <el-button class="tools_btn1" link :icon="AlarmClock" @click="emit('hisRecords')" />
+        <el-button :disabled="disHistory" class="tools_btn1" link :icon="AlarmClock" @click="emit('hisRecords')" />
       </el-tooltip>
       <el-tooltip effect="dark" content="新对话" placement="top">
-        <el-button class="tools_btn1" link :icon="CirclePlus" @click="emit('addNewDialogue')" />
+        <el-button :disabled="disHistory" class="tools_btn1" link :icon="CirclePlus" @click="emit('addNewDialogue')" />
       </el-tooltip>
     </div>
   </div>

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

@@ -201,7 +201,7 @@ export function useMsg(scrollbar?: any) {
       obj.content = '未找到标签,请重试'
       return { status: 'error' }
     }
-    await new Promise((resolve) => setTimeout(resolve, 2000))
+    // await new Promise((resolve) => setTimeout(resolve, 2000))
     obj.content = `点击${res.data.tag}元素`
     const res2 = await new Promise((resolve, rej) => {
       chrome.runtime.sendMessage(
@@ -216,7 +216,6 @@ export function useMsg(scrollbar?: any) {
             if (status === 'error') {
               obj.content = data
               resolve({ data, status })
-              console.log(222)
             }
             if (res.data.next === '是') {
               const arr = str.split(',')

+ 34 - 14
src/utils/contentUtils.js

@@ -65,16 +65,24 @@ export function formatDate(date) {
         d = new Date(date)
       }
     }
-    // 处理短横线分隔格式:YYYY-M-D
+    // 处理短横线分隔格式:YYYY-M-D 或包含多个连字符的情况
     else if (date.includes('-')) {
-      const parts = date.split('-')
-      if (parts.length === 3) {
-        const year = parts[0]
-        const month = String(parseInt(parts[1])).padStart(2, '0')
-        const day = String(parseInt(parts[2])).padStart(2, '0')
-        d = new Date(`${year}-${month}-${day}`)
+      // 提取所有数字部分
+      const parts = date.split('-').filter(part => part.trim() !== '');
+
+      if (parts.length >= 3) {
+        // 只取前三个部分作为年月日
+        const year = parts[0];
+        const month = String(parseInt(parts[1])).padStart(2, '0');
+        const day = String(parseInt(parts[2])).padStart(2, '0');
+        d = new Date(`${year}-${month}-${day}`);
+      } else if (parts.length === 2) {
+        // 如果只有两部分,假设是年月,日设为1
+        const year = parts[0];
+        const month = String(parseInt(parts[1])).padStart(2, '0');
+        d = new Date(`${year}-${month}-01`);
       } else {
-        d = new Date(date)
+        d = new Date(date);
       }
     }
     // 处理纯数字格式:YYYYMMDD
@@ -117,6 +125,7 @@ export async function simulateCompleteUserAction(
   inputText,
   tdTitle
 ) {
+  if (!inputText) return
   // 1. 模拟鼠标弹起事件
   const simulateMouseUp = (element) => {
     const mouseUpEvent = new MouseEvent('mouseup', {
@@ -134,10 +143,16 @@ export async function simulateCompleteUserAction(
   // 123456qq?
   // 2. 模拟键盘输入
   const simulateTyping = async (element, text, delay = 50) => {
-    element.focus()
+    await new Promise((resolve) => setTimeout(resolve, 100))
+    // element.focus()
+    const focusEvent = new FocusEvent('focus', {
+      bubbles: true,
+      cancelable: true,
+      view: window
+    });
+    element.dispatchEvent(focusEvent);
     for (let char of text) {
       await new Promise((resolve) => setTimeout(resolve, delay))
-
       // 按键按下
       const keydownEvent = new KeyboardEvent('keydown', {
         key: char,
@@ -168,7 +183,12 @@ export async function simulateCompleteUserAction(
       })
       element.dispatchEvent(keyupEvent)
     }
-
+    const bulurEvent = new FocusEvent('blur', {
+      bubbles: true,
+      cancelable: true,
+      view: window
+    });
+    element.dispatchEvent(bulurEvent);
     // 触发change事件
     element.dispatchEvent(new Event('change', { bubbles: true }))
   }
@@ -229,7 +249,7 @@ export async function simulateCompleteUserAction(
     })
     setTimeout(() => {
       inputElement.dispatchEvent(enterKeyEvent)
-    }, 0)
+    }, 50)
     await new Promise((resolve) => setTimeout(resolve, 500))
     return true
   } catch (error) {
@@ -238,7 +258,7 @@ export async function simulateCompleteUserAction(
 }
 export async function simulateUserInput(element, value) {
   // 设置值
-  if (element.tagName.toLowerCase() === 'textarea') {
+  if (element?.tagName?.toLowerCase() === 'textarea') {
     element.focus()
     element.value = value
     element.blur()
@@ -491,7 +511,7 @@ export function cleanPage2(element) {
   // 销毁临时容器
   temp.remove()
   // console.log(cleanedHtml);
-  
+
   // content.outerHTML.trim().replace(/\s+/g, " ");
   return cleanedHtml
 }

+ 1 - 1
wxt.config.ts

@@ -19,7 +19,7 @@ export default defineConfig({
   srcDir: 'src',
   manifest: {
     name: '派维斯智能体助手',
-    version: '0.1.9',
+    version: '0.2.0',
     permissions: [
       'storage',
       'history',