Browse Source

1. debug下,提供导出表格数据
2. 右键菜单,批量插入多行
3. 台账修订,实现剪切板,以保证剪切按钮有效

MaiXinRong 5 years ago
parent
commit
2d94f97d84

+ 73 - 1
app/base/base_tree_service.js

@@ -160,6 +160,32 @@ class TreeService extends Service {
         return order instanceof Array ? result : (result.length > 0 ? result[0] : null);
     }
 
+    async getChildBetween(mid, pid, order1, order2) {
+        this.initSqlBuilder();
+        this.sqlBuilder.setAndWhere(this.setting.mid, {
+            value: mid,
+            operate: '=',
+        });
+        this.sqlBuilder.setAndWhere(this.setting.pid, {
+            value: pid,
+            operate: '=',
+        });
+        this.sqlBuilder.setAndWhere(this.setting.order, {
+            value: order1,
+            operate: '>',
+        });
+        this.sqlBuilder.setAndWhere(this.setting.order, {
+            value: order2,
+            operate: '<',
+        });
+        this.sqlBuilder.orderBy = [['order', 'ASC']];
+
+        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+        const data = await this.db.query(sql, sqlParam);
+
+        return data;
+    }
+
     /**
      * 根据 父节点ID 和 节点排序order 获取全部后节点数据
      * @param {Number} mid - master id
@@ -398,7 +424,7 @@ class TreeService extends Service {
         try {
             if (select) await this._updateChildrenOrder(mid, select[this.setting.pid], select[this.setting.order]+1);
             const newNode = await this._addNodeData(mid, select, data);
-            if (newNode.affectedRows !== 1) throw '新增节点数据错误';
+            if (newNode.affectedRows !== 1) throw '新增节点数据错误';
             await this.transaction.commit();
             this.transaction = null;
         } catch (err) {
@@ -417,6 +443,52 @@ class TreeService extends Service {
         }
     }
 
+    async addNodeBatch(mid, kid, data, count = 1) {
+        if (!mid) return null;
+        const select = kid ? await this.getDataByKid(mid, kid) : null;
+        if (kid && !select) throw '新增节点数据错误';
+
+        this.transaction = await this.db.beginTransaction();
+        try {
+            if (select) await this._updateChildrenOrder(mid, select[this.setting.pid], select[this.setting.order] + 1, count);
+            const newDatas = [];
+            const maxId = await this._getMaxLid(mid);
+            for (let i = 1; i < count + 1; i++) {
+                const newData = Object.assign({}, data);
+                if (this.setting.uuid) newData.id = this.uuid.v4();
+                newData[this.setting.kid] = maxId + i;
+                newData[this.setting.pid] = select ? select[this.setting.pid] : rootId;
+                newData[this.setting.mid] = mid;
+                newData[this.setting.level] = select ? select[this.setting.level] : 1;
+                newData[this.setting.order] = select ? select[this.setting.order] + i : i;
+                newData[this.setting.fullPath] = newData[this.setting.level] > 1
+                    ? select[this.setting.fullPath].replace('-' + select[this.setting.kid], '-' + newData[this.setting.kid])
+                    : newData[this.setting.kid] + '';
+                newData[this.setting.isLeaf] = true;
+                newDatas.push(newData);
+            }
+            const insertResult = await this.transaction.insert(this.tableName, newDatas);
+            this._cacheMaxLid(mid, maxId + count);
+
+            if (insertResult.affectedRows !== count) throw '新增节点数据错误';
+            await this.transaction.commit();
+            this.transaction = null;
+        } catch (err) {
+            await this.transaction.rollback();
+            this.transaction = null;
+            throw err;
+        }
+
+        if (select) {
+            const createData = await this.getChildBetween(mid, select[this.setting.pid], select[this.setting.order], select[this.setting.order] + count + 1);
+            const updateData = await this.getNextsData(mid, select[this.setting.pid], select[this.setting.order] + count);
+            return {create: createData, update: updateData};
+        } else {
+            const createData = await this.getChildBetween(mid, -1, 0, count + 1);
+            return {create: createData};
+        }
+    }
+
     /**
      * 删除相关数据 用于继承
      * @param mid

+ 4 - 1
app/controller/deal_bills_controller.js

@@ -105,6 +105,7 @@ module.exports = app => {
                     let fileName;
                     if (file === '签约清单导入格式.xls') {
                         fileName = this.app.baseDir + '/app/public/deal_bills/template.xls';
+                        ctx.body = await fs.readFileSync(fileName);
                     } else if (file === '签约清单.xlsx') {
                         const create_time = Date.parse(new Date()) / 1000;
                         fileName = this.app.baseDir + '/app/public/deal_bills/downloads/' + ctx.tender.id + '-' + create_time + '.xlsx';
@@ -128,8 +129,10 @@ module.exports = app => {
                                 'Sheet1': arraySheetData,
                             },
                         }, fileName);
+                        ctx.body = await fs.readFileSync(fileName);
+                        // 输出文件后删除
+                        fs.unlinkSync(fileName);
                     }
-                    ctx.body = await fs.readFileSync(fileName);
                 } catch (err) {
                     this.log(err);
                 }

+ 1 - 1
app/controller/ledger_controller.js

@@ -194,7 +194,7 @@ module.exports = app => {
             }
             switch (type) {
                 case 'add':
-                    return await ctx.service.ledger.addNode(ctx.tender.id, data.id);
+                    return await ctx.service.ledger.addNodeBatch(ctx.tender.id, data.id, {}, data.count);
                 case 'delete':
                     return await ctx.service.ledger.delete(ctx.tender.id, data.id, data.count);
                 case 'up-move':

+ 1 - 1
app/controller/revise_controller.js

@@ -386,7 +386,7 @@ module.exports = app => {
             }
             switch (type) {
                 case 'add':
-                    return await this.ctx.service.reviseBills.addReviseNode(revise.tid, revise.id, data.id);
+                    return await this.ctx.service.reviseBills.addReviseNode(revise.tid, revise.id, data.id, data.count);
                 case 'delete':
                     return await this.ctx.service.reviseBills.delete(revise.tid, data.id, data.count);
                 case 'up-move':

+ 89 - 20
app/public/js/ledger.js

@@ -94,7 +94,7 @@ $(document).ready(function() {
         loadExprToInput(sheet) {
             const sel = sheet.getSelections()[0];
             const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
-            if (col.type === 'Number') {
+            if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
                     $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
@@ -260,7 +260,7 @@ $(document).ready(function() {
          * 新增节点
          * @param spread
          */
