Browse Source

feat: 清单精灵后台,增加配置材料,增加单位、材料列

vian 3 years ago
parent
commit
730a92eb79

+ 2 - 0
modules/all_models/std_billsGuidance_items.js

@@ -24,6 +24,8 @@ const stdBillsGuidanceItems = new Schema({
     deleted: {type: Boolean, default: false},
     outputItemCharacter: {type: Boolean, default: false},
     required: {type: Boolean, default: false},
+    unit: String, // 单位,辅助运距功能
+    isMaterial: {type: Boolean, default: false}, // 材料,辅助替换材料规格
 }, {versionKey: false});
 
 mongoose.model('std_billsGuidance_items', stdBillsGuidanceItems, 'std_billsGuidance_items');

+ 21 - 0
modules/all_models/std_billsGuidance_material.js

@@ -0,0 +1,21 @@
+//清单指引材料数据
+const mongoose = require('mongoose');
+const Schema = mongoose.Schema;
+
+const materialSchema = {
+  gljID: { type: Number, required: true }
+};
+
+const stdBillsGuidanceMaterial = new Schema({
+    libID: String,
+    ID: { type: String, index: true },
+    billID: String, //关联清单的ID
+    materials: {
+      type: [materialSchema],
+      default: [],
+    },
+}, {versionKey: false});
+
+stdBillsGuidanceMaterial.index({ libID: 1, billID: 1 });
+
+mongoose.model('std_billsGuidance_materials', stdBillsGuidanceMaterial, 'std_billsGuidance_materials');

+ 23 - 0
modules/std_billsGuidance_lib/controllers/libController.js

@@ -103,6 +103,29 @@ class BillsGuideLibController extends BaseController{
         }
     }
 
