'use strict';
/**
 * 台账修订页面js
 *
 * @author Mai
 * @date
 * @version
 */
const ckBillsSpread = window.location.pathname + '-billsSelect';
const invalidFields = {
    parent: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'deal_qty', 'deal_tp', 'unit_price'],
    gcl: ['dgn_qty1', 'dgn_qty2'],
    posCode: ['b_code'],
    posCalc: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp'],
    posXmj: ['code'],
};
function getExprInfo (field) {
    const exprField = [
        {qty: 'sgfh_qty', expr: 'sgfh_expr'},
        {qty: 'sjcl_qty', expr: 'sjcl_expr'},
        {qty: 'qtcl_qty', expr: 'qtcl_expr'},
    ];
    return _.find(exprField, {qty: field});
}
function transExpr(expr) {
    return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
}
$(document).ready(() => {
    let stdXmj, stdGcl, searchLedger;
    autoFlashHeight();
    // 初始化spread
    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
    const billsSheet = billsSpread.getActiveSheet();
    sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
    SpreadJsObj.initSheet(billsSheet, billsSpreadSetting);
    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
    const posSheet = posSpread.getActiveSheet();
    SpreadJsObj.initSheet(posSheet, posSpreadSetting);
    const posSearch = $.posSearch({selector: '#pos-search', searchSpread: posSpread});
    const errorList = $.cs_errorList({
        tabSelector: '#error-list-tab',
        selector: '#error-list',
        relaSpread: billsSpread,
        storeKey: 'revise-error-' + window.location.pathname.split('/')[2] + '-' + window.location.pathname.split('/')[4],
        afterLocated:  function () {
            posSpreadObj.loadCurPosData();
        },
        afterShow: function () {
            billsSpread.refresh();
            if (posSpread) posSpread.refresh();
        },
    });
    // 初始化 节点树结构
    const treeSetting = {
        id: 'ledger_id',
        pid: 'ledger_pid',
        order: 'order',
        level: 'level',
        rootId: -1,
        keys: ['id', 'tender_id', 'ledger_id'],
        calcFields: ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price'],
    };
    if (!isTz) {
        treeSetting.calcFields.push('deal_tp');
    }
    treeSetting.calcFun = function (node) {
        node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
    };
    const billsTree = createNewPathTree('revise', treeSetting);    
    // 初始化 计量单元
    const pos = new PosData({ id: 'id', ledgerId: 'lid' });
    // 清单 相关方法&绑定spreadjs事件
    const billsTreeSpreadObj = {
        loadExprToInput(sheet) {
            const sel = sheet.getSelections()[0];
            const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
            if (col && col.type === 'Number') {
                const data = SpreadJsObj.getSelectObject(sheet);
                if (data) {
                    const exprInfo = getExprInfo(col.field);
                    if (exprInfo) {
                        $('#bills-expr').val(data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
                            .attr('field', col.field).attr('org', data[col.field]);
                    } else {
                        $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
                    }
                    if (col.field.indexOf('dgn') >= 0) {
                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (_.isString(data.b_code) && data.b_code !== ''));
                    } else if (col.field === 'unit_price') {
                        $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0) || (_.isBoolean(data.used) && data.used === true));
                    } else {
                        const nodePos = pos.getLedgerPos(data.id);
                        if (nodePos && nodePos.length > 0) {
                            $('#bills-expr').val('').attr('readOnly', true);
                            $('#bills-expr').removeAttr('data-row');
                        } else {
                            $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (data.children && data.children.length > 0));
                        }
                    }
                    $('#bills-expr').attr('data-row', sel.row);
                } else {
                    $('#bills-expr').val('').attr('readOnly', true);
                    $('#bills-expr').removeAttr('data-row');
                }
            } else {
                $('#bills-expr').val('').attr('readOnly', true);
                $('#bills-expr').removeAttr('data-row');
            }
        },
        getDefaultSelectInfo: function (sheet) {
            const tree = sheet.zh_tree;
            if (!tree) return;
            const sel = sheet.getSelections()[0];
            const node = sheet.zh_tree.nodes[sel.row];
            if (!node) return;
            let count = 1;
            if (sel.rowCount > 1) {
                for (let r = 1; r < sel.rowCount; r++) {
                    const rNode = sheet.zh_tree.nodes[sel.row + r];
                    if (rNode.level > node.level) continue;
                    if ((rNode.level < node.level) || (rNode.level === node.level && rNode.pid !== node.pid)) {
                        toastr.warning('请选择同一节点下的节点,进行该操作');
                        return;
                    }
                    count += 1;
                }
            }
            return [tree, node, count];
        },
        /**
         * 刷新顶部按钮是否可用
         * @param sheet
         * @param selections
         */
        refreshOperationValid: function (sheet, selection) {
            const setObjEnable = function (obj, enable) {
                if (enable) {
                    obj.removeClass('disabled');
                } else {
                    obj.addClass('disabled');
                }
            };
            const invalidAll = function () {
                setObjEnable($('a[name=base-opr][type=add]'), false);
                setObjEnable($('a[name=base-opr][type=delete]'), false);
                setObjEnable($('a[name=base-opr][type=up-move]'), false);
                setObjEnable($('a[name=base-opr][type=down-move]'), false);
                setObjEnable($('a[name=base-opr][type=up-level]'), false);
                setObjEnable($('a[name=base-opr][type=down-level]'), false);
            };
            const sel = selection ? selection[0] : sheet.getSelections()[0];
            const row = sel ? sel.row : -1;
            const tree = sheet.zh_tree;
            if (!tree) {
                invalidAll();
                return;
            }
            const first = sheet.zh_tree.nodes[row];
            if (!first) {
                invalidAll();
                return;
            }
            let last = first, sameParent = true, nodeUsed = first.used;
            if (sel.rowCount > 1 && first) {
                for (let r = 1; r < sel.rowCount; r++) {
                    const rNode = tree.nodes[sel.row + r];
                    if (!rNode) {
                        sameParent = false;
                        break;
                    }
                    nodeUsed = nodeUsed || rNode.used;
                    if (rNode.level > first.level) continue;
                    if ((rNode.level < first.level) || (rNode.level === first.level && rNode.pid !== first.pid)) {
                        sameParent = false;
                        break;
                    }
                    last = rNode;
                }
            }
            const preNode = tree.getPreSiblingNode(first);
            const valid = !sheet.zh_setting.readOnly;
            setObjEnable($('a[name=base-opr][type=add]'), valid && first && first.level > 1);
            setObjEnable($('a[name=base-opr][type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed);
            setObjEnable($('a[name=base-opr][type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
            setObjEnable($('a[name=base-opr][type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
            const posRange = last ? pos.getLedgerPos(last.id) : [];
            setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
                && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
            const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
            setObjEnable($('a[name=base-opr][type=down-level]'), valid && first && sameParent
                && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0) && !preNode.used);
            setObjEnable($('#cut'), valid);
            setObjEnable($('#paste'), valid);
        },
        /**
         *
         * @param sheet
         * @param data
         */
        refreshTree: function (sheet, 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);
                }
                // 处理展开
                if (data.expand) {
                    const expanded = [];
                    for (const e of data.expand) {
                        if (expanded.indexOf(e) === -1) {
                            const posterity = tree.getPosterity(e);
                            for (const p of posterity) {
                                sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
                                expanded.push(p);
                            }
                        }
                    }
                }
            });
        },
        selectionChanged: function (e, info) {
            if (info.newSelections) {
                if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
                    billsTreeSpreadObj.refreshOperationValid(info.sheet);
                    posSpreadObj.loadCurPosData();
                    SpreadJsObj.saveTopAndSelect(billsSheet, ckBillsSpread);
                    posSearch.search($('#pos-keyword').val());
                }
            }
            billsTreeSpreadObj.loadExprToInput(info.sheet);
        },
        /**
         * 新增节点
         * @param spread
         */
        baseOpr: function (sheet, type, addCount = 1) {
            const self = this;
            const [tree, node, count] = this.getDefaultSelectInfo(sheet);
            if (!tree || !node || !count) return;
            if (type === 'delete') {
                const parent = tree.getParent(node);
                const children = parent ? parent.children : tree.children;
                const index = children.indexOf(node);
                for (let i = 0; i < count; i++) {
                    const child = children[i+index];
                    if (tree.checkNodeUsed(child, pos)) {
                        toastr.warning('选中的节点已计量,不可删除');
                        return;
                    }
                }
            } else if (type === 'up-level') {
                const parent = tree.getParent(node);
                const children = parent ? parent.children : tree.children;
                const index = children.indexOf(node);
                for (let i = index; i < children.length; i++) {
                    const child = children[index];
                    if (tree.checkNodeUsed(child, pos)) {
                        if (i >= index + count) {
                            toastr.warning('其后节点已计量,选中的节点不可升级');
                        } else {
                            toastr.warning('选中的节点已计量,不可升级');
                        }
                        return;
                    }
                }
            } else if (type === 'down-level') {
                const parent = tree.getParent(node);
                const children = parent ? parent.children : tree.children;
                const index = children.indexOf(node);
                for (let i = 0; i < count; i++) {
                    const child = children[i+index];
                    if (tree.checkNodeUsed(child, pos)) {
                        toastr.warning('选中的节点已计量,不可降级');
                        return;
                    }
                }
            }
            if (type === 'delete') {
                deleteAfterHint(function () {
                    postData(window.location.pathname + '/update', {
                        postType: type,
                        postData: {
                            id: node.ledger_id,
                            count: type === 'add' ? addCount : count,
                        }
                    }, function (result) {
                        const refreshData = tree.loadPostData(result);
                        self.refreshTree(sheet, refreshData);
                        const sel = sheet.getSelections()[0];
                        if (sel) {
                            sheet.setSelection(sel.row, sel.col, 1, sel.colCount);
                        }
                        self.refreshOperationValid(sheet);
                    });
                });
            } else {
                postData(window.location.pathname + '/update', {
                    postType: type,
                    postData: {
                        id: node.ledger_id,
                        count: type === 'add' ? addCount : count,
                    }
                }, function (result) {
                    const refreshData = tree.loadPostData(result);
                    self.refreshTree(sheet, refreshData);
                    if (['up-move', 'down-move'].indexOf(type) > -1) {
                        const sel = sheet.getSelections()[0];
                        if (sel) {
                            sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
                        }
                    } else if (type === 'add') {
                        const sel = sheet.getSelections()[0];
                        if (sel) {
                            sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
                        }
                    }
                    self.refreshOperationValid(sheet);
                });
            }
        },
        /**
         * 编辑单元格响应事件
         * @param {Object} e
         * @param {Object} info
         */
        editEnded: function (e, info) {
            if (info.sheet.zh_setting) {
                const col = info.sheet.zh_setting.cols[info.col];
                const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
                const node = sortData[info.row];
                const data = {
                    id: node.id,
                    tender_id: node.tender_id,
                    ledger_id: node.ledger_id
                };
                // 未改变值则不提交
                const orgValue = node[col.field];
                const newValue = trimInvalidChar(info.editingText);
                if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) {
                    return;
                }
                // 台账模式,检查计量单元相关
                if (isTz) {
                    if (col.field === 'sgfh_qty' || col.field === 'sgfh_tp' ||
                        col.field === 'sjcl_qty' || col.field === 'sjcl_tp' ||
                        col.field === 'qtcl_qty' || col.field === 'qtcl_tp') {
                        if (!node.children || node.children.length ===0) {
                            const lPos = pos.getLedgerPos(node.id);
                            if (lPos && lPos.length > 0) {
                                toastr.error('清单含有计量单元,不可修改施工图复核数量');
                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                                return;
                            }
                        }
                    }
                    if (col.field === 'b_code' && (newValue === '' || !newValue)) {
                        const lPos = pos.getLedgerPos(node.id);
                        if (lPos && lPos.length > 0) {
                            toastr.error('清单含有计量单元,请先删除计量单元,再删除清单编号');
                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
                            return;
                        }
                    }
                }
                // 获取更新数据
                const exprInfo = getExprInfo(col.field);
                if (info.editingText) {
                    const text = newValue;
                    if (node.used && (col.field === 'code' || col.field ==='b_code') && orgValue !== '' && text === '') {
                        toastr.error('节点已计量,请勿删除编号');
                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
                        return;
                    }
                    if (col.type === 'Number') {
                        const num = _.toNumber(text);
                        if (_.isFinite(num)) {
                            data[col.field] = num;
                            if (exprInfo) {
                                data[exprInfo.expr] = '';
                            }
                        } else {
                            try {
                                data[col.field] = math.evaluate(transExpr(text));
                                if (exprInfo) {
                                    data[exprInfo.expr] = newValue;
                                }
                            } catch(err) {
                                toastr.error('输入的表达式非法');
                                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                                return;
                            }
                        }
                    } else {
                        data[col.field] = text;
                    }
                } else {
                    if (node.used && (col.field === 'code' || col.field ==='b_code') && orgValue !== '') {
                        toastr.error('节点已计量,请勿删除编号');
                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
                        return;
                    }
                    data[col.field] = null;
                    if (exprInfo) {
                        data[exprInfo.expr] = '';
                    }
                }
                // 更新至服务器
                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                    const refreshNode = billsTree.loadPostData(result);
                    billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                });
            }
        },
        clipboardPasting: function (e, info) {
            const tree = info.sheet.zh_tree, setting = info.sheet.zh_setting;
            info.cancel = true;
            if (!setting || !tree) return;
            // const range = info.cellRange;
            // for (let iRow = range.row; iRow < range.row + range.rowCount; iRow++) {
            //     const node = tree.nodes[iRow];
            //     if (tree.checkNodeUsed(node, pos)) {
            //         toastr.warning('"' + node.code + node.b_code + ' ' + node.name +'"已计量,请勿修改');
            //         return;
            //     }
            // }
            const pasteData = info.pasteData.html
                ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
                : (info.pasteData.text === ''
                    ? SpreadJsObj.Clipboard.getAnalysisPasteText()
                    : SpreadJsObj.analysisPasteText(info.pasteData.text));
            const hint = {
                usedUp: {type: 'warning', msg: '节点已计量,不可修改单价'},
                usedCode: {type: 'warning', msg: '节点已计量,编号不可修改为空值'},
                invalidExpr: {type: 'warning', msg: '粘贴的表达式非法'},
                posCode: {type: 'warning', msg: '清单含有计量单元,不可粘贴清单编号为空'},
                posQty: {type: 'warning', msg: '清单含有计量单元,数量金额根据计量单元汇总计算所得,不可粘贴'},
                parent: {type: 'warning', msg: '含有子项的清单,不可粘贴数量、单价、金额'},
                gcl: {type: 'warning', msg: '工程量清单,不可粘贴项目节数量'},
                posXmj: {type: 'warning', msg: '清单含有计量单元,不可粘贴项目节编号'},
                sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
            };
            const datas = [], filterNodes = [];
            let pid, level, filterRow = 0;
            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
                const curRow = info.cellRange.row + iRow;
                const node = tree.nodes[curRow];
                if (!node) continue;
                if (!pid) pid = node.ledger_pid;
                if (!level) level = node.level;
                if (node.ledger_pid !== pid) {
                    toastMessageUniq(hint.sameParent);
                    filterRow+=1;
                    continue;
                }
                if (node.level < level) break;
                let bPaste = false;
                const data = info.sheet.zh_tree.getNodeKeyData(node);
                for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                    const curCol = info.cellRange.col + iCol;
                    const colSetting = info.sheet.zh_setting.cols[curCol];
                    const value = trimInvalidChar(pasteData[iRow-filterRow][iCol]);
                    if (node.children && node.children.length > 0 && invalidFields.parent.indexOf(colSetting.field) >= 0) {
                        toastMessageUniq(hint.parent);
                        continue;
                    }
                    if (!_.isEmpty(node.b_code) && invalidFields.gcl.indexOf(colSetting.field) >= 0) {
                        toastMessageUniq(hint.gcl);
                        continue;
                    }
                    const lPos = pos.getLedgerPos(node.id);
                    if (lPos && lPos.length > 0) {
                        if (value === '' && colSetting.field === 'b_code') {
                            toastMessageUniq(hint.posCode);
                            continue;
                        }
                        if (colSetting.field === 'sgfh_qty' || colSetting.field === 'sgfh_tp' ||
                            colSetting.field === 'sjcl_qty' || colSetting.field === 'sjcl_tp' ||
                            colSetting.field === 'qtcl_qty' || colSetting.field === 'qtcl_tp') {
                            toastMessageUniq(hint.posQty);
                            continue;
                        }
                        if (value !== '' && invalidFields.posXmj.indexOf(colSetting.field) >= 0) {
                            toastMessageUniq(hint.posXmj);
                            continue;
                        }
                    }
                    if (tree.checkNodeUsed(node, pos) && col.field === 'unit_price') {
                        toastMessageUniq (hint.usedUp);
                        continue;
                    }
                    if (colSetting.type === 'Number') {
                        const num = _.toNumber(value);
                        if (num) {
                            data[colSetting.field] = num;
                        } else {
                            try {
                                data[colSetting.field] = math.evaluate(transExpr(value));
                                const exprInfo = getExprInfo(colSetting.field);
                                if (exprInfo) {
                                    data[exprInfo.expr] = value;
                                }
                                bPaste = true;
                            } catch (err) {
                                toastMessageUniq(hint.invalidExpr);
                                continue;
                            }
                        }
                    } else {
                        if (node.used && (col.field === 'code' || col.field ==='b_code')
                            && data[colSetting.field] !== '' && value === '') {
                            toastMessageUniq(hint.usedCode);
                            continue;
                        }
                        data[colSetting.field] = value;
                    }
                    bPaste = true;
                }
                if (bPaste) {
                    datas.push(data);
                } else {
                    filterNodes.push(node);
                }
            }
            if (datas.length > 0) {
                postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                    const refreshNode = tree.loadPostData(result);
                    if (refreshNode.update) {
                        refreshNode.update = refreshNode.update.concat(filterNodes);
                    }
                    billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                }, function () {
                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
                });
            } else {
                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
            }
        },
        clipboardPasted: function (e, info) {
            const hint = {
                usedUp: {type: 'error', msg: '节点已计量,不可修改单价'},
                usedCode: {type: 'error', msg: '节点已计量,编号不可修改为空值'},
                invalidExpr: {type: 'error', msg: '粘贴的表达式非法'},
                posCode: {type: 'error', msg: '清单含有计量单元,请先删除计量单元,再修改清单编号为空'},
                posQty: {type: 'error', msg: '清单含有计量单元,数量金额根据计量单元汇总计算所得,不可修改'},
            };
            const tree = info.sheet.zh_tree;
            if (!tree) { return; }
            const sortData = info.sheet.zh_tree.nodes;
            const datas = [], filterNodes = [];
            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
                let bPaste = false;
                const curRow = info.cellRange.row + iRow;
                const node = sortData[curRow];
                if (node) {
                    const data = info.sheet.zh_tree.getNodeKeyData(node);
                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                        const curCol = info.cellRange.col + iCol;
                        const colSetting = info.sheet.zh_setting.cols[curCol];
                        const value = trimInvalidChar(info.sheet.getText(curRow, curCol));
                        const lPos = pos.getLedgerPos(node.id);
                        if (lPos && lPos.length > 0) {
                            if (value === '' && colSetting.field === 'b_code') {
                                toastMessageUniq(hint.posCode);
                                continue;
                            }
                            if (colSetting.field === 'sgfh_qty' || colSetting.field === 'sgfh_tp' ||
                                colSetting.field === 'sjcl_qty' || colSetting.field === 'sjcl_tp' ||
                                colSetting.field === 'qtcl_qty' || colSetting.field === 'qtcl_tp') {
                                toastMessageUniq(hint.posQty);
                                continue;
                            }
                        }
                        if (node.used && col.field === 'unit_price') {
                            toastMessageUniq (hint.usedUp);
                            continue;
                        }
                        if (colSetting.type === 'Number') {
                            const num = _.toNumber(value);
                            if (num) {
                                data[colSetting.field] = num;
                            } else {
                                try {
                                    data[colSetting.field] = math.evaluate(transExpr(value));
                                } catch (err) {
                                    toastMessageUniq(hint.invalidExpr);
                                    continue;
                                }
                            }
                        } else {
                            if (node.used && (col.field === 'code' || col.field ==='b_code')
                                && data[colSetting.field] !== '' && value === '') {
                                toastMessageUniq(hint.usedCode);
                                continue;
                            }
                            data[colSetting.field] = value;
                        }
                        bPaste = true;
                    }
                    if (bPaste) {
                        datas.push(data);
                    } else {
                        filterNodes.push(node);
                    }
                }
            }
            if (datas.length > 0) {
                postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                    const refreshNode = tree.loadPostData(result);
                    if (refreshNode.update) {
                        refreshNode.update = refreshNode.update.concat(filterNodes);
                    }
                    billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                }, function () {
                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
                });
            } else {
                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
            }
        },
        deletePress: function (sheet) {
            if (!sheet.zh_setting) return;
            const sel = sheet.getSelections()[0], datas = [];
            for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
                const node = sheet.zh_tree.nodes[iRow];
                if (sheet.zh_tree.checkNodeUsed(node, pos)) {
                    toastr.warning('"' + node.code + node.b_code + ' ' + node.name +'"已计量,请勿修改');
                    return;
                }
                const data = sheet.zh_tree.getNodeKeyData(node);
                for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                    const col = sheet.zh_setting.cols[iCol];
                    data[col.field] = null;
                    const exprInfo = getExprInfo(col.field);
                    if (exprInfo) {
                        data[exprInfo.expr] = '';
                    }
                }
                datas.push(data);
            }
            if (datas.length > 0) {
                postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                    const refreshNode = sheet.zh_tree.loadPostData(result);
                    billsTreeSpreadObj.refreshTree(sheet, refreshNode);
                    billsTreeSpreadObj.loadExprToInput(sheet);
                });
            }
        },
        pasteBlock: function (spread, copyInfo) {
            const self = this;
            const sheet = spread.getActiveSheet();
            const [tree, node] = this.getDefaultSelectInfo(spread.getActiveSheet());
            postData(window.location.pathname + '/update', {
                postType: 'paste-block',
                postData: {
                    id: tree.getNodeKey(node),
                    tid: copyInfo.tid,
                    block: copyInfo.block,
                }
            }, function (data) {
                pos.updateDatas(data.pos);
                const result = tree.loadPostData(data.ledger);
                self.refreshTree(sheet, result);
                const sel = sheet.getSelections()[0];
                if (sel) {
                    sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
                    SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
                }
                self.refreshOperationValid(sheet);
                removeLocalCache(copyBlockTag);
            }, null, true);
        },
        topRowChanged: function (e, info) {
            SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
        },
        editStarting(e, info) {
            if (!info.sheet.zh_setting || !info.sheet.zh_tree) return;
            const col = info.sheet.zh_setting.cols[info.col];
            const node = info.sheet.zh_tree.nodes[info.row];
            if (!node) {
                info.cancel = true;
                return;
            }
            const exprInfo = getExprInfo(col.field);
            if (exprInfo) {
                if (node[exprInfo.expr] && node[exprInfo.expr] !== '') {
                    info.sheet.getCell(info.row, info.col).text(node[exprInfo.expr]);
                }
            }
            switch (col.field) {
                case 'code':
                    const posRange = pos.getLedgerPos(node.id);
                    info.cancel = posRange && posRange.length > 0;
                    break;
                case 'unit_price':
                    info.cancel = (node.children && node.children.length > 0) || node.used;
                    break;
                case 'sgfh_qty':
                case 'sgfh_tp':
                case 'sjcl_qty':
                case 'sjcl_tp':
                case 'qtcl_qty':
                case 'qtcl_tp':
                case 'deal_qty':
                case 'deal_tp':
                    info.cancel = (node.children && node.children.length > 0);
                    break;
                case 'dgn_qty1':
                case 'dgn_qty2':
                    info.cancel = !_.isEmpty(node.b_code);
                    break;
            }
        },
        cut: function (sheet, sel, callback) {
            if (!sheet || !sel) return;
            if (sel.colCount >= sheet.zh_setting.cols.length) {
                toastr.warning('请勿选中整行剪切');
                return;
            }
            const sortData = SpreadJsObj.getSortData(sheet), datas = [];
            for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
                const node = sortData[iRow];
                if (node) {
                    const data = sheet.zh_tree.getNodeKeyData(node);
                    for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                        const col = sheet.zh_setting.cols[iCol];
                        if (col.field === 'b_code' || col.field === 'sgfh_qty' || col.field === 'sgfh_tp' ||
                            col.field === 'sjcl_qty' || col.field === 'sjcl_tp' ||
                            col.field === 'qtcl_qty' || col.field === 'qtcl_tp') {
                            const lPos = pos.getLedgerPos(node.id);
                            if (lPos && lPos.length > 0) {
                                toastr.error('不可剪切');
                                return;
                            }
                        }
                        const style = sheet.getStyle(iRow, iCol);
                        if (style.locked) {
                            toastr.error('不可剪切');
                            return;
                        }
                        const colSetting = sheet.zh_setting.cols[iCol];
                        data[colSetting.field] = null;
                        const exprInfo = getExprInfo(colSetting.field);
                        if (exprInfo) {
                            data[exprInfo.expr] = '';
                        }
                    }
                    datas.push(data);
                }
            }
            if (datas.length > 0) {
                callback();
                postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                    const refreshNode = sheet.zh_tree.loadPostData(result);
                    billsTreeSpreadObj.refreshTree(sheet, refreshNode);
                });
            }
        },
        sortCode: function (sheet) {
            const tree = sheet.zh_tree;
            if (!tree) return;
            const select = SpreadJsObj.getSelectObject(sheet);
            if (!select || !select.code || select.code === '') return;
            const recursiveSortCode = function (data, parentCode, children) {
                if (!children || children.length === 0) return;
                for (const [i, child] of children.entries()) {
                    if (!child.b_code || child.b_code === '') {
                        const code = parentCode + '-' + (i + 1);
                        const cData = tree.getNodeKeyData(child);
                        cData.code = code;
                        data.push(cData);
                        if (!tree.isLeafXmj(child)) {
                            recursiveSortCode(data, code, child.children);
                        }
                    }
                }
            };
            const data = [];
            recursiveSortCode(data, select.code, select.children);
            if (data.length > 0) {
                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                    const refreshNode = tree.loadPostData(result);
                    billsTreeSpreadObj.refreshTree(sheet, refreshNode);
                })
            }
        }
    };
    billsTreeSpreadObj.refreshOperationValid(billsSheet);
    billsTreeSpreadObj.loadExprToInput(billsSheet);
    billsSpread.bind(spreadNS.Events.SelectionChanged, billsTreeSpreadObj.selectionChanged);
    billsSpread.bind(spreadNS.Events.topRowChanged, billsTreeSpreadObj.topRowChanged);
    if (!readOnly) {
        // 增删上下移升降级
        $('a[name="base-opr"]').click(function () {
            billsTreeSpreadObj.baseOpr(billsSheet, this.getAttribute('type'));
        });
        $('a[name=cpc]').click(function () {
            billsSpread.commandManager().execute({
                cmd: this.getAttribute('type'),
                sheetName: billsSheet.name()
            });
        });
        $('#bills-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 = billsTree.getItemsByIndex(row);
            if (!select) return;
            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = trimInvalidChar(expr.val());
            if (orgValue === newValue || (!orgValue && newValue == '')) { return; }
            const data = {
                id: select.id,
                tender_id: select.tender_id,
                ledger_id: select.ledger_id
            };
            const exprInfo = getExprInfo(field);
            if (newValue !== '') {
                const num = _.toNumber(newValue);
                if (num) {
                    data[field] = num;
                    if (exprInfo) {
                        data[exprInfo.expr] = '';
                    }
                } else {
                    try {
                        data[field] = math.evaluate(transExpr(newValue));
                        if (exprInfo) {
                            data[exprInfo.expr] = newValue;
                        }
                    } catch (err) {
                        toastr.error('输入的表达式非法');
                        return;
                    }
                }
            } else {
                data[field] = null;
                if (exprInfo) {
                    data[exprInfo.expr] = '';
                }
            }
            // 更新至服务器
            postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                const refreshNode = billsTree.loadPostData(result);
                billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
            });
        });
        billsSpread.bind(spreadNS.Events.EditStarting, billsTreeSpreadObj.editStarting);
        billsSpread.bind(spreadNS.Events.EditEnded, billsTreeSpreadObj.editEnded);
        billsSpread.bind(spreadNS.Events.ClipboardPasting, billsTreeSpreadObj.clipboardPasting);
        billsSpread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
            const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
            SpreadJsObj.Clipboard.setCopyData(copyText);
        });
        SpreadJsObj.addDeleteBind(billsSpread, billsTreeSpreadObj.deletePress);
        SpreadJsObj.addCutEvents(billsSpread, billsTreeSpreadObj.cut);
        let batchInsertObj;
        $.contextMenu.types.batchInsert = function (item, opt, root) {
            const self = this;
            if ($.isFunction(item.icon)) {
                item._icon = item.icon.call(this, this, $t, key, item);
            } else {
                if (typeof(item.icon) === 'string' && item.icon.substring(0, 3) === 'fa-') {
                    // to enable font awesome
                    item._icon = root.classNames.icon + ' ' + root.classNames.icon + '--fa fa ' + item.icon;
                } else {
                    item._icon = root.classNames.icon + ' ' + root.classNames.icon + '-' + item.icon;
                }
            }
            this.addClass(item._icon);
            const $obj = $('
' + item.name + '行
')
                .appendTo(this);
            const $input = $obj.find('input');
            const event = () => {
                if (self.hasClass('context-menu-disabled')) return;
                item.batchInsert($input[0], root);
            };
            $obj.on('click', event).keypress(function (e) {if (e.keyCode === 13) { event(); }});
            $input.click((e) => {e.stopPropagation();})
                .keyup((e) => {if (e.keyCode === 13) item.batchInsert($input[0], root);})
                .on('input', function () {this.value = this.value.replace(/[^\d]/g, '');});
        };
        // 右键菜单
        $.contextMenu({
            selector: '#bills-spread',
            build: function ($trigger, e) {
                const target = SpreadJsObj.safeRightClickSelection($trigger, e, billsSpread);
                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
            },
            items: {
                'create': {
                    name: '新增',
                    icon: 'fa-sign-in',
                    callback: function (key, opt) {
                        billsTreeSpreadObj.baseOpr(billsSheet, 'add');
                    },
                    disabled: function (key, opt) {
                        const sheet = billsSheet;
                        const selection = sheet.getSelections();
                        const sel = selection ? selection[0] : sheet.getSelections()[0];
                        const row = sel ? sel.row : -1;
                        const tree = sheet.zh_tree;
                        if (!tree) return true;
                        const first = sheet.zh_tree.nodes[row];
                        const valid = !sheet.zh_setting.readOnly;
                        return !(valid && first && first.level > 1);
                    }
                },
                'delete': {
                    name: '删除',
                    icon: 'fa-remove',
                    callback: function (key, opt) {
                        billsTreeSpreadObj.baseOpr(billsSheet, 'delete');
                    },
                    disabled: function (key, opt) {
                        const sheet = billsSheet;
                        const selection = sheet.getSelections();
                        const sel = selection ? selection[0] : sheet.getSelections()[0];
                        const row = sel ? sel.row : -1;
                        const tree = sheet.zh_tree;
                        if (!tree) return true;
                        const first = sheet.zh_tree.nodes[row];
                        let last = first, sameParent = true, nodeUsed = first.used;
                        if (sel.rowCount > 1 && first) {
                            for (let r = 1; r < sel.rowCount; r++) {
                                const rNode = tree.nodes[sel.row + r];
                                if (!rNode) {
                                    sameParent = false;
                                    break;
                                }
                                nodeUsed = nodeUsed || rNode.used;
                                if (rNode.level > first.level) continue;
                                if ((rNode.level < first.level) || (rNode.level === first.level && rNode.pid !== first.pid)) {
                                    sameParent = false;
                                    break;
                                }
                                last = rNode;
                            }
                        }
                        const valid = !sheet.zh_setting.readOnly;
                        return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed);
                    }
                },
                'sprBase': '-----------',
                'batchInsert': {
                    name: '批量插入',
                    type: 'batchInsert',
                    value: '2',
                    icon: 'fa-sign-in',
                    batchInsert: function (obj, root) {
                        if (_.toNumber(obj.value) > _.toNumber(obj.max)) {
                            obj.value = obj.max;
                            toastr.warning('批量插入不可多于' + obj.max);
                        } else if (_.toNumber(obj.value) < _.toNumber(obj.min)) {
                            obj.value = obj.min;
                            toastr.warning('批量插入不可少于' + obj.min);
                        } else {
                            billsTreeSpreadObj.baseOpr(billsSheet, 'add', parseInt(obj.value));
                            root.$menu.trigger('contextmenu:hide');
                        }
                    },
                    disabled: function (key, opt) {
                        const sheet = billsSheet;
                        const selection = sheet.getSelections();
                        const sel = selection ? selection[0] : sheet.getSelections()[0];
                        const row = sel ? sel.row : -1;
                        const tree = sheet.zh_tree;
                        if (!tree) return true;
                        const first = sheet.zh_tree.nodes[row];
                        const valid = !sheet.zh_setting.readOnly;
                        return !(valid && first && first.level > 1);
                    }
                },
                'batchInsertBillsPos': {
                    name: '批量插入清单-计量单元',
                    icon: 'fa-sign-in',
                    disabled: function (key, opt) {
                        if (!isTz) return true;
                        const select = SpreadJsObj.getSelectObject(billsSheet);
                        if (select) {
                            if (select.code && select.code !== '') {
                                return !billsTree.isLeafXmj(select);
                            } else {
                                const parent = billsTree.getParent(select);
                                return !(parent && billsTree.isLeafXmj(parent));
                            }
                        } else {
                            return false;
                        }
                    },
                    callback: function (key, opt) {
                        if (!batchInsertObj) {
                            batchInsertObj = new BatchInsertBillsPosObj($('#batch'));
                        } else {
                            batchInsertObj.initView();
                        }
                        $('#batch').modal('show');
                    }
                },
                'sprBatch': '-----------',
                'importGclBills2Xmj': {
                    name: '导入工程量清单至项目节',
                    icon: 'fa-file-excel-o',
                    disabled: function (key, opt) {
                        const node = SpreadJsObj.getSelectObject(billsSheet);
                        return readOnly
                            || (node.children && node.children.length > 0)
                            || (!_.isNil(node.b_code) && node.b_code !== '')
                            || billsTree.checkNodeUsed(node, pos);
                    },
                    callback: function (key, opt) {
                        const node = SpreadJsObj.getSelectObject(billsSheet);
                        importExcel.doImport({
                            template: {
                                hint: '工程量清单',
                                url: '/template/导入工程量清单EXCEL格式.xls',
                            },
                            callback: function (sheet) {
                                postDataCompress(window.location.pathname + '/upload-excel/gcl2xmj', {id: node.id, sheet: sheet}, function (result) {
                                    const sel = billsSheet.getSelections();
                                    const refreshNode = billsTree.loadPostData(result);
                                    billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
                                    if (refreshNode.create[0]) {
                                        if (sel && sel[0]) {
                                            billsSheet.setSelection(refreshNode.create[0].index, sel[0].col, sel[0].rowCount, sel[0].colCount);
                                            SpreadJsObj.reloadRowsBackColor(billsSheet, [sel[0].row, refreshNode.create[0].index]);
                                        } else {
                                            billsSheet.setSelection(refreshNode.create[0].index, 0, 1, 1);
                                            SpreadJsObj.reloadRowsBackColor(billsSheet, [refreshNode.create[0].index]);
                                        }
                                    }
                                    billsTreeSpreadObj.refreshOperationValid(billsSheet);
                                }, null);
                            }
                        });
                    }
                },
            }
        });
    }
    // 计量单元 相关方法&绑定spreadjs事件
    const posSpreadObj = {
        refreshOperationValid: function (sheet, selection) {
            const setObjEnable = function (obj, enable) {
                if (enable) {
                    obj.removeClass('disabled');
                } else {
                    obj.addClass('disabled');
                }
            };
            const invalidAll = function () {
                setObjEnable($('a[name=pos-opr][type=up-move]'), false);
                setObjEnable($('a[name=pos-opr][type=down-move]'), false);
            };
            const sel = selection ? selection[0] : sheet.getSelections()[0];
            const row = sel ? sel.row : -1;
            const first = sheet.zh_data[row];
            if (!first) {
                invalidAll();
                return;
            }
            let last = first;
            if (sel.rowCount > 1 && first) {
                for (let r = 1; r < sel.rowCount; r++) {
                    const rNode = sheet.zh_data[sel.row + r];
                    if (!rNode) break;
                    last = rNode;
                }
            }
            const preNode = sheet.zh_data[row - 1];
            const valid = !sheet.zh_setting.readOnly;
            setObjEnable($('a[name=pos-opr][type=up-move]'), valid && first && preNode);
            setObjEnable($('a[name=pos-opr][type=down-move]'), valid && first && (sheet.zh_data.indexOf(last) < sheet.zh_data.length - 1));
        },
        loadExprToInput: function () {
            const sel = posSheet.getSelections()[0];
            if (!sel) return;
            const col = posSheet.zh_setting.cols[sel.col];
            const cell = posSheet.getCell(sel.col, sel.col);
            if (col && col.type === 'Number') {
                const data = SpreadJsObj.getSelectObject(posSheet);
                if (data) {
                    const exprInfo = getExprInfo(col.field);
                    const value = exprInfo
                        ? (data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
                        : data[col.field];
                    $('#pos-expr').val(value).attr('field', col.field).attr('org', data[col.field])
                        .attr('readOnly', readOnly || cell.locked()).attr('data-row', sel.row);
                } else {
                    $('#pos-expr').val('').attr('readOnly', true);
                    $('#pos-expr').removeAttr('data-row');
                }
            } else {
                $('#pos-expr').val('').attr('readOnly', true);
                $('#pos-expr').removeAttr('data-row');
            }            
        },
        /**
         * 加载计量单元 根据当前台账选择节点
         */
        loadCurPosData: function () {
            const node = SpreadJsObj.getSelectObject(billsSheet);
            if (node) {
                const posData = pos.getLedgerPos(node.id) || [];
                SpreadJsObj.loadSheetData(posSheet, 'data', posData);
            } else {
                SpreadJsObj.loadSheetData(posSheet, 'data', []);
            }
            SpreadJsObj.resetFieldReadOnly(posSheet);
            posSpreadObj.loadExprToInput();
            posSpreadObj.refreshOperationValid(posSheet);
        },
        baseOpr: function (sheet, type) {
            const data = {
                postType: 'pos',
                posPostType: type === 'delete' ? type : 'update',
                postData: [],
            };
            const selection = sheet.getSelections();
            const row = selection[0].row, count = selection[0].rowCount;
            const first = sheet.zh_data[row];
            if (type === 'delete') {
                for (let iRow = 0; iRow < count; iRow++) {
                    const posData = sheet.zh_data[iRow + row];
                    if (posData) {
                        if (posData.used) {
                            toastr.error('"' + posData.name + '"已计量,请勿删除');
                            return;
                        }
                        data.postData.push(sheet.zh_data[iRow + row].id);
                    }
                }
            } else if (type === 'up-move') {
                const pre = sheet.zh_data[row - 1], preUpdate = {id: pre.id};
                for (let iRow = 0; iRow < count; iRow++) {
                    const posData = sheet.zh_data[iRow + row];
                    if (posData) {
                        data.postData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row - 1].porder});
                        preUpdate.porder = posData.porder;
                    }
                }
                data.postData.push(preUpdate);
            } else if (type === 'down-move') {
                const next = sheet.zh_data[row + count], nextUpdate = {id: next.id};
                for (let iRow = count - 1; iRow >= 0; iRow--) {
                    const posData = sheet.zh_data[iRow + row];
                    if (posData) {
                        data.postData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row + 1].porder});
                        nextUpdate.porder = posData.porder;
                    }
                }
                data.postData.push(nextUpdate);
            }
            if (data.postData.length > 0) {
                postData(window.location.pathname + '/update', data, function (result) {
                    if (type === 'delete') {
                        pos.removeDatas(result.pos);
                        sheet.deleteRows(row, count);
                        const loadResult = billsTree.loadPostData(result.ledger);
                        billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                        billsTreeSpreadObj.refreshOperationValid(billsSheet);
                        posSpreadObj.refreshOperationValid(posSheet);
                    } else {
                        pos.updateDatas(result.pos);
                        const sel = selection[0];
                        if (sel) {
                            sheet.setSelection(sheet.zh_data.indexOf(first), sel.col, sel.rowCount, sel.colCount);
                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, sheet.zh_data.indexOf(first)]);
                        }
                        posSpreadObj.loadCurPosData();
                    }
                });
            }
        },
        editStarting: function (e, info) {
            posSpreadObj.billsNode = SpreadJsObj.getSelectObject(billsSheet);
            const sel = info.sheet.getSelections();
            if (!sel || !sel[0]) return;
            const col = info.sheet.zh_setting.cols[sel[0].col];
            const node = SpreadJsObj.getSelectObject(info.sheet);
            const exprInfo = getExprInfo(col.field);
            if (exprInfo) {
                if (node[exprInfo.expr] && node[exprInfo.expr] !== '') {
                    info.sheet.getCell(info.row, info.col).text(node[exprInfo.expr]);
                }
            }
        },
        /**
         * 编辑单元格响应事件
         * @param {Object} e
         * @param {Object} info
         */
        editEnded: function (e, info) {
            if (!info.sheet.zh_setting) {
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                return;
            }
            const posData = info.sheet.zh_data ? info.sheet.zh_data[info.row] : null;
            const col = info.sheet.zh_setting.cols[info.col];
            const orgText = posData ? posData[col.field] : null;
            const newText = trimInvalidChar(info.editingText);
            if (orgText === newText || ((!orgText || orgText === '') && (newText === ''))) return;
            const node = posSpreadObj.billsNode;
            if (!node) {
                toastr.error('数据错误,请选择台账节点后再试');
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                return;
            } else if (newText && newText !== '' && node.children && node.children.length > 0) {
                toastr.error('父节点不可插入计量单元');
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                return;
            } else if (newText && newText !== '' && (!node.b_code || node.b_code === '')) {
                toastr.error('项目节不可插入计量单元');
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                return;
            }
            const data = {postType: 'pos'};
            if (col.field === 'name') {
                if (newText === '' && posData) {
                    toastr.error('部位名称不可为空', 'error');
                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
                    return;
                } else if (!posData) {
                    if (newText && newText !== '') {
                        data.posPostType = 'add';
                        const sortData = info.sheet.zh_data;
                        const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].porder + 1, sortData.length + 1);
                        data.postData = { name: newText, lid: node.id, porder: order};
                    } else {
                        return;
                    }
                } else {
                    data.posPostType = 'update';
                    data.postData = {id: posData.id, name: newText};
                }
            } else if (!posData) {
                toastr.warning('新增计量单元请先输入名称');
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                return;
            } else {
                data.posPostType = 'update';
                data.postData = {id: posData.id};
                if (col.type === 'Number') {
                    const exprInfo = getExprInfo(col.field);
                    const num = _.toNumber(newText);
                    if (_.isFinite(num)) {
                        data.postData[col.field] = num;
                        if (exprInfo) {
                            data.postData[exprInfo.expr] = '';
                        }
                    } else {
                        try {
                            data.postData[col.field] = math.evaluate(transExpr(newText));
                            if (exprInfo) {
                                data.postData[exprInfo.expr] = newText;
                            }
                        } catch(err) {
                            toastr.error('输入的表达式非法');
                            SpreadJsObj.reLoadRowData(info.sheet, info.row);
                            return;
                        }
                    }
                } else {
                    data.postData[col.field] = newText;
                }
            }
            postData(window.location.pathname + '/update', data, function (result) {
                const updateRst = pos.updateDatas(result.pos);
                // 刷新当前行, 不适用于新增(在非下一空白行新增)
                if (updateRst.create.length > 0) {
                    posSpreadObj.loadCurPosData();
                } else {
                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
                }
                const loadResult = billsTree.loadPostData(result.ledger);
                billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                billsTreeSpreadObj.refreshOperationValid(billsSheet);
            }, function () {
                SpreadJsObj.reLoadRowData(info.sheet, info.row);
            });
        },
        /**
         * 删除按钮响应事件
         * @param sheet
         */
        deletePress: function (sheet) {
            if (!sheet.zh_setting) return;
            const sortData = sheet.zh_data;
            const datas = [], posSelects = [];
            const sel = sheet.getSelections()[0];
            for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
                let bDel = false;
                const node = sortData[iRow];
                if (node) {
                    const data = {id: node.id};
                    for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                        const colSetting = sheet.zh_setting.cols[iCol];
                        if (colSetting.field === 'name') {
                            toastr.error('部位名称不能为空');
                            return;
                        }
                        const style = sheet.getStyle(iRow, iCol);
                        if (!style.locked) {
                            const colSetting = sheet.zh_setting.cols[iCol];
                            data[colSetting.field] = null;
                            const exprInfo = getExprInfo(colSetting.field);
                            if (exprInfo) {
                                data[exprInfo.expr] = '';
                            }
                            bDel = true;
                        }
                    }
                    if (bDel) {
                        datas.push(data);
                        posSelects.push(node);
                    }
                }
            }
            if (datas.length > 0) {
                postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'update', postData: datas}, function (result) {
                    pos.updateDatas(result.pos);
                    posSpreadObj.loadCurPosData();
                    const loadResult = billsTree.loadPostData(result.ledger);
                    billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                    billsTreeSpreadObj.refreshOperationValid(billsSheet);
                }, function () {
                    posSpreadObj.loadCurPosData();
                });
            }
        },
        /**
         * 删除 计量单元
         * @param sheet
         */
        deletePos: function (sheet) {
            const selection = sheet.getSelections();
            const data = {
                postType: 'pos',
                posPostType: 'delete',
                postData: [],
            };
            const row = selection[0].row, count = selection[0].rowCount;
            const sortData = sheet.zh_data;
            for (let iRow = 0; iRow < count; iRow++) {
                const posData = sortData[iRow + row];
                if (posData) {
                    if (posData.used) {
                        toastr.error('"' + posData.name + '"已计量,请勿删除');
                        return;
                    }
                    data.postData.push(sortData[iRow + row].id);
                }
            }
            if (data.postData.length > 0) {
                postData(window.location.pathname + '/update', data, function (result) {
                    pos.removeDatas(result.pos);
                    sheet.deleteRows(row, count);
                    const loadResult = billsTree.loadPostData(result.ledger);
                    billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                    billsTreeSpreadObj.refreshOperationValid(billsSheet);
                });
            }
        },
        clipboardPasting: function (e, info) {
            if (!info.sheet.zh_setting) {
                info.cancel = true;
                return;
            }
            const range = info.cellRange;
            for (let iRow = range.row; iRow < range.row + range.rowCount; iRow++) {
                const posData = info.sheet.zh_data[iRow];
                if (posData && posData.used) {
                    toastr.warning('"' + pos.name +'"已计量,请勿修改');
                    info.cancel = true;
                    return;
                }
            }
        },
        /**
         * 粘贴单元格响应事件
         * @param e
         * @param info
         */
        clipboardPasted: function (e, info) {
            if (info.sheet.getColumnCount() > info.sheet.zh_setting.cols.length) {
                info.sheet.setColumnCount(info.sheet.zh_setting.cols.length);
            }
            const node = SpreadJsObj.getSelectObject(billsSheet);
            if (node.code && (node.code !== '')) {
                toastr.error('项目节不可含有节点明细');
                posSpreadObj.loadCurPosData();
                return;
            }
            if (node.children && (node.children.length > 0)) {
                toastr.error('仅节点子项可以含有计量单元');
                posSpreadObj.loadCurPosData();
                return;
            }
            if (!info.sheet.zh_setting) {
                posSpreadObj.loadCurPosData();
                return;
            }
            const data = [];
            const sortData = info.sheet.zh_data || [];
            if (sortData.length === 0 || info.cellRange.row + info.cellRange.rowCount > sortData.length) {
                if (info.cellRange.col !== 0) {
                    toastr.warning('新增计量单元请先输入名称');
                    posSpreadObj.loadCurPosData();
                    return;
                }
            }
            let bHint = false;
            const lastOrder = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                let bPaste = true;
                const curRow = info.cellRange.row + iRow;
                const posData = curRow >= sortData.length ? {lid: node.id, porder: lastOrder + curRow - sortData.length} : {id: sortData[curRow].id, lid: node.id};
                for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                    const curCol = info.cellRange.col + iCol;
                    const colSetting = info.sheet.zh_setting.cols[curCol];
                    if (!colSetting) continue;
                    
                    posData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
                    if (colSetting.type === 'Number') {
                        const num = _.toNumber(posData[colSetting.field]);
                        if (num) {
                            posData[colSetting.field] = num;
                        } else {
                            try {
                                posData[colSetting.field] = math.evaluate(transExpr(posData[colSetting.field]));
                                const exprInfo = getExprInfo(colSetting.field);
                                if (exprInfo) {
                                    posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                }
                            } catch (err) {
                                if (!bHint) {
                                    toastr.warning('粘贴了非法表达式,已过滤');
                                    bHint = true;
                                }
                                bPaste = false;
                            }
                        }
                    }
                }
                if (bPaste) {
                    data.push(posData);
                }
            }
            if (data.length === 0) {
                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
                return;
            }
            postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'paste', postData: data}, function (result) {
                pos.updateDatas(result.pos);
                posSpreadObj.loadCurPosData();
                const loadResult = billsTree.loadPostData(result.ledger);
                billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                posSpreadObj.loadCurPosData();
                billsTreeSpreadObj.refreshOperationValid(billsSheet);
            }, function () {
                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
            });
        },
        selectionChanged: function (e, info) {
            posSpreadObj.loadExprToInput();
            posSpreadObj.refreshOperationValid(posSheet);
        },
        addPegs: function (pegs) {
            if (!pegs || pegs.length <= 0) return;
            const node = SpreadJsObj.getSelectObject(billsSheet);
            if (!node) return;
            const sheet = posSpread.getActiveSheet();
            const sortData = sheet.zh_data || [];
            let order = sortData.length > 0 ? sortData[sortData.length - 1].porder + 1 : 1;
            pegs.forEach(function (p) {p.porder = ++order; p.lid = node.id});
            postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'paste', postData: pegs}, function (result) {
                pos.updateDatas(result.pos);
                posSpreadObj.loadCurPosData();
                billsTreeSpreadObj.refreshOperationValid(billsSheet);
            });
        }
    };
    posSpread.bind(spreadNS.Events.SelectionChanged, posSpreadObj.selectionChanged);
    if (!readOnly && isTz) {
        $('a[name="pos-opr"]').click(function () {
            posSpreadObj.baseOpr(posSheet, this.getAttribute('type'));
        });
        $('#pos-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 = posSheet.zh_data ? posSheet.zh_data[row] : null;
            if (!select) return;
            const field = expr.attr('field'), orgValue = expr.attr('org'), newValue = trimInvalidChar(expr.val());
            if (orgValue === newValue || (!orgValue && newValue == '')) return;
            const data = {id: select.id};
            const exprInfo = getExprInfo(field);
            if (newValue !== '') {
                const num = _.toNumber(newValue);
                if (num) {
                    data[field] = num;
                    if (exprInfo) data[exprInfo.expr] = '';
                } else {
                    try {
                        data[field] = math.evaluate(transExpr(newValue));
                        if (exprInfo) data[exprInfo.expr] = newValue;
                    } catch (err) {
                        toastr.error('输入的表达式非法');
                        return;
                    }
                }
            } else {
                data[field] = null;
                if (exprInfo) data[exprInfo.expr] = '';
            }
            // 更新至服务器
            postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'update', postData: data}, function (result) {
                const updateRst = pos.updateDatas(result.pos);
                // 刷新当前行, 不适用于新增(在非下一空白行新增)
                SpreadJsObj.reLoadRowData(posSheet, row);
                const loadResult = billsTree.loadPostData(result.ledger);
                billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
                billsTreeSpreadObj.refreshOperationValid(billsSheet);
            });
        });
        posSpread.bind(spreadNS.Events.EditStarting, posSpreadObj.editStarting);
        posSpread.bind(spreadNS.Events.EditEnded, posSpreadObj.editEnded);
        posSpread.bind(spreadNS.Events.ClipboardPasting, posSpreadObj.clipboardPasting);
        posSpread.bind(spreadNS.Events.ClipboardPasted, posSpreadObj.clipboardPasted);
        SpreadJsObj.addDeleteBind(posSpread, posSpreadObj.deletePress);
        const mergePeg = NewMergePeg({ callback: posSpreadObj.addPegs });
        $.contextMenu({
            selector: '#pos-spread',
            build: function ($trigger, e) {
                const target = SpreadJsObj.safeRightClickSelection($trigger, e, posSpread);
                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
            },
            items: {
                'delete': {
                    name: '删除',
                    icon: 'fa-remove',
                    disabled: function (key, opt) {
                        if (posSheet.zh_data) {
                            const selection = posSheet.getSelections();
                            return posSheet.zh_data.length < selection[0].row + selection[0].rowCount;
                        } else {
                            return true;
                        }
                    },
                    callback: function (key, opt) {
                        posSpreadObj.deletePos(posSheet);
                    }
                },
                'merge-peg': {
                    name: '合并起讫桩号',
                    disabled: function (key, opt) {
                        const node = SpreadJsObj.getSelectObject(billsSheet);
                        return _.isNil(node) || _.isNil(node.b_code) || node.b_code === '';
                    },
                    callback: function (key, opt) {
                        mergePeg.show();
                    },
                    visible: function (key, opt) {
                        return isTz;
                    }
                }
            }
        });
    }
    // 加载清单&计量单元数据
    postData(window.location.pathname + '/load', {}, function (result) {
        billsTree.loadDatas(result.bills);
        treeCalc.calculateAll(billsTree);
        SpreadJsObj.loadSheetData(billsSheet, SpreadJsObj.DataType.Tree, billsTree);
        SpreadJsObj.loadTopAndSelect(billsSheet, ckBillsSpread);        
        pos.loadDatas(result.pos);
        posSpreadObj.loadCurPosData();
        SpreadJsObj.resetTopAndSelect(posSheet);
    }, null);    
    $.divResizer({
        select: '#revise-resize',
        callback: function () {
            billsSpread.refresh();
            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
            $(".sp-wrap").height(bcontent-30);
            posSpread.refresh();
        }
    });
    class DealBills {
        constructor (selector, spreadSetting) {
            const self = this;
            this.loaded = false;
            this.obj = $(selector)[0];
            this.url = '/tender/' + window.location.pathname.split('/')[2] + '/deal';
            this.spreadSetting = spreadSetting;
            this.spread = SpreadJsObj.createNewSpread(this.obj);
            SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
            if (!readOnly) {
                this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
                    const dealSheet = info.sheet;
                    const mainSheet = billsSheet;
                    const dealBills = SpreadJsObj.getSelectObject(dealSheet);
                    if (!dealBills) { return; }
                    const mainTree = mainSheet.zh_tree;
                    const mainNode = SpreadJsObj.getSelectObject(mainSheet);
                    if (!mainNode || !mainTree) { return; }
                    if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
                        toastr.warning('非最底层项目下,不应添加节点');
                        return;
                    }
                    postData(window.location.pathname + '/update', {
                        postType: 'add-deal',
                        postData: {
                            id: mainNode.ledger_id,
                            type: mainNode.code ? 'child' : 'next',
                            dealBills: {
                                b_code: dealBills.code, name: dealBills.name, unit: dealBills.unit,
                                unit_price: dealBills.unit_price,
                            }
                        },
                    }, function (result) {
                        const refreshData = mainTree.loadPostData(result);
                        billsTreeSpreadObj.refreshTree(mainSheet, refreshData);
                        const sel = mainSheet.getSelections()[0];
                        if (sel && refreshData.create[0]) {
                            mainSheet.setSelection(mainTree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
                            SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(refreshData.create[0])]);
                        }
                        billsTreeSpreadObj.refreshOperationValid(mainSheet);
                        billsSpread.focus();
                        posSpreadObj.loadCurPosData();
                    });
                });
            }
            SpreadJsObj.forbiddenSpreadContextMenu(selector, this.spread);
        }
        loadData (callback) {
            if (this.loaded) {
                if (callback) callback();
                return;
            }
            const self = this;
            postData(this.url+'/get-data', {}, function (data) {
                self.data = data;
                SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', data);
                self.loaded = true;
                if (callback) callback();
            });
        }
        calculateData () {
            if (this.data) {
                for (const d of this.data) {
                    d.total_price = _.multiply(d.quantity, d.unit_price);
                }
            }
        }
    }
    class BgBills {
        constructor (selector, spreadSetting) {
            this.loaded = false;
            this.obj = $(selector)[0];
            this.spreadSetting = spreadSetting;
            this.spread = SpreadJsObj.createNewSpread(this.obj);
            this.sheet = this.spread.getActiveSheet();
            SpreadJsObj.initSheet(this.sheet, this.spreadSetting);
            if (!readOnly) {
                this.spread.bind(spreadNS.Events.CellDoubleClick, function (e, info) {
                    const dealSheet = info.sheet;
                    const mainSheet = billsSheet;
                    const bgBills = SpreadJsObj.getSelectObject(dealSheet);
                    if (!bgBills) { return; }
                    const mainTree = mainSheet.zh_tree;
                    const mainNode = SpreadJsObj.getSelectObject(mainSheet);
                    if (!mainNode || !mainTree) { return; }
                    if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
                        toastr.warning('非最底层项目下,不应添加变更清单');
                        return;
                    }
                    postData(window.location.pathname + '/update', {
                        postType: 'add-bg',
                        postData: {
                            id: mainNode.ledger_id,
                            type: mainNode.code ? 'child' : 'next',
                            dealBills: {
                                b_code: bgBills.code, name: bgBills.name, unit: bgBills.unit,
                                unit_price: bgBills.unit_price,
                            }
                        },
                    }, function (result) {
                        const refreshData = mainTree.loadPostData(result);
                        billsTreeSpreadObj.refreshTree(mainSheet, refreshData);
                        const sel = mainSheet.getSelections()[0];
                        if (sel && refreshData.create[0]) {
                            mainSheet.setSelection(mainTree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
                            SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(refreshData.create[0])]);
                        }
                        billsTreeSpreadObj.refreshOperationValid(mainSheet);
                        billsSpread.focus();
                        posSpreadObj.loadCurPosData();
                    });
                });
            }
            SpreadJsObj.forbiddenSpreadContextMenu(selector, this.spread);
        }
        loadData () {
            if (this.loaded) return;
            const self = this;
            postData('/tender/' + window.location.pathname.split('/')[2] +'/change/bills', {type: 'gather'}, function (data) {
                self.data = data;
                SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', data);
                self.loaded = true;
            });
        }
    }
    class BatchInsertBillsPosObj {
        constructor (obj) {
            const self = this;
            this.obj = obj;
            this.billsCount = 6;
            this.posCount = 1000;
            this.filter = getLocalCache('zh-calc-batch-filter');
            if (!this.filter) {
                this.filter = '1';
            }
            $('input[name=batch-filter]')[0].checked = this.filter && this.filter != '0';
            // 初始化 清单编号窗口 参数
            this.qdSpreadSetting = {
                cols: [
                    {title: '编号', field: 'code', hAlign: 0, width: 80, formatter: '@'},
                    {title: '名称', field: 'name', hAlign: 0, width: 120, formatter: '@'},
                    {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'},
                    {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
                    {title: '图册号', field: 'name', hAlign: 0, width: 60, formatter: '@'},
                ],
                emptyRows: this.billsCount,
                headRows: 1,
                headRowHeight: [32],
                headerFont: '12px 微软雅黑',
                font: '12px 微软雅黑',
            };
            this.qdSpread = SpreadJsObj.createNewSpread($('.batch-l-t', this.obj)[0]);
            // 初始化 部位数量复核表 参数
            this.posSpreadSetting = {
                cols: [
                    {title: '名称', field: 'bw', hAlign: 0, width: 80, formatter: '@'},
                    {title: '图册号', field: 'drawingCode', hAlign: 0, formatter: '@', width: 60},
                ],
                emptyRows: this.posCount,
                headRows: 1,
                headRowHeight: [32],
                headerFont: '12px 微软雅黑',
                font: '12px 微软雅黑',
            };
            for (let iNum = 1; iNum <= this.billsCount; iNum++) {
                this.posSpreadSetting.cols.push(
                    {title: '清单' + iNum, field: 'bills' + iNum, hAlign: 2, width: 50}
                )
            }
            this.posSpread = SpreadJsObj.createNewSpread($('.batch-l-b', this.obj)[0]);
            // 初始化 签约节点 参数
            this.dealSpreadSetting = {
                cols: [
                    {title: '清单编号', field: 'code', width: 80, hAlign: 0, formatter: '@', readOnly: true},
                    {title: '名称', field: 'name', width: 230, hAlign: 0, formatter: '@', readOnly: true},
                    {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
                    {title: '单价', field: 'unit_price', hAlign: 2, width: 60, readOnly: true},
                ],
                emptyRows: 0,
                headRows: 1,
                headRowHeight: [32],
                headerFont: '12px 微软雅黑',
                font: '12px 微软雅黑',
            };
            this.dealSpread = SpreadJsObj.createNewSpread($('.batch-r', this.obj)[0]);
            // 初始化 清单编号、部位数量复核表 表格
            this.initView();
            SpreadJsObj.initSheet(this.dealSpread.getActiveSheet(), this.dealSpreadSetting);
            SpreadJsObj.refreshColumnAlign(this.dealSpread.getActiveSheet());
            // 拉取签约节点数据
            dealBills.loadData(() => {
                SpreadJsObj.loadSheetData(this.dealSpread.getActiveSheet(), 'data', dealBills.data);
            });
            // 双击签约节点,自动添加到清单编号窗口
            this.dealSpread.bind(GC.Spread.Sheets.Events.CellDoubleClick, function (e, info) {
                const deal = info.sheet.zh_data[info.row];
                const qdSheet = self.qdSpread.getActiveSheet(), posSheet = self.posSpread.getActiveSheet();
                const sel = qdSheet.getSelections()[0];
                qdSheet.getCell(sel.row, 0).value(deal.code);
                qdSheet.getCell(sel.row, 1).value(deal.name);
                qdSheet.getCell(sel.row, 2).value(deal.unit);
                qdSheet.getCell(sel.row, 3).value(deal.unit_price);
                if (sel.row + 1 === qdSheet.getRowCount()) {
                    const count = sel.row + 2;
                    qdSheet.setRowCount(count);
                    qdSheet.getCell(sel.row + 1, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单' + count);
                    const colCount = posSheet.getColumnCount() + 1;
                    posSheet.setColumnCount(colCount);
                    posSheet.getCell(0, colCount - 1, GC.Spread.Sheets.SheetArea.colHeader).text('清单' + count);
                }
                qdSheet.setSelection(sel.row + 1, sel.col, 1, 1);
                qdSheet.getParent().focus();
            });
            this.qdSpread.bind(spreadNS.Events.ClipboardPasted, function (e, info) {
                const billsCount = info.sheet.getRowCount(), posSheet = self.posSpread.getActiveSheet();
                const count = posSheet.getColumnCount() - 2;
                if (billsCount > count) {
                    posSheet.setColumnCount(billsCount + 2);
                    for (let i = count + 1; i <= billsCount; i++) {
                        info.sheet.getCell(i - 1, 0, spreadNS.SheetArea.rowHeader).text('清单' + i);
                        posSheet.getCell(0, i + 2 - 1, spreadNS.SheetArea.colHeader).text('清单' + i);
                    }
                }
                if (info.cellRange.col === 0 && info.cellRange.colCount === 1) {
                    const dealBills = self.dealSpread.getActiveSheet().zh_data;
                    if (dealBills && dealBills.length > 0) {
                        for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
                            const curRow = iRow + info.cellRange.row;
                            const bills = _.find(dealBills, {code: info.sheet.getText(curRow, 0)});
                            if (bills) {
                                info.sheet.getCell(curRow, 1).value(bills.name);
                                info.sheet.getCell(curRow, 2).value(bills.unit);
                                info.sheet.getCell(curRow, 3).value(bills.unit_price);
                            }
                        }
                    }
                }
            });
            this.qdSpread.bind(spreadNS.Events.EditEnded, function (e, info) {
                if (info.col === 0) {
                    const dealBills = self.dealSpread.getActiveSheet().zh_data;
                    if (dealBills && dealBills.length > 0) {
                        const bills = _.find(dealBills, {code: info.editingText});
                        if (bills) {
                            info.sheet.getCell(info.row, 1).value(bills.name);
                            info.sheet.getCell(info.row, 2).value(bills.unit);
                            info.sheet.getCell(info.row, 3).value(bills.unit_price);
                        }
                    }
                }
            });
            this.obj.bind('shown.bs.modal', function () {
                self.qdSpread.refresh();
                self.posSpread.refresh();
                self.dealSpread.refresh();
            });
            $('#batch-ok').click(function () {
                const selection = billsSheet.getSelections();
                const row = selection[0].row;
                const select = billsTree.nodes[row];
                if (select) {
                    const insertData = {};
                    insertData.batchType = (select.code && select.code !== '') ? 'child' : 'next';
                    insertData.id = select[billsTree.setting.id];
                    insertData.batchData = self.getBatchData();
                    if (insertData.batchData.length > 0) {
                        postData(window.location.pathname + '/update', {
                            postType: 'batch-insert',
                            postData: insertData
                        }, function (data) {
                            pos.updateDatas(data.pos);
                            const result = billsTree.loadPostData(data.ledger);
                            billsTreeSpreadObj.refreshTree(billsSheet, result);
                            billsTreeSpreadObj.refreshOperationValid(billsSheet, selection);
                            self.obj.modal('hide');
                        }, null, true);
                    } else {
                        self.obj.modal('hide');
                    }
                }
            });
            $('input[name=batch-filter]').change(function () {
                setLocalCache('zh-calc-batch-filter', this.checked ? 1 : 0);
            });
            $.contextMenu({
                selector: '.batch-l-t',
                build: function ($trigger, e) {
                    const target = SpreadJsObj.safeRightClickSelection($trigger, e, self.qdSpread);
                    return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
                },
                items: {
                    'create': {
                        name: '新增行',
                        icon: 'fa-sign-in',
                        callback: function (key, opt) {
                            const qdSheet = self.qdSpread.getActiveSheet();
                            const posSheet = self.posSpread.getActiveSheet();
                            qdSheet.addRows(qdSheet.getRowCount(), 1);
                            const index = qdSheet.getRowCount();
                            qdSheet.getCell(qdSheet.getRowCount() - 1, 0, spreadNS.SheetArea.rowHeader).text('清单' + index);
                            posSheet.addColumns(posSheet.getColumnCount(), 1);
                            posSheet.getCell(0, index + 2 - 1, spreadNS.SheetArea.colHeader).text('清单' + index);
                        },
                    },
                    'delete': {
                        name: '删除行',
                        icon: 'fa-remove',
                        callback: function (key, opt) {
                            const qdSheet = self.qdSpread.getActiveSheet();
                            const posSheet = self.posSpread.getActiveSheet();
                            const sel = qdSheet.getSelections()[0];
                            qdSheet.deleteRows(sel.row, sel.rowCount);
                            posSheet.deleteColumns(sel.row + 2, sel.rowCount);
                            for (let iRow = 0, iLen = qdSheet.getRowCount(); iRow < iLen; iRow++) {
                                qdSheet.getCell(iRow, 0, spreadNS.SheetArea.rowHeader).text('清单' + (iRow+1));
                            }
                            for (let iCol = 0, iLen = posSheet.getColumnCount() - 2; iCol < iLen; iCol++) {
                                posSheet.getCell(0, iCol + 2, spreadNS.SheetArea.colHeader).text('清单' + (iCol+1));
                            }
                            qdSheet.setSelection(sel.row, sel.col, 1, 1);
                        },
                    },
                }
            });
        }
        // 初始化左侧表格
        initView () {
            // 初始化 清单编号
            const qdSheet = this.qdSpread.getActiveSheet();
            SpreadJsObj.initSheet(qdSheet, this.qdSpreadSetting);
            SpreadJsObj.refreshColumnAlign(qdSheet);
            // 清理原有数据
            SpreadJsObj.beginMassOperation(qdSheet);
            qdSheet.clear(0, 0, qdSheet.getRowCount(), qdSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
            for (let iRow = 1; iRow <= this.billsCount; iRow++) {
                qdSheet.getCell(iRow - 1, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单' + iRow);
            }
            qdSheet.setSelection(0, 0, 1 ,1);
            SpreadJsObj.endMassOperation(qdSheet);
            // 初始化 部位数量复核表
            const posSheet = this.posSpread.getActiveSheet();
            SpreadJsObj.initSheet(posSheet, this.posSpreadSetting);
            SpreadJsObj.refreshColumnAlign(posSheet);
            // 清理原有数据
            SpreadJsObj.beginMassOperation(posSheet);
            posSheet.setColumnWidth(0, 45, GC.Spread.Sheets.SheetArea.rowHeader);
            posSheet.clear(0, 0, posSheet.getRowCount(), posSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
            posSheet.setSelection(0, 0, 1 ,1);
            SpreadJsObj.endMassOperation(posSheet);
            // 检查签约节点数据,以工具栏数据为准
            if (dealBills) {
                SpreadJsObj.loadSheetData(this.dealSpread.getActiveSheet(), 'data', dealBills.data);
            }
            this.dealSpread.getActiveSheet().setSelection(0, 0, 1, 1);
        }
        // 获取界面数据
        getBatchData () {
            const result = [];
            const qdSheet = this.qdSpread.getActiveSheet(), posSheet = this.posSpread.getActiveSheet();
            for (let iRow = 0; iRow < qdSheet.getRowCount(); iRow++) {
                if (qdSheet.getText(iRow, 0) === '') { continue; }
                const qd = {
                    b_code: qdSheet.getText(iRow, 0),
                    name: qdSheet.getText(iRow, 1),
                    unit: qdSheet.getText(iRow, 2),
                    price: _.toNumber(qdSheet.getText(iRow, 3)),
                    pos: [],
                };
                for (let iPosRow = 0; iPosRow < posSheet.getRowCount(); iPosRow++) {
                    const value = _.toNumber(posSheet.getText(iPosRow, iRow + 2));
                    if (value !== 0 && !isNaN(value)) {
                        qd.pos.push({
                            name: posSheet.getText(iPosRow, 0),
                            drawing_code: posSheet.getText(iPosRow, 1),
                            quantity: value, porder: qd.pos.length + 1,
                        });
                    }
                }
                if (!$('input[name=batch-filter]')[0].checked || qd.pos.length > 0) result.push(qd);
            }
            return result;
        }
    }
    const dealBills = new DealBills('#deal-bills-spread', {
        cols: [
            {title: '清单编号', field: 'code', hAlign: 0, width: 85, formatter: '@', readOnly: true},
            {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
            {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
            {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},
            {title: '数量', field: 'quantity', hAlign: 2, width: 50, readOnly: true},
            {title: '金额', field: 'total_price', hAlign: 2, width: 50, readOnly: true},
        ],
        emptyRows: 0,
        headRows: 1,
        headRowHeight: [32],
        defaultRowHeight: 21,
        headerFont: '12px 微软雅黑',
        font: '12px 微软雅黑',
        selectedBackColor: '#fffacd',
    });
    const bgBills = new BgBills('#bg-bills-spread', {
        cols: [
            {title: '清单编号', field: 'code', hAlign: 0, width: 85, formatter: '@', readOnly: true},
            {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
            {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
            {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},
            {title: '数量', field: 'quantity', hAlign: 2, width: 50, readOnly: true},
            {title: '金额', field: 'total_price', hAlign: 2, width: 50, readOnly: true},
        ],
        emptyRows: 0,
        headRows: 1,
        headRowHeight: [32],
        defaultRowHeight: 21,
        headerFont: '12px 微软雅黑',
        font: '12px 微软雅黑',
        selectedBackColor: '#fffacd',
    });
    $.divResizer({
        select: '#revise-right-spr',
        callback: function () {
            billsSpread.refresh();
            if (posSpread) {
                posSpread.refresh();
            }
            if (stdXmj) {
                stdXmj.spread.refresh();
            }
            if (stdGcl) {
                stdGcl.spread.refresh();
            }
            if (dealBills) {
                dealBills.spread.refresh();
            }
            if (bgBills) {
                bgBills.spread.refresh();
            }
            if (searchLedger) {
                searchLedger.spread.refresh();
            }
        }
    });
    $.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();
            billsSpread.refresh();
            if (posSpread) {
                posSpread.refresh();
            }
            if (stdXmj) {
                stdXmj.spread.refresh();
            }
            if (stdGcl) {
                stdGcl.spread.refresh();
            }
            if (dealBills) {
                dealBills.spread.refresh();
            }
            if (bgBills) {
                bgBills.spread.refresh();
            }
            if (searchLedger) {
                searchLedger.spread.refresh();
            }
        }
    });
    $('#content-tab').addClass('active');
    $('#xd-content').addClass('active');
    showSideTools(true);
    billsSpread.refresh();
    if (posSpread) {
        posSpread.refresh();
    }
    const stdLibCellDoubleClick = function (e, info) {
        const stdSheet = info.sheet;
        const mainSheet = billsSheet;
        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
        const stdTree = stdSheet.zh_tree;
        const stdNode = stdTree.nodes[info.row];
        const mainTree = mainSheet.zh_tree;
        const sel = mainSheet.getSelections()[0];
        const mainNode = mainTree.nodes[sel.row];
        if (!stdNode) return;
        if (info.sheet.zh_setting.stdType === 'gcl') {
            if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
                toastr.warning('非最底层项目下,不应添加节点');
                return;
            }
        }
        postData(window.location.pathname + '/update', {
            postType: 'add-std',
            postData: {
                id: mainTree.getNodeKey(mainNode),
                tender_id: mainNode.tender_id,
                stdType: info.sheet.zh_setting.stdType,
                stdLibId: stdNode.list_id,
                stdNode: stdTree.getNodeKey(stdNode)
            }
        }, function (result) {
            const refreshNode = mainTree.loadPostData(result);
            billsTreeSpreadObj.refreshTree(mainSheet, refreshNode);
            if (sel) {
                if (refreshNode.create && refreshNode.create.length > 0) {
                    mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
                } else {
                    const node = _.find(mainTree.nodes, {code: stdNode.code, name: stdNode.name});
                    if (node) {
                        mainSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
                        SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(node)]);
                    }
                }
            }
            billsTreeSpreadObj.refreshOperationValid(mainSheet);
            billsSpread.focus();
            posSpreadObj.loadCurPosData();
        });
    };
    const stdXmjSetting = {
        selector: '#std-xmj',
        stdType: 'xmj',
        treeSetting: {
            id: 'chapter_id',
            pid: 'pid',
            order: 'order',
            level: 'level',
            rootId: -1,
            keys: ['id', 'list_id', 'chapter_id'],
        },
        spreadSetting: {
            cols: [
                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true, cellType: 'tree'},
                {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
                {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true}
            ],
            treeCol: 0,
            emptyRows: 0,
            headRows: 1,
            headRowHeight: [32],
            defaultRowHeight: 21,
            headerFont: '12px 微软雅黑',
            font: '12px 微软雅黑',
            headColWidth: [0],
            selectedBackColor: '#fffacd',
        },
        cellDoubleClick: stdLibCellDoubleClick,
        page: 'revise',
        tid: window.location.pathname.split('/')[2],
    };
    const stdGclSetting = {
        selector: '#std-gcl',
        stdType: 'gcl',
        treeSetting: {
            id: 'bill_id',
            pid: 'pid',
            order: 'order',
            level: 'level',
            rootId: -1,
            keys: ['id', 'list_id', 'bill_id']
        },
        spreadSetting: {
            cols: [
                {title: '清单编号', field: 'b_code', hAlign: 0, width: 120, formatter: '@', readOnly: true, cellType: 'tree'},
                {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
                {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true}
            ],
            treeCol: 0,
            emptyRows: 0,
            headRows: 1,
            headRowHeight: [32],
            defaultRowHeight: 21,
            headerFont: '12px 微软雅黑',
            font: '12px 微软雅黑',
            headColWidth: [0],
            selectedBackColor: '#fffacd',
        },
        cellDoubleClick: stdLibCellDoubleClick,
        page: 'revise',
        tid: window.location.pathname.split('/')[2],
    };
    // 展开收起标准节点
    $('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.addClass('active');
            $('.tab-content .tab-pane').removeClass('active');
            tabPanel.addClass('active');
            showSideTools(tab.hasClass('active'));
            if (tab.attr('content') === '#std-xmj') {
                if (!stdXmj) {
                    stdXmj = new stdLib(stdXmjSetting);
                }
                stdXmj.spread.refresh();
            } else if (tab.attr('content') === '#std-gcl') {
                if (!stdGcl) {
                    stdGcl = new stdLib(stdGclSetting);
                }
                stdGcl.spread.refresh();
            } else if (tab.attr('content') === '#deal-bills') {
                dealBills.loadData();
                dealBills.spread.refresh();
            } else if (tab.attr('content') === '#bg-bills') {
                bgBills.loadData();
                bgBills.spread.refresh();
            } else if (tab.attr('content') === '#search') {
                if (!searchLedger) {
                    searchLedger = $.billsSearch({
                        selector: '#search',
                        searchSpread: billsSpread,
                        resultSpreadSetting: {
                            cols: [
                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
                                {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
                                {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
                                {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},
                                {title: '数量', field: 'quantity', hAlign: 2, width: 50, readOnly: true},
                            ],
                            emptyRows: 0,
                            headRows: 1,
                            headRowHeight: [32],
                            defaultRowHeight: 21,
                            headerFont: '12px 微软雅黑',
                            font: '12px 微软雅黑',
                            selectedBackColor: '#fffacd',
                        },
                        afterLocated: function () {
                            posSpreadObj.loadCurPosData();
                        }
                    });
                }
                searchLedger.spread.refresh();
            } else if (tab.attr('content') === '#error-list') {
                errorList.spread.refresh();
            }
        }
        else {// 收起工具栏
            tab.removeClass('active');
            tabPanel.removeClass('active');
            showSideTools(tab.hasClass('active'));
        }
        billsSpread.refresh();
        if (posSpread) {
            posSpread.refresh();
        }
    });
    if (!readOnly) {
        // 修订详情 保存
        $('#save').click(function () {
            const content = $('#content').val();
            postData('save', { content: content }, function () {
                $('#content').attr('org-value', content);
            });
        });
    }
    // 显示层次
    (function (select, sheet) {
        $(select).click(function () {
            if (!sheet.zh_tree) return;
            const tag = $(this).attr('tag');
            const tree = sheet.zh_tree;
            switch (tag) {
                case "1":
                case "2":
                case "3":
                case "4":
                case "5":
                    tree.expandByLevel(parseInt(tag));
                    SpreadJsObj.refreshTreeRowVisible(sheet);
                    break;
                case "last":
                    tree.expandByCustom(() => { return true; });
                    SpreadJsObj.refreshTreeRowVisible(sheet);
                    break;
                case "leafXmj":
                    tree.expandToLeafXmj();
                    SpreadJsObj.refreshTreeRowVisible(sheet);
                    break;
            }
        });
    })('a[name=showLevel]', billsSheet);
    const dataChecker = DataChecker({
        checkUrl: window.location.pathname + '/check',
        completeData: function (data) {
            pos.updateDatas({update: data.source.pos});
            const loadResult = billsTree.loadPostData({update: data.source.bills});
            billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
            posSpreadObj.loadCurPosData();
            for (const e of data.error) {
                e.serialNo = billsTree.getNodeIndex(billsTree.getItems(e.ledger_id)) + 1;
            }
        },
        errorList: errorList,
    });
    $('[name=revise-start]').submit(function (e) {
        if (checkAuditorFrom()) {
            $(this).parent().parent().parent().modal('hide');
            dataChecker.checkAndPost(this.action, {});
            $('#hide-all').hide();
        }
        return false;
    });
});