Selaa lähdekoodia

造价书右键 自定义数量插入清单

vian 5 vuotta sitten
vanhempi
commit
6da681cb0a

+ 9 - 0
modules/main/controllers/bills_controller.js

@@ -266,6 +266,15 @@ module.exports = {
             }
 
         });
+    },
+    insertBills: async function (req, res) {
+        let data = JSON.parse(req.body.data);
+        try {
+            await bill_facade.insertBills(data.postData);
+            callback(req, res, 0, 'success', null);
+        } catch (err) {
+            callback(req, res, 1, err, null);
+        }
     }
 };
 

+ 13 - 0
modules/main/facade/bill_facade.js

@@ -111,6 +111,19 @@ module.exports={
         return results;
 
     },
+    insertBills: async function(datas) {
+        let bulks = [];
+        for (let data of datas) {
+            if (data.updateType === 'update') {
+                bulks.push({updateOne: {filter: {ID: data.updateData.ID}, update: {NextSiblingID: data.updateData.NextSiblingID}}});
+            } else {
+                bulks.push({insertOne: {document: data.updateData}});
+            }
+        }
+        if (bulks.length > 0) {
+            await bill_Model.bulkWrite(bulks);
+        }
+    }
 };
 
 async function pasteOtherData(data) {

+ 1 - 0
modules/main/routes/bills_route.js

@@ -21,6 +21,7 @@ module.exports = function (app) {
     billsRouter.post('/pasteBlock', billsController.pasteBlock);
     billsRouter.post('/import', billsController.import);
     billsRouter.get('/downloadExamp', billsController.downloadExample);
+    billsRouter.post('/insertBills', billsController.insertBills);
     app.use('/bills', billsRouter);
 };
 

+ 36 - 3
public/web/id_tree.js

@@ -611,7 +611,7 @@ var idTree = {
             }
             return data;
         };
-        Tree.prototype.insertByData = function (data, parentID, nextSiblingID, uid = null) {
+        Tree.prototype.insertByData = function (data, parentID, nextSiblingID, uid = null, resort = true) {
             var parent = parentID == -1 ? null : this.nodes[this.prefix + parentID];
             var nextSibling = nextSiblingID == -1 ? null : this.nodes[this.prefix + nextSiblingID];
             var node = this.nodes[this.prefix + data[this.setting.id]];
@@ -625,14 +625,47 @@ var idTree = {
                     tools.addNodes(this, parent, [node]);
                 }
                 this.nodes[this.prefix +  data[this.setting.id]] = node;
-                tools.sortTreeItems(this);
+                if (resort) {
+                    tools.sortTreeItems(this);
+                }
                 if(!uid){
                     this.maxNodeID( data[this.setting.id]);
                 }
                 return node;
             }
         };
