|
@@ -13,7 +13,6 @@ let defaultBillTemplate = {
|
|
|
code: "1",
|
|
|
name: "定额直接费",
|
|
|
dispExpr: "F2+F3+F4",
|
|
|
- expression: "@('2')+@('3')+@('4')",
|
|
|
statement: "人工费+材料费+机械费",
|
|
|
feeRate: null,
|
|
|
memo: ''
|
|
@@ -23,7 +22,6 @@ let defaultBillTemplate = {
|
|
|
code: "1.1",
|
|
|
name: "人工费",
|
|
|
dispExpr: "HJ",
|
|
|
- expression: "HJ",
|
|
|
statement: "合计",
|
|
|
feeRate: 50,
|
|
|
fieldName: 'labour',
|
|
@@ -34,7 +32,6 @@ let defaultBillTemplate = {
|
|
|
code: "1.2",
|
|
|
name: "材料费",
|
|
|
dispExpr: "HJ",
|
|
|
- expression: "HJ",
|
|
|
statement: "合计",
|
|
|
feeRate: 30,
|
|
|
fieldName: 'material',
|
|
@@ -45,7 +42,6 @@ let defaultBillTemplate = {
|
|
|
code: "1.3",
|
|
|
name: "机械费",
|
|
|
dispExpr: "HJ",
|
|
|
- expression: "HJ",
|
|
|
statement: "合计",
|
|
|
feeRate: 20,
|
|
|
fieldName: 'machine',
|
|
@@ -56,7 +52,6 @@ let defaultBillTemplate = {
|
|
|
code: "2",
|
|
|
name: "企业管理费",
|
|
|
dispExpr: "F1",
|
|
|
- expression: "@('1')",
|
|
|
statement: "定额直接费",
|
|
|
feeRate: null,
|
|
|
fieldName: 'manage',
|
|
@@ -67,7 +62,6 @@ let defaultBillTemplate = {
|
|
|
code: "3",
|
|
|
name: "利润",
|
|
|
dispExpr: "F1",
|
|
|
- expression: "@('1')",
|
|
|
statement: "定额直接费",
|
|
|
feeRate: null,
|
|
|
fieldName: 'profit',
|
|
@@ -78,7 +72,6 @@ let defaultBillTemplate = {
|
|
|
code: "4",
|
|
|
name: "风险费用",
|
|
|
dispExpr: "F1",
|
|
|
- expression: "@('1')",
|
|
|
statement: "定额直接费",
|
|
|
feeRate: null,
|
|
|
fieldName: 'risk',
|
|
@@ -89,7 +82,6 @@ let defaultBillTemplate = {
|
|
|
code: "5",
|
|
|
name: "综合单价",
|
|
|
dispExpr: "F1+F5+F6+F7",
|
|
|
- expression: "@('1')+@('5')+@('6')+@('7')",
|
|
|
statement: "定额直接费+企业管理费+利润+风险费用",
|
|
|
feeRate: null,
|
|
|
fieldName: 'common',
|
|
@@ -702,14 +694,13 @@ const rationCalcBases = {
|
|
|
};
|
|
|
|
|
|
let analyzer = {
|
|
|
- calcTemplate: null,
|
|
|
-
|
|
|
standard: function(expr){
|
|
|
let str = expr;
|
|
|
str = str.replace(/\s/g, ""); // 去空格、去中文空格
|
|
|
str = str.replace(/(/g, "("); // 中文括号"("换成英文括号"("
|
|
|
str = str.replace(/)/g, ")"); // 中文括号")"换成英文括号")"
|
|
|
str = str.replace(/f/g, "F"); // f换成F
|
|
|
+ str = str.replace(/l/g, "L"); // l换成L
|
|
|
return str;
|
|
|
},
|
|
|
getFArr: function (expr) {
|
|
@@ -722,40 +713,44 @@ let analyzer = {
|
|
|
let arr = expr.match(patt);
|
|
|
return arr ? arr : [];
|
|
|
},
|
|
|
- getFID: function (FName) { // F3、F22
|
|
|
- let me = this;
|
|
|
+ getBaseArr: function (expr) {
|
|
|
+ let pattBase = new RegExp(/\[[\u4E00-\u9FA5]+\]/gi);
|
|
|
+ let arrBase = expr.match(pattBase);
|
|
|
+ return arrBase ? arrBase : [];
|
|
|
+ },
|
|
|
+
|
|
|
+ getFID: function (FName, calcTemplate) { // F3、F22 → 4、99
|
|
|
let idx = FName.slice(1) - 1;
|
|
|
- let id = me.calcTemplate.calcItems[idx] ? me.calcTemplate.calcItems[idx].ID : null;
|
|
|
+ let id = calcTemplate.calcItems[idx] ? calcTemplate.calcItems[idx].ID : null;
|
|
|
return id;
|
|
|
},
|
|
|
- getFItem: function (FName){
|
|
|
- let me = this;
|
|
|
+ getFItem: function (FName, calcTemplate){ // F3 → calcItems[2] → {Object}
|
|
|
let idx = FName.slice(1) - 1;
|
|
|
- return me.calcTemplate.calcItems[idx];
|
|
|
+ return calcTemplate.calcItems[idx];
|
|
|
},
|
|
|
- isCycleCalc: function (expr) { // @5+@6 这里已经是ID引用
|
|
|
- let me = this;
|
|
|
+ isCycleCalc: function (expression, calcTemplate) { // 这里判断expression,是ID引用: @5+@6
|
|
|
+ let me = analyzer;
|
|
|
|
|
|
- function isCycle(nodeExpr) {
|
|
|
+ function isCycle(nodeExpr, template) {
|
|
|
let atIDArr = me.getAtIDArr(nodeExpr);
|
|
|
for (let atID of atIDArr){
|
|
|
let ID = atID.slice(1);
|
|
|
- let item = me.calcTemplate.compiledCalcItems[ID];
|
|
|
+ let item = template.compiledCalcItems[ID];
|
|
|
if (item.expression.includes(atID)) {
|
|
|
return true;
|
|
|
}
|
|
|
else {
|
|
|
- isCycle(item.expression);
|
|
|
+ isCycle(item.expression, template);
|
|
|
}
|
|
|
};
|
|
|
return false;
|
|
|
};
|
|
|
|
|
|
- return isCycle(expr);
|
|
|
+ return isCycle(expression, calcTemplate);
|
|
|
},
|
|
|
- isLegal: function (expr) { // 调用前必须先标准化
|
|
|
- let me = this;
|
|
|
-
|
|
|
+ isLegal: function (dispExpr, calcTemplate) { // 检测包括:无效字符、基数是否中括号、基数是否定义、行引用、循环计算
|
|
|
+ let me = analyzer;
|
|
|
+ let expr = me.standard(dispExpr);
|
|
|
let invalidChars = /[^0-9\u4e00-\u9fa5\+\-\*\/\(\)\.\[\]F%]/g;
|
|
|
if (invalidChars.test(expr)){
|
|
|
alert('表达式中含有无效的字符!');
|
|
@@ -787,38 +782,72 @@ let analyzer = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
- // 行引用检测、行引用转ID引用
|
|
|
let arrF = me.getFArr(expr);
|
|
|
for (let F of arrF){
|
|
|
let num = F.slice(1);
|
|
|
- if (num > me.calcTemplate.calcItems.length){
|
|
|
+ if (num > calcTemplate.calcItems.length){
|
|
|
alert('表达式中 “F'+ num +'” 行号引用错误!');
|
|
|
return false;
|
|
|
};
|
|
|
- let id = me.getFID(F);
|
|
|
- let fn = new RegExp(F, "g");
|
|
|
- expr = expr.replace(fn, `@('${id}')`);
|
|
|
};
|
|
|
|
|
|
- // 循环计算
|
|
|
- if (me.isCycleCalc(expr)){
|
|
|
+ let expression = me.getExpression(expr, calcTemplate);
|
|
|
+ if (me.isCycleCalc(expression, calcTemplate)){
|
|
|
alert('表达式中有循环计算!');
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
},
|
|
|
- analyzeUserExpr: function(calcTemplate, calcItem){
|
|
|
- let me = this;
|
|
|
- me.calcTemplate = calcTemplate;
|
|
|
+ analyzeUserExpr: function(calcItem, calcTemplate){
|
|
|
+ let me = analyzer;
|
|
|
let expr = calcItem.dispExpr;
|
|
|
expr = me.standard(expr);
|
|
|
calcItem.dispExpr = expr;
|
|
|
- if (me.isLegal(expr)){
|
|
|
+
|
|
|
+ if (me.isLegal(expr, calcTemplate)){
|
|
|
calcItem.expression = expr;
|
|
|
return true;
|
|
|
}else
|
|
|
return false;
|
|
|
+ },
|
|
|
+
|
|
|
+ getExpression: function (dispExpr, calcTemplate) {
|
|
|
+ function refLineToID(expr, template) {
|
|
|
+ let rst = expr;
|
|
|
+ let fArr = me.getFArr(rst);
|
|
|
+ let IDArr = [];
|
|
|
+ for (let F of fArr){
|
|
|
+ let ID = me.getFID(F, template);
|
|
|
+ IDArr.push(ID);
|
|
|
+ };
|
|
|
+ for (let i = 0; i < fArr.length; i++) {
|
|
|
+ let patt = new RegExp(fArr[i]);
|
|
|
+ let val = `@${IDArr[i]}`;
|
|
|
+ rst = rst.replace(patt, val);
|
|
|
+ };
|
|
|
+ return rst;
|
|
|
+ };
|
|
|
+
|
|
|
+ let me = analyzer;
|
|
|
+ let expr = me.standard(dispExpr);
|
|
|
+ return refLineToID(expr, calcTemplate);
|
|
|
+ },
|
|
|
+ getCompiledExpr: function (expression) {
|
|
|
+ let me = analyzer;
|
|
|
+ let rst = expression;
|
|
|
+ let atIDArr = me.getAtIDArr(rst);
|
|
|
+ let IDArr = atIDArr.map(function (atID) {
|
|
|
+ return atID.slice(1);
|
|
|
+ });
|
|
|
+ for (var i = 0; i < atIDArr.length; i++) {
|
|
|
+ let patt = new RegExp(atIDArr[i]);
|
|
|
+ let val = `$CE.at(${IDArr[i]})`;
|
|
|
+ rst = rst.replace(patt, val);
|
|
|
+ };
|
|
|
+ rst = rst.replace(/\[/g, "$CE.base('");
|
|
|
+ rst = rst.replace(/\]/g, "')");
|
|
|
+ return rst;
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -950,7 +979,7 @@ class CalcProgram {
|
|
|
template.hasCompiled = false;
|
|
|
template.errs = [];
|
|
|
|
|
|
- let private_extract_ID = function(str, idx){
|
|
|
+ /*let private_extract_ID = function(str, idx){
|
|
|
let rst = '', lBracket = 0, rBracket = 0, firstIdx = idx, lastIdx = 0;
|
|
|
for (let i = idx; i < str.length; i++) {
|
|
|
if (str[i] === '(') {
|
|
@@ -973,8 +1002,8 @@ class CalcProgram {
|
|
|
}
|
|
|
}
|
|
|
return rst;
|
|
|
- };
|
|
|
- let private_parse_ref = function(item, itemIdx){
|
|
|
+ };*/
|
|
|
+ /*let private_parse_ref = function(item, itemIdx){
|
|
|
let idx = item.expression.indexOf('@(', 0);
|
|
|
while (idx >= 0) {
|
|
|
let ID = private_extract_ID(item.expression, idx);
|
|
@@ -996,6 +1025,37 @@ class CalcProgram {
|
|
|
if (template.compiledSeq.indexOf(itemIdx) < 0) {
|
|
|
template.compiledSeq.push(itemIdx);
|
|
|
}
|
|
|
+ };*/
|
|
|
+ let private_extract_ID = function(str, idx){
|
|
|
+ let subStr = str.slice(idx);
|
|
|
+ let patt = new RegExp(/@\d+/);
|
|
|
+ let atID = subStr.match(patt);
|
|
|
+ let ID = atID ? atID[0].slice(1) : null;
|
|
|
+ return ID;
|
|
|
+ };
|
|
|
+ let private_parse_ref = function(item, itemIdx){
|
|
|
+ let idx = item.expression.indexOf('@', 0);
|
|
|
+ while (idx >= 0) {
|
|
|
+ let ID = private_extract_ID(item.expression, idx);
|
|
|
+ let len = ID ? ID.toString().length : 0;
|
|
|
+ if (len) {
|
|
|
+ let subItem = template.compiledCalcItems[ID];
|
|
|
+ if (subItem) {
|
|
|
+ if (subItem.ID !== item.ID) {
|
|
|
+ private_parse_ref(subItem, template.compiledCalcItems[ID + "_idx"]);
|
|
|
+ } else {
|
|
|
+ template.errs.push("循环引用ID: " + ID);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ template.errs.push("找不到ID: " + ID);
|
|
|
+ console.log('找不到ID: ' + ID);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ idx = item.expression.indexOf('@', idx + len + 1);
|
|
|
+ }
|
|
|
+ if (template.compiledSeq.indexOf(itemIdx) < 0) {
|
|
|
+ template.compiledSeq.push(itemIdx);
|
|
|
+ }
|
|
|
};
|
|
|
let private_compile_items = function() {
|
|
|
for (let idx of template.compiledSeq) {
|
|
@@ -1003,10 +1063,8 @@ class CalcProgram {
|
|
|
item.dispExprUser = item.dispExpr; // 用于界面显示。disExpr是公式模板,不允许修改:人工系数占位符被修改后变成数值,第二次无法正确替换。
|
|
|
if (item.expression == 'HJ')
|
|
|
item.compiledExpr = '$CE.HJ()'
|
|
|
- else{
|
|
|
- item.compiledExpr = item.expression.split('@(').join('$CE.at(');
|
|
|
- item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
|
|
|
- };
|
|
|
+ else
|
|
|
+ item.compiledExpr = analyzer.getCompiledExpr(item.expression);
|
|
|
|
|
|
if (item.labourCoeID){
|
|
|
let lc = me.compiledLabourCoes[item.labourCoeID].coe;
|
|
@@ -1035,6 +1093,7 @@ class CalcProgram {
|
|
|
|
|
|
for (let i = 0; i < template.calcItems.length; i++) {
|
|
|
let item = template.calcItems[i];
|
|
|
+ item.expression = analyzer.getExpression(item.dispExpr, template);
|
|
|
template.compiledCalcItems[item.ID] = item;
|
|
|
template.compiledCalcItems[item.ID + "_idx"] = i;
|
|
|
}
|
|
@@ -1379,7 +1438,8 @@ class CalcProgram {
|
|
|
/* 计算所有树结点(分3种情况),并返回发生变动的零散的多个树结点。参数取值如下:
|
|
|
calcAllType.catAll 计算所有树结点 (不指定参数时的默认值)
|
|
|
calcAllType.catBills 计算所有清单 (改变项目属性中清单取费算法时会用到)
|
|
|
- calcAllType.catRations 计算所有定额、工料机形式的定额、量价,因为它们都走自己的计算程序 (改变人工系数、费率值、工料机单价时会用到)
|
|
|
+ calcAllType.catRations 计算所有定额、工料机形式的定额、量价,因为它们都走自己的计算程序 (改变人工系数、费率值、工料机单价时会用到) 不要用
|
|
|
+ 缺陷:calcAllType.catRations 参数情况不会计算父结点。(calcAllType.catBills 可以,因为清单的父结点也是清单会计算)
|
|
|
*/
|
|
|
calcAllNodes(calcType = calcAllType.catAll){
|
|
|
let me = this;
|
|
@@ -1514,4 +1574,6 @@ class CalcProgram {
|
|
|
baseNodes.push(calcTools.getNodeByFlag(fixedFlag.CHARGE));
|
|
|
return me.getTotalFee(baseNodes, excludeNodes);
|
|
|
};
|
|
|
-};
|
|
|
+};
|
|
|
+
|
|
|
+// export default analyzer;
|