-        addNode: function (sheet) {
+        addNode: function (sheet, count = 1) {
             const self = this;
             const sel = sheet.getSelections()[0];
             const row = sheet.getSelections()[0].row;
@@ -275,6 +275,7 @@ $(document).ready(function() {
                 postType: 'add',
                 postData: {
                     id: tree.getNodeKey(node),
+                    count: count,
                 }
             }, function (result) {
                 const refreshNode = tree.loadPostData(result);
@@ -559,8 +560,7 @@ $(document).ready(function() {
             }
         },
         clipboardPasting: function (e, info) {
-            const sheet = info.sheet, tree = info.sheet.zh_tree, setting = info.sheet.zh_setting;
-            console.log(info);
+            const tree = info.sheet.zh_tree, setting = info.sheet.zh_setting;
             info.cancel = true;
             if (!setting || !tree) return;
             const pasteData = info.pasteData.html
@@ -912,6 +912,27 @@ $(document).ready(function() {
         });
 
         let batchInsertObj;
+        $.contextMenu.types.batchInsert = function (item, opt, root) {
+            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 = $('<div>' + item.name + '<input class="text-right ml-1 mr-1" type="tel" max="20" min="1" value="' + item.value + '" style="width: 30px; padding-right: 4px;">行</div>')
+                .appendTo(this);
+            const $input = $obj.find('input');
+            const event = () => { 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: '#ledger-spread',
@@ -1033,6 +1054,47 @@ $(document).ready(function() {
                         }
                     }
                 },
+                '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 {
+                            treeOperationObj.addNode(ledgerSpread.getActiveSheet(), parseInt(obj.value));
+                            root.$menu.trigger('contextmenu:hide');
+                        }
+                    },
+                    disabled: function (key, opt) {
+                        const sheet = ledgerSpread.getActiveSheet();
+                        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;
+                        if (sel.rowCount > 1) {
+                            for (let r = 1; r < sel.rowCount; r++) {
+                                const rNode = tree.nodes[sel.row + r];
+                                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 && first.level > 1);
+                    }
+                },
                 'batchInsertBillsPos': {
                     name: '批量插入清单-计量单元',
                     icon: 'fa-sign-in',
@@ -1072,20 +1134,27 @@ $(document).ready(function() {
                         $('#upload-ledger').modal('show');
                     }
                 },
-                // 'exportExcel': {
-                //     name: '导出表格数据',
-                //     icon: 'fa-file-excel-o',
-                //     callback: function (key, opt) {
-                //         const excelIo = new GC.Spread.Excel.IO();
-                //         const date = new Date();
-                //         const fileName = $('.text-truncate').attr('data-original-title') + '.xlsx';
-                //         const sJson = JSON.stringify(ledgerSpread.toJSON());
-                //         excelIo.save(sJson, function(blob) {
-                //             saveAs(blob, fileName);
-                //         });
-                //     }
-                // }
-            }
+                'exportExcel': {
+                    name: '导出表格数据',
+                    icon: 'fa-file-excel-o',
+                    callback: function (key, opt) {
+                        const excelIo = new GC.Spread.Excel.IO();
+                        const date = new Date();
+                        const fileName = $('.text-truncate').attr('data-original-title') + '.xlsx';
+                        const sJson = JSON.stringify(ledgerSpread.toJSON({columnHeadersAsFrozenRows: true, rowHeadersAsFrozenColumns: true}));
+                        excelIo.save(sJson, function(blob) {
+                            saveAs(blob, fileName);
+                        });
+                    },
+                    visible: function (key, opt) {
+                        try {
+                            return is_debug;
+                        } catch (err) {
+                            return false;
+                        }
+                    }
+                }
+            },
         });
     } else {
         SpreadJsObj.forbiddenSpreadContextMenu('#ledger-spread', ledgerSpread);
@@ -1371,7 +1440,7 @@ $(document).ready(function() {
         selectionChanged: function (e, info) {
             const col = info.sheet.zh_setting.cols[info.newSelections[0].col];
             const cell = info.sheet.getCell(info.newSelections[0].row, info.newSelections[0].col);
-            if (col.type === 'Number') {
+            if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(info.sheet);
                 if (data) {
                     $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
@@ -1384,7 +1453,7 @@ $(document).ready(function() {
             }
         },
     };
-    posSpread.bind(spreadNS.Events.SelectionChanged, posOperationObj.selectionChanged)
+    posSpread.bind(spreadNS.Events.SelectionChanged, posOperationObj.selectionChanged);
     if (!posSpreadSetting.readOnly) {
         $('#pos-expr').bind('change mouseleave', function () {
             const expr = $(this);

+ 158 - 14
app/public/js/revise.js

@@ -9,6 +9,12 @@
  */
 
 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'],
+};
 function transExpr(expr) {
     return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
 }
@@ -56,7 +62,7 @@ $(document).ready(() => {
         loadExprToInput(sheet) {
             const sel = sheet.getSelections()[0];
             const col = sheet.zh_setting.cols[sel.col], cell = sheet.getCell(sel.row, sel.col);
-            if (col.type === 'Number') {
+            if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
                     $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
@@ -233,7 +239,7 @@ $(document).ready(() => {
          * 新增节点
          * @param spread
          */
-        baseOpr: function (sheet, type) {
+        baseOpr: function (sheet, type, addCount = 1) {
             const self = this;
             const [tree, node, count] = this.getDefaultSelectInfo(sheet);
             if (!tree || !node || !count) return;
@@ -281,7 +287,7 @@ $(document).ready(() => {
                 postType: type,
                 postData: {
                     id: node.ledger_id,
-                    count: count,
+                    count: type === 'add' ? addCount : count,
                 }
             }, function (result) {
                 const refreshData = tree.loadPostData(result);
@@ -389,17 +395,113 @@ $(document).ready(() => {
             }
         },
         clipboardPasting: function (e, info) {
-            if (info.sheet.zh_setting) {
-                const range = info.cellRange;
-                for (let iRow = range.row; iRow < range.row + range.rowCount; iRow++) {
-                    const node = info.sheet.zh_tree.nodes[iRow];
-                    if (info.sheet.zh_tree.checkNodeUsed(node, pos)) {
-                        toastr.warning('"' + node.code + node.b_code + ' ' + node.name +'"已计量,请勿修改');
-                        info.cancel = true;
-                        return;
+            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: '工程量清单,不可粘贴项目节数量'},
+            };
+            const datas = [], filterNodes = [];
+
+            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
+                const curRow = info.cellRange.row + iRow;
+                const node = tree.nodes[curRow];
+                if (!node) continue;
+
+                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];
+                    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;
+                        }
+                    }
+
+                    const value = trimInvalidChar(pasteData[iRow][iCol]);
+                    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));
+                            } 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 = {
@@ -616,7 +718,7 @@ $(document).ready(() => {
         $('a[name=cpc]').click(function () {
             billsSpread.commandManager().execute({
                 cmd: this.getAttribute('type'),
-                sheetName: billsSpread.getActiveSheet().name()
+                sheetName: billsSheet.name()
             });
         });
 
@@ -660,10 +762,34 @@ $(document).ready(() => {
         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.ClipboardPasted, billsTreeSpreadObj.clipboardPasted);
+        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) {
+            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 = $('<div>' + item.name + '<input class="text-right ml-1 mr-1" type="tel" max="20" min="1" value="' + item.value + '" style="width: 30px; padding-right: 4px;">行</div>')
+                .appendTo(this);
+            const $input = $obj.find('input');
+            const event = () => { 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',
@@ -725,6 +851,24 @@ $(document).ready(() => {
                         return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed);
                     }
                 },
+                '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');
+                        }
+                    },
+                },
                 'batchInsertBillsPos': {
                     name: '批量插入清单-计量单元',
                     icon: 'fa-sign-in',
@@ -1036,7 +1180,7 @@ $(document).ready(() => {
         selectionChanged: function (e, info) {
             const col = info.sheet.zh_setting.cols[info.newSelections[0].col];
             const cell = info.sheet.getCell(info.newSelections[0].col, info.newSelections[0].col);
-            if (col.type === 'Number') {
+            if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(info.sheet);
                 if (data) {
                     $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])

+ 2 - 2
app/service/revise_bills.js

@@ -43,9 +43,9 @@ module.exports = app => {
          * @param {Number} lid - 清单节点id
          * @returns {Promise<void>}
          */
-        async addReviseNode(tid, rid, lid) {
+        async addReviseNode(tid, rid, lid, count) {
             if (!rid) return null;
-            return await this.addNode(tid, lid, {crid: rid});
+            return await this.addNodeBatch(tid, lid, {crid: rid}, count);
         }
 
         /**

+ 3 - 0
app/view/layout/layout.ejs

@@ -69,6 +69,9 @@
         toastr[toastInfo.type](toastInfo.message);
     }
     let user = '<%= ctx.session.sessionUser.name %>';
+    <% if (ctx.app.config.is_debug) { %>
+    const is_debug = true;
+    <% } %>
 </script>
 </body>
 

+ 3 - 3
app/view/revise/info.ejs

@@ -29,9 +29,9 @@
                     <a href="javascript: void(0);" name="base-opr" type="down-level" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
                     <a href="javascript: void(0);" name="base-opr" type="down-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
                     <a href="javascript: void(0);" name="base-opr" type="up-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制" style="display: none"><i class="fa fa-files-o" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切" style="display: none"><i class="fa fa-scissors" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴" style="display: none"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
                 </div>
                 <div class="d-inline-block">
                     <div class="input-group input-group-sm ml-2">

+ 2 - 0
config/config.qa.js

@@ -64,5 +64,7 @@ module.exports = appInfo => {
         disableConsoleAfterReady: false,
     };
 
+    config.is_debug = true;
+
     return config;
 };