'use strict';
const showSideTools = function (show) {
const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
if (show) {
right.show();
autoFlashHeight();
/**
* right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
* 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
* 故需要通过最终的parent.width再计算一次left.width
*
* Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
* A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
*
*/
//left.css('width', parent.width() - right.outerWidth());
//left.css('width', parent.width() - right.outerWidth());
const percent = 100 - right.outerWidth() /parent.width() * 100;
left.css('width', percent + '%');
} else {
left.width(parent.width());
right.hide();
}
};
$(document).ready(() => {
autoFlashHeight();
const payUtils = {
tips: {
name: function(data) {
const tips = [];
if (data) {
if (data.pause) tips.push('当前项已停用');
if (!data.is_yf) tips.push('当前项不参与本期应付计算');
}
return tips.join('
');
},
range_tp: function (data) {
if (!data || (!data.range_expr && !data.range_tp) || !data.dl_type) return '';
if (data.dl_type === 1) {
return '计提期限为(当 计量期数 ≥ ' + data.dl_count + ')';
} else if (data.dl_type === 2) {
switch (data.dl_tp_type) {
case 'contract':
return '计提期限为(累计合同计量 ≥ ' + data.dl_tp + ')';
case 'qc':
return '计提期限为(累计变更计量 ≥ ' + data.dl_tp + ')';
case 'gather':
return '计提期限为(累计完成计量 ≥ ' + data.dl_tp + ')';
}
}
}
},
check: {
isFixed: function(data) {
return data.is_fixed;
},
isStarted: function (data) {
return data.pre_used;
},
isYf: function(data) {
return data.pay_type === 'bqyf';
},
isSf: function(data) {
return data.pay_type === 'bqsf';
},
isGatherValid: function(data) {
return !data.pay_type && (!data.children || data.children.length === 0);
},
isOwner: function(data) {
return data.create_user_id === userID;
},
isFinish: function(data) {
return data.pre_finish;
},
isYB: function() {
return userID === phasePay.create_user_id;
},
isOld: function(data) {
return data.phase_id !== data.create_phase_id;
},
isLock: function (data) {
const result = !!lockPayExpr && payUtils.check.isStarted(data) && payCalc.hasBase(data.expr);
return result;
},
tpReadOnly: function(data) {
return payUtils.check.isYf(data) || payUtils.check.isLock(data);
},
startTpReadOnly: function(data) {
if (payUtils.check.isOld(data)) {
return payUtils.check.isStarted(data) || !payUtils.check.isYB(data) || payUtils.check.isLock(data);
} else {
return payUtils.check.isWC(data) || payUtils.check.isSF(data) || payUtils.check.isYf(data) || !(payUtils.check.isOwner(data) || payUtils.check.isYB());
}
}
},
menuVisible: {
pause: function (data) {
if (payUtils.check.isOld(data)) {
return payUtils.check.isYB();
} else {
return payUtils.check.isOwner(data) || payUtils.check.isYB();
}
},
deadline: function (data) {
if (payUtils.check.isOld(data)) {
return !payUtils.check.isFinish(data) && payUtils.check.isYB();
} else {
return payUtils.check.isOwner(data) || payUtils.check.isYB();
}
}
},
};
const payCalc = (function (b, a) {
class PayCalc {
constructor (bases, add) {
this.percentReg = /((\d+)|((\d+)(\.\d+)))%/g;
this.bases = bases;
this.bases.sort(function (a, b) {
return a.sort - b.sort;
});
for (const b of this.bases) {
b.reg = new RegExp(b.code, 'igm');
}
this.addBase = add;
this.orderReg = /f\d+/ig;
this.nodeReg = /<<[a-z0-9\-]+>>/ig;
}
hasBase(expr) {
if (!expr) return false;
for (const b of this.bases) {
if (data.expr.indexOf(b.code) >= 0) return true;
}
return false;
}
trans2OrderExpr(expr, payTree) {
const nodeParam = expr.match(this.nodeReg);
if (nodeParam) {
for (const op of nodeParam) {
const id = op.substring(2, op.length - 2);
const payNode = payTree.nodes.find(x => { return x.uuid === id; });
expr = expr.replace(op, payNode ? `f${payTree.getNodeIndex(payNode) + 1}` || '' : 0);
}
}
return expr;
}
trans2NodeExpr(expr, payTree) {
const orderParam = expr.match(this.orderReg);
if (orderParam) {
for (const op of orderParam) {
const order = parseInt(op.substring(1, op.length));
const payNode = payTree.nodes[order - 1];
expr = expr.replace(op, payNode ? `<<${payNode.uuid}>>` || '' : 0);
}
}
return expr;
}
checkExprValid(expr, invalidParam, selfId, payTree) {
if (!expr) return [true, ''];
const param = [];
let num = '', base = '';
let fixedIdParam;
for (let i = 0, iLen = expr.length; i < iLen; i++) {
const subExpr = expr.substring(i, expr.length);
if (/^[\d\.%]+/.test(expr[i])) {
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
num = num + expr[i];
} else if (this.nodeReg.test(subExpr)) {
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
// const node = this.nodeReg.exec(subExpr);
const node = subExpr.match(this.nodeReg);
param.push({type: 'node', value: node[0]});
i = i + node[0].length - 1;
} else if (/^[a-z]/.test(expr[i])) {
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
base = base + expr[i];
} else if (expr[i] === '(') {
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
param.push({type: 'left', value: '('});
} else if (expr[i] === ')') {
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
param.push({type: 'right', value: ')'});
} else if (/^[\+\-*\/]/.test(expr[i])) {
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
param.push({type: 'calc', value: expr[i]});
} else {
return [false, '输入的表达式含有非法字符: ' + expr[i]];
}
}
if (num !== '') {
param.push({type: 'num', value: num});
num = '';
}
if (base !== '') {
param.push({type: 'base', value: base});
base = '';
}
if (param.length === 0) return [true, ''];
if (param.length > 1) {
if (param[0].value === '-' && param[1].type === 'num') {
param[1].value = '-' + param[1].value;
param.shift();
}
}
const iLen = param.length;
let iLeftCount = 0, iRightCount = 0;
for (const [i, p] of param.entries()) {
if (p.type === 'calc') {
if (i === 0 || i === iLen - 1)
return [false, '输入的表达式非法:计算符号' + p.value + '前后应有数字或计算基数'];
}
if (p.type === 'num') {
num = p.value.replace('%', '');
if (p.value.length - num.length > 1)
return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
num = _.toNumber(num);
if (num === undefined || num === null || _.isNaN(num))
return [false, '输入的表达式非法:' + p.value + '不是一个有效的数字'];
if (i > 0) {
if (param[i - 1].type !== 'calc' && param[i - 1].type !== 'left') {
return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
} else if (param[i - 1].value === '/' && num === 0) {
return [false, '输入的表达式非法:请勿除0'];
}
}
}
if (p.type === 'base') {
const baseParam = _.find(calcBase, {code: p.value});
if (!baseParam)
return [false, '输入的表达式非法:不存在计算基数' + p.value];
if (invalidParam && invalidParam.indexOf(p.value) >= 0)
return [false, '不可使用计算基数' + p.value];
if (i > 0 && (param[i - 1].type === 'num' || param[i - 1].type === 'right'))
return [false, '输入的表达式非法:' + p.value + '前应有运算符'];
}
if (p.type === 'node') {
if (!selfId) return [false, '输入的表达式错误:不支持行号引用'];
if ([`<<${selfId}>>`].indexOf(p.value) >= 0) return [false, '输入的表达式非法:请勿引用自己'];
if (!fixedIdParam) {
fixedIdParam = payTree.nodes.filter(x => { return x.is_fixed; }).map(x => { return `<<${x.uuid}>>`});
}
if (fixedIdParam.indexOf(p.value) >= 0) return [false, '输入的表达式非法:请勿引用固定项'];
}
if (p.type === 'left') {
iLeftCount += 1;
if (i !== 0 && param[i-1].type !== 'calc')
return [false, '输入的表达式非法:(前应有运算符'];
}
if (p.type === 'right') {
iRightCount += 1;
if (i !== iLen - 1 && param[i+1].type !== 'calc')
return [false, '输入的表达式非法:)后应有运算符'];
if (iRightCount > iLeftCount)
return [false, '输入的表达式非法:")"前无对应的"("'];
}
}
if (iLeftCount > iRightCount)
return [false, '输入的表达式非法:"("后无对应的")"'];
if (selfId) {
const circular = payCalc.checkCircularExpr(expr, selfId, payTree);
// 当前循环计算不检查父项
if (circular) return [false, '输入的表达式非法:循环引用'];
}
return [true, ''];
}
checkSfExpr(text, data, payNode, payTree) {
if (text) {
const num = _.toNumber(text);
if (num) {
data.expr = num;
} else {
const expr = this.trans2NodeExpr($.trim(text).replace('\t', '').replace('=', '').toLowerCase(), payTree);
const [valid, msg] = this.checkExprValid(expr, [], payNode.uuid, payTree);
if (!valid) return [valid, msg];
data.expr = expr;
}
} else {
data.tp = 0;
data.expr = '';
}
return [true, ''];
}
checkExpr(text, data, payNode, payTree) {
if (text) {
const num = _.toNumber(text);
if (num) {
data.tp = num;
data.expr = '';
} else {
const expr = this.trans2NodeExpr($.trim(text).replace('\t', '').replace('=', '').toLowerCase(), payTree);
const [valid, msg] = this.checkExprValid(expr, ['bqyf'], payNode.uuid, payTree);
if (!valid) return [valid, msg];
data.expr = expr;
data.tp = 0;
}
} else {
data.tp = 0;
data.expr = '';
}
return [true, ''];
}
checkRangeExpr(payNode, text, data) {
if (!payNode) return [false, '数据错误'];
const num = text ? _.toNumber(text) : 0;
let expr = text ? (num ? '' : text) : '';
expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase() : '';
const [valid, msg] = this.checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg', 'bqyf']);
if (!valid) return [valid, msg];
if (payUtils.check.isStarted(payNode)) {
if (payUtils.check.isSf(payNode)) {
const value = expr ? payCalc.calculateExpr(expr) : num;
if (payNode.pre_tp && value < payNode.pre_tp) return [false, '截止上期已计量' + payNode.pre_tp + ',扣款限额请勿少于改值'];
data.range_tp = num;
data.range_expr = expr;
return [true, ''];
} else {
// if (payNode.pre_finish) return [false, '已达扣款限额,请勿修改'];
// const value = expr ? payCalc.calculateExpr(expr) : num;
// if (payNode.pre_tp && value < payNode.pre_tp) return [false, '截止上期已计量' + payNode.pre_tp + ',扣款限额请勿少于改值'];
// data.range_tp = num;
// data.range_expr = expr;
return [false, '已经开始使用,请勿修改扣款限额'];
}
} else {
data.range_tp = num;
data.range_expr = expr;
return [true, ''];
}
}
checkStartExpr(payNode, text, data) {
if (!payNode) return [false, '数据错误'];
const num = text ? _.toNumber(text) : 0;
let expr = text ? (num ? '' : text) : '';
expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase() : '';
const [valid, msg] = this.checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg', 'bqyf']);
if (!valid) return [valid, msg];
if (payUtils.check.isStarted(payNode)) {
return [false, '已经开始计量,请勿修改起扣金额'];
} else {
if (this.addBase.pre_gather_tp) {
const value = expr ? payCalc.calculateExpr(expr) : num;
if (this.addBase.pre_gather_tp && value < this.addBase.pre_gather_tp)
return [false, '起扣金额请勿少于本期完成截止上期计量金额' + this.addBase.pre_gather_tp];
data.start_tp = num;
data.start_expr = expr;
return [true, ''];
} else {
data.start_tp = num;
data.start_expr = expr;
return [true, ''];
}
}
}
getExprInfo(field, converse = false) {
const exprField = [
{qty: 'tp', expr: 'expr'},
{qty: 'start_tp', expr: 'start_expr'},
{qty: 'range_qty', expr: 'range_expr'},
];
if (converse) return _.find(exprField, { expr: field });
return _.find(exprField, {qty: field});
}
getLeafOrder(data, parentReg, tree) {
if (!data) return [];
const defaultResult = data.uuid ? [`<<${data.uuid}>>`] : [];
if (!data.expr) return defaultResult;
const nodeParam = data.expr.match(this.nodeReg);
if (!nodeParam || nodeParam.length === 0) return defaultResult;
const result = [];
for (const op of nodeParam) {
const id = op.substring(2, op.length - 2);
if (data.uuid === id || op === parentReg) {
result.push(op);
} else {
const payNode = tree.nodes.find(x => {return x.uuid === id; });
const subOrderParam = this.getLeafOrder(payNode, data.uuid ? `<<${data.uuid}>>` : parentReg, tree);
result.push(...subOrderParam);
}
}
return result;
}
checkCircularExpr(expr, selfId, tree) {
const leafOrder = this.getLeafOrder({expr}, `<<${selfId}>>`, tree);
if (leafOrder.indexOf(`<<${selfId}>>`) >= 0) return true;
return false;
}
calculateExpr(expr) {
let formula = expr;
for (const b of this.bases) {
formula = formula.replace(b.reg, b.value);
}
const percent = formula.match(this.percentReg);
if (percent) {
for (const p of percent) {
const v = math.evaluate(p.replace(new RegExp('%', 'gm'), '/100'));
formula = formula.replace(p, v);
}
}
try {
const value = ZhCalc.mathCalcExpr(formula);
return value;
} catch(err) {
return 0;
}
}
refreshBaseHtml() {
const html = [];
for (const [i, b] of this.bases.entries()) {
html.push('