-        //批量新增节点,节点已有树结构数据
+        //  一次性插入多个连续的同层节点
+        Tree.prototype.multiInsert = function (datas, preID) {
+            const newNodes = [];
+            for (const data of datas) {
+                this.nodes[this.prefix + data.ID] = new Node(this, data.ID);
+                this.nodes[this.prefix + data.ID]['data'] = data;
+                newNodes.push(this.nodes[this.prefix + data.ID]);
+            }
+            const fisrtNode = this.nodes[this.prefix + datas[0].ID];
+            const firstPre = this.nodes[this.prefix + preID];
+            if (firstPre) {
+                firstPre.nextSibling = fisrtNode;
+                firstPre.data.NextSiblingID = fisrtNode.getID();
+            }
+            const parent = this.nodes[this.prefix + datas[0].ParentID] || null;
+            const parentChildren = parent ? parent.children : this.roots;
+            let baseIndex = firstPre ? parentChildren.indexOf(firstPre) + 1 : 0;
+            datas.forEach(data => {
+                const node = this.nodes[this.prefix + data.ID];
+                node.parent = parent;
+                parentChildren.splice(baseIndex++, 0, node);
+                const next = this.nodes[this.prefix + data.NextSiblingID] || null;
+                node.nextSibling = next;
+                if (next) {
+                    next.preSibling = node;
+                }
+            });
+            this.roots = tools.reSortNodes(this.roots, true);
+            tools.sortTreeItems(this);
+            return newNodes;
+        }
+       // 插入某一完整片段到某节点的子项中
         Tree.prototype.insertByDatas = function (datas) {
             for(let data of datas){
                 this.nodes[this.prefix + data.ID] = new Node(this, data.ID);

+ 84 - 0
public/web/sheet/sheet_common.js

@@ -1505,5 +1505,89 @@ var sheetCommonObj = {
             sheet.getCell(row, col).wordWrap(wordWrap);
             sheet.autoFitRow(row);
         });
+    },
+    // 获取带输入框的右键子目
+    registerInputContextMenuItem(name, html, icon, callback) {
+        $.contextMenu.types[name] = function (item, opt, root) {
+            // 因为contextMenu有自己的键盘事件处理,因此输入框的键盘控制光标需要自己定义事件实现覆盖。
+            const Direction = {
+                BACKWARD: 'backward',
+                FORWARD: 'forward'
+            };
+            function moveLeft(input, isShifting) {
+                const start = input.selectionStart;
+                const end = input.selectionEnd;
+                const direction = end === start ? Direction.BACKWARD : input.selectionDirection;
+                if (isShifting) {
+                    if (direction === Direction.FORWARD) {
+                        const curEnd = end - 1;
+                        input.setSelectionRange(start, curEnd);
+                    } else {
+                        const curStart = start - 1 < 0 ? 0 : start - 1;
+                        input.setSelectionRange(curStart, end, Direction.BACKWARD);
+                    }
+                } else {
+                    const idx = end > start
+                        ? start
+                        : start - 1 < 0 
+                            ? 0 
+                            : start - 1;
+                    input.setSelectionRange(idx, idx);
+                }
+            }
+            function moveRight(input, isShifting) {
+                const start = input.selectionStart;
+                const end = input.selectionEnd;
+                const direction = start === end ? Direction.FORWARD : input.selectionDirection;
+                if (isShifting) {
+                    if (direction === Direction.BACKWARD) {
+                        const curStart = start + 1;
+                        input.setSelectionRange(curStart, end);
+                    } else {
+                        const curEnd = end + 1;
+                        input.setSelectionRange(start, curEnd, Direction.FORWARD);
+                    }
+                } else {
+                    const idx = start < end
+                        ? end
+                        : end + 1
+                    input.setSelectionRange(idx, idx);
+                }
+            }
+            $(html)
+                .appendTo(this)
+                .on('input', 'input', function () {
+                    const number = +$(this).val();
+                    if (isNaN(number)) {
+                        $(this).val(1);
+                    } else if (number > 99) {
+                        $(this).val(99);
+                    }
+                })
+                .on('keydown', 'input', function (e) {
+                    const key = e.key;
+                    if (key === 'ArrowUp' || key === 'ArrowDown') {
+                        return false;
+                    }
+                    const input = $(this)[0];
+                    if (key === 'ArrowLeft') {
+                        moveLeft(input, e.shiftKey);
+                    } else if (key === 'ArrowRight') {
+                        moveRight(input, e.shiftKey);
+                    }
+                })
+                .parent().on('click', function (e) {
+                    if (e.target.tagName === 'INPUT') {
+                        return false;
+                    }
+                    if (callback) {
+                        callback();
+                    }
+                    root.$menu.trigger('contextmenu:hide');
+                });
+
+            this.addClass(`context-menu-icon context-menu-icon--fa fa ${icon}`);
+        };
+        return name;
     }
 }

+ 12 - 0
web/building_saas/css/custom.css

@@ -489,4 +489,16 @@ margin-right: 100px !important;
 .form-control-inline {
   display: inline-block !important;
   width: 82%;
+}
+
+/* 右键菜单input */
+.menu-input {
+  width: 2.5rem;
+  border: 1px solid rgb(221, 221, 221);
+  border-radius: 2px;
+  height: 1.3rem;
+  text-align: center;
+}
+.menu-input:focus {
+  outline: none;
 }

+ 79 - 0
web/building_saas/main/js/controllers/project_controller.js

@@ -53,6 +53,85 @@ ProjectController = {
             cbTools.refreshFormulaNodes();
         });
     },
