import { create, all, number, pow } from 'mathjs' const math = create(all) math.config({ number: 'BigNumber', precision: 64 }) export { math } /* * 计算当前样品分析值*/ export function calcAnalysisValue(group) { const MAX_ITERATIONS = 5 // 防止无限循环 let iterations = 0 let changed = true while (changed && iterations < MAX_ITERATIONS) { changed = false iterations++ try { for (const g of group) { for (const ele of g.fields) { if (!ele.formula || ele.formula == '' || ele.formula.startsWith('From')) continue let formula = ele.formula let formulas = formula.split('|') let formulaVal = '' console.log(formulas) formulas.forEach(f => { let value = '' if (f.charAt(0) === 'p') { let o = findFieldInGroup(f, group, 'p') value = o.value || 0 } else if (f.charAt(0) === 'e') { let o = findFieldInGroup(f, group, 'e') value = o.value || 0 } else if (f.charAt(0) === '<' || f.charAt(0) === '>') { value = "'" + f + "'" } else { value = f } formulaVal += value }) console.log(formulaVal) let v if (formulaVal.startsWith('Get')) { //计算公式为Get开头的,都是执行方法 v = eval(formulaVal) } else { v = math.evaluate(formulaVal).toString() v = isFinite(v) ? v.toString() : 0 } console.log(v) ele.value = handleRoundFiveNumber(v, ele.decimalPosition) changed = true } } } catch (error) { console.log(error) } } } // 根据样品和配置列计算分析值 export function calcRowAnalysisValue(row, columnObj, dynamicsColumns) { if (!columnObj.paramNo) return for (let i = 0; i < dynamicsColumns.length; i++) { let curItem = dynamicsColumns[i] if (curItem.fieldIndex === columnObj.fieldIndex) continue let param = columnObj.fieldIndex.charAt(0) === 'p' ? 'p' + columnObj.paramNo : 'e' + columnObj.paramNo if (curItem.formula && curItem.formula.includes(param)) { let formula = curItem.formula let formulas = formula.split('|') let formulaVal = '' formulas.forEach(f => { if (f.charAt(0) === 'p') { let o = dynamicsColumns.find(i => 'p' + i.paramNo === f && i.type !== 'project') formulaVal += row[o.fieldIndex]?.value ? row[o.fieldIndex].value : 0 } else if (f.charAt(0) === 'e') { let o = dynamicsColumns.find(i => 'e' + i.paramNo === f) formulaVal += row[o.fieldIndex]?.value ? row[o.fieldIndex].value : 0 } else { formulaVal += f } }) let v if (formulaVal.startsWith('Get')) { formulaVal = formulaVal.replace(')', ",'" + row.conBaseSampleId + "')") v = eval(formulaVal) } else { v = math.evaluate(formulaVal).toString() v = isFinite(v) ? v : 0 } row[curItem.fieldIndex].value = handleRoundFiveNumber(Number(v), row[curItem.fieldIndex].decimalPosition) calcRowAnalysisValue(row, curItem, dynamicsColumns) } } } const findFieldInGroup = function (paramNo, group, p) { for (const g of group) { for (const f of g.fields) { if (p === 'p' && f.type == 'project') continue if (p + f.paramNo === paramNo) { return f } } } return { value: null } } const GetRecoveryRate = function (elementFormulaCode, value) { // //读取conRecoveryRateList const conRecoveryRateList = uni.getStorageSync('ConRecoveryRateList') if (!conRecoveryRateList) return let o = conRecoveryRateList.find(i => { //console.log("取得的" + i + "回收率:" + i); if (value === 0) { return i.elementFormulaCode === elementFormulaCode && i.minValue === value } return i.elementFormulaCode === elementFormulaCode && i.minValue < value && value <= i.maxValue }) // console.log(o); const rate = o ? o.rate : 100 // console.log("取得的" + elementFormulaCode + "回收率:" + rate); return rate // console.log("执行方法:GetRecoveryRate"); // return 100; } /* * 根据硫值计算:支持碳、硝酸钾(滇中) * 当S%<22%时,需加入淀粉的量=(75-S%*22*M)/12 * 当 S%>22%时,需加入硝酸钾的量=(S%*22*M-75)/4 * 当 S%==22%时,硝酸钾为0,淀粉为空 * sValueKey:硫值对应的dicKey * weightKey:重量对应的dicKey * percent:计算参数 * * 举例:Get_C_KNO3_bySValue|(|p14|, |p2|, |>|) * */ const Get_C_KNO3_bySValue = function (sValue, weight, operator) { //判断sValue是数字 if (isNaN(sValue) || isNaN(weight)) { return '' } let v = number(sValue) const w = number(weight) if (w === 0) return '' v = v * 0.01 //当S%<22%时,需加入淀粉的量=(75-S%*22*M)/12 if (operator === '<' && v < 0.22) { // return ( 75 - v * 22 * w )/12; return accDiv(accSub(75, accMul(accMul(v, 22), w)), 12) } //S%>22%时,需加入硝酸钾的量=(S%*22*M-75)/4 if (operator === '>=' && v >= 0.22) { //S%==22%时,硝酸钾为0,淀粉为空 if (v === 22) return 0 // return ( v * 22 * w - 75 ) + 44; return accDiv(accSub(accMul(v, accMul(22, w)), 75), 4) } return '' } /** 处理数值数据:四舍六入奇进偶不进 * 1,如果取小数的最后一位为5,5前为奇数进位,为偶不进,五后非零就进一,五后皆零看奇偶,五前为偶应舍去,五前为奇要进一 * 2.5后不为0时就入,为0时看5前,奇进偶不进 * 3.四舍,六入 * 传入数值和保留位数 */ export function handleRoundFiveNumber(number, fixed = 0) { if (number == null || number === '' || isNaN(number)) return number if (fixed === -1) return number number = String(number) //可以考虑清掉末尾的0,暂时限制了不会有,因为传入数值,末尾0是去掉的 const index = number.indexOf('.') if (index == -1) { if (fixed > 0) { number += '.' } for (let i = 0; i < fixed; i++) { number += '0' } //补0返回 return number } //取到保留小数位的下一位,5.5555取保留俩位,那就是小数点后第三位 const indexFixed = index + fixed + 1 if (indexFixed >= number.length) { //如果小数位数不够, 补0直接返回 const zerolen = indexFixed - number.length for (let i = 0; i < zerolen; i++) { number += '0' } return number } //取保留位数的后一位做判断,以下是有5的判断 const endNumber = number.substr(indexFixed, 1) if (endNumber != '5') { //如果做判断的数不是五,就按正常的四舍五入,即忽略了5,后面补0的那些由于是数字传进来,0已经去掉,此处不做处理 return Number(number).toFixed(fixed) } if (indexFixed != number.length - 1) { //由于当前判断位不是最后一位,而又去除了0,那么后面后的位数应该直接入位,五后不为0时入,由于会有小于五的,四舍五入肯定不行,那么只能截取到当前保留位数,然后转成数字加上10的负fixed的次方即可 number = number.substring(0, indexFixed) if (number.indexOf('-') != -1) { //需要考虑到负数的情况 return (Number(number) - Number(Math.pow(10, -fixed))).toFixed(fixed) } return (Number(number) + Number(Math.pow(10, -fixed))).toFixed(fixed) //return this.accAdd(number,Math.pow(10,-fixed)) } //接下来就时五后没有值也就是0的需要看前面的奇入偶不入了,取当前位的上一位 let twoNumber = number.substr(indexFixed - 1, 1) if (twoNumber == '.') { //取到小数点,再取一次,要取到小数点左边第一位 twoNumber = number.substr(indexFixed - 2, 1) } if ('13579'.indexOf(twoNumber) != -1) { //奇进 number = number.substring(0, indexFixed) if (number.indexOf('-') != -1) { //需要考虑到负数的情况 return (Number(number) - Math.pow(10, -fixed)).toFixed(fixed) } return (Number(number) + Math.pow(10, -fixed)).toFixed(fixed) } //偶不进,将取值的当前位数,直接截取字符即可 return number.substr(0, indexFixed) } /** ** 除法函数,用来得到精确的除法结果 ** 说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。 ** 调用:accDiv(arg1,arg2) ** 返回值:arg1除以arg2的精确结果 **/ function accDiv(arg1, arg2) { var t1 = 0, t2 = 0, r1, r2 try { t1 = arg1.toString().split('.')[1].length } catch (e) {} try { t2 = arg2.toString().split('.')[1].length } catch (e) {} r1 = Number(arg1.toString().replace('.', '')) r2 = Number(arg2.toString().replace('.', '')) return (r1 / r2) * pow(10, t2 - t1) } /** ** 乘法函数,用来得到精确的乘法结果 ** 说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。 ** 调用:accMul(arg1,arg2) ** 返回值:arg1乘以 arg2的精确结果 **/ function accMul(arg1, arg2) { var m = 0, s1 = arg1.toString(), s2 = arg2.toString() try { m += s1.split('.')[1].length } catch (e) {} try { m += s2.split('.')[1].length } catch (e) {} return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m) } /** ** 减法函数,用来得到精确的减法结果 ** 说明:javascript的减法结果会有误差,在两个浮点数相减的时候会比较明显。这个函数返回较为精确的减法结果。 ** 调用:accSub(arg1,arg2) ** 返回值:arg1加上arg2的精确结果 **/ function accSub(arg1, arg2) { var r1, r2, m, n try { r1 = arg1.toString().split('.')[1].length } catch (e) { r1 = 0 } try { r2 = arg2.toString().split('.')[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)) //last modify by deeka //动态控制精度长度 n = r1 >= r2 ? r1 : r2 return ((arg1 * m - arg2 * m) / m).toFixed(n) } /** ** 加法函数,用来得到精确的加法结果 ** 说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。 ** 调用:accAdd(arg1,arg2) ** 返回值:arg1加上arg2的精确结果 **/ function accAdd(arg1, arg2) { var r1, r2, m, c try { r1 = arg1.toString().split('.')[1].length } catch (e) { r1 = 0 } try { r2 = arg2.toString().split('.')[1].length } catch (e) { r2 = 0 } c = Math.abs(r1 - r2) m = Math.pow(10, Math.max(r1, r2)) if (c > 0) { var cm = Math.pow(10, c) if (r1 > r2) { arg1 = Number(arg1.toString().replace('.', '')) arg2 = Number(arg2.toString().replace('.', '')) * cm } else { arg1 = Number(arg1.toString().replace('.', '')) * cm arg2 = Number(arg2.toString().replace('.', '')) } } else { arg1 = Number(arg1.toString().replace('.', '')) arg2 = Number(arg2.toString().replace('.', '')) } return (arg1 + arg2) / m } // 通过配置项分组 export function groupByField(list, cupNumFieldIndex, groupKey = 'groupDictionaryBusinessKey') { const groupMap = new Map() // 插入“全部”项 list.unshift({ groupDictionaryBusinessKey: 'all', groupDictionaryBusinessName: '全部' }) for (const item of list) { // 赋值杯号 fieldIndex if (item.title === '杯号') { cupNumFieldIndex.value = item.fieldIndex } let key = item[groupKey] // 如果没有有效 key,统一归入自定义分组 if (!key) { key = 'customParameter' // 使用固定字符串作为无 key 项的分组标识 } // 如果该分组不存在,初始化 if (!groupMap.has(key)) { groupMap.set(key, { value: key, label: key === 'customParameter' ? '分析项目' : item.groupDictionaryBusinessName || '未知分组', fields: [] }) } // 将当前项加入对应分组 groupMap.get(key).fields.push(item) } return Array.from(groupMap.values()) } export function validateElementRange(fieldIndex, row, conRangeElementAnalysisList) { if (!conRangeElementAnalysisList?.length) return null const rangeElementAnalysis = conRangeElementAnalysisList.find( ele => ele.dictionaryProjectId === row[fieldIndex]?.dicId && row[fieldIndex]['type'] === 'project' ) if (!rangeElementAnalysis) return null const val = row[fieldIndex]?.value if (val == '' || val == null) return null const value = Number(val) let result = { promptType: '', promptMsg: '' } switch (Number(rangeElementAnalysis.rangeType)) { case 1: //如果值小于最小警告值或者大于最大警告值,则校验不通过 if (value < rangeElementAnalysis.minimumWarningValue || value > rangeElementAnalysis.maximumWarningValue) { result.promptType = 'warning' result.promptMsg = `警告:值超出范围(${rangeElementAnalysis.minimumWarningValue} ~ ${rangeElementAnalysis.maximumWarningValue})` } break case 2: //如果值小于最小限制值或者大于最大限制值,则校验不通过 if (value < rangeElementAnalysis.minimumValue || value > rangeElementAnalysis.maximumValue) { result.promptType = 'error' result.promptMsg = `错误:值超出范围(${rangeElementAnalysis.minimumValue} ~ ${rangeElementAnalysis.maximumValue})` } break case 3: //如果值小于最小限制值或者大于最大限制值,则校验不通过 if (value < rangeElementAnalysis.minimumValue || value > rangeElementAnalysis.maximumValue) { result.promptType = 'error' result.promptMsg = `错误:值超出范围(${rangeElementAnalysis.minimumValue} ~ ${rangeElementAnalysis.maximumValue})` } else if ( (rangeElementAnalysis.minimumValue < value && value < rangeElementAnalysis.minimumWarningValue) || (rangeElementAnalysis.maximumWarningValue < value && value <= rangeElementAnalysis.maximumValue) ) { result.promptType = 'warning' result.promptMsg = `警告:值超出范围(${rangeElementAnalysis.minimumWarningValue} ~ ${rangeElementAnalysis.maximumWarningValue})` } break default: return null } return result }