Procházet zdrojové kódy

Merge branch 'master' of http://119.23.210.103:3000/ZhuJiaHao/zdqy_firm_former

tianxinyu před 1 týdnem
rodič
revize
addc17257b

+ 98 - 2
public/luckysheet.html

@@ -29,6 +29,102 @@
   <script>
     let isInitialized = false
     
+    // 修复luckysheet公式数据
+    function fixSheetDataFormulas(sheetData) {
+      if (!sheetData || !Array.isArray(sheetData)) return sheetData
+      
+      return sheetData.map(sheet => {
+        if (!sheet) return sheet
+        
+        // 修复celldata中的公式
+        if (Array.isArray(sheet.celldata)) {
+          sheet.celldata.forEach(cell => {
+            if (cell && cell.v && cell.v.f) {
+              let formula = cell.v.f
+              let newFormula = formula
+              
+              // 将 _xleta.N 替换为 "N"
+              if (formula.includes('_xleta.')) {
+                newFormula = newFormula.replace(/_xleta\.([A-Za-z]+)/g, (match, variable) => {
+                  // 如果是单个字母,替换为字符串
+                  if (/^[A-Za-z]$/.test(variable)) {
+                    return `"${variable}"`
+                  }
+                  return variable
+                })
+              }
+              
+              // 修复百分比格式问题
+              // 将百分比转换为数值,因为Excel中100%就是数值1
+              const percentRegex = /(\d+)%/g
+              const percentMatches = [...newFormula.matchAll(percentRegex)]
+              
+              if (percentMatches.length > 0) {
+                percentMatches.forEach(match => {
+                  const fullMatch = match[0]
+                  const number = match[1]
+                  const percentValue = parseFloat(number) / 100
+                  
+                  // 将百分比转换为数值
+                  newFormula = newFormula.replace(fullMatch, percentValue.toString())
+                })
+              }
+              
+              if (newFormula !== formula) {
+                cell.v.f = newFormula
+              }
+            }
+          })
+        }
+        
+        // 修复data数组中的公式
+        if (Array.isArray(sheet.data)) {
+          sheet.data.forEach(row => {
+            if (Array.isArray(row)) {
+              row.forEach(cell => {
+                if (cell && cell.f) {
+                  let formula = cell.f
+                  let newFormula = formula
+                  
+                  // 将 _xleta.N 替换为 "N"
+                  if (formula.includes('_xleta.')) {
+                    newFormula = newFormula.replace(/_xleta\.([A-Za-z]+)/g, (match, variable) => {
+                      // 如果是单个字母,替换为字符串
+                      if (/^[A-Za-z]$/.test(variable)) {
+                        return `"${variable}"`
+                      }
+                      return variable
+                    })
+                  }
+                  
+                  // 修复百分比格式问题
+                  const percentRegex = /(\d+)%/g
+                  const percentMatches = [...newFormula.matchAll(percentRegex)]
+                  
+                  if (percentMatches.length > 0) {
+                    percentMatches.forEach(match => {
+                      const fullMatch = match[0]
+                      const number = match[1]
+                      const percentValue = parseFloat(number) / 100
+                      
+                      // 将百分比转换为数值
+                      newFormula = newFormula.replace(fullMatch, percentValue.toString())
+                    })
+                  }
+                  
+                  if (newFormula !== formula) {
+                    cell.f = newFormula
+                  }
+                }
+              })
+            }
+          })
+        }
+        
+        return sheet
+      })
+    }
+    
     window.addEventListener('message', function(event) {
       const { type, data, readonly } = event.data
       
@@ -80,7 +176,7 @@
             rowHeight: !readonly,
             columnWidth: !readonly
           },
-          data: data || [{ name: 'Sheet1', color: '', status: 1, order: 0, data: [[{ v: '' }]] }],
+          data: data ? fixSheetDataFormulas(data) : [{ name: 'Sheet1', color: '', status: 1, order: 0, data: [[{ v: '' }]] }],
           hook: {}
         })
         isInitialized = true