+    async getBillMaterials(req, res){
+        try{
+            const data = JSON.parse(req.body.data);
+            const materials = await billsGuidanceFacade.getBillMaterials(data.libID, data.billID);
+            callback(req, res, 0, '', materials);
+        }
+        catch(err){
+            callback(req, res, 1, err, []);
+        }
+    }
+
+    async editBillMaterials(req, res){
+        try{
+            const data = JSON.parse(req.body.data);
+            const materials = await billsGuidanceFacade.editBillMaterials(data.libID, data.billID, data.gljCodes, data.compilationID);
+            callback(req, res, 0, '', materials);
+        }
+        catch(err){
+            console.log(err);
+            callback(req, res, 1, err.message, []);
+        }
+    }
+
     async testItems(req, res){
         try{
             let data = JSON.parse(req.body.data);

+ 2 - 0
modules/std_billsGuidance_lib/routes/routes.js

@@ -23,6 +23,8 @@ module.exports = function (app) {
     router.post('/getLibWithBills', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.getLibWithBills);
     router.post('/getItemsByBills', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.getItemsByBills);
     router.post('/updateItems', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.updateItems);
+    router.post('/getBillMaterials', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.getBillMaterials);
+    router.post('/editBillMaterials', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.editBillMaterials);
     //test
     //router.post('/testItems', billsGuideLibController.auth, billsGuideLibController.init, billsGuideLibController.testItems);
 

+ 4 - 2
public/web/common_ajax.js

@@ -119,7 +119,7 @@ var CommonAjax = {
     }
 };
 
-async function ajaxPost(url, data, timeout = 50000) {
+async function ajaxPost(url, data, timeout = 50000, skipAlert = false) {
     return new Promise(function (resolve, reject) {
         $.ajax({
             type:"POST",
@@ -132,7 +132,9 @@ async function ajaxPost(url, data, timeout = 50000) {
                 if (result.error === 0 ||result.error ===false) {
                     resolve(result.data);
                 } else {
-                    alert('error: ' + result.message);
+                    if (!skipAlert) {
+                        alert('error: ' + result.message);
+                    }
                     reject(result.message);
                 }
             },

+ 38 - 0
web/maintain/billsGuidance_lib/html/zhiyin.html

@@ -195,6 +195,44 @@
             </div>
         </div>
     </div>
+    <!-- 配置材料 -->
+    <div class="modal fade" id="bill-material-modal" data-backdrop="static" style="display: none;" aria-hidden="true">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content" style="width: 520px">
+                <div class="modal-header">
+                    <h5 class="modal-title">配置材料</h5>
+                    <button type="button"  class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">×</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <div id="bill-material-spread" style="height: 400px;"></div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary"  data-dismiss="modal">关闭</button>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="modal fade" id="alert" data-backdrop="static" style="display: none;" aria-hidden="true">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">警告</h5>
+                    <button type="button"  class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">×</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <div id="alert-info" style="font-weight: 700; max-height: 300px; overflow-y: auto;"></div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-danger" data-dismiss="modal">确定</button>
+                    <button type="button" class="btn btn-secondary"  data-dismiss="modal">取消</button>
+                </div>
+            </div>
+        </div>
+    </div>
     <!-- JS. -->
     <script src="/lib/jquery/jquery.min.js"></script>
     <script src="/lib/tether/tether.min.js"></script>

+ 272 - 34
web/maintain/billsGuidance_lib/js/billsGuidance.js

@@ -45,7 +45,7 @@ const billsGuidance = (function () {
             return recurCompare(aArr, bArr, 0);
         });
     }
-
+    let curCompilationID = '';
     const locked = lockUtil.getLocked();
     let moduleName = 'stdBillsGuidance';
     //上下拖动的拖动条高度
@@ -168,6 +168,25 @@ const billsGuidance = (function () {
                 }
             },
             {
+                width: 50,
+                readOnly: locked,
+                head: {
+                    titleNames: ["单位"],
+                    spanCols: [1],
+                    spanRows: [1],
+                    vAlign: [1],
+                    hAlign: [1],
+                    font: ["Arial"]
+                },
+                data: {
+                    field: "unit",
+                    vAlign: 1,
+                    hAlign: 1,
+                    font: "Arial",
+                    formatter: "@"
+                }
+            },
+            {
                 width: 40,
                 readOnly: true,
                 head: {
@@ -202,13 +221,33 @@ const billsGuidance = (function () {
                     hAlign: 1,
                     font: "Arial"
                 }
+            },
+            {
+                width: 40,
+                readOnly: true,
+                head: {
+                    titleNames: ["材料"],
+                    spanCols: [1],
+                    spanRows: [1],
+                    vAlign: [1],
+                    hAlign: [1],
+                    font: ["Arial"]
+                },
+                data: {
+                    field: "isMaterial",
+                    vAlign: 1,
+                    hAlign: 1,
+                    font: "Arial"
+                }
             }
         ]
         },
         headers: [
             {name: '项目指引', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@'},
+            {name: '单位', dataCode: 'unit', width: 50, vAlign: 'center', hAlign: 'center', formatter: '@'},
             {name: '输出特征', dataCode: 'outputItemCharacter', width: 40, vAlign: 'center', hAlign: 'center'},
             {name: '必填', dataCode: 'required', width: 40, vAlign: 'center', hAlign: 'center'},
+            {name: '材料', dataCode: 'isMaterial', width: 40, vAlign: 'center', hAlign: 'center'},
         ],
         events: {
             SelectionChanged: function (sender, info) {
@@ -313,6 +352,108 @@ const billsGuidance = (function () {
         }
     };
 
+    /* 清单材料表 */
+    const billMaterial = {
+        dom: $('#bill-material-spread'),
+        workBook: null,
+        cache: [],
+        headers: [
+            {name: '材料编码', dataCode: 'code', width: 110, vAlign: 'center', hAlign: 'left', formatter: '@'},
+            {name: '材料名称', dataCode: 'name', width: 150, vAlign: 'center', hAlign: 'left', formatter: '@'},
+            {name: '规格', dataCode: 'specs', width: 150, vAlign: 'center', hAlign: 'left', formatter: '@'}
+        ],
+        events: {
+            EditEnded: function (sender, args) {
+                editMaterials(args.sheet, [{row: args.row, col: args.col}]);
+            },
+            RangeChanged: function (sender, args) {
+                editMaterials(args.sheet, args.changedCells);
+            },
+        }
+    }
+
+    // 显示清单材料数据
+    function showBillMaterialData(sheet, headers, datas, emptyRow = 0){
+        let fuc = function () {
+            const rowCount = datas.length + emptyRow;
+            sheet.setRowCount(rowCount);
+            for(let col = 0, cLen = headers.length; col < cLen; col++){
+                for(let row = 0; row < rowCount; row++){
+                    if (datas[row]) {
+                        sheet.setValue(row, col, datas[row][headers[col]['dataCode']] || '');
+                    } else {
+                        sheet.setValue(row, col, '');
+                    }
+                }
+            }
+        };
+        renderSheetFunc(sheet, fuc);
+    }
+
+    // 获取清单材料数据
+    async function getBillMaterials() {
+        if (!bills.tree.selected) {
+            return;
+        }
+        billMaterial.cache = [];
+        try {
+            $.bootstrapLoading.start();
+            billMaterial.cache = await ajaxPost('/billsGuidance/api/getBillMaterials', { libID, billID: bills.tree.selected.data.ID });
+            sortByCode(billMaterial.cache);
+        } catch (error) {
+            $('#alert-info').text(error.message);
+            $('#alert').modal('show');
+        } finally {
+            showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30);
+            $.bootstrapLoading.end();
+        }
+    }
+
+    function getText(sheet, row, col) {
+        let text = sheet.getValue(row, col);
+        text = text ? text.toString().trim() : '';
+        return text;
+    }
+
+    // 编辑材料表
+    async function editMaterials(sheet, cells){
+        let isChanged = false;
+        for(let cell of cells){
+            const itemCode = billMaterial.cache[cell.row] && billMaterial.cache[cell.row].code || '';
+            if (itemCode !== getText(sheet, cell.row, 0)) {
+                isChanged = true;
+            }
+        }
+        if (!isChanged) {
+            return;
+        }
+        let gljCodes = new Set();
+        const count = sheet.getRowCount();
+        for (let row = 0; row < count; row++) {
+            const code = getText(sheet, row, 0);
+            if (code) {
+                gljCodes.add(code);
+            }
+        }
+        // 提交数据
+        try {
+            $.bootstrapLoading.start();
+            billMaterial.cache = await ajaxPost('/billsGuidance/api/editBillMaterials', { libID, billID: bills.tree.selected.data.ID, gljCodes: [...gljCodes], compilationID: curCompilationID }, undefined, true);
+            sortByCode(billMaterial.cache);
+        } catch (error) {
+            $('#alert-info').html(error);
+            $('#alert').modal('show');
+        } finally {
+            showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30);
+            $.bootstrapLoading.end();
+        }
+    }
+
+    // 是否为工序行
+    function isProcessNode(node) {
+        return node && node.depth() % 2 === 0 && _isDef(node.data.type) && node.data.type === itemType.job
+    }
+
     //渲染时方法,停止渲染
     //@param {Object}sheet {Function}func @return {void}
     function renderSheetFunc(sheet, func){
@@ -384,6 +525,12 @@ const billsGuidance = (function () {
             else if(module === guideItem){
                 sheetCommonObj.bindEscKey(module.workBook, [{sheet: sheet, editStarting: null, editEnded: module.events.EditEnded}]);
             }
+            else if (module === billMaterial) {
+                sheet.options.isProtected = true;
+                sheet.getRange(-1, 0, -1, 1).locked(locked);
+                sheet.getRange(-1, 1, -1, -1).locked(true);
+                sheet.getRange(-1, 2, -1, -1).locked(true);
+            }
             setOptions(module.workBook, options);
             buildHeader(module.workBook.getActiveSheet(), module.headers);
             bindEvent(module.workBook, module.events);
@@ -438,7 +585,7 @@ const billsGuidance = (function () {
             getItemsByBills(libID, node.data.ID, function (rstData) {
                 initTree(node.guidance, guideSheet, guideItem.treeSetting, rstData);
                 setNodesExpandState(node.guidance.tree.items, curExpandState);
-                showCheckBox(guideSheet, node.guidance.tree.items);
+                setProcessNodes(guideSheet, node.guidance.tree.items);
                 renderSheetFunc(guideSheet, function () {
                     TREE_SHEET_HELPER.refreshNodesVisible(node.guidance.tree.roots, guideSheet, true);
                 });
@@ -450,14 +597,14 @@ const billsGuidance = (function () {
         } else{
             setNodesExpandState(node.guidance.tree.items, curExpandState);
             node.guidance.controller.showTreeData();
-            showCheckBox(guideSheet, node.guidance.tree.items);
+            setProcessNodes(guideSheet, node.guidance.tree.items);
             //设置底色
             setNodesColor(guideSheet, node.guidance.tree.items);
             //项目指引初始焦点
             guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0);
         }
     }
-    
+
     function showCheckBox(sheet, nodes) {
         // const checkBoxType = locked ? sheetCommonObj.getReadOnlyCheckBox() : sheetCommonObj.getCheckBox();
         const checkBoxType = new GC.Spread.Sheets.CellTypes.CheckBox();
@@ -465,21 +612,46 @@ const billsGuidance = (function () {
         renderSheetFunc(sheet, function () {
             nodes.forEach(node => {
                 const row = node.serialNo();
-                if (node.depth() % 2 === 0 && _isDef(node.data.type) && node.data.type === itemType.job) {
-                    sheet.setCellType(row, 1, checkBoxType);
+                if (isProcessNode(node)) {
                     sheet.setCellType(row, 2, checkBoxType);
-                    sheet.setValue(row, 1, node.data.outputItemCharacter || false);
-                    sheet.setValue(row, 2, node.data.required || false);
+                    sheet.setCellType(row, 3, checkBoxType);
+                    sheet.setCellType(row, 4, checkBoxType);
+                    sheet.setValue(row, 2, node.data.outputItemCharacter || false);
+                    sheet.setValue(row, 3, node.data.required || false);
+                    sheet.setValue(row, 4, node.data.isMaterial || false);
                 } else {
-                    sheet.setCellType(row, 1, baseType);
                     sheet.setCellType(row, 2, baseType);
-                    sheet.setValue(row, 1, '');
+                    sheet.setCellType(row, 3, baseType);
+                    sheet.setCellType(row, 4, baseType);
                     sheet.setValue(row, 2, '');
+                    sheet.setValue(row, 3, '');
+                    sheet.setValue(row, 4, '');
                 }
             })
         });
     }
 
+    function setReadOnly(sheet, nodes) {
+        if (locked) {
+            return;
+        }
+        // 单位,可输入单位文本。仅特征/工序行可输入
+        const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit');
+        renderSheetFunc(sheet, function () {
+            nodes.forEach(node => {
+                const row = node.serialNo();
+                sheet.getCell(row, unitCol).locked(!isProcessNode(node));
+            });
+        });
+
+    }
+
+    // 设置选项行相关
+    function setProcessNodes(sheet, nodes) {
+        showCheckBox(sheet, nodes);
+        setReadOnly(sheet, nodes);
+    }
+
     //设置项目节点展开收起状态:展开全部、收起定额
     //@param {Array}nodes(当前清单下的所有项目指引节点) {Number}expandState(展开全部1或收起定额0).
     function setNodesExpandState(nodes, expandState) {
@@ -524,8 +696,7 @@ const billsGuidance = (function () {
     function setNodesColor(sheet, nodes) {
         renderSheetFunc(sheet, function () {
             for(let node of nodes){
-                const nDepth = node.depth();
-                const color = nDepth % 2 == 0 && _isDef(node.data.type) && node.data.type === itemType.job ? selectedBgColor : 'White';
+                const color = isProcessNode(node) ? selectedBgColor : 'White';
                 setBgColor(sheet, node.serialNo(), color);
             }
         });
@@ -620,7 +791,7 @@ const billsGuidance = (function () {
             $('#insertAll').removeClass('disabled');
         }
         //备注,奇数节点可用
-        if(node && (node.depth() + 1) % 2 === 1 && node.data.type !== itemType.ration){
+        if(isProcessNode(node)){
             $('.main-bottom-content').find('textarea').attr('readonly', false);
         }
     }
@@ -983,6 +1154,7 @@ const billsGuidance = (function () {
         CommonAjax.post('/billsGuidance/api/getLibWithBills', {libID: libID}, function (rstData) {
             billsLibId = rstData.guidanceLib.billsLibId;
             initRationLibs(rstData.guidanceLib.compilationId);
+            curCompilationID = rstData.guidanceLib.compilationId;
             bills.cache = rstData.bills;
             initLibName(rstData.guidanceLib.name);
             /*initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, bills.cache);
@@ -1067,16 +1239,16 @@ const billsGuidance = (function () {
         for(let cell of cells){
             const field = guideItem.headers[cell.col].dataCode;
             let node = bills.tree.selected.guidance.tree.items[cell.row];
-            if (field === 'name') {
+            if (field === 'name' || field === 'unit') {
                 let text = sheet.getValue(cell.row, cell.col);
                 text = text ? text.toString() : '';
                 text = text.replace(deESC, '');
                 sheet.setValue(cell.row, cell.col, text);
-                if(node.data.name != text){
+                if(node.data[field] != text){
                     syncDatas.push({node: node, text: text, field, cell});
-                    updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {name: text}});
+                    updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {[field]: text}});
                 }
-            } else if (field === 'outputItemCharacter' || field === 'required') {
+            } else if (field === 'outputItemCharacter' || field === 'required' || field === 'isMaterial') {
                 const val = !sheet.getValue(cell.row, cell.col);
                 sheet.setValue(cell.row, cell.col, val);
                 syncDatas.push({node: node, text: val, field, cell });
@@ -1144,9 +1316,9 @@ const billsGuidance = (function () {
                     const row = newNode.serialNo();
                     sheet.setValue(row, 0, newNode.data.name);
                     if (newNode.data.outputItemCharacter !== undefined) {
-                        sheet.setValue(row, 1, newNode.data.outputItemCharacter);
+                        sheet.setValue(row, 2, newNode.data.outputItemCharacter);
                     }
-                    showCheckBox(sheet, [newNode]);
+                    setProcessNodes(sheet, [newNode]);
                     refreshBtn(newNode);
                 }
             }
@@ -1245,7 +1417,7 @@ const billsGuidance = (function () {
             guideItemInitSel(sheet.getActiveRowIndex());
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             if (bills.tree.selected.data.hasGuide && !bills.tree.selected.guidance.tree.items.length) {
                 bills.tree.selected.data.hasGuide = false;
                 setBillsForeColor([bills.tree.selected]);
@@ -1265,7 +1437,7 @@ const billsGuidance = (function () {
         updateDatas.push({updateType: updateType.update, findData: {ID: selected.getParentID()}, updateData: {NextSiblingID: selected.getID()}});
         //更新选中节点
         updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()},
-            updateData: {ParentID: selected.parent.getParentID(), NextSiblingID: selected.parent.getNextSiblingID()}});
+            updateData: {ParentID: selected.parent.getParentID(), NextSiblingID: selected.parent.getNextSiblingID(), unit: ''}});
         if(selected.nextSibling && selected.children.length > 0){
             //更新选中节点最末子节点
             let lastChild = selected.children[selected.children.length - 1];
@@ -1283,9 +1455,12 @@ const billsGuidance = (function () {
         }
         updateGuideItems(updateDatas, function () {
             controller.upLevel();
+            const sheet = guideItem.workBook.getActiveSheet();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
-            setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setNodesColor(sheet, bills.tree.selected.guidance.tree.items);
+            setProcessNodes(sheet, bills.tree.selected.guidance.tree.items);
+            const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit');
+            sheet.setValue(selected.serialNo(), unitCol, '');
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)//31574
         });
@@ -1305,12 +1480,15 @@ const billsGuidance = (function () {
             updateDatas.push({updateType: updateType.update, findData: {ID: lastChild.getID()}, updateData: {NextSiblingID: selected.getID()}});
         }
         //更新选中节点
-        updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {ParentID: selected.preSibling.getID(), NextSiblingID: -1}});
+        updateDatas.push({updateType: updateType.update, findData: {ID: selected.getID()}, updateData: {ParentID: selected.preSibling.getID(), NextSiblingID: -1, unit: ''}});
         updateGuideItems(updateDatas, function () {
             controller.downLevel();
+            const sheet = guideItem.workBook.getActiveSheet();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
-            setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setNodesColor(sheet, bills.tree.selected.guidance.tree.items);
+            setProcessNodes(sheet, bills.tree.selected.guidance.tree.items);
+            const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit');
+            sheet.setValue(selected.serialNo(), unitCol, '');
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1334,7 +1512,7 @@ const billsGuidance = (function () {
             controller.upMove();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1358,7 +1536,7 @@ const billsGuidance = (function () {
             controller.downMove();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1572,7 +1750,7 @@ const billsGuidance = (function () {
             cleanData(guideItem.workBook.getActiveSheet(), guideItem.headers, -1);
             itemObj.controller.showTreeData();
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
-            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
         }, function () {
             $.bootstrapLoading.end();
         });
@@ -1752,7 +1930,7 @@ const billsGuidance = (function () {
         });
     }
 
-    // 初始化定额右键菜单
+    // 初始化清单右键菜单
     function initRationContextMenu() {
         $.contextMenu({
             selector: '#rationSpread',
@@ -1803,6 +1981,53 @@ const billsGuidance = (function () {
         });
     }
 
+    // 初始化定额右键菜单
+    function initBillsContextMenu() {
+        $.contextMenu({
+            selector: '#billsSpread',
+            build: function($triggerElement, e){
+                //控制允许右键菜单在哪个位置出现
+                let sheet = bills.workBook.getActiveSheet();;
+                let offset = $("#billsSpread").offset(),
+                    x = e.pageX - offset.left,
+                    y = e.pageY - offset.top;
+                let target = sheet.hitTest(x, y);
+                if(target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'){//在表格内
+                    let sel = sheet.getSelections()[0];
+                    if(sel && sel.rowCount === 1){
+                        sheet.setActiveCell(target.row, target.col);
+                    }
+                    sel = sheet.getSelections()[0];
+                    if(sel){
+                        sel.row =  sel.row === -1 ? 0 : sel.row;
+                    }
+                    //右键在多选内则不重设焦点
+                    if(!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)){
+                        sheet.setActiveCell(target.row, target.col);
+                    }
+                    billsInitSel(target.row, { row: bills.tree.selected.serialNo() });
+                    return {
+                        callback: function(){},
+                        items: {
+                            "replace": {
+                                name: "配置材料",
+                                disabled: function () {
+                                    return !bills.tree.selected;
+                                },
+                                icon: "fa-edit",
+                                callback: function (key, opt) {
+                                    $('#bill-material-modal').modal('show');
+                                }},
+                        }
+                    };
+                }
+                else{
+                    return false;
+                }
+            }
+        });
+    }
+
     //展开至搜索出来点的节点
     //@param {Array}nodes @return {void}
     function expandSearchNodes(sheet, nodes, roots){
@@ -1933,9 +2158,21 @@ const billsGuidance = (function () {
 
     }
 
-    //初始化个按钮点击
+    //初始化dom时间
     //@return {void}
-    function initBtn(){
+    function initDomEvents(){
+        // 清单材料窗口
+        $("#bill-material-modal").on('hidden.bs.modal', function () {
+            billMaterial.cache = [];
+            showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30);
+        });
+        $("#bill-material-modal").on('shown.bs.modal', function () {
+            if (billMaterial.workBook) {
+                billMaterial.workBook.refresh();
+                getBillMaterials();
+            }
+        });
+
         $('#insert').click(function () {
             insert([{type: itemType.job, name: '', outputItemCharacter: true }], false);
         });
@@ -2227,13 +2464,14 @@ const billsGuidance = (function () {
     //初始化视图
     //@param {void} @return {void}
     function initViews(){
-        let modules = [bills, guideItem, section, ration];
+        let modules = [bills, guideItem, section, ration, billMaterial];
         initWorkBooks(modules);
         lockUtil.lockTools($(document.body), locked);
         getLibWithBills(libID);
-        initBtn();
+        initDomEvents();
         initContextMenu();
         initRationContextMenu();
+        initBillsContextMenu();
         initSlideSize();
     }