Преглед изворни кода

造价书,多选升降级、上下移

vian пре 5 година
родитељ
комит
7f2bb9bb1b

+ 85 - 1
public/web/id_tree.js

@@ -292,7 +292,7 @@ var idTree = {
         Node.prototype.upLevel = function () {
             var result = {success: false, updateDatas: []};
             var iIndex = this.parent.children.indexOf(this), orgParent = this.parent, newNextSibling = this.parent.nextSibling;
-            if (this.canUpLevel) {
+            if (this.canUpLevel()) {
                 // NextSiblings become child
                 tools.addNodes(this.tree, this, this.parent.children.slice(iIndex + 1));
                 // Orginal Parent remove node and nextSiblings
@@ -813,6 +813,90 @@ var idTree = {
             let node = this.nodes[this.prefix+ID];
             return node;
         };
+        // nodes必须是有效的同层节点(选中的可升级的节点)
+        Tree.prototype.getMultiUpLevelData = function (nodes) {
+            const firstNode = nodes[0];
+            const lastNode = nodes[nodes.length - 1];
+            const data = [];
+            if (!firstNode.canUpLevel()) {
+                return;
+            }
+            if (!lastNode.isLast()) {
+                const children = lastNode.parent.children.slice(lastNode.siblingIndex() + 1);
+                tools.addUpdateDataForParent(data, children, lastNode.getID());
+            }
+            if (firstNode.preSibling) {
+                tools.addUpdateDataForNextSibling(data, firstNode.preSibling, this.setting.rootId);
+            }
+            tools.addUpdateDataForNextSibling(data, firstNode.parent, firstNode.getID());
+            const updateData = nodes.map(node => {
+                const nextID = node === lastNode ? node.parent.getNextSiblingID() : node.getNextSiblingID();
+                const nodeData = this.getDataTemplate(node.getID(), node.parent.getParentID(), nextID);
+                return { type: 'update', data: nodeData };
+
+            });
+            data.push(...updateData);
+            return data;
+        };
+        Tree.prototype.getMultiDownLevelData = function (nodes) {
+            const firstNode = nodes[0];
+            const lastNode = nodes[nodes.length - 1];
+            const data = [];
+            if (!firstNode.canDownLevel()) {
+                return;
+            }
+            if (firstNode.preSibling.children.length) {
+                tools.addUpdateDataForNextSibling(data, firstNode.preSibling.lastChild(), firstNode.getID());
+            }
+            const preSibling = firstNode.preSibling;
+            const updatePreData = {
+                type: 'update',
+                data: this.getDataTemplate(preSibling.getID(), preSibling.getParentID(), lastNode.getNextSiblingID())
+            };
+            //如果设置了专项暂定,则需要清空
+            if (preSibling.data.specialProvisional) {
+                preSibling.data.specialProvisional = '';
+                updatePreData.specialProvisional = '';
+            }
+            data.push(updatePreData);
+            const updateData = nodes.map(node => {
+                const nextID = node === lastNode ? this.setting.rootId : node.getNextSiblingID();
+                return {
+                    type: 'update',
+                    data: this.getDataTemplate(node.getID(), preSibling.getID(), nextID)
+                };
+            });
+            data.push(...updateData);
+            return data;
+        };
+        Tree.prototype.getMultiUpMoveData = function (nodes) {
+            const firstNode = nodes[0];
+            const lastNode = nodes[nodes.length - 1];
+            const data = [];
+            if (!firstNode.canUpMove()) {
+                return;
+            }
+            if (firstNode.preSibling.preSibling) {
+                tools.addUpdateDataForNextSibling(data, firstNode.preSibling.preSibling, firstNode.getID());
+            }
+            tools.addUpdateDataForNextSibling(data, firstNode.preSibling, lastNode.getNextSiblingID());
+            tools.addUpdateDataForNextSibling(data, lastNode, firstNode.preSibling.getID());
+            return data;
+        };
+        Tree.prototype.getMultiDownMoveData = function (nodes) {
+            const firstNode = nodes[0];
+            const lastNode = nodes[nodes.length - 1];
+            const data = [];
+            if (!lastNode.canDownMove()) {
+                return;
+            }
+            if (firstNode.preSibling) {
+                tools.addUpdateDataForNextSibling(data, firstNode.preSibling, lastNode.getNextSiblingID());
+            }
+            tools.addUpdateDataForNextSibling(data, lastNode, lastNode.nextSibling.getNextSiblingID());
+            tools.addUpdateDataForNextSibling(data, lastNode.nextSibling, firstNode.getID());
+            return data;
+        };
         return new Tree(setting);
     },
     updateType: {update: 'update', new: 'new', delete: 'delete'}

+ 91 - 0
public/web/tree_sheet/tree_sheet_controller.js

@@ -150,6 +150,16 @@ var TREE_SHEET_CONTROLLER = {
                 }
             }
         };
+        controller.prototype.multiUpLevel = function (nodes) {
+            nodes.forEach(node => node.upLevel());
+            TREE_SHEET_HELPER.massOperationSheet(this.sheet, () => {
+                TREE_SHEET_HELPER.refreshNodesVisible(nodes, this.sheet, true);
+                this.sheet.showRow(nodes[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
+                if (this.event.refreshBaseActn) {
+                    this.event.refreshBaseActn(this.tree);
+                }
+            });
+        };
         controller.prototype.downLevel = function () {
             var that = this;
             if (this.tree.selected) {
@@ -164,6 +174,17 @@ var TREE_SHEET_CONTROLLER = {
                 }
             }
         };
+        controller.prototype.multiDownLevel = function (nodes) {
+            nodes.forEach(node => node.downLevel());
+            const firstNode = nodes[0];
+            TREE_SHEET_HELPER.massOperationSheet(this.sheet, () => {
+                TREE_SHEET_HELPER.refreshNodesVisible([firstNode.parent], this.sheet, true);
+                this.sheet.showRow(firstNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
+                if (this.event.refreshBaseActn) {
+                    this.event.refreshBaseActn(this.tree);
+                }
+            });
+        };
         controller.prototype.upMove = function () {
             var that = this, sels = this.sheet.getSelections();
             if (this.tree.selected) {
@@ -182,6 +203,44 @@ var TREE_SHEET_CONTROLLER = {
                 });
             }
         };
+        controller.prototype.multiUpMove = function (nodes) {
+            TREE_SHEET_HELPER.massOperationSheet(this.sheet, () => {
+                const { row, col, rowCount, colCount } = this.sheet.getSelections()[0];
+                nodes.forEach(node => {
+                    TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, node, node.serialNo(), true); //为了处理移动前子项是隐藏的情况
+                });
+                const preNode = nodes[0].preSibling;
+                TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, preNode, preNode.serialNo(), true);
+                nodes.forEach(node => node.upMove());
+                TREE_SHEET_HELPER.refreshTreeNodeData(this.setting, this.sheet, [...nodes, preNode], true);
+                nodes.forEach(node => TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, node, node.serialNo()));
+                TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, preNode, preNode.serialNo());
+                this.sheet.setSelection(row - preNode.posterityCount() - 1, col, rowCount, colCount);
+                if (this.event.refreshBaseActn) {
+                    this.event.refreshBaseActn(this.tree);
+                }
+            });
+        };
+        controller.prototype.multiDownMove = function (nodes) {
+            TREE_SHEET_HELPER.massOperationSheet(this.sheet, () => {
+                const { row, col, rowCount, colCount } = this.sheet.getSelections()[0];
+                nodes.forEach(node => {
+                    TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, node, node.serialNo(), true); //为了处理移动前子项是隐藏的情况
+                });
+                const nextNode = nodes[nodes.length - 1].nextSibling;
+                TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, nextNode, nextNode.serialNo(), true);
+                for (let i = nodes.length - 1; i >= 0; i--) {
+                    nodes[i].downMove();
+                }
+                TREE_SHEET_HELPER.refreshTreeNodeData(this.setting, this.sheet, [...nodes, nextNode], true);
+                nodes.forEach(node => TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, node, node.serialNo()));
+                TREE_SHEET_HELPER.refreshChildrenVisiable(this.sheet, this.tree, nextNode, nextNode.serialNo());
+                this.sheet.setSelection(row + nextNode.posterityCount() + 1, col, rowCount, colCount);
+                if (this.event.refreshBaseActn) {
+                    this.event.refreshBaseActn(this.tree);
+                }
+            });
+        };
         controller.prototype.downMove = function () {
             var that = this, sels = this.sheet.getSelections();
             if (this.tree.selected) {
@@ -225,6 +284,38 @@ var TREE_SHEET_CONTROLLER = {
             this.event[eventName] = eventFun;
         };
 
+        // 获取有效的选中同层节点
+        // 如果选中的节点内,有比第一个选中节点深度小的节点,则此选中范围内无有效节点
+        // 如果没用上述的情况,则有效选中节点为,跟第一个选中节点深度相同的节点
+        controller.prototype.getValidNodesWithinSelection = function () {
+            const selection = this.sheet.getSelections()[0];
+            if (!selection || !commonUtil.isDef(selection.row)) {
+                return [];
+            }
+            const nodes = [];
+            let firstNode;
+            let firstNodeDepth;
+            const maxRow = selection.row + selection.rowCount;
+            for (let row = selection.row; row < maxRow; row++) {
+                const node = this.tree.items[row];
+                if (!node) {
+                    return [];
+                }
+                const depth = node.depth();
+                if (!firstNode) {
+                    firstNode = node;
+                    firstNodeDepth = firstNode.depth();
+                }
+                if (depth < firstNodeDepth) {
+                    return [];
+                }
+                if (depth === firstNodeDepth) {
+                    nodes.push(node);
+                }
+            }
+            return nodes;
+        };
+
         return new controller();
     },
     eventName: {

+ 24 - 0
web/building_saas/main/js/models/bills.js

@@ -334,18 +334,35 @@ var Bills = {
 
             return node.upMove();
         };
+        bills.prototype.multiUpMoveBills = function (tree, nodes) {
+            const upMoveData = tree.getMultiUpMoveData(nodes);;
+            project.pushNow('upMoveBills', this.getSourceType(), tools.coverseTreeUpdateData(upMoveData, this.project.ID()));
+            nodes.forEach(node => node.upMove());
+        };
         bills.prototype.downMoveBills = function (node) {
             var downMoveData = node.getDownMoveData();
             project.pushNow('downMoveBills', this.getSourceType(), tools.coverseTreeUpdateData(downMoveData, this.project.ID()));
 
             return node.downMove();
         };
+        bills.prototype.multiDownMoveBills = function (tree, nodes) {
+            const downMoveData = tree.getMultiDownMoveData(nodes);
+            project.pushNow('downMoveBills', this.getSourceType(), tools.coverseTreeUpdateData(downMoveData, this.project.ID()));
+            for (let i = nodes.length - 1; i >= 0; i--) {
+                nodes[i].downMove();
+            }
+        };
         bills.prototype.upLevelBills = function (node) {
             var upLevelData = node.getUpLevelData();
             project.pushNow('upLevelBills', this.getSourceType(), tools.coverseTreeUpdateData(upLevelData, this.project.ID()));
 
             return node.upLevel();
         };
+        bills.prototype.multiUpLevelBills = function (tree, nodes) {
+            const upLevelData = tree.getMultiUpLevelData(nodes);
+            project.pushNow('upLevelBills', this.getSourceType(), tools.coverseTreeUpdateData(upLevelData, this.project.ID()));
+            nodes.forEach(node => node.upLevel());
+        };
         bills.prototype.downLevelBills = function (node) {
             var downLevelData = node.getDownLevelData();
             this.setQuantityAfterDownLevel(node,downLevelData);
@@ -353,6 +370,13 @@ var Bills = {
 
             return node.downLevel();
         };
+        bills.prototype.multiDownLevelBills = function (tree, nodes) {
+            const downLevelData = tree.getMultiDownLevelData(nodes);
+            const firstNode = nodes[0];
+            this.setQuantityAfterDownLevel(firstNode, downLevelData);
+            project.pushNow('downLevelBills', [this.getSourceType()], [tools.coverseTreeUpdateData(downLevelData, this.project.ID())]);
+            nodes.forEach(node => node.downLevel());
+        };
         bills.prototype.setQuantityAfterDownLevel = function (node,downLevelData) {
             if(projectObj.project.projectInfo.property && projectObj.project.projectInfo.property.valuationType == "ration"){//工程量清单项目中,将B降级为A的子项后应清空A的工程量
                 let preNode = node.preSibling;

+ 1 - 1
web/building_saas/main/js/models/cache_tree.js

@@ -231,7 +231,7 @@ var cacheTree = {
         Node.prototype.upLevel = function () {
             var success = false,
                 iIndex = this.parent.children.indexOf(this), orgParent = this.parent, newNextSibling = this.parent.nextSibling;
-            if (this.canUpLevel) {
+            if (this.canUpLevel()) {
                 // NextSiblings become child
                 tools.addNodes(this.tree, this, this.parent.children.slice(iIndex + 1));
                 // Orginal Parent remove node and nextSiblings

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

@@ -318,6 +318,25 @@ var Ration = {
             ration1.serialNo = ration2.serialNo;
             ration2.serialNo = preSerialNo;
         };
+        ration.prototype.changeMultiPos = function (baseRation, rations) {
+            const updateData = [];
+            let tempSerialNo = baseRation.serialNo;
+            rations.forEach(ration => {
+                updateData.push({
+                    updateType: 'ut_update',
+                    updateData: this.getTempRationData(ration.ID, ration.billsItemID, tempSerialNo)
+                });
+                let orgSerialNo = ration.serialNo;
+                ration.serialNo = tempSerialNo;
+                tempSerialNo = orgSerialNo;
+            });
+            updateData.push({
+                updateType: 'ut_update',
+                updateData: this.getTempRationData(baseRation.ID, baseRation.billsItemID, tempSerialNo)
+            });
+            baseRation.serialNo = tempSerialNo;
+            this.project.pushNow('insertRation', [this.getSourceType()], [updateData]);
+        };
 
         ration.prototype.updateField = function (ration, field, newValue) {
             calcFees.setFee(ration, field, newValue);

+ 104 - 97
web/building_saas/main/js/views/project_view.js

@@ -69,78 +69,66 @@ var projectObj = {
             }
         };
         let selected = tree.selected, that = projectObj;
-
-        let canUpLevel = function (node) {
-            if(!node){
-                return false;
-            }
-            if(projectObj.project.isBillsLocked()== true && projectObj.project.withinBillsLocked(node)){
+        const validNodes = projectObj.mainController.getValidNodesWithinSelection();
+        const canUpLevel = function (nodes) {
+            if (!nodes.length || projectObj.project.isBillsLocked()) {
                 return false;
             }
-            if(node.depth()<=1){//焦点行是树结构的第一/二层节点,灰显。
+            const firstNode = nodes[0];
+            const lastNode = nodes[nodes.length - 1];
+            if (firstNode.depth() <= 1) { // 首节点是树结构的第一/二层节点,灰显。
                 return false;
             }
-            if(node.sourceType !== that.project.Bills.getSourceType()){//焦点行是定额/量价/工料机,灰显。
+            if (firstNode.sourceType !== commonConstants.SourceType.BILLS) { // 首节点是定额/量价/工料机,灰显。
                 return false;
-            }else {
-                if(node.data.type == billType.FX || node.data.type == billType.BX){//是分项,或者补项灰显。
-                    return false;
-                }
-                if(node.data.type == billType.FB&&node.nextSibling&&node.children.length>0){//焦点行是分部有后兄弟,有子项.
-                     if(node.children[0].data.type==billType.FX || node.children[0].data.type==billType.BX){ //焦点行子项是分项
-                         return false;
-                     }
-                }
-                if(node.data.type == billType.BILL &&node.nextSibling){//焦点行是清单有后兄弟
-                    if(node.data.calcBase&&node.data.calcBase!=""){//有基数计算
+            } else {
+                if (lastNode.data.type == billType.BILL && lastNode.nextSibling) { // 末节点是清单有后兄弟
+                    if (lastNode.data.calcBase) { // 有基数计算
                         return false;
                     }
-                    if(node.children.length>0&&node.children[0].sourceType !== that.project.Bills.getSourceType()){//有子项,并且子项不是清单
+                    if (lastNode.children.length > 0 && lastNode.children[0].sourceType !== commonConstants.SourceType.BILLS) { // 有子项,并且子项不是清单
                         return false;
                     }
                 }
             }
             return true;
         };
-        let canDownLevel = function (node) {//
-            if(!node){
+        const canDownLevel = function (nodes) {
+            if (!nodes.length || projectObj.project.isBillsLocked()) {
                 return false;
             }
-            if(projectObj.project.isBillsLocked()== true && projectObj.project.withinBillsLocked(node)){
+            const firstNode = nodes[0];
+            if (firstNode.depth() === 0) {// 首节点是树结构的第一层节点,灰显。
                 return false;
             }
-            if(node.depth()==0){//焦点行是树结构的第一层节点,灰显。
+            if (firstNode.sourceType !== commonConstants.SourceType.BILLS) { // 首节点是定额/量价/工料机,灰显。
                 return false;
-            }
-            if(node.sourceType !== that.project.Bills.getSourceType()) {//焦点行是定额/量价/工料机,灰显。
-                return false;
-            }else {
-                if(node.data.type == billType.FX || node.data.type == billType.BX){//是分项,灰显。
-                    return false;
-                }
-                if(!node.preSibling){//无前兄弟,灰显
+            } else {
+                const preNode = firstNode.preSibling;
+                if (!preNode) { // 首节点无前兄弟,灰显
                     return false;
-                }else if(node.preSibling.data.calcBase&&node.preSibling.data.calcBase!=""){//前兄弟有基数计算
+                } else if (preNode.data.calcBase) { // 首节点前兄弟有基数计算
                     return false
                 }
-                if(node.preSibling.children.length>0){//前兄弟有子项,子项是分项,灰显。
-                    if(node.data.type==billType.FB&&(node.preSibling.children[0].data.type==billType.FX||node.preSibling.children[0].data.type==billType.BX)){//焦点行是分部前兄弟有子项,子项是分项,灰显。
-                        return false;
-                    }
-                    if(node.data.type==billType.BILL&&node.preSibling.children[0].sourceType !== that.project.Bills.getSourceType()){//焦点行是清单,子项不是清单
+                if (preNode.children.length > 0) {//前兄弟有子项,子项是分项,灰显。
+                    if (firstNode.data.type == billType.BILL && preNode.children[0].sourceType !== commonConstants.SourceType.BILLS) { // 首节点是清单,首节点前兄弟子项不是清单
                         return false
                     }
                 }
             }
             return true;
         };
-        let canUpMove = function (node) {
-            if(node&&node.preSibling){//有前兄弟
-                if(node.sourceType==that.project.Bills.getSourceType()){
-                    if(projectObj.project.isBillsLocked()== true && projectObj.project.withinBillsLocked(node)){
+        const canUpMove = function (nodes) {
+            if (!nodes.length) {
+                return false;
+            }
+            const firstNode = nodes[0];
+            if (firstNode && firstNode.preSibling) {
+                if (firstNode.sourceType == commonConstants.SourceType.BILLS) {
+                    if (projectObj.project.isBillsLocked()) {
                         return false;
                     }
-                    if(node.data.type == billType.DXFY&&node.data.isAdd!==1){
+                    if (firstNode.data.type == billType.DXFY && firstNode.data.isAdd !== 1) {
                         return false;
                     }
                 }
@@ -148,13 +136,17 @@ var projectObj = {
             }
             return false
         };
-        let canDownMove = function (node) {
-            if(node&&node.nextSibling){
-                if(node.sourceType==that.project.Bills.getSourceType()){
-                    if(projectObj.project.isBillsLocked()== true && projectObj.project.withinBillsLocked(node)){
+        const canDownMove = function (nodes) {
+            if (!nodes.length) {
+                return false;
+            }
+            const lastNode = nodes[nodes.length - 1];
+            if (lastNode && lastNode.nextSibling) {
+                if (lastNode.sourceType == commonConstants.SourceType.BILLS) {
+                    if (projectObj.project.isBillsLocked()) {
                         return false;
                     }
-                    if(node.data.type == billType.DXFY&&node.data.isAdd!==1){
+                    if (lastNode.data.type == billType.DXFY && lastNode.data.isAdd !== 1) {
                         return false;
                     }
                 }
@@ -165,10 +157,10 @@ var projectObj = {
 
         setButtonValid(projectObj.project.Ration.canAdd(selected), $('#insertRation'));
         setButtonValid(ifCanDelete(), $('#delete'));
-        setButtonValid(canUpLevel(selected), $('#upLevel'));
-        setButtonValid(canDownLevel(selected), $('#downLevel'));
-        setButtonValid(canUpMove(selected) , $('#upMove'));
-        setButtonValid(canDownMove(selected), $('#downMove'));
+        setButtonValid(canUpLevel(validNodes), $('#upLevel'));
+        setButtonValid(canDownLevel(validNodes), $('#downLevel'));
+        setButtonValid(canUpMove(validNodes), $('#upMove'));
+        setButtonValid(canDownMove(validNodes), $('#downMove'));
         setButtonValid(!projectObj.project.isBillsLocked(), $('#ZLFB_btn'));
 
     },
@@ -2128,26 +2120,34 @@ $('#delete').click(function () {
     }
 });
 
-$('#upLevel').click(function () {
-    var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected, orgParent = selected.parent;
-    if (selected && selected.sourceType === project.Bills.getSourceType()) {
-        project.Bills.upLevelBills(selected.source);
-        controller.upLevel();
-        controller.refreshTreeNode([orgParent, selected]);
-        projectObj.project.calcProgram.calcNodesAndSave([selected,orgParent]);
-    }
-});
-$('#downLevel').click(function () {
-    var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected;
-    if (selected && selected.sourceType === project.Bills.getSourceType()) {
-        project.Bills.downLevelBills(selected.source);
-        controller.downLevel();
-        controller.refreshTreeNode([selected.parent]);
-        projectObj.converseCalculateBills(selected.parent);
-    }
-});
+// TODO 造价书节点的升降级、上下移都是异步的,不等服务器响应直接进行前端更新的。有风险,后续可能需要改。
+// 节流,防止快速触发
+const throttleTime = 400;
+const throttleUpLevel = _.throttle(() => {
+    const controller = projectObj.mainController;
+    const project = projectObj.project;
+    const mainTreeNodes = controller.getValidNodesWithinSelection();
+    const billsNodes = mainTreeNodes.map(node => node.source);
+    const orgParent = mainTreeNodes[0].parent;
+    project.Bills.multiUpLevelBills(project.Bills.tree, billsNodes);
+    controller.multiUpLevel(mainTreeNodes);
+    const refreshNodes = [orgParent, ...mainTreeNodes];
+    controller.refreshTreeNode(refreshNodes);
+    projectObj.project.calcProgram.calcNodesAndSave(refreshNodes);
+}, throttleTime);
+$('#upLevel').click(throttleUpLevel);
+const throttleDownLevel = _.throttle(() => {
+    const controller = projectObj.mainController
+    const project = projectObj.project;
+    const mainTreeNodes = controller.getValidNodesWithinSelection();
+    const billsNodes = mainTreeNodes.map(node => node.source);
+    const orgParent = mainTreeNodes[0].parent;
+    project.Bills.multiDownLevelBills(project.Bills.tree, billsNodes);
+    controller.multiDownLevel(mainTreeNodes);
+    controller.refreshTreeNode([orgParent, ...mainTreeNodes]);
+    projectObj.converseCalculateBills(orgParent);
+}, throttleTime);
+$('#downLevel').click(throttleDownLevel);
 $('#insertRation').click(function () {
     // projectObj.project.Ration.addNewRation(null,rationType.ration);
     // 连续点工具栏的插入定额按钮,显示树结构画线有问题。
@@ -2155,32 +2155,39 @@ $('#insertRation').click(function () {
     //2020-01-08  要限制定额数量,所以还是要用等回传信息的方式插入定额
     projectObj.project.Ration.addNewRation(null,rationType.ration,projectObj.selectColAndFocus,false);
 });
-$('#upMove').click(function () {
-    var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected, pre = selected.preSibling, preSerialNo;
-
-    if (selected.sourceType === project.Bills.getSourceType()) {
-        project.Bills.upMoveBills(selected.source);
-        controller.upMove();
-    } else if (selected.sourceType === project.Ration.getSourceType()) {
-        project.Ration.changePos(selected.source, selected.preSibling.source);
-        controller.upMove();
+const throttleUpMove = _.throttle(() => {
+    const controller = projectObj.mainController
+    const project = projectObj.project;
+    const mainTreeNodes = controller.getValidNodesWithinSelection();
+    const sourceNodes = mainTreeNodes.map(node => node.source);
+    const firstNode = mainTreeNodes[0];
+    if (firstNode.sourceType === commonConstants.SourceType.BILLS) {
+        project.Bills.multiUpMoveBills(project.Bills.tree, sourceNodes);
+        controller.multiUpMove(mainTreeNodes);
+    } else if (firstNode.sourceType === commonConstants.SourceType.RATION) {
+        //project.Ration.changePos(selected.source, selected.preSibling.source);
+        project.Ration.changeMultiPos(mainTreeNodes[0].preSibling.source, sourceNodes);
+        controller.multiUpMove(mainTreeNodes);
     };
-});
-$('#downMove').click(function () {
-    var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected, next, nextSerialNo;
-
-    if (selected) {
-        if (selected.sourceType === project.Bills.getSourceType()) {
-            project.Bills.downMoveBills(selected.source);
-            controller.downMove();
-        } else if (selected.sourceType === project.Ration.getSourceType()) {
-            project.Ration.changePos(selected.source, selected.nextSibling.source);
-            controller.downMove();
-        };
-    }
-});
+}, throttleTime);
+$('#upMove').click(throttleUpMove);
+const throttleDownMove = _.throttle(() => {
+    const controller = projectObj.mainController
+    const project = projectObj.project;
+    const mainTreeNodes = controller.getValidNodesWithinSelection();
+    const sourceNodes = mainTreeNodes.map(node => node.source);
+    const firstNode = mainTreeNodes[0];
+    if (firstNode.sourceType === commonConstants.SourceType.BILLS) {
+        project.Bills.multiDownMoveBills(project.Bills.tree, sourceNodes);
+        controller.multiDownMove(mainTreeNodes);
+    } else if (firstNode.sourceType === commonConstants.SourceType.RATION) {
+        const nextSource = mainTreeNodes[mainTreeNodes.length - 1].nextSibling.source;
+        const reverseSource = [...sourceNodes].reverse();
+        project.Ration.changeMultiPos(nextSource, reverseSource);
+        controller.multiDownMove(mainTreeNodes);
+    };
+}, throttleTime);
+$('#downMove').click(throttleDownMove);
 $("a[name='lockBills']").click(function () {//点击锁定/解锁清单
     let lockBills = projectObj.project.projectInfo.property.lockBills;
     lockBills = !lockBills;