ttk-app-core
Version:
enterprise develop framework
479 lines (442 loc) • 18.6 kB
JavaScript
// import { number, math } from 'edf-utils'
import math from '../math'
let calcjs = math
const typeMap = {
calculateAndCheck: 0,
onlySetValue: 1,
onlyCalculate: 2,
onlyCheck: 3,
}
const CHECK = 1; //校验类型
const CALC = 0; //计算类型
const PATHRGX = /[a-zA-Z0-9]+[_]{1}[a-zA-Z0-9_]+/g
const SIGNRGX = /[==|\!=|<=|>=|<|>|?|\|\||\&\&|\:]+/g
const GSRGX = /[^=&^\!&^<&^>&^?&^\|&\&|\:]+/g
//数据格式
/*****
* data = {所有数据,每个子元素是一个表格,必有项
* table1Data: {},
* table2Data: {},
* table3Data: {}
* }
* changeData = {
* changeTableName: 'table1Data',改变的表的名称
* changeTable: {},改变所修改项后的数据(比如把表格第一行第一列由0改为1,changeTable的数据是改为1后的数据)
* changeItems: {},改变单元格的路径,即值
* failCount: 0 非公式校验后得出的错误数,如果表内无计算则为0。(非公式校验,调用这个公式校验前的校验。)
* }
* formulas 所有计算公式
* checks 所有校验公式
* notFormulasCheckFailCounts = {}, //非公式校验的错误数 必填项
* formulasCheckFailCounts = {} //公式校验的错误数 必填项
*/
// type 为 1计算加校验,type为2计算,type为3校验,type为4不计算也不校验
function setDataByFormula ( { type, data, changeData, formulas, checks, notFormulasCheckFailCounts = {}, formulasCheckFailCounts = {} } ) {
let willCalcFormula = [],
checkRes
//第一步 将更新数据
//第二步 根据 更改项 提取出有关公式,并对公式进行排序
//第三步 计算
//第四步 校验
// 1
if( !data ) return console.log('没有数据')
let oldData = JSON.parse( JSON.stringify( data ) )
if( !changeData || !changeData.changeTableName || !changeData.changeTable ) return console.log('没有改变的表名或没有改变表的数据')
data[ changeData.changeTableName ] = changeData.changeTable
notFormulasCheckFailCounts[ changeData.changeTableName ] = changeData.failCount || 0
if( type == calculate.onlySetValue ) return {
type,
data,
changeData,
notFormulasCheckFailCounts,
formulasCheckFailCounts,
checkData: undefined
}
// 2提取有关公式
let changeItems = changeData.changeItems
if( type == typeMap.onlyCalculate || type == typeMap.calculateAndCheck ) {
willCalcFormula = getAboutFormulas( changeItems, formulas, willCalcFormula, 0 )//得到所有相关公式
}
// 3计算
if( type == typeMap.onlyCalculate || type == typeMap.calculateAndCheck ) {
data = calculate( { changeItems ,data, oldData, formulas, willCalcFormula } )
}
// 4校验
if( type == typeMap.onlyCheck || type == typeMap.calculateAndCheck ) {
checkRes = checkData( data, checks )
formulasCheckFailCounts = checkRes.tableFailCounts
}
//返回数据
return {
type,
data,
changeData,
notFormulasCheckFailCounts,
formulasCheckFailCounts,
checkData: checkRes.tableFailCounts[ changeData.changeTableName ]? checkRes.tableFailCounts[ changeData.changeTableName ].target:[]
}
}
//得到所有需要计算的相关公式
function getAboutFormulas ( changeItems, formulas, willCalcFormula, index ) {
if( !changeItems || changeItems.length ==0 ) return []
if( !index ) index = 0
changeItems.map( ( item , itemIndex ) => {
--index
let response = getAboutFormulasItem( changeItems[itemIndex].path, formulas, willCalcFormula , index, true )
index = response.index
willCalcFormula = response.willCalcFormula
})
willCalcFormula = sortFormulaList( willCalcFormula )
return willCalcFormula
}
//通过递归给将有依赖关系的公式排序 把公式中第一个等号右边含有修改路径的公式 取出来
function getAboutFormulasItem ( path, tFormulaList, willCalcFormula, index, fisrtTime ) {
if( !willCalcFormula || !willCalcFormula.length ) {
willCalcFormula = []
}
let formulaStrArr = willCalcFormula.map( item => item.formula)
tFormulaList.map( item => {
let tFormulaStrList = item.split('='),
tFormulaRight
if( tFormulaStrList.length > 1 ) {
tFormulaStrList.shift()
tFormulaRight = tFormulaStrList.join('=')
} else {
tFormulaRight = ''
}
if( item.split('=').length>1 && tFormulaRight.match(PATHRGX).indexOf( path ) != -1 ) {
if( formulaStrArr.indexOf( item )<0 ) {
formulaStrArr.push( item )
willCalcFormula.push( {
formula: item,
path
} )
}
}
})
if( willCalcFormula.length ==0 || index == willCalcFormula.length -1 ) {
return {
willCalcFormula: willCalcFormula,
index: index
}
} else {
index++
return getAboutFormulasItem( willCalcFormula[index].formula.split('=')[0], tFormulaList, willCalcFormula, index )
}
}
//将得到的所有相关公式进行排序 把公式第一个等号左边的路径取出(设为公式1), 如果前边公式时的第一个等号右边含这个路径(设为公式2),就把这个公式1提前到公式2前
function sortFormulaList ( willCalcFormula ) {
for(let i=willCalcFormula.length-1; i>0; i--) {
let leftPath = willCalcFormula[i].formula.split('=')[0],
lastIndex //最后公式右侧出现字段的地方
for(let j=i-1; j>0; j--) {
let rightStr = willCalcFormula[j].formula.split('=')[1]
if( rightStr.match(PATHRGX).indexOf( leftPath ) >=0 ) {
lastIndex = j
}
}
if(lastIndex!==undefined) {
let curItem = willCalcFormula[i]
willCalcFormula.splice(i , 1 )
willCalcFormula.splice(lastIndex , 0 , curItem )
lastIndex = undefined
i++
}
}
return willCalcFormula
}
//计算方法入口 如果只改变了一个单元格 只调用一次 calculationData方法;如果改了多个单元格,多次调用calculationData方法
function calculate ( { changeItems , data, oldData ,formulas ,willCalcFormula } ) {
let res
if(changeItems && changeItems.length ==1 ) {
//如果有涉及的公式
if( willCalcFormula && willCalcFormula.length ) {
let selfCals = isSelfCals( willCalcFormula[0].formula, changeItems[0] )
data = calculationData( changeItems[0].newValue , changeItems[0].oldValue , changeItems[0].path , data, oldData, willCalcFormula[0], formulas, willCalcFormula, selfCals )
}
} else if(changeItems && changeItems.length >1 ) {
if( willCalcFormula && willCalcFormula.length ) {
changeItems.map( item => {
let selfCals = isSelfCals( willCalcFormula[0].formula, item )
data = calculationData( item.newValue , item.oldValue , item.path , data, oldData, willCalcFormula[0], formulas, willCalcFormula, selfCals )
})
}
}
return data
}
//本年累计类型的特殊计算 减去原来的值加上新值
function isSelfCals( formula, changeItem ) {
if( !changeItem ) return
let newValue = changeItem.newValue,
oldValue = changeItem.oldValue,
path = changeItem.path
if( oldValue === undefined ) {
oldValue = '0.00'
}
if( formula.split('=')[1].indexOf( formula.split('=')[0]) >-1 ) {
let str = formula.split(path)[0].substr(-1),
value
if( str =='-' ) {
value = '(' + ( clearThousPos(newValue) + clearThousPos(oldValue)) + ')'
} else if( str == '*') {
value = '(' + (clearThousPos(newValue) / clearThousPos(oldValue)) + ')'
} else if(str == '/') {
value = '(' + clearThousPos(newValue) * clearThousPos(oldValue) + ')'
} else {
value = '(' + (clearThousPos(newValue) - clearThousPos(oldValue)) + ')'
}
return {
newValue: value,
oldValue: oldValue,
path: path
}
} else {
return false
}
}
//计算通过递归所有公式计算出最新数据
function calculationData ( newValue, oldValue, pathName, changeData, oldData, formulaObj, formulas, willCalcFormula, selfCals ) {
let formulaStr = formulaObj.formula,
responsePath = formulaStr.split('=')[0],//目标值
responseTName,
formula = formulaStr.split('=').splice(1).join('='),
responseNum,
response,
preValue
response = transNum( formula ,changeData, selfCals )
preValue = getPathData( changeData, responsePath )// 取出计算前值
changeData = setPathData( changeData, responsePath, mathRound( response, 2 ) )//更新计算结果
willCalcFormula.shift()
if( willCalcFormula && willCalcFormula.length==0 ) {
//这时计算完毕
return changeData
} else {
let nextPath ,nextOldValue, nextNewValue
if( willCalcFormula[0].path == pathName ) {
nextPath = pathName
nextOldValue = oldValue
nextNewValue = newValue
} else {
nextPath = willCalcFormula[0].path
nextOldValue = getPathData( oldData, willCalcFormula[0].path )
nextNewValue = getPathData( changeData, willCalcFormula[0].path )
}
let nextSelfCals = isSelfCals( willCalcFormula[0].formula ,{ newValue: nextNewValue, oldValue: nextOldValue,path: nextPath} )
return calculationData( nextNewValue, nextOldValue, nextPath, changeData, oldData, willCalcFormula[0], formulas, willCalcFormula, nextSelfCals )
}
}
//将带有数字的路径改成可以解析的路径,将名字由 a_b_c_4_f 改成 a_b_c[4]_f
function transPath( path ) {
let NUMRGX = /\_[\d]+\_/g,
numArr = path.match( NUMRGX )
if(numArr != null) {
numArr.map( item => {
let numStr = '[' + parseInt( item.replace('_','') ) + ']_'
path = path.replace( item, numStr )
})
}
return path.replace(/\_/g,'.')
}
//将传入的数字num 保留digit位小数(默认两位)
function mathRound ( num , digit = 2 ) {//数字,精确位数 默认
return Math.round(num * (Math.pow(10 , digit)) ) / (Math.pow(10 , digit))
}
//把公式中的路径 转换成 路径所对应的值 ,并计算出公式的结果
function transNum (formula, changeData, selfCals, type) { //
console.log( formula )
if (selfCals) {
formula = formula.replace(selfCals.path, selfCals.newValue)
}
let paths = formula.match(PATHRGX)
paths.map(item => {
if (isNaN(item - 0)) {
try {
let pathStr = transPath(item),
value
value = eval('changeData.' + pathStr)
if( pathStr.indexOf('.hmc')>=0 || value == 'N' || value == 'Y' || value=='false' || value=='true' ) {
value = "'"+value+"'"
} else {
value = value ? value : 0
value = value == '- -' ? 0 : value
value = '(' + value + ')'
}
formula = formula.replace(item, value)
} catch (err) {
console.log(err)
}
}
})
//
let signArr = formula.match(SIGNRGX),
gsArr = formula.match(GSRGX),
signChangeArr,
newFormula = ''
if (signArr) {//校验时 计算的结果保留两位数字再比较,防止等式两边因有一边未保留两位小数导致校验出现问题
gsArr.map((item, index) => {
if( formula.indexOf(signArr[0]) == 0 && gsArr.length == signArr.length ) {
if( item.match(/[\+|\-|\*|\\|\%]/g) ) {
if( gsArr[index].match(/\(/g).length == gsArr[index].match(/\)/g).length ) {
newFormula += (signArr[index] + "(calcjs("+gsArr[index]+",2))" )
} else {
let leftLen = gsArr[index].match(/\(/g).length,
rightLen = gsArr[index].match(/\)/g).length
if( leftLen > rightLen ) {
newFormula += (signArr[index] + gsArr[index].substr(0,leftLen-rightLen) + "(calcjs("+ gsArr[index].substr(leftLen-rightLen)+ ",2))" )
} else if( gsArr[index].match(/\(/g).length < gsArr[index].match(/\)/g).length ) {
newFormula += (signArr[index] + "(calcjs("+gsArr[index].substr(0,gsArr[index].length+leftLen-rightLen)+",2))"+ gsArr[index].substr(gsArr[index].length+leftLen-rightLen) )
}
newFormula += (signArr[index] + gsArr[index] )
}
} else {
newFormula += (signArr[index] + gsArr[index] )
}
} else {
if( item.match(/[\+|\-|\*|\\|\%]/g) ) {
let leftLen = gsArr[index].match(/\(/g).length,
rightLen = gsArr[index].match(/\)/g).length
if (index <= signArr.length - 1) {
if( leftLen == rightLen ) {
newFormula += ("(calcjs("+gsArr[index]+",2))" + signArr[index])
} else {
if( leftLen > rightLen ) {
newFormula += (gsArr[index].substr(0,leftLen-rightLen)+"(calcjs("+ gsArr[index].substr(leftLen-rightLen)+",2))" + signArr[index])
} else if( leftLen < rightLen ) {
newFormula += ("(calcjs("+ gsArr[index].substr(0,gsArr[index].length+leftLen-rightLen)+",2))"+gsArr[index].substr(gsArr[index].length+leftLen-rightLen) + signArr[index])
}
}
} else {
if( leftLen == rightLen ) {
newFormula += "(calcjs("+gsArr[index]+",2))"
} else {
if( leftLen > rightLen ) {
newFormula += gsArr[index].substr(0,leftLen-rightLen)+"(calcjs("+ gsArr[index].substr(leftLen-rightLen)+",2))"
} else if( leftLen < rightLen ) {
newFormula += "(calcjs("+ gsArr[index].substr(0,gsArr[index].length+leftLen-rightLen)+",2))"+gsArr[index].substr(gsArr[index].length+leftLen-rightLen)
}
}
}
} else {
if (index <= signArr.length - 1) {
newFormula += (gsArr[index] + signArr[index])
} else {
newFormula += gsArr[index]
}
}
}
})
formula = newFormula
}
console.log( formula )
try {
return eval(formula)
} catch(e) {
console.log( formula + '---' + e + ' 公式有问题')
return false
}
}
//通过路径(path)获取数据(changeData)中指定字段的值
function getPathData ( changeData, path ) {
let pathStr = transPath( path ),
value = eval('changeData.' + pathStr )
return value
}
//通过路径(path)设置数据(changeData)中指定字段的值为(value)
function setPathData ( changeData, path, value ) {
let pathStr = transPath( path )
eval('changeData.' + pathStr + '=' + value )
return changeData
}
//校验
function checkData ( changeData, checkList ) {
let newFailList = []
checkList.map( item => {//创建新的校验不通过的列表
let res = transNum( item.formula , changeData , undefined, CHECK )
if( res ) { //计算成功为 需要弹出提示
newFailList.push( item )
}
})
let tableFailCounts = getFailData( newFailList )
return {
tableFailCounts: tableFailCounts,
newFailList: newFailList
}
}
//得到每个表 有几个校验未通过项
function getFailData ( newFailList ) {
let checkItemList = [],
cellNameList = [],
checkTargetList = []
let tableFailCounts = {}
newFailList.map( item => {
let targetArr = item.targetName.split(',')
targetArr.map( target => {
checkItemList.push({
formula: item.formula,
target: target,
message: item.message
})
})
})
//checkItemList 分开所有需要提示的单元格
newFailList.map( item => {
cellNameList = cellNameList.concat( item.targetName.split(',') )
})
cellNameList = Array.from( new Set( cellNameList ) ) //去除重复元素
//相同单元格的合并
cellNameList.map( item => {
let message = []
checkItemList.map( cell => {
if( cell.target == item ) {
message.push( cell.message )
}
} )
checkTargetList.push({
target: item,
message: message
})
})
//将不同的表格项分组
checkTargetList.map ( cell => {
let tName = cell.target.split('_')[0]
if( tableFailCounts[tName] ) {
tableFailCounts[tName].num += 1
tableFailCounts[tName].target.push(cell)
} else {
let arr = [ cell ]
tableFailCounts[tName] = {
num:1,
target: arr
}
}
})
return tableFailCounts
}
/**
* 去除数值的千分位
* num 具体数值
* isRetOriginalVal true: 保留原始值,false:返回0
*/
function clearThousPos(num, isRetOriginalVal) {
let ret
if (num && num.toString().indexOf(',') > -1) {
ret = parseFloat(num.toString().replace(/,/g, ""))
} else {
if (num == undefined || isNaN(num) || num == null || num.toString().replace(/\s+/g, '') == '') {
if (!!isRetOriginalVal) {
ret = num
} else {
ret = 0
}
} else {
ret = parseFloat(num)
}
}
return ret
}
export default {
setDataByFormula,
calculationData,
checkData,
getFailData
}