Browse Source

1. 台账修订,批量插入清单计量单元后,定位至第一条插入的清单
2. 台账分解,复制、粘贴整块重做
3. 台账修订,复制、粘贴整块

MaiXinRong 5 years ago
parent
commit
270d283606

+ 147 - 0
app/base/base_bills_service.js

@@ -17,6 +17,7 @@ const upFields = ['unit_price'];
 const qtyFields = ['sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'quantity', 'deal_qty', 'dgn_qty1', 'dgn_qty2'];
 const tpFields = ['sgfh_tp', 'sjcl_tp', 'qtcl_tp', 'total_price', 'deal_tp'];
 const measureType = require('../const/tender').measureType;
+const math = require('mathjs');
 
 class BaseBillsSerivce extends TreeService {
 
@@ -517,6 +518,152 @@ class BaseBillsSerivce extends TreeService {
         const sqlParam = [tid];
         await this.db.query(sql, sqlParam);
     }
+
+    _calcExpr(data, field, expr, defaultValue, precision) {
+        if (expr) {
+            try {
+                data[field] = this.ctx.helper.round(math(expr), precision.value);
+            } catch (err) {
+            }
+        } else {
+            data[field] = this.ctx.helper.round(defaultValue, precision.value);
+        }
+    }
+
+    async pasteBlockData (tid, sid, pasteData, defaultData) {
+        if ((tid <= 0) || (sid <= 0)) return [];
+
+        if (!pasteData || pasteData.length <= 0) throw '复制数据错误';
+        for (const pd of pasteData) {
+            if (!pd || pd.length <= 0) throw '复制数据错误';
+            pd.sort(function (x, y) {
+                return x.level - y.level
+            });
+            if (pd[0].ledger_pid !== pasteData[0][0].ledger_pid) throw '复制数据错误:仅可操作同层节点';
+        }
+
+        const selectData = await this.getDataByKid(tid, sid);
+        if (!selectData) throw '粘贴数据错误';
+        const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
+
+        const pasteBillsData = [], pastePosData = [], leafBillsId = [];
+        const tpDecimal = this.ctx.tender.info.decimal.tp;
+        let maxId = await this._getMaxLid(this.ctx.tender.id);
+        for (const [i, pd] of pasteData.entries()) {
+            for (const d of pd) {
+                d.children = pd.filter(function (x) {
+                    return x.ledger_pid === d.ledger_id;
+                });
+            }
+            const pbd = [];
+            for (const [j, d] of pd.entries()) {
+                const newBills = {
+                    id: this.uuid.v4(),
+                    tender_id: tid,
+                    ledger_id: maxId + j + 1,
+                    ledger_pid: j === 0 ? selectData.ledger_pid : d.ledger_pid,
+                    level: d.level + selectData.level - pd[0].level,
+                    order: j === 0 ? selectData.order + i + 1 : d.order,
+                    is_leaf: d.is_leaf,
+                    code: d.code,
+                    b_code: d.b_code,
+                    name: d.name,
+                    unit: d.unit,
+                    unit_price: this.ctx.helper.round(d.unit_price, this.ctx.tender.info.decimal.up),
+                    source: d.source,
+                    remark: d.remark,
+                    drawing_code: d.drawaing_code,
+                    memo: d.memo,
+                    node_type: d.node_type,
+                    sgfh_expr: d.sgfh_expr,
+                    sjcl_expr: d.sjcl_expr,
+                    qtcl_expr: d.qtcl_expr,
+                };
+                for (const c of d.children) {
+                    c.ledger_pid = newBills.ledger_id;
+                }
+                const precision = this.ctx.helper.findPrecision(this.ctx.tender.info.precision, newBills.unit);
+                if (d.pos && d.pos.length > 0) {
+                    for (const pos of d.pos) {
+                        const newPos = {
+                            id: this.uuid.v4(),
+                            tid: tid,
+                            lid: newBills.id,
+                            name: pos.name,
+                            drawing_code: pos.drawing_code,
+                            add_user: this.ctx.session.sessionUser.accountId,
+                            in_time: new Date(),
+                            porder: pos.porder,
+                            position: pos.position,
+                            sgfh_expr: pos.sgfh_expr,
+                            sjcl_expr: pos.sjcl_expr,
+                            qtcl_expr: pos.qtcl_expr,
+                            add_stage: 0,
+                            add_times: 0,
+                        };
+                        this._calcExpr(newPos, 'sgfh_qty', pos.sgfh_expr, pos.sgfh_qty, precision);
+                        this._calcExpr(newPos, 'sjcl_qty', pos.sjcl_expr, pos.sjcl_qty, precision);
+                        this._calcExpr(newPos, 'qtcl_qty', pos.qtcl_expr, pos.qtcl_qty, precision);
+                        newPos.quantity = this.ctx.helper.add(newPos.sgfh_qty,
+                            this.ctx.helper.add(newPos.sjcl_qty, newPos.qtcl_qty));
+                        newBills.sgfh_qty = this.ctx.helper.add(newBills.sgfh_qty, pos.sgfh_qty);
+                        newBills.sjcl_qty = this.ctx.helper.add(newBills.sjcl_qty, pos.sjcl_qty);
+                        newBills.qtcl_qty = this.ctx.helper.add(newBills.qtcl_qty, pos.qtcl_qty);
+                        if (defaultData) this.ctx.helper._.assignIn(newPos, defaultData);
+                        pastePosData.push(newPos);
+                    }
+                } else {
+                    this._calcExpr(newBills, 'sgfh_qty', newBills.sgfh_expr, d.sgfh_qty, precision);
+                    this._calcExpr(newBills, 'sjcl_qty', newBills.sjcl_expr, d.sjcl_qty, precision);
+                    this._calcExpr(newBills, 'qtcl_qty', newBills.qtcl_expr, d.qtcl_qty, precision);
+                }
+                newBills.quantity = this.ctx.helper.add(newBills.sgfh_qty,
+                    this.ctx.helper.add(newBills.sjcl_qty, newBills.qtcl_qty));
+                newBills.sgfh_tp = this.ctx.helper.mul(newBills.sgfh_qty, newBills.unit_price, tpDecimal);
+                newBills.sjcl_tp = this.ctx.helper.mul(newBills.qtcl_qty, newBills.unit_price, tpDecimal);
+                newBills.qtcl_tp = this.ctx.helper.mul(newBills.sjcl_qty, newBills.unit_price, tpDecimal);
+                newBills.total_price = this.ctx.helper.mul(newBills.quantity, newBills.unit_price, tpDecimal);
+                if (defaultData) this.ctx.helper._.assignIn(newBills, defaultData);
+                pbd.push(newBills);
+            }
+            for (const d of pbd) {
+                const parent = pbd.find(function (x) {
+                    return x.ledger_id === d.ledger_pid;
+                });
+                d.full_path = parent
+                    ? parent.full_path + '-' + d.ledger_id
+                    : newParentPath + '-' + d.ledger_id;
+                if (defaultData) this.ctx.helper._.assignIn(pbd, defaultData);
+                pasteBillsData.push(d);
+            }
+            maxId = maxId + pbd.length;
+        }
+
+        this.transaction = await this.db.beginTransaction();
+        try {
+            // 选中节点的所有后兄弟节点,order+粘贴节点个数
+            await this._updateChildrenOrder(tid, selectData.ledger_pid, selectData.order + 1, pasteData.length);
+            // 数据库创建新增节点数据
+            if (pasteBillsData.length > 0) {
+                const newData = await this.transaction.insert(this.tableName, pasteBillsData);
+            }
+            this._cacheMaxLid(tid, maxId);
+            if (pastePosData.length > 0) {
+                await this.transaction.insert(this.ctx.service.pos.tableName, pastePosData);
+            }
+            await this.transaction.commit();
+        } catch (err) {
+            await this.transaction.rollback();
+            throw err;
+        }
+
+        // 查询应返回的结果
+        const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + pasteData.length);
+        return {
+            ledger: { create: pasteBillsData, update: updateData },
+            pos: pastePosData,
+        };
+    }
 }
 
 module.exports = BaseBillsSerivce;

