| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147 | '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('<br/>');            },            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());                }            },            rangeTpReadOnly: function(data) {                if (payUtils.check.isOld(data)) {                    return !payUtils.check.isYB(data) || payUtils.check.isLock(data);                } else {                    return payUtils.check.isWC(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;                this.firstNodeReg = /^<<[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.firstNodeReg.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.firstNodeReg);                        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' || param[i-1].type !== 'left'))                        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('<tr>');                    html.push(`<td>${i+1}</td><td>${b.name}</td><td>${b.code}</td>`);                    if (b.code === 'bqyf') {                        html.push('<td class="text-right">--</td>');                    } else {                        html.push(`<td class="text-right">${b.formatValue}</td>`);                    }                    html.push('</tr>');                }                $('#base-list').html(html.join(''));            }            reloadBase(bases, add) {                this.bases = bases;                this.refreshBaseHtml();                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;            }        }        return new PayCalc(b, a);    })(calcBase, addBase);    const payObj = (function() {        const spread = SpreadJsObj.createNewSpread($('#pay-spread')[0]);        const sheet = spread.getActiveSheet();        const spreadSetting = {            cols: [                {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 230, formatter: '@', cellType: 'tree', getTip: payUtils.tips.name},                {title: '本期金额(F)', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number'},                {title: '截止上期金额',  colSpan: '1', rowSpan: '1', field: 'pre_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},                {title: '截止本期金额',  colSpan: '1', rowSpan: '1', field: 'end_tp', hAlign: 2, width: 100, readOnly: true, type: 'Number'},                {title: '起扣金额',  colSpan: '1', rowSpan: '1', field: 'start_tp', hAlign: 2, width: 100, type: 'Number'},                {title: '付(扣)款限额',  colSpan: '1', rowSpan: '1', field: 'range_tp', hAlign: 2, width: 100, cellType: 'tip', type: 'Number', getTip: payUtils.tips.range_tp},                {title: '汇总',  colSpan: '1', rowSpan: '1', field: 'is_gather', hAlign: 1, width: 60, cellType: 'signalCheckbox', show: payUtils.check.isGatherValid},                {title: '本期批注', colSpan: '1', rowSpan: '1', field: 'postil', hAlign: 0, width: 120, formatter: '@', cellType: 'autoTip'},            ],            emptyRows: 0,            headRows: 1,            headRowHeight: [32],            headColWidth: [30],            defaultRowHeight: 21,            headerFont: '12px 微软雅黑',            font: '12px 微软雅黑',            readOnly: readOnly,            localCache: {                key: 'phase-pay',                colWidth: true,            },            pos: SpreadJsObj.getObjPos($('#pay-spread')[0]),            getColor: function (sheet, data, row, col, defaultColor) {                if (!data) return defaultColor;                if (data.expr && data.expr.indexOf('#ref!') >= 0) {                    return spreadColor.pay.expr_err;                } else if (data.is_pause) {                    return '#f2f2f2';                } else if (data.create_phase_order > 1 && data.create_phase_order === phasePay.phase_order) {                    return '#FFFFE1';                } else {                    return defaultColor;                }            }        };        sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.phasePay);        SpreadJsObj.initSheet(sheet, spreadSetting);        const payTree = createNewPathTree('base', {            id: 'tree_id', pid: 'tree_pid', order: 'tree_order',            level: 'tree_level', isLeaf: 'tree_is_leaf', fullPath: 'tree_full_path',            rootId: -1,        });        const payEvent = {            refreshActn: function() {                const setObjEnable = function (obj, enable) {                    if (enable) {                        obj.removeClass('disabled');                    } else {                        obj.addClass('disabled');                    }                };                const select = SpreadJsObj.getSelectObject(sheet);                if (!select) {                    setObjEnable($('a[name=base-opr][type=add]'), false);                    setObjEnable($('a[name=base-opr][type=del]'), false);                    setObjEnable($('a[name=base-opr][type=up-move]'), false);                    setObjEnable($('a[name=base-opr][type=down-move]'), false);                    return;                }                const preNode = payTree.getPreSiblingNode(select);                setObjEnable($('a[name=base-opr][type=add]'), !readOnly && !payUtils.check.isSf(select) && !payUtils.check.isYf(select));                const delValid = !payUtils.check.isFixed(select) && !payUtils.check.isStarted(select);                setObjEnable($('a[name=base-opr][type=delete]'), !readOnly && delValid);                setObjEnable($('a[name=base-opr][type=up-move]'), !readOnly && !payUtils.check.isFixed(select) && preNode);                setObjEnable($('a[name=base-opr][type=down-move]'), !readOnly && !payUtils.check.isFixed(select) && !payTree.isLastSibling(select));            },            loadExprToInput: function() {                const sel = sheet.getSelections()[0];                const col = sheet.zh_setting.cols[sel.col];                const data = SpreadJsObj.getSelectObject(sheet);                if (data && (!data.children || data.children.length === 0)) {                    if (col.field === 'tp') {                        const expr = payCalc.trans2OrderExpr(data.expr, payTree);                        $('#pay-expr').val(expr).attr('field', 'expr').attr('org', expr)                            .attr('readOnly', readOnly|| payUtils.check.tpReadOnly(data));                    } else if (col.field === 'start_tp') {                        const expr = payCalc.trans2OrderExpr(data.start_expr, payTree) || data.start_tp;                        $('#pay-expr').val(expr).attr('field', 'start_expr').attr('org', expr)                            .attr('readOnly', readOnly|| payUtils.check.startTpReadOnly(data) || payUtils.check.isYf(data));                    } else if (col.field === 'range_tp') {                        const expr = payCalc.trans2OrderExpr(data.range_expr, payTree);                        $('#pay-expr').val(expr).attr('field', 'range_expr').attr('org', expr)                            .attr('readOnly', readOnly|| payUtils.check.rangeTpReadOnly(data) || payUtils.check.isYf(data));                    } else {                        $('#pay-expr').val('').attr('readOnly', true);                    }                    $('#pay-expr').attr('data-row', sel.row);                } else {                    $('#pay-expr').val('').attr('readOnly', true);                    $('#pay-expr').removeAttr('data-row');                }            },            refreshTree: function (data) {                SpreadJsObj.massOperationSheet(sheet, function () {                    const tree = sheet.zh_tree;                    // 处理删除                    if (data.delete) {                        data.delete.sort(function (a, b) {                            return b.deleteIndex - a.deleteIndex;                        });                        for (const d of data.delete) {                            sheet.deleteRows(d.deleteIndex, 1);                        }                    }                    // 处理新增                    if (data.create) {                        const newNodes = data.create;                        if (newNodes) {                            newNodes.sort(function (a, b) {                                return a.index - b.index;                            });                            for (const node of newNodes) {                                sheet.addRows(node.index, 1);                                SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);                            }                        }                    }                    // 处理更新                    if (data.update) {                        const rows = [];                        for (const u of data.update) {                            rows.push(tree.nodes.indexOf(u));                        }                        SpreadJsObj.reLoadRowsData(sheet, rows);                    }                });            },            editStarting: function(e, info) {                const col = info.sheet.zh_setting.cols[info.col];                const select = SpreadJsObj.getSelectObject(info.sheet);                switch (col.field) {                    case 'name':                        info.cancel = payUtils.check.isFixed(select);                        break;                    case 'tp':                    case 'is_gather':                        info.cancel = !payUtils.check.isGatherValid(select);                        break;                    case 'start_tp':                    case 'range_tp':                        info.cancel = (select.children && select.children.length > 0) || payUtils.check.isYf(select);                        break;                    case 'is_gather':                        info.cancel = true;                        break;                }                if (col.field === 'tp') {                    if (select.expr && select.expr !== '') {                        info.sheet.getCell(info.row, info.col).text(payCalc.trans2OrderExpr(select.expr, payTree));                    }                } else if (col.field === 'start_tp') {                    if (select.start_expr && select.start_expr !== '') {                        info.sheet.getCell(info.row, info.col).text(select.start_expr);                    }                } else if (col.field === 'range_tp') {                    if (select.range_expr && select.range_expr !== '') {                        info.sheet.getCell(info.row, info.col).text(select.range_expr);                    }                }            },            editEnded: function(e, info) {                if (!info.sheet.zh_setting) return;                const select = SpreadJsObj.getSelectObject(info.sheet);                const col = info.sheet.zh_setting.cols[info.col];                if (col.field === 'is_gather') return;                // 未改变值则不提交                const validText = info.editingText ? info.editingText.replace('\n', '') : '';                let orgValue;                if (col.field === 'tp') {                    orgValue = select.expr ? payCalc.trans2OrderExpr(select.expr, payTree) : select.tp;                } else if (col.field === 'start_tp') {                    orgValue = select.start_expr ? select.start_expr : select.start_tp;                } else if (col.field === 'range_tp') {                    orgValue = select.range_expr ? select.range_expr : select.range_tp;                } else {                    orgValue = select[col.field];                }                orgValue = orgValue || '';                if (orgValue == validText) {                    SpreadJsObj.reLoadRowData(info.sheet, info.row);                    return;                }                const data = { postType: 'update', postData: { id: select.id } };                switch(col.field) {                    case 'tp':                        const [tpValid, tpMsg] = payUtils.check.isSf(select)                            ? payCalc.checkSfExpr(validText, data.postData, select, payTree)                            : payCalc.checkExpr(validText, data.postData, select, payTree);                        if (!tpValid) {                            toastr.warning(tpMsg);                            SpreadJsObj.reLoadRowData(info.sheet, info.row);                            return;                        }                        break;                    case 'start_tp':                        const [sValid, sMsg] = payCalc.checkStartExpr(select, validText, data.postData);                        if (!sValid) {                            toastr.warning(sMsg);                            SpreadJsObj.reLoadRowData(info.sheet, info.row);                            return;                        }                        break;                    case 'range_tp':                        const [rValid, rMsg] = payCalc.checkRangeExpr(select, validText, data.postData);                        if (!rValid) {                            toastr.warning(rMsg);                            SpreadJsObj.reLoadRowData(info.sheet, info.row);                            return;                        }                        break;                    default:                        if (col.type === 'Number') {                            data.postData[col.field] = _.toNumber(validText) || 0;                        } else {                            data.postData[col.field] = validText || '';                        }                        break;                }                postData('update', data, function (result) {                    if (result.reload) {                        payEvent.reloadPays(result.reload);                    } else {                        const refreshData = payTree.loadPostData(result);                        payEvent.refreshTree(refreshData);                    }                }, function () {                    SpreadJsObj.reLoadRowData(info.sheet, info.row);                });            },            selectionChanged: function(e, info) {                if (info.newSelections) {                    if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {                        payEvent.refreshActn();                    }                }                payEvent.loadExprToInput();            },            buttonClicked: function (e, info) {                if (!info.sheet.zh_setting) return;                const select = SpreadJsObj.getSelectObject(info.sheet);                const col = info.sheet.zh_setting.cols[info.col];                if (col.field !== 'is_gather') return;                if (!payUtils.check.isGatherValid(select)) return;                if (info.sheet.isEditing()) info.sheet.endEdit(true);                const data = {                    postType: 'update',                    postData: { id: select.id },                };                data.postData[col.field] = info.sheet.getValue(info.row, info.col) || 0;                // 更新至服务器                postData('update', data, function (result) {                    if (result.reload) payEvent.reloadPays(result.reload);                });            },            deletePress: function (sheet) {                if (!sheet.zh_setting) return;                const sel = sheet.getSelections()[0];                if (!sel) return;                const col = sheet.zh_setting.cols[sel.col];                if (col.readOnly === true) return;                if (sel.colCount > 1) toastr.warning('请勿同时删除多列数据');                const data = { postType: 'update', postData: [] };                for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow ++) {                    const node = sheet.zh_tree.nodes[iRow];                    if (node && (!node.pay_type || col.field === 'postil')) {                        const updateData = { id: node.id };                        switch(col.field) {                            case 'tp':                                updateData.tp = 0;                                updateData.expr = '';                                break;                            case 'start_tp':                                updateData.start_tp = 0;                                updateData.start_expr = '';                                break;                            case 'range_tp':                                updateData.range_tp = 0;                                updateData.range_expr = '';                                break;                            case 'is_gather':                                updateData.is_gather = 0;                                break;                            default:                                updateData[col.field] = col.type === 'Number' ? 0 : '';                        }                        data.postData.push(updateData);                    }                }                postData('update', data, function (result) {                    if (result.reload) {                        payEvent.reloadPays(result.reload);                    } else {                        const refreshData = payTree.loadPostData(result);                        payEvent.refreshTree(refreshData);                    }                }, function () {                    SpreadJsObj.reLoadRowData(sheet, sel.row, sel.rowCount);                });            },            baseOpr: function(type) {                const self = this;                const node = SpreadJsObj.getSelectObject(sheet);                if (type === 'delete') {                    postData('update', { postType: 'delete', postData: { id: node.tree_id }}, function(result) {                        payEvent.reloadPays(result.reload);                    });                } else {                    postData('update', { postType: type, postData: { id: node.tree_id }}, function (result) {                        const refreshData = payTree.loadPostData(result);                        payEvent.refreshTree(refreshData);                        const sel = sheet.getSelections()[0];                        if (sel) {                            if (['up-move', 'down-move'].indexOf(type) > -1) {                                sheet.setSelection(payTree.getNodeIndex(node), sel.col, sel.rowCount, sel.colCount);                                SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(node)]);                            } else if (type === 'add') {                                sheet.setSelection(payTree.getNodeIndex(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);                                SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, payTree.getNodeIndex(refreshData.create[0])]);                            }                        }                        self.refreshActn(sheet);                    });                }            },            calculateAll: function() {                postData('update', { postType: 'calc', postData: {}}, function (result) {                    payEvent.reloadPays(result.reload);                });            },            reloadCalcBase: function() {                postData('update', { postType: 'refreshBase', postData: {}}, function (result) {                    payEvent.reloadPays(result.reload);                    payCalc.reloadBase(result.calcBase, result.addBase);                });            },            reloadPays: function(datas){                payTree.loadDatas(datas);                SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Tree, payTree);                payEvent.refreshActn();            },        };        spread.bind(spreadNS.Events.SelectionChanged, payEvent.selectionChanged);        if (!readOnly) {            spread.bind(spreadNS.Events.EditStarting, payEvent.editStarting);            spread.bind(spreadNS.Events.EditEnded, payEvent.editEnded);            spread.bind(spreadNS.Events.ButtonClicked, payEvent.buttonClicked);            SpreadJsObj.addDeleteBind(spread, payEvent.deletePress);            $('a[name="base-opr"]').click(function () {                payEvent.baseOpr(this.getAttribute('type'));            });            $('#pay-expr').bind('change onblur', function () {                if (this.readOnly) return;                const expr = $(this);                const row = expr.attr('data-row') ? _.toInteger(expr.attr('data-row')) : -1;                const select = payTree.nodes[row];                const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = expr.val();                if (orgValue === newValue || (!orgValue && newValue == '')) { return; }                const data = { postType: 'update' };                if (field === 'expr') {                    data.postData = { id: select.id, tp: 0, expr: newValue };                    const [valid, msg] = payUtils.check.isSf(select)                        ? payCalc.checkSfExpr(newValue, data.postData, select, payTree)                        : payCalc.checkExpr(newValue, data.postData, select, payTree);                    if (!valid) {                        toastr.warning(msg);                        this.value = select.expr;                        return;                    }                } else if (field === 'start_expr') {                    data.updateData = {id: select.id};                    const [valid, msg] = payCalc.checkStartExpr(select, newValue, data.postData);                    if (!valid) {                        toastr.warning(msg);                        this.value = select.start_expr;                        return;                    }                } else if (field === 'range_expr') {                    data.updateData = {id: select.id};                    const [valid, msg] = payCalc.checkRangeExpr(select, newValue, data.postData);                    if (!valid) {                        toastr.warning(msg);                        this.value = select.range_expr;                        return;                    }                } else {                    expr.val('');                    return;                }                // 更新至服务器                postData('update', data, function (result) {                    if (result.reload) {                        payEvent.reloadPays(result.reload);                    } else {                        const refreshData = payTree.loadPostData(result);                        payEvent.refreshTree(refreshData);                    }                });            });            $('#calc-all').click(function() {                payEvent.calculateAll();            });            $('#reload-calc-base').click(function() {                payEvent.reloadCalcBase();            });            const deadlineObj = {                payNode: null,                refreshHint: function() {                    const dlType = $('[name=dl-type]:checked').val();                    const dt = deadlineType[dlType];                    if (dlType && dt) {                        const dlValue = $('#dl-value').val();                        $('#range-hint').text(`当 ${dt.name} >= ${dlValue} 时 `);                        $('#dl-hint').show();                    } else {                        $('#dl-hint').hide();                    }                },                initView: function(data) {                    this.payNode = data;                    $('#dl-pay-name').html(data.name);                    // 模式                    if (data.dl_type) {                        $('[name=dl-type][value=' + data.dl_type +']')[0].checked = true;                    } else {                        $('#dl-type-none')[0].checked = true;                    }                    $('#dl-value').val(data.dl_value);                    this.refreshHint();                },                getDlCount: function() {                    try {                        const result = parseInt($('#dl-value').val());                        if (result <= 0) throw '限制值请输入正整数';                        return result;                    } catch (err) {                        toastr.warning('限制值请输入正整数');                        return 0;                    }                },                getDlTp: function() {                    try {                        const result = parseFloat($('#dl-value').val());                        return result;                    } catch (err) {                        toastr.warning('限制值请输入数值');                        return 0;                    }                },                getUpdateData: function() {                    const result = { postType: 'update', postData: { id: this.payNode.id } };                    result.postData.dl_type = $('[name=dl-type]:checked').val();                    if (result.postData.dl_type) {                        if (result.postData.dl_type === deadlineType.phaseCount.key) {                            result.postData.dl_value = this.getDlCount();                            if (result.postData.dl_value < phasePay.phase_order) {                                toastr.warning(`已计量至第${phasePay.phase_order}期,计提期限不可小于该期`);                                return null;                            }                        } else if (result.postData.dl_type === deadlineType.stageCount.key) {                            result.postData.dl_value = this.getDlCount();                            if (result.postData.dl_value < maxStageOrder) {                                toastr.warning(`已计量至第${maxStageOrder}期,计提期限不可小于该期`);                                return null;                            }                        } else {                            result.postData.dl_value = this.getDlTp();                            const dt = deadlineType[result.postData.dl_type];                            if (!dt) {                                toastr.warning('限制模式错误,请刷新页面重试');                                return null;                            }                            const compareValue = payCalc.addBase[`pre_${dt.key}_tp`];                            if (result.postData.dl_type < compareValue) {                                toastr.warning(`截止上期,${dt.name}已计量${compareValue},计提期限不可小于该值`);                                return null;                            }                        }                    } else {                        result.postData.dl_value = 0;                    }                    return result;                },            };            $('[name=dl-type]').change(deadlineObj.refreshHint);            $('#dl-value').change(deadlineObj.refreshHint);            $('#deadline-ok').click(function() {                const updateData = deadlineObj.getUpdateData();                if (!updateData) return;                postData('update', updateData, function(result) {                    payEvent.reloadPays(result.reload);                    $('#deadline').modal('hide');                });            });            // 右键菜单            $.contextMenu({                selector: '#pay-spread',                build: function ($trigger, e) {                    const target = SpreadJsObj.safeRightClickSelection($trigger, e, spread);                    return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;                },                items: {                    'start': {                        name: '启用',                        icon: 'fa-play',                        callback: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            const data = {                                postType: 'update',                                postData: { id: select.id, is_pause: 0 }                            };                            // 更新至服务器                            postData('update', data, function (result) {                                payEvent.reloadPays(result.reload);                            });                        },                        visible: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            return (!select.children || select.children.length ===0) && !readOnly  && select.is_pause && payUtils.menuVisible.pause(select);                        }                    },                    'stop': {                        name: '停用',                        icon: 'fa-pause',                        callback: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            const data = {                                postType: 'update',                                postData: { id: select.id, is_pause: 1 }                            };                            // 更新至服务器                            postData('update', data, function (result) {                                payEvent.reloadPays(result.reload);                            });                        },                        visible: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            return (!select.children || select.children.length === 0) && !readOnly && !select.is_pause && payUtils.menuVisible.pause(select);                        },                    },                    'setDeadline': {                        name: '设置计提期限',                        icon: 'fa-clipboard',                        callback: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            if (select.range_tp) {                                deadlineObj.initView(select);                                $('#deadline').modal('show');                            } else {                                toastr.warning('计提期限用于达到条件时,即刻计量至付(扣)款限额,应先设置付(扣)款限额');                            }                        },                        visible: function (key, opt) {                            const select = SpreadJsObj.getSelectObject(sheet);                            return (!select.children || select.children.length === 0) && !readOnly && payUtils.menuVisible.deadline(select);                        }                    },                }            });        }        return { spread, sheet, payTree, loadDatas: payEvent.reloadPays }    })();    payObj.loadDatas(details);    const payFile = $.ledger_att({        selector: '#fujian',        key: 'uuid',        masterKey: 'rela_id',        uploadUrl: 'file/upload',        deleteUrl: 'file/delete',        checked: false,        zipName: `附件.zip`,        readOnly: false,        fileIdType: 'string',        fileInfo: {            user_name: 'user_name',            user_id: 'user_id',            create_time: 'create_time',        },        getCurHint: function(node) {            return`${node.name || ''}`;        },        locate: function (att) {            if (!att) return;            SpreadJsObj.locateTreeNode(payObj.sheet, att.node.tree_id, true);            payFile.getCurAttHtml(att.node);        }    });    // 展开收起标准清单    $('a', '#side-menu').bind('click', function (e) {        e.preventDefault();        const tab = $(this), tabPanel = $(tab.attr('content'));        // 展开工具栏、切换标签        if (!tab.hasClass('active')) {            // const close = $('.active', '#side-menu').length === 0;            $('a', '#side-menu').removeClass('active');            $('.tab-content .tab-select-show.tab-pane.active').removeClass('active');            tab.addClass('active');            tabPanel.addClass('active');            // $('.tab-content .tab-pane').removeClass('active');            showSideTools(tab.hasClass('active'));        } else { // 收起工具栏            tab.removeClass('active');            tabPanel.removeClass('active');            showSideTools(tab.hasClass('active'));        }        payObj.spread.refresh();    });    postData('load', {filter: 'file'}, function(result) {        for (const f of result.file) {            f.node = payObj.payTree.datas.find(x => { return x.uuid === f.rela_id; });        }        payFile.loadDatas(result.file);        payFile.getCurAttHtml(SpreadJsObj.getSelectObject(payObj.sheet));    });    // 工具栏spr    $.divResizer({        select: '#right-spr',        callback: function () {            payObj.spread.refresh();        }    });    // todo 加载审批列表    $.subMenu({        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',        key: 'menu.1.0.0',        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',        callback: function (info) {            if (info.mini) {                $('.panel-title').addClass('fluid');                $('#sub-menu').removeClass('panel-sidebar');            } else {                $('.panel-title').removeClass('fluid');                $('#sub-menu').addClass('panel-sidebar');            }            autoFlashHeight();            payObj.spread.refresh();        }    });});
 |