|
|
@@ -3,8 +3,12 @@ import * as XLSX from 'xlsx'
|
|
|
|
|
|
/**
|
|
|
* 修复luckysheet中的公式解析问题
|
|
|
- * 主要解决:Excel公式 =IF(E6=100%,"Y",N) 被luckyexcel解析为 =IF(E6=100%,"Y",_xleta.N) 的问题
|
|
|
- * 同时修复百分比格式和公式语法问题
|
|
|
+ * 主要解决以下问题:
|
|
|
+ * 1. Excel公式 =IF(E6=100%,"Y",N) 被luckyexcel解析为 =IF(E6=100%,"Y",_xleta.N) 的问题
|
|
|
+ * 2. 百分比格式问题:将100%转换为数值1
|
|
|
+ * 3. 计算链缺失问题:确保所有包含公式的单元格都被正确添加到calcChain中
|
|
|
+ * 4. 通用公式语法修复
|
|
|
+ *
|
|
|
* @param {Array} sheetData - luckysheet数据
|
|
|
* @returns {Array} 修复后的数据
|
|
|
*/
|
|
|
@@ -16,18 +20,150 @@ export function fixLuckySheetFormulas(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) {
|
|
|
- const formula = cell.v.f
|
|
|
- let newFormula = formula
|
|
|
+ // 第一步:修复公式语法问题(百分比格式、_xleta前缀等)
|
|
|
+ sheet = fixFormulaSyntax(sheet)
|
|
|
+
|
|
|
+ // 第二步:修复计算链,确保所有公式单元格都能正常计算
|
|
|
+ sheet = fixCalcChain(sheet)
|
|
|
+
|
|
|
+ return sheet
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ return sheetData
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 修复公式语法问题
|
|
|
+ * 包括:_xleta前缀修复、百分比格式转换等
|
|
|
+ *
|
|
|
+ * @param {Object} sheet - 单个工作表数据
|
|
|
+ * @returns {Object} 修复后的工作表
|
|
|
+ */
|
|
|
+function fixFormulaSyntax(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能理解的格式
|
|
|
+ // 同时需要处理小数百分比,如 =IF(E15<=0.01%,"Y","N"),应该转换为 =IF(E15<=0.0001,"Y","N")
|
|
|
+
|
|
|
+ // 第一步:先修复可能存在的重复小数点问题(如0.0.01应该修正为0.0001)
|
|
|
+ // 这种问题可能发生在luckyexcel解析过程中
|
|
|
+ // 问题:0.01% 被错误解析为 0.0.01,实际上应该是 0.0001
|
|
|
+ const duplicateDotRegex = /(\d+)\.(\d+)\.(\d+)/g
|
|
|
+ const duplicateDotMatches = [...newFormula.matchAll(duplicateDotRegex)]
|
|
|
+
|
|
|
+ if (duplicateDotMatches.length > 0) {
|
|
|
+ duplicateDotMatches.forEach(match => {
|
|
|
+ const fullMatch = match[0]
|
|
|
+ const integerPart = match[1] // 如 "0"
|
|
|
+ const firstDecimalPart = match[2] // 如 "0"
|
|
|
+ const secondDecimalPart = match[3] // 如 "01"
|
|
|
+
|
|
|
+ // 处理常见的百分比解析错误模式
|
|
|
+ // 1. 0.01% 被解析为 0.0.01,应该修正为 0.0001
|
|
|
+ // 2. 0.1% 被解析为 0.0.1,应该修正为 0.001
|
|
|
+ // 3. 0.001% 被解析为 0.0.001,应该修正为 0.00001
|
|
|
+ // 经过分析发现规律:0.0.xxx 模式通常表示 xxx/100 的百分比
|
|
|
+ // 例如:0.0.01 表示 0.01% = 0.0001,0.0.1 表示 0.1% = 0.001
|
|
|
|
|
|
- // 修复 _xleta.N 问题
|
|
|
- // 检查公式模式: =IF(E6=100%,"Y",_xleta.N)
|
|
|
- // 修复为: =IF(E6=100%,"Y","N")
|
|
|
+ let correctNumber
|
|
|
+ if (integerPart === '0' && firstDecimalPart === '0') {
|
|
|
+ // 针对 0.0.xxx 模式的特殊修复
|
|
|
+ // 规则:转换为 0.00xxx,其中 xxx 是 secondDecimalPart
|
|
|
+ // 例如:0.0.01 → 0.0001,0.0.1 → 0.001,0.0.001 → 0.00001
|
|
|
+ correctNumber = parseFloat(`0.00${secondDecimalPart}`)
|
|
|
+ } else {
|
|
|
+ // 其他情况:使用更保守的修复
|
|
|
+ // 将 X.Y.Z 合并为 X.YZ(假设第一个小数部分是整数部分的小数)
|
|
|
+ correctNumber = parseFloat(`${integerPart}.${firstDecimalPart}${secondDecimalPart}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ newFormula = newFormula.replace(fullMatch, correctNumber.toString())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二步:处理百分比转换为数值的问题
|
|
|
+ // 支持整数和小数百分比,如 100%、0.01%、1.5%
|
|
|
+ const percentRegex = /(\d+(?:\.\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,0.01%就是0.0001
|
|
|
+ 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
|
|
|
+ // 修复 _xleta.N 问题(与上面相同的逻辑)
|
|
|
const regex = /(IF\([^,]+,[^,]+,)_xleta\.(N)(\))/g
|
|
|
const matches = [...formula.matchAll(regex)]
|
|
|
|
|
|
@@ -37,18 +173,15 @@ export function fixLuckySheetFormulas(sheetData) {
|
|
|
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)]
|
|
|
|
|
|
@@ -57,7 +190,6 @@ export function fixLuckySheetFormulas(sheetData) {
|
|
|
const fullMatch = match[0]
|
|
|
const variableName = match[1]
|
|
|
|
|
|
- // 判断是否为简单变量名(单个字母)
|
|
|
if (/^[A-Za-z]$/.test(variableName)) {
|
|
|
newFormula = newFormula.replace(fullMatch, `"${variableName}"`)
|
|
|
}
|
|
|
@@ -65,8 +197,36 @@ export function fixLuckySheetFormulas(sheetData) {
|
|
|
}
|
|
|
|
|
|
// 智能修复百分比格式问题
|
|
|
- // 对于公式 =IF(E6=100%,"Y","N"),需要将100%转换为luckysheet能理解的格式
|
|
|
- const percentRegex = /(\d+)%/g
|
|
|
+ // 处理整数和小数百分比,如 100% 转换为 1,0.01% 转换为 0.0001
|
|
|
+
|
|
|
+ // 第一步:先修复可能存在的重复小数点问题(如0.0.01应该修正为0.0001)
|
|
|
+ const duplicateDotRegex = /(\d+)\.(\d+)\.(\d+)/g
|
|
|
+ const duplicateDotMatches = [...newFormula.matchAll(duplicateDotRegex)]
|
|
|
+
|
|
|
+ if (duplicateDotMatches.length > 0) {
|
|
|
+ duplicateDotMatches.forEach(match => {
|
|
|
+ const fullMatch = match[0]
|
|
|
+ const integerPart = match[1] // 如 "0"
|
|
|
+ const firstDecimalPart = match[2] // 如 "0"
|
|
|
+ const secondDecimalPart = match[3] // 如 "01"
|
|
|
+
|
|
|
+ // 处理常见的百分比解析错误模式
|
|
|
+ let correctNumber
|
|
|
+ if (integerPart === '0' && firstDecimalPart === '0') {
|
|
|
+ // 针对 0.0.xxx 模式的特殊修复
|
|
|
+ // 规则:转换为 0.00xxx,其中 xxx 是 secondDecimalPart
|
|
|
+ correctNumber = parseFloat(`0.00${secondDecimalPart}`)
|
|
|
+ } else {
|
|
|
+ // 其他情况:使用更保守的修复
|
|
|
+ correctNumber = parseFloat(`${integerPart}.${firstDecimalPart}${secondDecimalPart}`)
|
|
|
+ }
|
|
|
+
|
|
|
+ newFormula = newFormula.replace(fullMatch, correctNumber.toString())
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二步:处理百分比转换为数值的问题
|
|
|
+ const percentRegex = /(\d+(?:\.\d+)?)%/g
|
|
|
const percentMatches = [...newFormula.matchAll(percentRegex)]
|
|
|
|
|
|
if (percentMatches.length > 0) {
|
|
|
@@ -75,90 +235,113 @@ export function fixLuckySheetFormulas(sheetData) {
|
|
|
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
|
|
|
+ cell.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
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 修复计算链(calcChain)
|
|
|
+ * 问题描述:某些包含公式的单元格没有被正确添加到calcChain中,导致公式不计算
|
|
|
+ * 例如:G11和H11公式工作,但I11及以后的公式不工作
|
|
|
+ *
|
|
|
+ * 解决方案:
|
|
|
+ * 1. 遍历所有包含公式的单元格(包括celldata和data数组)
|
|
|
+ * 2. 检查这些单元格是否已经在calcChain中
|
|
|
+ * 3. 如果不在,则添加到calcChain中
|
|
|
+ * 4. 确保计算链的正确性,使所有公式都能正常计算
|
|
|
+ *
|
|
|
+ * @param {Object} sheet - 单个工作表数据
|
|
|
+ * @returns {Object} 修复后的工作表
|
|
|
+ */
|
|
|
+function fixCalcChain(sheet) {
|
|
|
+ if (!sheet) return sheet
|
|
|
+
|
|
|
+ // 确保calcChain存在
|
|
|
+ if (!sheet.calcChain) {
|
|
|
+ sheet.calcChain = []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建现有计算链的快速查找映射
|
|
|
+ // 使用 "行_列" 作为键,例如 "10_6" 表示第11行G列
|
|
|
+ const existingCalcMap = new Map()
|
|
|
+ sheet.calcChain.forEach(item => {
|
|
|
+ if (item && typeof item.r === 'number' && typeof item.c === 'number') {
|
|
|
+ const key = `${item.r}_${item.c}`
|
|
|
+ existingCalcMap.set(key, true)
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 辅助函数:添加单元格到计算链
|
|
|
+ * @param {number} row - 行索引(0-based)
|
|
|
+ * @param {number} col - 列索引(0-based)
|
|
|
+ */
|
|
|
+ const addToCalcChain = (row, col) => {
|
|
|
+ const key = `${row}_${col}`
|
|
|
+
|
|
|
+ // 如果已经存在,跳过
|
|
|
+ if (existingCalcMap.has(key)) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到计算链
|
|
|
+ sheet.calcChain.push({
|
|
|
+ r: row,
|
|
|
+ c: col,
|
|
|
+ index: sheet.index || "0" // 使用工作表索引,默认为"0"
|
|
|
+ })
|
|
|
+
|
|
|
+ // 更新映射
|
|
|
+ existingCalcMap.set(key, true)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第一步:遍历celldata,查找有公式的单元格
|
|
|
+ if (Array.isArray(sheet.celldata)) {
|
|
|
+ sheet.celldata.forEach(cell => {
|
|
|
+ if (cell && typeof cell.r === 'number' && typeof cell.c === 'number') {
|
|
|
+ // 检查是否有公式
|
|
|
+ const hasFormula = cell.v && cell.v.f
|
|
|
+ if (hasFormula) {
|
|
|
+ addToCalcChain(cell.r, cell.c)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二步:遍历data数组,查找有公式的单元格
|
|
|
+ if (Array.isArray(sheet.data)) {
|
|
|
+ sheet.data.forEach((row, rowIndex) => {
|
|
|
+ if (Array.isArray(row)) {
|
|
|
+ row.forEach((cell, colIndex) => {
|
|
|
+ if (cell && cell.f) {
|
|
|
+ addToCalcChain(rowIndex, colIndex)
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
- return sheet
|
|
|
})
|
|
|
- } catch (error) {
|
|
|
- return sheetData
|
|
|
}
|
|
|
+
|
|
|
+ // 第三步:处理特殊情况 - 检查是否有合并单元格影响公式计算
|
|
|
+ // 如果单元格是合并单���格的一部分,可能需要特殊处理
|
|
|
+ if (sheet.config && sheet.config.merge) {
|
|
|
+ // 可以在这里添加合并单元格的特殊处理逻辑
|
|
|
+ // 例如:确保合并单元格的主单元格在计算链中
|
|
|
+ }
|
|
|
+
|
|
|
+ return sheet
|
|
|
}
|
|
|
|
|
|
/**
|