+    addBillsByData: async function (postData, isSameDepth = false) {
+        if (!postData || !postData.length) {
+            return [];
+        }
+        await ajaxPost('/bills/insertBills', { postData });
+        // 插入
+        const insertData = postData.filter(item => item.updateType === 'create');
+        const treeData = insertData.map(item => item.updateData);
+        // 插入清单节点和主树节点
+        projectObj.project.Bills.datas = projectObj.project.Bills.datas.concat(treeData);
+        let newNodes;
+        if (isSameDepth) {
+            const pre = postData.find(item => item.updateType === 'update');
+            const preID = pre && pre.updateData.ID || null;
+            projectObj.project.Bills.tree.multiInsert(treeData, preID);
+            newNodes = projectObj.project.mainTree.multiInsert(treeData, preID);
+        } else {
+            projectObj.project.Bills.tree.insertByDatas(treeData);
+            newNodes = projectObj.project.mainTree.insertByDatas(treeData);
+        }
+        for (const node of newNodes) {
+            node.source = projectObj.project.Bills.tree.nodes[projectObj.project.Bills.tree.prefix + node.getID()];
+            node.data = node.source.data;
+            node.sourceType = projectObj.project.Bills.getSourceType();
+        }
+        ProjectController.syncDisplayNewNodes(projectObj.mainController, newNodes, true);
+        return newNodes;
+    },
+    getBillsPostData: function (number, type) {
+        const project = projectObj.project;
+        const target = project.getParentTarget(project.mainTree.selected, 'sourceType', project.Bills.getSourceType());
+        const targetType = target.data.type;
+        let baseParentID;
+        let baseNextID;
+        let updateNode;
+
+        const targetIsFXOrBX = targetType === billType.FX || targetType === billType.BX;
+        const beLastChild = (type === billType.FX && targetType === billType.FB) || 
+            (type === billType.BILL && target.depth() === 0);
+        const beNextBrother = (type === billType.FX  && targetIsFXOrBX) ||
+            (type === billType.BILL && target.depth() > 0);
+        if (beLastChild) {
+            baseParentID = target.source.getID();
+            baseNextID = -1;
+            updateNode = target.source.children[target.source.children.length - 1];
+        } else if (beNextBrother) {
+            baseParentID = target.source.getParentID();
+            baseNextID =  target.source.getNextSiblingID();
+            updateNode = target;
+        } else {
+            return [];
+        }
+        const insertData = [];
+        for (let i = 0; i < number; i++) {
+            const data = {
+                //type: billType.BILL,
+                type,
+                projectID: project.ID(),
+                ID: uuid.v1(),
+                ParentID: baseParentID,
+            };
+            const pre = insertData[i - 1];
+            if (pre) {
+                pre.NextSiblingID = data.ID;
+            }
+            if (i === number - 1) {
+                data.NextSiblingID = baseNextID;
+            }
+            insertData.push(data);
+        }
+        const postData = insertData.map(item => ({
+            updateType: 'create',
+            updateData: item
+        }));
+        if (updateNode) {
+            postData.push({ updateType: 'update', updateData: { ID: updateNode.getID(), NextSiblingID: insertData[0].ID } });
+        }
+        return postData;
+    },
     addBills: function (project, sheetController, std) {
         if (!project || !sheetController) { return null; }
         let target = project.getParentTarget(project.mainTree.selected, 'sourceType', project.Bills.getSourceType());

+ 31 - 0
web/building_saas/main/js/models/cache_tree.js

@@ -415,6 +415,37 @@ var cacheTree = {
 
             return newNode;
         };
+        //  一次性插入多个连续的同层节点
+        Tree.prototype.multiInsert = function (datas, preID) {
+            const newNodes = [];
+            for (const data of datas) {
+                this.nodes[this.prefix + data.ID] = new Node(this, data.ID);
+                newNodes.push(this.nodes[this.prefix + data.ID]);
+            }
+            const fisrtNode = this.nodes[this.prefix + datas[0].ID];
+            const firstPre = this.nodes[this.prefix + preID];
+            if (firstPre) {
+                firstPre.nextSibling = fisrtNode;
+                firstPre.data.NextSiblingID = fisrtNode.getID();
+            }
+            const parent = this.nodes[this.prefix + datas[0].ParentID] || null;
+            const parentChildren = parent ? parent.children : this.roots;
+            let baseIndex = firstPre ? parentChildren.indexOf(firstPre) + 1 : 0;
+            datas.forEach(data => {
+                const node = this.nodes[this.prefix + data.ID];
+                node.parent = parent;
+                parentChildren.splice(baseIndex++, 0, node);
+                const next = this.nodes[this.prefix + data.NextSiblingID] || null;
+                node.nextSibling = next;
+                if (next) {
+                    next.preSibling = node;
+                }
+            });
+            this.roots = tools.reSortNodes(this.roots, true);
+            this.sortTreeItems();
+            return newNodes;
+        }
+        // 插入某一完整片段到某节点的子项中
         Tree.prototype.insertByDatas = function (datas) {
             let rst = [];
             for(let data of datas){

+ 1 - 0
web/building_saas/main/js/models/ration.js

@@ -451,6 +451,7 @@ var Ration = {
             })
         };
         ration.prototype.addMultiRation = function (items, callback) {
+            debugger;
             console.log('addMultiRation');
             let me = this;
             let project = projectObj.project, sheetController = projectObj.mainController;

+ 41 - 4
web/building_saas/main/js/views/project_view.js

@@ -1200,6 +1200,41 @@ var projectObj = {
         me.mainSpreadEnterCell({type: 'EnterCell'}, {sheet: sheet, sheetName: sheet.name(), cancel: false, row: newRow, col: newCol});
 
     },
+    // 注册自定义插入清单数量
+    registerFlexibleInsertBillMenu: function (type) {
+        const project = projectObj.project;
+        const name = `插入${billText[type]}`;
+        const inputID = `insert-bills-number${type}`;
+        const insertBillsHtml = `<span>${name}&nbsp;&nbsp;<input id=${inputID} class="menu-input" type="text" value="1" onfocus="this.select()">&nbsp;&nbsp;行</span>`;
+        return sheetCommonObj.registerInputContextMenuItem(`insertBills${type}`, insertBillsHtml, 'fa-sign-in', async function () {
+            if (project.mainTree.selected.data.type == billType.DXFY) {
+                if (project.mainTree.selected.data.calcBase && project.mainTree.selected.data.calcBase != "") {
+                    alert("当前有基数计算,不能插入子项。");
+                    return;
+                }
+            }
+            try {
+                const number = +$(`#${inputID}`).val();
+                if (!number) {
+                    return;
+                }
+                $.bootstrapLoading.start();
+                const postData = ProjectController.getBillsPostData(number, type);
+                const newNodes = await ProjectController.addBillsByData(postData, true);
+                if (newNodes.length) {
+                    projectObj.mainController.setTreeSelected(newNodes[0]);
+                    projectObj.selectColAndFocus(project.mainTree.selected);
+                }
+            } catch (err) {
+                console.log(err);
+                if (!$('hintBox_form').is(':visible')) {
+                    alert(err);
+                }
+            } finally {
+                $.bootstrapLoading.end();
+            }
+        });
+    },
     loadMainSpreadContextMenu: function () {
         var project = this.project, spread = this.mainSpread, controller = this.mainController;
         $.contextMenu({
@@ -1261,6 +1296,7 @@ var projectObj = {
                     }
                 },
                 "insertFX": {
+                    type: projectObj.registerFlexibleInsertBillMenu(billType.FX),
                     name: "插入分项",
                     icon: 'fa-sign-in',
                     disabled: function () {
@@ -1289,10 +1325,10 @@ var projectObj = {
                         }
                         return true;//除了清单,其它类型都只读
                     },
-                    callback: function (key, opt) {
+                   /*  callback: function (key, opt) {
                         ProjectController.addFX(project, controller);
                         projectObj.selectColAndFocus(project.mainTree.selected);
-                    },
+                    }, */
                     visible: function(key, opt){
                         if(project.mainTree.selected){
                             return project.Bills.isFBFX(project.mainTree.selected );//不属于分部分项的话隐藏
@@ -1302,6 +1338,7 @@ var projectObj = {
                     }
                 },
                 "insertBills": {
+                    type: projectObj.registerFlexibleInsertBillMenu(billType.BILL),
                     name: "插入清单",
                     icon: 'fa-sign-in',
                     disabled: function () {
@@ -1314,7 +1351,7 @@ var projectObj = {
                         }
                         return true;
                     },
-                    callback: function (key, opt) {
+                    /* callback: function (key, opt) {
                         if(project.mainTree.selected.data.type == billType.DXFY){
                             if(project.mainTree.selected.data.calcBase&&project.mainTree.selected.data.calcBase!=""){
                                 alert("当前有基数计算,不能插入子项。");
@@ -1323,7 +1360,7 @@ var projectObj = {
                         }
                         ProjectController.addBills(project, controller);
                         projectObj.selectColAndFocus(project.mainTree.selected);
-                    },
+                    }, */
                     visible: function(key, opt){
                         if(project.mainTree.selected){
                             return  project.Bills.isFBFX(project.mainTree.selected)==true?false:true;