@@ -128,7 +224,7 @@
             rowHeight: !readonly,
             columnWidth: !readonly
           },
-          data: data,
+          data: fixSheetDataFormulas(data),
           hook: {}
         })
         isInitialized = true

+ 35 - 55
src/views/component/onlineSheet/onlineSheetData.vue

@@ -59,10 +59,9 @@
 </template>
 
 <script>
-import LuckyExcel from 'luckyexcel'
-import * as XLSX from 'xlsx'
 import { saveSheet, getSheet } from '@/api/platform/onlineSheet/onlineSheetApi'
 import { exportToExcel } from './onlineSheetExcelExport'  // Excel导出模块
+import { importExcelFile, getFormatTip } from './onlineSheetExcelImport'  // Excel导入模块
 
 export default {
   name: 'OnlineSheetData',
@@ -204,8 +203,9 @@ export default {
 
       // 如果是 .xls 文件,提示用户样式可能丢失
       if (fileExt === '.xls') {
+        const tipMessage = getFormatTip(fileExt)
         this.$confirm(
-          '.xls 格式导入时可能无法保留单元格样式(如颜色、边框等)。建议将文件另存为 .xlsx 格式后再导入,以完整保留样式。是否继续导入?',
+          tipMessage || '.xls 格式导入时可能无法保留单元格样式(如颜色、边框等)。建议将文件另存为 .xlsx 格式后再导入,以完整保留样式。是否继续导入?',
           '格式提示',
           {
             confirmButtonText: '继续导入',
@@ -213,72 +213,52 @@ export default {
             type: 'warning'
           }
         ).then(() => {
-          this.processFile(file, fileExt)
+          this.importFile(file)
         }).catch(() => {
           e.target.value = ''
         })
       } else {
-        this.processFile(file, fileExt)
+        this.importFile(file)
       }
     },
-    // 处理文件读取
-    processFile(file, fileExt) {
-      const reader = new FileReader()
-      reader.onload = (evt) => {
+    // 导入文件
+    async importFile(file) {
+      try {
+        // 显示加载状态
+        const loading = this.$loading({
+          lock: true,
+          text: '正在导入Excel文件...',
+          spinner: 'el-icon-loading',
+          background: 'rgba(0, 0, 0, 0.7)'
+        })
+        
         try {
-          // 如果是 .xls 文件,先用 xlsx 转换为 .xlsx
-          if (fileExt === '.xls') {
-            this.handleXlsFile(evt.target.result)
+          // 使用导入模块处理文件
+          const sheetData = await importExcelFile(file)
+          
+          if (sheetData && sheetData.length > 0) {
+            // 更新本地数据并发送到iframe显示
+            this.sheetData = sheetData
+            this.sendToIframe('setData', this.sheetData)
+            this.$message.success(`导入成功!共 ${sheetData.length} 个 Sheet`)
+            // 导入后不自动保存,等待用户点击保存按钮
           } else {
-            // .xlsx 文件直接用 LuckyExcel 解析
-            this.handleXlsxFile(evt.target.result)
+            this.$message.error('导入失败:Excel 文件为空或格式错误!')
           }
         } catch (error) {
           console.error('导入错误:', error)
           this.$message.error(`导入失败:${error.message || '文件解析错误,请检查文件格式是否正确'}`)
+        } finally {
+          loading.close()
+          // 清空文件选择器
+          if (this.$refs.fileInput) {
+            this.$refs.fileInput.value = ''
+          }
         }
+      } catch (outerError) {
+        console.error('导入外层错误:', outerError)
+        this.$message.error('导入过程发生异常')
       }
-      reader.onerror = () => {
-        this.$message.error('导入失败:文件读取错误!')
-      }
-      reader.readAsArrayBuffer(file)
-    },
-    // 处理 .xls 文件
-    handleXlsFile(arrayBuffer) {
-      try {
-        // 使用 xlsx 读取 .xls,保留单元格样式
-        const workbook = XLSX.read(arrayBuffer, { 
-          type: 'array',
-          cellStyles: true  // 保留样式信息
-        })
-        
-        // 转换为 .xlsx 格式的 ArrayBuffer,保留样式
-        const xlsxArrayBuffer = XLSX.write(workbook, { 
-          bookType: 'xlsx', 
-          type: 'array',
-          cellStyles: true,  // 保留样式
-          bookSST: false
-        })
-        
-        // 用 LuckyExcel 解析转换后的数据
-        this.handleXlsxFile(xlsxArrayBuffer)
-      } catch (error) {
-        console.error('XLS 转换错误:', error)
-        this.$message.error('导入失败:.xls 文件格式不支持或已损坏!')
-      }
-    },
-    // 处理 .xlsx 文件
-    handleXlsxFile(arrayBuffer) {
-      LuckyExcel.transformExcelToLucky(arrayBuffer, (exportJson, luckysheetfile) => {
-        if (exportJson.sheets && exportJson.sheets.length > 0) {
-          this.sheetData = exportJson.sheets
-          this.sendToIframe('setData', this.sheetData)
-          this.$message.success(`导入成功!共 ${exportJson.sheets.length} 个 Sheet`)
-          // 导入后不自动保存,等待用户点击保存按钮
-        } else {
-          this.$message.error('导入失败:Excel 文件为空或格式错误!')
-        }
-      })
     },
     // 保存表格数据
     async handleSave() {

+ 277 - 0
src/views/component/onlineSheet/onlineSheetExcelImport.js

@@ -0,0 +1,277 @@
+import LuckyExcel from 'luckyexcel'
+import * as XLSX from 'xlsx'
+
+/**
+ * 修复luckysheet中的公式解析问题
+ * 主要解决:Excel公式 =IF(E6=100%,"Y",N) 被luckyexcel解析为 =IF(E6=100%,"Y",_xleta.N) 的问题
+ * 同时修复百分比格式和公式语法问题
+ * @param {Array} sheetData - luckysheet数据
+ * @returns {Array} 修复后的数据
+ */
+export function fixLuckySheetFormulas(sheetData) {
+  try {
+    if (!sheetData || !Array.isArray(sheetData)) return sheetData
+    
+    // 遍历所有sheet
+    return sheetData.map(sheet => {
+      if (!sheet) return sheet
+      
+      // 修复celldata中的公式
+      if (Array.isArray(sheet.celldata)) {
+        sheet.celldata.forEach(cell => {
+          if (cell && cell.v && cell.v.f) {
+            const formula = cell.v.f
+            let newFormula = formula
+            
+            // 修复 _xleta.N 问题
+            // 检查公式模式: =IF(E6=100%,"Y",_xleta.N)
+            // 修复为: =IF(E6=100%,"Y","N")
+            
+            // 更精确的匹配:只匹配在函数参数位置且不在函数名位置的_xleta.N
+            const regex = /(IF\([^,]+,[^,]+,)_xleta\.(N)(\))/g
+            const matches = [...formula.matchAll(regex)]
+            
+            if (matches.length > 0) {
+              matches.forEach(match => {
+                const before = match[1]
+                const variable = match[2]
+                const after = match[3]
+                
+                // 对于字母N、Y等,作为字符串处理
+                const isSimpleLetter = /^[A-Za-z]$/.test(variable)
+                if (isSimpleLetter) {
+                  // 对于简单字母变量,替换为字符串
+                  const replacement = `${before}"${variable}"${after}`
+                  newFormula = newFormula.replace(match[0], replacement)
+                }
+              })
+            }
+            
+            // 更通用的修复:处理其他_xleta前缀的变量
+            // 例如: _xleta.Y, _xleta.VAR 等
+            const genericRegex = /_xleta\.([A-Za-z]+)/g
+            const genericMatches = [...newFormula.matchAll(genericRegex)]
+            
+            if (genericMatches.length > 0) {
+              genericMatches.forEach(match => {
+                const fullMatch = match[0]
+                const variableName = match[1]
+                
+                // 判断是否为简单变量名(单个字母)
+                if (/^[A-Za-z]$/.test(variableName)) {
+                  newFormula = newFormula.replace(fullMatch, `"${variableName}"`)
+                }
+              })
+            }
+            
+            // 智能修复百分比格式问题
+            // 对于公式 =IF(E6=100%,"Y","N"),需要将100%转换为luckysheet能理解的格式
+            const percentRegex = /(\d+)%/g
+            const percentMatches = [...newFormula.matchAll(percentRegex)]
+            
+            if (percentMatches.length > 0) {
+              percentMatches.forEach(match => {
+                const fullMatch = match[0]
+                const number = match[1]
+                const percentValue = parseFloat(number) / 100
+                
+                // 将百分比转换为数值,因为Excel中100%就是数值1
+                // 这是最可能工作的方案
+                newFormula = newFormula.replace(fullMatch, percentValue.toString())
+              })
+            }
+            
+            if (newFormula !== formula) {
+              cell.v.f = newFormula
+            }
+          }
+        })
+      }
+      
+      // 修复data数组中的公式
+      if (Array.isArray(sheet.data)) {
+        sheet.data.forEach((row, rowIndex) => {
+          if (Array.isArray(row)) {
+            row.forEach((cell, colIndex) => {
+              if (cell && cell.f) {
+                const formula = cell.f
+                let newFormula = formula
+                
+                // 修复 _xleta.N 问题(与上面相同的逻辑)
+                const regex = /(IF\([^,]+,[^,]+,)_xleta\.(N)(\))/g
+                const matches = [...formula.matchAll(regex)]
+                
+                if (matches.length > 0) {
+                  matches.forEach(match => {
+                    const before = match[1]
+                    const variable = match[2]
+                    const after = match[3]
+                    
+                    const isSimpleLetter = /^[A-Za-z]$/.test(variable)
+                    if (isSimpleLetter) {
+                      const replacement = `${before}"${variable}"${after}`
+                      newFormula = newFormula.replace(match[0], replacement)
+                    }
+                  })
+                }
+                
+                // 通用修复
+                const genericRegex = /_xleta\.([A-Za-z]+)/g
+                const genericMatches = [...newFormula.matchAll(genericRegex)]
+                
+                if (genericMatches.length > 0) {
+                  genericMatches.forEach(match => {
+                    const fullMatch = match[0]
+                    const variableName = match[1]
+                    
+                    if (/^[A-Za-z]$/.test(variableName)) {
+                      newFormula = newFormula.replace(fullMatch, `"${variableName}"`)
+                    }
+                  })
+                }
+                
+                // 智能修复百分比格式问题
+                const percentRegex = /(\d+)%/g
+                const percentMatches = [...newFormula.matchAll(percentRegex)]
+                
+                if (percentMatches.length > 0) {
+                  percentMatches.forEach(match => {
+                    const fullMatch = match[0]
+                    const number = match[1]
+                    const percentValue = parseFloat(number) / 100
+                    
+                    // 将百分比转换为数值
+                    newFormula = newFormula.replace(fullMatch, percentValue.toString())
+                  })
+                }
+                
+                if (newFormula !== formula) {
+                  cell.f = newFormula
+                }
+              }
+            })
+          }
+        })
+      }
+      
+      return sheet
+    })
+  } catch (error) {
+    return sheetData
+  }
+}
+
+/**
+ * 处理.xls文件导入
+ * @param {ArrayBuffer} arrayBuffer - .xls文件的ArrayBuffer
+ * @returns {Promise} 返回包含luckysheet数据的Promise
+ */
+export function handleXlsFile(arrayBuffer) {
+  return new Promise((resolve, reject) => {
+    try {
+      // 使用 xlsx 读取 .xls,保留单元格样式
+      const workbook = XLSX.read(arrayBuffer, { 
+        type: 'array',
+        cellStyles: true  // 保留样式信息
+      })
+      
+      // 转换为 .xlsx 格式的 ArrayBuffer,保留样式
+      const xlsxArrayBuffer = XLSX.write(workbook, { 
+        bookType: 'xlsx', 
+        type: 'array',
+        cellStyles: true,  // 保留样式
+        bookSST: false
+      })
+      
+      // 用 LuckyExcel 解析转换后的数据
+      handleXlsxFile(xlsxArrayBuffer)
+        .then(resolve)
+        .catch(reject)
+      
+    } catch (error) {
+      reject(new Error('导入失败:.xls 文件格式不支持或已损坏!'))
+    }
+  })
+}
+
+/**
+ * 处理.xlsx文件导入
+ * @param {ArrayBuffer} arrayBuffer - .xlsx文件的ArrayBuffer
+ * @returns {Promise} 返回包含luckysheet数据的Promise
+ */
+export function handleXlsxFile(arrayBuffer) {
+  return new Promise((resolve, reject) => {
+    LuckyExcel.transformExcelToLucky(arrayBuffer, (exportJson, luckysheetfile) => {
+      if (exportJson.sheets && exportJson.sheets.length > 0) {
+        // 修复导入后的公式问题
+        const fixedSheets = fixLuckySheetFormulas(exportJson.sheets)
+        resolve(fixedSheets)
+      } else {
+        reject(new Error('导入失败:Excel 文件为空或格式错误!'))
+      }
+    })
+  })
+}
+
+/**
+ * 导入Excel文件的主函数
+ * @param {File} file - Excel文件
+ * @returns {Promise} 返回包含luckysheet数据的Promise
+ */
+export async function importExcelFile(file) {
+  return new Promise((resolve, reject) => {
+    const fileName = file.name
+    const fileExt = fileName.substring(fileName.lastIndexOf('.')).toLowerCase()
+    
+    if (fileExt !== '.xlsx' && fileExt !== '.xls') {
+      reject(new Error('仅支持导入 .xlsx 和 .xls 格式的 Excel 文件!'))
+      return
+    }
+    
+    const reader = new FileReader()
+    
+    reader.onload = (evt) => {
+      try {
+        if (fileExt === '.xls') {
+          // .xls 文件先用 xlsx 转换为 .xlsx
+          handleXlsFile(evt.target.result)
+            .then(resolve)
+            .catch(reject)
+        } else {
+          // .xlsx 文件直接用 LuckyExcel 解析
+          handleXlsxFile(evt.target.result)
+            .then(resolve)
+            .catch(reject)
+        }
+      } catch (error) {
+        reject(new Error(`导入失败:${error.message || '文件解析错误,请检查文件格式是否正确'}`))
+      }
+    }
+    
+    reader.onerror = () => {
+      reject(new Error('导入失败:文件读取错误!'))
+    }
+    
+    reader.readAsArrayBuffer(file)
+  })
+}
+
+/**
+ * 获取导入文件的格式提示
+ * @param {string} fileExt - 文件扩展名
+ * @returns {string} 提示信息
+ */
+export function getFormatTip(fileExt) {
+  if (fileExt === '.xls') {
+    return '.xls 格式导入时可能无法保留单元格样式(如颜色、边框等)。建议将文件另存为 .xlsx 格式后再导入,以完整保留样式。是否继续导入?'
+  }
+  return ''
+}
+
+export default {
+  fixLuckySheetFormulas,
+  handleXlsFile,
+  handleXlsxFile,
+  importExcelFile,
+  getFormatTip
+}