+ 0 - 3
app/base/base_tree_service.js

@@ -1033,9 +1033,6 @@ class TreeService extends Service {
     async pasteBlock(mid, kid, paste) {
         if ((mid <= 0) || (kid <= 0)) return [];
 
-        console.log(mid);
-        console.log(kid);
-        console.log(paste);
         const selectData = await this.getDataByKid(mid, kid);
         if (!selectData) throw '数据错误';
 

+ 1 - 1
app/controller/ledger_controller.js

@@ -226,7 +226,7 @@ module.exports = app => {
             if ((isNaN(data.id) || data.id <= 0) ||
                 (!data.tid && data.tid <= 0) ||
                 (!data.block || data.block.length <= 0)) throw '参数错误';
-            return await ctx.service.ledger.pasteBlock(ctx.tender.id, data.id, { tid: data.tid, block: data.block });
+            return await ctx.service.ledger.pasteBlockData(ctx.tender.id, data.id, data.block);
         }
         /**
          * 从标准项目表添加数据

+ 1 - 1
app/controller/revise_controller.js

@@ -567,7 +567,7 @@ module.exports = app => {
             if ((isNaN(data.id) || data.id <= 0) ||
                 (!data.tid && data.tid <= 0) ||
                 (!data.block || data.block.length <= 0)) throw '参数错误';
-            return await this.ctx.service.revise.pasteBlock(ctx.tender.id, data.id, {tid: data.tid, block: data.block});
+            return await this.ctx.service.reviseBills.pasteBlockData(this.ctx.tender.id, data.id, data.block, {crid: revise.id});
         }
         async _addStd(revise, data) {
             if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';

+ 10 - 2
app/public/js/ledger.js

@@ -1188,10 +1188,18 @@ $(document).ready(function() {
                     toastr.error('仅可同时选中同层节点');
                     return;
                 }
-                copyBlockList.push(node.ledger_id);
-                iRow += sheet.zh_tree.getPosterity(node).length + 1;
+                const posterity = sheet.zh_tree.getPosterity(node);
+                iRow += posterity.length + 1;
+                posterity.unshift(node);
+                copyBlockList.push(sheet.zh_tree.getDefaultData(posterity));
             }
             const tenderId = _.toInteger(getTenderId());
+            for (const cbl of copyBlockList) {
+                for (b of cbl) {
+                    const posRange = pos.getLedgerPos(b.id);
+                    if (posRange && posRange.length > 0) b.pos = posRange;
+                }
+            }
             setLocalCache(copyBlockTag, JSON.stringify({tid: tenderId, block: copyBlockList}));
             //treeOperationObj.block = copyBlockList;
         },

+ 21 - 0
app/public/js/path_tree.js

@@ -799,6 +799,27 @@ const createNewPathTree = function (type, setting) {
                 });
             }
         };
+
+        _getDefaultNodeData(node) {
+            const result = {};
+            for (const prop in node) {
+                if (['children', 'visible', 'expanded'].indexOf(prop) >= 0) continue;
+                result[prop] = node[prop];
+            }
+            return result;
+        }
+
+        getDefaultData(node) {
+            if (node instanceof Array) {
+                const arr = [];
+                for (const n of node) {
+                    arr.push(this._getDefaultNodeData(n));
+                }
+                return arr;
+            } else {
+                this._getDefaultNodeData(node);
+            }
+        }
     }
 
     class MeasureTree extends BaseTree {

+ 250 - 175
app/public/js/revise.js

@@ -27,6 +27,7 @@ function getExprInfo (field) {
 function transExpr(expr) {
     return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
 }
+const copyBlockTag = 'zh.calc.copyBlock';
 
 $(document).ready(() => {
     let stdXmj, stdGcl, searchLedger;
@@ -716,8 +717,8 @@ $(document).ready(() => {
                 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])]);
+                    sheet.setSelection(tree.nodes.indexOf(result.create[0]), sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(result.create[0])]);
                 }
                 self.refreshOperationValid(sheet);
                 removeLocalCache(copyBlockTag);
@@ -917,192 +918,263 @@ $(document).ready(() => {
         });
         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);
+    }
+    // 右键菜单
+    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 {
-                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;
-                }
+                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; height: 18px; padding-right: 4px;">行</div>')
-                .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, '');});
+        }
+        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; height: 18px; padding-right: 4px;">行</div>')
+            .appendTo(this);
+        const $input = $obj.find('input');
+
+        const event = () => {
+            if (self.hasClass('context-menu-disabled')) return;
+            item.batchInsert($input[0], root);
         };
-        // 右键菜单
-        $.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;
+        $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, '');});
+    };
+    const billsContextMenuOptions = {
+        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: {}
+    };
+    if (!readOnly) {
+        billsContextMenuOptions.items.create = {
+            name: '新增',
+            icon: 'fa-sign-in',
+            callback: function (key, opt) {
+                billsTreeSpreadObj.baseOpr(billsSheet, 'add');
             },
-            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;
-                            }
+            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);
+            }
+        };
+        billsContextMenuOptions.items.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;
                         }
-                        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');
+                        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;
                         }
-                    },
-                    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);
+                        last = rNode;
                     }
-                },
-                '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');
+                }
+                const valid = !sheet.zh_setting.readOnly;
+                return !(valid && first && sameParent && !(first.level === 1 && first.node_type) && !nodeUsed);
+            }
+        };
+        billsContextMenuOptions.items.sprBase = '-----------';
+    }
+    billsContextMenuOptions.items.copyBlock = {
+        name: '复制整块',
+        icon: 'fa-files-o',
+        callback: function (key, opt) {
+            const copyBlockList = [];
+            const sheet = billsSheet;
+            const sel = sheet.getSelections()[0];
+            let iRow = sel.row;
+            const pid = sheet.zh_tree.nodes[iRow].ledger_pid;
+            while (iRow < sel.row + sel.rowCount) {
+                const node = sheet.zh_tree.nodes[iRow];
+                if (node.ledger_pid !== pid) {
+                    toastr.error('仅可同时选中同层节点');
+                    return;
+                }
+                const posterity = sheet.zh_tree.getPosterity(node);
+                iRow += posterity.length + 1;
+                posterity.unshift(node);
+                copyBlockList.push(sheet.zh_tree.getDefaultData(posterity));
+            }
+            const tenderId = _.toInteger(getTenderId());
+            for (const cbl of copyBlockList) {
+                const posRange = pos.getLedgerPos(cbl.id);
+                if (posRange && posRange.length > 0) cbl.pos = posRange;
+            }
+            setLocalCache(copyBlockTag, JSON.stringify({tid: tenderId, block: copyBlockList}));
+        },
+        visible: function (key, opt) {
+            const sheet = billsSheet;
+            const selection = sheet.getSelections();
+            const row = selection[0].row;
+            const select = billsTree.nodes[row];
+            return select;
+        },
+        disabled: function (key, opt) {
+            const sheet = billsSheet;
+            const selection = sheet.getSelections();
+            const row = selection[0].row;
+            const select = billsTree.nodes[row];
+            return select && select.level <= 1;
+        }
+    };
+    if (!readOnly) {
+        billsContextMenuOptions.items.pasteBlock = {
+            name: '粘贴整块',
+            icon: 'fa-clipboard',
+            disabled: function (key, opt) {
+                //const block = treeOperationObj.block || [];
+                const copyInfo = JSON.parse(getLocalCache(copyBlockTag));
+                return !(copyInfo && copyInfo.tid && copyInfo.tid > 0 && copyInfo.block && copyInfo.block.length > 0);
+            },
+            callback: function (key, opt) {
+                //const block = treeOperationObj.block || [];
+                const copyInfo = JSON.parse(getLocalCache(copyBlockTag));
+                if (copyInfo.block.length > 0) {
+                    billsTreeSpreadObj.pasteBlock(billsSpread, copyInfo);
+                } else {
+                    document.execCommand('paste');
+                }
+            },
+            visible: function (key, opt) {
+                return !readOnly;
+            }
+        };
+        billsContextMenuOptions.items.sprBlock = '-----------';
+    }
+    if (!readOnly) {
+        billsContextMenuOptions.items.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);
+            }
+        };
+        billsContextMenuOptions.items.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));
                     }
-                },
-                '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);
+                } else {
+                    return false;
+                }
+            },
+            callback: function (key, opt) {
+                if (!batchInsertObj) {
+                    batchInsertObj = new BatchInsertBillsPosObj($('#batch'));
+                } else {
+                    batchInsertObj.initView();
+                }
+                $('#batch').modal('show');
+            }
+        };
+        billsContextMenuOptions.items.sprBatch = '-----------';
+        billsContextMenuOptions.items.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 (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);
+                    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);
                     }
-                },
+                });
             }
-        });
+        };
     }
+    $.contextMenu(billsContextMenuOptions);
+
     // 计量单元 相关方法&绑定spreadjs事件
     const posSpreadObj = {
         refreshOperationValid: function (sheet, selection) {
@@ -1951,6 +2023,7 @@ $(document).ready(() => {
 
             $('#batch-ok').click(function () {
                 const selection = billsSheet.getSelections();
+                const sel = selection[0];
                 const row = selection[0].row;
                 const select = billsTree.nodes[row];
                 if (select) {
@@ -1967,6 +2040,8 @@ $(document).ready(() => {
                             pos.updateDatas(data.pos);
                             const result = billsTree.loadPostData(data.ledger);
                             billsTreeSpreadObj.refreshTree(billsSheet, result);
+                            billsSheet.setSelection(result.create[0].index, sel.col, sel.rowCount, sel.colCount);
+                            SpreadJsObj.reloadRowsBackColor(billsSheet, [sel.row, result.create[0].index]);
                             billsTreeSpreadObj.refreshOperationValid(billsSheet, selection);
                             posSpreadObj.loadCurPosData();
                             self.obj.modal('hide');

+ 3 - 0
app/service/ledger.js

@@ -373,6 +373,8 @@ module.exports = app => {
                     }
                     for (const data of datas) {
                         delete data.children;
+                        delete data.crid;
+                        delete data.is_tp;
                         const p = datas.find(function (x) {
                             return x.ledger_id === data.ledger_pid;
                         });
@@ -392,6 +394,7 @@ module.exports = app => {
                     const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: paste.tid, lid: id.org } });
                     if (posData.length > 0) {
                         for (const pd of posData) {
+                            delete pd.crid;
                             pd.id = this.uuid.v4();
                             pd.lid = id.new;
                             pd.tid = this.ctx.tender.id;