wzg hai 5 meses
pai
achega
e9c20037d1

+ 9 - 10
src/entrypoints/content.js

@@ -88,8 +88,7 @@ export default defineContentScript({
                 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]
@@ -104,7 +103,6 @@ export default defineContentScript({
             }
             item.addEventListener('click', handleClick, true)
           }
-
           // if (!form) {
           //   sendResponse({
           //     status: 'error',
@@ -152,15 +150,15 @@ export default defineContentScript({
             item.type === 'number'
           ) {
             if (!input) {
-              if (item.item) {
+              if (item.label) {
                 const label = [...form.getElementsByTagName('label')].find(
-                  (label) => label.innerText.includes(item.item)
+                  (label) => label.innerText.includes(item.label)
                 )
                 console.log(item, label)
                 if (label) {
                   const input = findLabelForInput(label)
                   if (input) {
-                    await simulateUserInput(input, excelDataA[item.excelColumn])
+                    await simulateUserInput(input, excelDataA[item.content])
                   }
                 }
               }
@@ -170,9 +168,9 @@ export default defineContentScript({
             }
           }
           if (item.type === 'radio' || item.type === 'checkbox') {
-            if (item.item) {
+            if (item.label) {
               const label = [...form.getElementsByTagName('label')].find(
-                (label) => label.innerText.includes(item.item)
+                (label) => label.innerText.includes(item.label)
               )
               if (label) {
                 const span = findLabelForSpan(label)
@@ -261,14 +259,15 @@ export default defineContentScript({
             if (item.type === 'textarea') {
               input = findLabelForTextarea(label)
             } 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])
             }
           }
-          if (item.type === 'select') {
+          if (item.type === 'select' || item.type === "cascader") {
             const input = findLabelForInput(label)
             if (input) {
               await simulateUserInput(input, excelDataA[item.excelColumn])

+ 46 - 15
src/entrypoints/sidepanel/Chat.vue

@@ -13,12 +13,13 @@
             <el-avatar :size="32" :src="message.avatar" />
             <div class="message-content">
               <div class="content" v-if="!message.isSelf" :class="{ 'loading-content': message.content === '' }">
-                <span v-html="message.content"></span>
+                <formTable v-if="message.type === 'form'" :content="message.rawContent" />
+                <span v-else v-html="message.content"></span>
                 <span class="loading-indicator" v-if="sendLoading && index === messages.length - 1">
-                <span class="dot"></span>
-                <span class="dot"></span>
-                <span class="dot"></span>
-              </span>
+                  <span class="dot"></span>
+                  <span class="dot"></span>
+                  <span class="dot"></span>
+                </span>
               </div>
               <document v-else-if="message.type === 'document' && message.isSelf" :content="message.content"
                         :rawContent="message.rawContent" />
@@ -120,6 +121,8 @@ import historyComponent from '@/entrypoints/sidepanel/component/historyComponent
 import document from '@/entrypoints/sidepanel/component/document.vue'
 import pageMask from '@/entrypoints/sidepanel/component/pageMask.vue'
 import ScrollToBottom from '@/entrypoints/sidepanel/component/ScrollToBottom.vue'
+import formTable from '@/entrypoints/sidepanel/component/formTable.vue'
+
 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'
@@ -317,6 +320,7 @@ function deletePageInfo(i) {
   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
   }
 }
@@ -334,13 +338,22 @@ function addNewDialogue() {
 }
 
 async function handleAsk() {
+  const str = inputMessage.value.trim()
+  inputMessage.value = ''
   if (sendLoading.value) return
   addMessage(inputMessage.value.trim())
   if (type.value === FunctionList.Intelligent_Form_filling) {
-    const res = await fetchRes(inputMessage.value.trim())
+    const res = await fetchRes(str)
+    console.log(res);
+
     if (res.status === 'ok') {
       formInfo.value = res.data
       console.log(res)
+    } else {
+      type.value = FunctionList.File_Operation
+      isShowPage.value = false
+      taklToHtml.value = false
+      pageInfoList.value = []
     }
   } else streamRes(taklToHtml.value)
   inputMessage.value = ''
@@ -380,18 +393,36 @@ const handleUpload = async (file) => {
         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]))
+      const {rawContent,status} = await streamRes()
+    if (status === 'ok') {
+        let form = []
+      if (rawContent.includes('json')) form = JSON.parse(rawContent.split('json')[1].split('```')[0])
+      else form = JSON.parse(rawContent)
+      handleInput(xlsxData.value, form)
+    }
     } 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]))
+     try {
+       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 { rawContent,status } = await streamRes()
+       if (status === 'ok') {
+         let form = []
+         if (rawContent.includes('json')) form = JSON.parse(rawContent.split('json')[1].split('```')[0])
+         else form = JSON.parse(rawContent)
+         handleInput(xlsxData.value, form)
+       }
+     } catch (error) {
+       msg.content = '文件解析出错'
+     } finally {
+      sendLoading.value = false
+     }
     }
     type.value = FunctionList.File_Operation
+    isShowPage.value = false
+    taklToHtml.value = false
+    pageInfoList.value = []
     return
   }
   if (type.value === FunctionList.File_Operation) {

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

@@ -0,0 +1,80 @@
+<script setup lang="ts">
+import { onMounted, computed,ref } from 'vue'
+
+const props = defineProps({
+  content: {
+    type: String,
+    default: ''
+  },
+
+})
+const show = ref(false)
+const tableData = computed(() => {
+  let arr = []
+  if (props.content.includes('json')) {
+    arr = JSON.parse(props.content.split('json')[1].split('```')[0])
+  }
+  else arr = JSON.parse(props.content)
+  if (arr[0].data) show.value = true
+  return arr.map((_:any) => ({
+    label: _.label ?? _.findByValue,
+    source: _.excelColumn,
+    value:_.data
+  }))
+})
+</script>
+
+<template>
+  <div class="mb-2" >抽取对应关系</div>
+  <el-table :data="tableData" style="width: 100%">
+    <el-table-column show-overflow-tooltip prop="label" label="表单项" width="100" />
+    <el-table-column show-overflow-tooltip prop="source" label="数据源" width="100" />
+    <el-table-column v-if="show" show-overflow-tooltip prop="value" label="数据值" width="100" />
+  </el-table>
+
+</template>
+
+<style scoped lang="scss">
+.els {
+  white-space: nowrap; /* 强制文本不换行 */
+  overflow: hidden; /* 隐藏溢出内容 */
+  text-overflow: ellipsis; /* 显示省略号 */
+}
+
+.document_r {
+  display: flex;
+  flex-direction: column;
+  align-items: end;
+}
+
+.document_content {
+  width: 240px;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: start;
+  padding: 8px 8px;
+  background-color: rgba(255, 255, 255, 0.3);
+  border: 1px solid rgba(102, 102, 102, 0.3);
+  border-radius: 6px;
+  margin-bottom: 6px;
+
+  .document_img {
+    margin-right: 8px;
+    height: 32px;
+  }
+
+  .document_text {
+    flex-shrink: 0;
+    width: calc(100% - 56px);
+  }
+}
+
+.document_content1 {
+  width: fit-content;
+  max-width: 240px;
+  padding: 10px 12px;
+  border-radius: 6px;
+  background-color: rgba(255, 255, 255, 0.8);
+}
+</style>

+ 53 - 97
src/entrypoints/sidepanel/hook/useMsg.ts

@@ -6,8 +6,7 @@ import moment from 'moment'
 import { cloneDeep } from 'lodash'
 import {
   getFormKey,
-  buildObjPrompt,
-  getFileSummaryPrompt,
+  hepl,
   formatMessage,
   sendMessage
 } from '../utils/ai-service.js'
@@ -28,30 +27,6 @@ export function useMsg(scrollbar?: any) {
   // 获取父组件提供的 Hook 实例
   const { useStore } = inject('indexedDBHook') as any
 
-  const getFileSummary = async (file: any) => {
-    const obj = reactive({
-      id: moment(),
-      username: '用户1',
-      rawContent: '',
-      content: '解析文件中',
-      timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
-      isSelf: false,
-      avatar: avator,
-      addToHistory: !taklToHtml.value
-    })
-    try {
-      sendLoading.value = true
-      messages.value.push(obj)
-      nextTick(() => scrollbar.value?.setScrollTop(99999))
-      const res = await getFileValue(file)
-      console.log(res.data, file)
-      await streamRes()
-    } catch (error) {
-      obj.content = '解析出错'
-    } finally {
-      sendLoading.value = false
-    }
-  }
   const getFormKeyAndValue = async (file: any, form?: any) => {
     // const obj = reactive({
     //   id: moment(),
@@ -113,7 +88,6 @@ export function useMsg(scrollbar?: any) {
           }
           if (status === 'select') {
             obj.content = '检测到左侧页面中有多个表单,请选择要填写的表单。'
-
             function handle(message, sender, sendResponse) {
               if (message.type === 'TO_SIDE_PANEL_FORM_INFO') {
                 console.log('收到一次性消息:', message.data)
@@ -152,20 +126,28 @@ export function useMsg(scrollbar?: any) {
       const res = await awaitFindForm(obj)
       return res
     }
-    const res = await fetchDataAndProcess(msg, obj)
-    sendLoading.value = false
-    if (res.status === 'ok') {
-      await new Promise((res: any) =>
-        setTimeout(() => {
-          res()
-        }, 2000)
-      )
-      const res = await awaitFindForm(obj)
-      return res
-    }
+   try {
+     const res = await fetchDataAndProcess(msg, obj)
+     sendLoading.value = false
+     if (res.status === 'ok') {
+       await new Promise((res: any) =>
+         setTimeout(() => {
+           res()
+         }, 2000)
+       )
+       const res = await awaitFindForm(obj)
+       console.log(res,34444);
+
+       return res
+     }
+   } catch (error) {
+     obj.content = '流程链执行出错'
+     return {status: 'error'}
+   } finally {
+     sendLoading.value = false
+   }
   }
   let str = ''
-
   async function fetchDataAndProcess(input: any, obj: any) {
     str = input
     console.log(str)
@@ -214,6 +196,8 @@ 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(',')
@@ -223,23 +207,6 @@ export function useMsg(scrollbar?: any) {
               const res = await fetchDataAndProcess(str, obj)
               resolve(res)
             } else resolve({ status: 'ok' })
-            // else {
-            //   await new Promise((resolve, reject) => {
-            //     setTimeout(() => {
-            //       resolve(1)
-            //     }, 2000)
-            //   })
-            //   obj.content = `请选择表单`
-            //   ElMessage({
-            //     message: '请选择表单',
-            //     type: 'success',
-            //     duration: 4 * 1000,
-            //     grouping: true
-            //   })
-            //   chrome.runtime.sendMessage({
-            //     type: 'FROM_SIDE_PANEL_TO_GET_PAGE_FORM'
-            //   })
-            // }
           }
         }
       )
@@ -258,6 +225,7 @@ export function useMsg(scrollbar?: any) {
       id: messages.value.length + 1,
       username: '用户1',
       content: '',
+      type: '', // form 用于展示抽取的内容
       rawContent: '', // 存储原始内容
       timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
       isSelf: false,
@@ -290,45 +258,36 @@ export function useMsg(scrollbar?: any) {
     nextTick(() => {
       scrollbar.value?.setScrollTop(99999)
     })
-    const iterator = await sendMessage(history)
-    for await (const chunk of iterator) {
-      if (chunk) {
-        const decodedChunk = chunk.choices[0].delta.content
-        if (decodedChunk) {
-          // 保存原始内容
-          obj.rawContent += decodedChunk
-          // 实时格式化显示内容
-          obj.content = formatMessage(obj.rawContent)
-          // if (type.value === '2') obj.content = obj.content.replace(/item/g, '表单项').replace(/excelColumn/g, '对应数据源')
-        }
-      }
-      scrollbar.value?.setScrollTop(99999)
-    }
-    console.log(obj.rawContent)
-
-    //添加到存储历史
+   try {
+     const iterator = await sendMessage(history)
+     for await (const chunk of iterator) {
+       if (chunk) {
+         const decodedChunk = chunk.choices[0].delta.content
+         if (decodedChunk) {
+           // 保存原始内容
+           obj.rawContent += decodedChunk
+           // 实时格式化显示内容
+           obj.content = formatMessage(obj.rawContent)
+         }
+       }
+       scrollbar.value?.setScrollTop(99999)
+     }
+     if (type.value === FunctionList.Intelligent_Form_filling) {
+       obj.type = 'form' //
+     }
+     return {rawContent:obj.rawContent,status:'ok'}
+   } catch (error) {
+     obj.content = '网络出错'
+     return { rawContent: obj.rawContent, status: 'error' }
+   } finally {
+     //添加到存储历史
     useStore(msgUuid.value).add(cloneDeep(obj))
-    // 处理最终内容
-    sendLoading.value = false
-    nextTick(() => {
-      scrollbar.value?.setScrollTop(99999)
-    })
-    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')
-      obj.content = formatMessage('```json' + JSON.stringify(newArr) + '```')
-      console.log(formatMessage('```json' + JSON.stringify(newArr) + '```'))
-    }
-    return obj.rawContent
+     // 处理最终内容
+     sendLoading.value = false
+     nextTick(() => {
+       scrollbar.value?.setScrollTop(99999)
+     })
+   }
   }
 
   /**
@@ -352,7 +311,6 @@ export function useMsg(scrollbar?: any) {
     messages.value.push(obj)
     scrollbar.value?.setScrollTop(99999)
     const iterator = await sendMessage(data)
-    console.log(iterator)
     if (iterator.error) {
       // 实时格式化显示内容
       obj.content = iterator.error
@@ -386,12 +344,10 @@ export function useMsg(scrollbar?: any) {
     indexTemp,
     taklToHtml,
     sendLoading,
-    formMap,
     type,
     fetchRes,
     streamRes,
     getFormKeyAndValue,
-    getFileSummary,
     requestFlowFn
   }
 }

+ 10 - 10
src/entrypoints/sidepanel/utils/ai-service.js

@@ -235,9 +235,9 @@ ${pageInfo}
 1. 请根据表单中的表单项和列标题进行匹配,一定以表单为准,列标题只能匹配一次,没有和列标题匹配到的表单项不返回!
 2. 生成表单项与列标题对应的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我excel文件中列标题的值。
 3. 表单项有id根据id匹配,findBy是id,并通过findByValue告诉我id的值,没有id根据label匹配,findBy是label,并通过findByValue给我label元素的值,没有label根据placeholder匹配,findBy是placeholder,并通过findByValue告诉我placeholder的值,没有placeholder,再根据其他内容匹配
-3. 并去除没有匹配到的表单项和excel文件中没有匹配到的列,
-4. 通过type字段告诉我输入项的类型,表单项有label,通过item字段返回label的值。
-5. 返回json格式数组,不要返回任何其他内容`
+4. 并去除没有匹配到的表单项和excel文件中没有匹配到的列,
+5. 通过type字段告诉我输入项的类型,表单项有label,通过label字段返回label的值。
+. 返回json格式数组,不要返回其他任何内容`
 }
 
 // 5. 如果表单项有label标签,同时返回label, 通过label字段告诉我label元素的文本
@@ -249,13 +249,13 @@ export function buildObjPrompt(obj, pageInfo) {
 ${pageInfo}
              
 要求:
-1. 请根据表单中的表单项和对象进行匹配,一定以表单为准,key同时在对象和表单中出现,才能返回!
-2. 表单项有label,根据label匹配,没有label根据placeholder匹配,没有placeholder,根据id匹配,再根据其他内容匹配
-3. 并根据实际可操作的表单项的所有信息与上传的对象的key进行匹配,生成表单项与key的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我对应的key值。在一个字段内返回
-4. 并去除没有匹配到的表单项和对象中没有匹配到的key,
-5. 通过type字段告诉我输入项的类型,如果对象中key对应的是日期,type统一返回date。
-6. 表单项有label,通过item字段返回label的值。
-7. 返回json格式返回数组,不要返回任何其他内容`
+1. 请根据表单中的表单项和对象的key进行匹配,一定以表单为准,对象的key只能匹配一次,使用data字段返回匹配到的key对应的value值。没有和对象匹配到的表单项不返回!
+2. 生成表单项与对象的key对应的数组,并使用findBy告诉我通过表单项的什么字段信息匹配到的,使用findByValue告诉我匹配到的表单项字段值,使用excelColumn字段告诉我对象key的值。
+3. 表单项有label,根据label匹配,没有label根据placeholder匹配,没有placeholder,根据id匹配,再根据其他内容匹配
+4. 表单中没有和对象的key匹配到的表单项不要返回!。
+5. 通过type字段告诉我输入项的类型,如果对象中key对应的是日期,type统一date。
+6. 表单项有label,通过label字段返回label的值。
+7. 将匹配到的所有对象放在数组中返回,返回json格式返回数组,不要返回其他任何内容`
 }
 
 function escapeHtml(html) {

+ 148 - 8
src/utils/contentUtils.js

@@ -1,12 +1,113 @@
 export function formatDate(date) {
-  // 直接创建北京时间的日期对象
-  const d = new Date(date)
+  // 检查日期是否为空
+  if (!date) {
+    return '';
+  }
+  
+  let d;
+  
+  // 处理各种日期格式
+  if (typeof date === 'string') {
+    // 处理中文年月日格式:YYYY年M月D日 或 YYYY年M月
+    if (date.includes('年')) {
+      const yearMatch = date.match(/(\d+)年/);
+      const monthMatch = date.match(/(\d+)月/);
+      const dayMatch = date.match(/(\d+)日/);
+      
+      if (yearMatch && monthMatch) {
+        const year = yearMatch[1];
+        const month = String(parseInt(monthMatch[1])).padStart(2, '0');
+        // 如果有日,使用日,否则默认为1日
+        const day = dayMatch ? String(parseInt(dayMatch[1])).padStart(2, '0') : '01';
+        d = new Date(`${year}-${month}-${day}`);
+      } else {
+        d = new Date(date);
+      }
+    }
+    // 处理特殊格式:YYYY.M.D 或 YYYY.M
+    else if (date.includes('.')) {
+      const parts = date.split('.');
+      if (parts.length >= 2) {
+        // 将 YYYY.M.D 或 YYYY.M 转换为 YYYY-MM-DD 格式
+        const year = parts[0];
+        const month = String(parseInt(parts[1])).padStart(2, '0');
+        // 如果有日,使用日,否则默认为1日
+        const day = parts.length > 2 ? String(parseInt(parts[2])).padStart(2, '0') : '01';
+        d = new Date(`${year}-${month}-${day}`);
+      } else {
+        d = new Date(date);
+      }
+    }
+    // 处理斜杠分隔格式:YYYY/M/D 或 D/M/YYYY
+    else if (date.includes('/')) {
+      const parts = date.split('/');
+      if (parts.length === 3) {
+        // 判断是 YYYY/MM/DD 还是 DD/MM/YYYY
+        if (parts[0].length === 4) {
+          // YYYY/MM/DD
+          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[2].length === 4) {
+          // DD/MM/YYYY
+          const year = parts[2];
+          const month = String(parseInt(parts[1])).padStart(2, '0');
+          const day = String(parseInt(parts[0])).padStart(2, '0');
+          d = new Date(`${year}-${month}-${day}`);
+        } else {
+          d = new Date(date);
+        }
+      } else {
+        d = new Date(date);
+      }
+    }
+    // 处理短横线分隔格式: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}`);
+      } else {
+        d = new Date(date);
+      }
+    }
+    // 处理纯数字格式:YYYYMMDD
+    else if (/^\d{8}$/.test(date)) {
+      const year = date.substring(0, 4);
+      const month = date.substring(4, 6);
+      const day = date.substring(6, 8);
+      d = new Date(`${year}-${month}-${day}`);
+    }
+    // 其他字符串格式
+    else {
+      d = new Date(date);
+    }
+  } 
+  // 处理时间戳(数字)
+  else if (typeof date === 'number') {
+    // 判断是否为13位时间戳(毫秒)或10位时间戳(秒)
+    d = date > 10000000000 ? new Date(date) : new Date(date * 1000);
+  }
+  // 其他类型(如Date对象)
+  else {
+    d = new Date(date);
+  }
+  
+  // 检查日期是否有效
+  if (isNaN(d.getTime())) {
+    return '';  // 如果日期无效,返回空字符串
+  }
+  
   // 获取年、月、日
-  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 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,
@@ -139,9 +240,48 @@ export async function simulateUserInput(element, value) {
     element.value = value
     element.blur()
     return
-  }
-  element.value = value
+    }
+    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 }))
+    }
+  element.value = value
+    // simulateTyping(element,value,50)
   // 创建并触发 input 事件
   const inputEvent = new Event('input', { bubbles: true })
   element.dispatchEvent(inputEvent)