Selaa lähdekoodia

材料替换库编辑工具

zhangweicheng 6 vuotta sitten
vanhempi
commit
01cae356ad

+ 32 - 2
modules/all_models/material_replace_lib.js

@@ -16,8 +16,38 @@ const material_lib = new Schema({
         billsLibId:Number,
         billsLibName:String,
         deleted: Boolean
-    },
-    {versionKey: false}
+    }, {versionKey: false}
 );
 
 mongoose.model("std_material_replace_lib", material_lib,"std_material_replace_lib");
+
+
+const std_replace_bills = new Schema({
+        ID: {type:String,index:true},
+        libID:{type:String,index:true},
+        code: {type:String,index:true},
+        name: String,
+        rule:Number//规则类型
+    }, {versionKey: false}
+);
+
+mongoose.model('std_replace_bills', std_replace_bills, 'std_replace_bills');
+
+
+const std_replace_material = new Schema({
+    ID: {type:String,index:true},
+    libID:{type:String,index:true},
+    billsItemID:{type:String,index:true},
+    code: String,
+    name: String,
+    specs: String,
+    type: Number,
+    unit: String
+},{versionKey: false});
+
+mongoose.model('std_replace_material', std_replace_material, 'std_replace_material');
+
+
+
+
+

+ 1 - 1
modules/all_models/stdBills_bills.js

@@ -17,7 +17,7 @@ const stdBills_bills = new Schema({
             jobs: [],
             items: [],
             recharge:String,
-            billsLibId: Number,
+            billsLibId: {type:Number,index:true},
             sectionInfo: Schema.Types.Mixed,
             deleted: Boolean
     },

+ 2 - 0
modules/bills_template_lib/facade/bills_template_facade.js

@@ -53,6 +53,8 @@ async function getTemplateDatasByLibID(libID) {
     return await billTemplateItemsModel.find({libID:libID});
 }
 async function deleteLibByID(ID){
+    //删除模板详情:
+    await billTemplateItemsModel.deleteMany({libID:ID});
     return billTemplateLibModel.deleteOne({ID:ID});
 }
 

+ 48 - 0
modules/material_replace_lib/controllers/material_replace_controller.js

@@ -3,6 +3,7 @@
  */
 import BaseController from "../../common/base/base_controller";
 import materialFacade from "../facade/material_replace_facade";
+let config = require("../../../config/config.js");
 
 class ReplaceController extends BaseController{
     /**
@@ -23,6 +24,37 @@ class ReplaceController extends BaseController{
         };
         response.render("maintain/material_replace_lib/html/main", randerData);
     }
+
+    async edit(request,response){
+        //先取出替换库信息:
+        let libID = request.params.libID;
+        let materialLib = await materialFacade.findByCondition({'ID':libID});
+        if(materialLib){
+            let billsLibId = materialLib.billsLibId;
+            let compilationId = materialLib.compilationId;
+            let gljLib = await  materialFacade.findGLJLibByComID(compilationId);
+            let billsList = await materialFacade.findBillsByLibID(libID);
+            //let templateDatas = await materialFacade.getTemplateDatasByLibID(libID);
+
+            let randerData = {
+                title:'材料替换库',
+                mainURL:'/materialReplace/main',
+                libName:materialLib.name,
+                userAccount: request.session.managerData.username,
+                userID: request.session.managerData.userID,
+                billsList:JSON.stringify(billsList),
+                billsLibId:billsLibId,
+                gljLibID:gljLib.ID,
+                libID:libID,
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                layout: 'maintain/common/html/edit_layout'
+            };
+            response.render("maintain/material_replace_lib/html/edit", randerData);
+        }else {
+            response.redirect(request.headers.referer);
+        }
+    }
+
     async findLib(request, response){
         let result={
             error:0
@@ -83,6 +115,22 @@ class ReplaceController extends BaseController{
         }
         response.json(result);
     }
+    async saveBills(request,response){
+        let result={
+            error:0
+        };
+        try {
+            let data = request.body.data;
+            data = JSON.parse(data);
+            let resultData= await materialFacade.saveBills(data);
+            result.data=resultData;
+        }catch (err){
+            console.log(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        response.json(result);
+    }
 }
 
 export default ReplaceController;

+ 82 - 1
modules/material_replace_lib/facade/material_replace_facade.js

@@ -7,7 +7,12 @@ const uuidV1 = require('uuid/v1');
 let moment = require("moment");
 let compilationModel = mongoose.model("compilation");
 let materialLibModel = mongoose.model("std_material_replace_lib");
+let replaceBillModel = mongoose.model("std_replace_bills");
+let replaceMaterialModel = mongoose.model("std_replace_material");
 let StdBillsLib = mongoose.model('std_bills_lib_list');
+let stdBillsModel = mongoose.model('std_bills_lib_bills');
+
+const stdGljLibModel = mongoose.model('std_glj_lib_map');
 
 let materialReplaceLib = {
     findByCondition:async function(conditions,options,single=true){
@@ -46,7 +51,83 @@ let materialReplaceLib = {
         return await materialLibModel.findOneAndUpdate(param.query,param.data,{new:true});
     },
     deleteLibByID:async function(ID){
-        return materialLibModel.deleteOne({ID:ID});
+        //删除材料数据
+        await replaceMaterialModel.deleteMany({libID:ID});
+        //删除清单数据
+        await replaceBillModel.deleteMany({libID:ID});
+        return await materialLibModel.deleteOne({ID:ID});
+    },
+    findGLJLibByComID:async function(compilationId){//跟据费用定额查找工料机库
+        return await stdGljLibModel.findOne({compilationId:compilationId},['ID']);
+    },
+    findBillsByLibID:async function(libID){
+        return await replaceBillModel.find({libID:libID});
+    },
+    saveBills:async function(data){
+        let addList = [],updateList =[],deleteList=[];
+        for(let d of data){
+            if(d.type == 'add') addList.push(d);
+            if(d.type == 'update') updateList.push(d);
+            if(d.type == 'delete') deleteList.push(d);
+        }
+        let p = await Promise.all([addBills(addList),updateBills(updateList)]);
+        return p;
+    }
+};
+
+async function addBills(datas) {
+    let newBills = [],missCodes=[];
+    for(let d of datas){
+        //先查找清单规则库找到对应的清单
+        let stdBill = await stdBillsModel.findOne({billsLibId:d.billsLibId,code:d.code});
+        if(stdBill){
+            let temBill = {
+                libID:d.libID,
+                code:d.code,
+                name:stdBill.name,
+            };
+            temBill.ID = uuidV1();
+            newBills.push(temBill);
+        }else {
+            missCodes.push(d.code);
+        }
+    }
+    if(newBills.length>0){
+        await replaceBillModel.create(newBills);
     }
+    return {type:'add',list:newBills,missCodes:missCodes};
 }
+
+async function updateBills(datas) {
+    let tasks = [],list=[],missCodes = [];
+    for(let d of datas){
+        let updateData = null;
+        let filter = {libID:d.libID, code:d.oldCode};
+        if(d.newCode && d.newCode !=''){//说明是要替换清单
+            let stdBill = await stdBillsModel.findOne({billsLibId:d.billsLibId,code:d.newCode});
+            if(stdBill){
+                 updateData = {code:d.newCode, name:stdBill.name};
+            }else {
+                missCodes.push(d.newCode);
+            }
+        }else {
+            updateData = d.updateData;
+        }
+        if(updateData){
+            let task = {
+                updateOne:{filter:filter, update :updateData}
+            };
+            tasks.push(task);
+            list.push({code:d.oldCode,updateData:updateData})
+        }
+    }
+    if(tasks.length > 0) await replaceBillModel.bulkWrite(tasks);
+    return {type:'update',list:list,missCodes:missCodes};
+}
+
+async function deleteBills(datas) {
+
+}
+
+
 export default materialReplaceLib

+ 3 - 1
modules/material_replace_lib/routes/material_replace_router.js

@@ -10,10 +10,12 @@ let replaceController = new ReplaceController();
 module.exports =function (app){
 
     repRouter.get("/main", replaceController.auth, replaceController.init, replaceController.main);
+    repRouter.get("/edit/:libID", replaceController.auth, replaceController.init, replaceController.edit);
     repRouter.post("/findLib", replaceController.auth, replaceController.init, replaceController.findLib);
-    repRouter.post("/add-lib", replaceController.auth, replaceController.init, replaceController.addLib);
+    repRouter.post("/addLib", replaceController.auth, replaceController.init, replaceController.addLib);
     repRouter.post("/saveLib", replaceController.auth, replaceController.init, replaceController.saveLib);
     repRouter.post("/deleteLibByID", replaceController.auth, replaceController.init, replaceController.deleteLibByID);
+    repRouter.post("/saveBills", replaceController.auth, replaceController.init, replaceController.saveBills);
     app.use("/materialReplace", repRouter);
 };
 

+ 177 - 0
public/scHintBox.html

@@ -0,0 +1,177 @@
+<!----------------------------------------------------------------------------------------------------------------------
+    chenshilong, 2018-04-29
+    自定义对话框,通用,用于替代系统对话框。系统自带的比较简陋,最严重的是一旦被浏览器屏蔽则无法再弹出。
+    自定义对话框包括:
+        1、只有一个按钮的信息提示框。
+        2、有两个按钮的操作确认询问框。
+        3、有三个按钮的多分支选择询问框。
+        4、输入文本值对话框。
+   使用示例:ConstructionCost/test/public/test_Box.html (直接运行该文件查看效果)
+----------------------------------------------------------------------------------------------------------------------->
+<div class="modal fade" id="hintBox_form" data-backdrop="static" style="z-index: 9999">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 id="hintBox_title" class="modal-title">标题</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close" id="hintBox_btn_cross">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+
+            <div class="modal-body">
+                <div id = "hintBox_caption" style="margin:5px 10px 10px 10px;">提示明细</div>
+                <div style="margin:5px 10px 5px 10px;">
+                    <input id="hintBox_value" type="text" class="form-control" value="" />
+                    <p id="hintBox_error" style="margin-top:7px; color:red; display:none;">“xxx”已存在!</p>
+                </div>
+            </div>
+
+            <div class="modal-footer" style="justify-content: center">
+                <button type="button" class="btn btn-primary" data-dismiss="modal" id="hintBox_btn_yes">是</button>
+                <button type="button" class="btn btn-primary" data-dismiss="modal" id="hintBox_btn_no">否</button>
+                <button type="button" class="btn btn-secondary" data-dismiss="modal" id="hintBox_btn_cancel">取消</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal" id="waitBox_form" style="z-index: 9999; cursor: wait"></div>
+
+<script>
+    const hintBox = {
+        value: null,
+        btnType: {yes: 1, yesNo: 2, yesNoCancel: 3},
+        defalultEvent: function () {
+            $.bootstrapLoading.end();
+//            alert('defalultEvent');
+            return;
+        },
+        init: function (){
+            // 事件类
+            $("#hintBox_value").unbind();
+            $("#hintBox_btn_yes").unbind();
+            $('#hintBox_btn_no').unbind();
+            $('#hintBox_btn_cancel').unbind();
+
+            $("#hintBox_value").keyup(
+                function (event) {
+                    hintBox.value = $('#hintBox_value').val();
+                    if (event.keyCode === 13) {
+                        $('#hintBox_btn_yes').click();
+                        return false;
+                    }
+                }
+            );
+            $('#hintBox_btn_cancel').click(
+                hintBox.defalultEvent
+            );
+
+            // 显示类
+            $('#hintBox_caption').hide();
+            $('#hintBox_value').hide();
+            $('#hintBox_error').hide();
+            $('#hintBox_btn_yes').hide();
+            $('#hintBox_btn_no').hide();
+            $('#hintBox_btn_cancel').hide();
+            $('#hintBox_btn_cross').show();
+        },
+        font: function(str){
+            return `<span style='color:red;font-weight:bold;font-size:15px'> ${str} </span>`;
+        },
+        error: function (err) {   // 注意:该方法只能用在valueBox()的doOK回调函数中。
+            $('#hintBox_error').text(err);
+            $('#hintBox_error').show(200);
+            $("#hintBox_value").focus();
+            $("#hintBox_value").select();
+        },
+        infoBox: function (title, caption, btnType,
+                           doYes, doNo = hintBox.defalultEvent,
+                           btnTextArr = null, showCrossBtn = true) {
+            this.init();
+            if (!showCrossBtn)
+                $('#hintBox_btn_cross').hide();
+            $('#hintBox_title').text(title);
+            $('#hintBox_caption').html(caption);
+            $('#hintBox_caption').show();
+
+            switch (btnType) {
+                case this.btnType.yes:
+                    if (btnTextArr){
+                        $('#hintBox_btn_yes').text(btnTextArr[0]);
+                    }else
+                        $('#hintBox_btn_yes').text('确定');
+
+                    $('#hintBox_btn_yes').show();
+                    break;
+                case this.btnType.yesNo:
+                    if (btnTextArr){
+                        $('#hintBox_btn_yes').text(btnTextArr[0]);
+                        $('#hintBox_btn_no').text(btnTextArr[1]);
+                    }else{
+                        $('#hintBox_btn_yes').text('是');
+                        $('#hintBox_btn_no').text('否');
+                    }
+
+                    $('#hintBox_btn_yes').show();
+                    $('#hintBox_btn_no').show();
+                    break;
+                case this.btnType.yesNoCancel:
+                    if (btnTextArr){
+                        $('#hintBox_btn_yes').text(btnTextArr[0]);
+                        $('#hintBox_btn_no').text(btnTextArr[1]);
+                        $('#hintBox_btn_cancel').text(btnTextArr[2]);
+                    }else{
+                        $('#hintBox_btn_yes').text('是');
+                        $('#hintBox_btn_no').text('否');
+                        $('#hintBox_btn_cancel').text('取消');
+                    }
+
+                    $('#hintBox_btn_yes').show();
+                    $('#hintBox_btn_no').show();
+                    $('#hintBox_btn_cancel').show();
+                    break;
+            }
+
+            if (doYes){
+                $('#hintBox_btn_yes').click(doYes);
+            }
+
+            if (doNo){
+                $('#hintBox_btn_no').click(doNo);
+            }
+
+            $("#hintBox_form").modal('show');
+
+        },
+        valueBox: function (title, value, doOK) {
+            this.init();
+            $('#hintBox_title').text(title);
+            this.value = value;
+            $('#hintBox_value').show();
+            $('#hintBox_value').val(value);
+            $("#hintBox_value").focus();
+            $("#hintBox_value").select();
+
+            $('#hintBox_btn_yes').text('确定');
+            $('#hintBox_btn_yes').show();
+            $('#hintBox_btn_yes').click(doOK);   // doOK不能给参数
+
+            $('#hintBox_btn_no').text('取消');
+            $('#hintBox_btn_no').show();
+            $('#hintBox_btn_no').click(hintBox.defalultEvent);
+
+            $("#hintBox_form").modal('show');
+        },
+        waitBox: function () {
+            $(`#waitBox_form`).modal({'backdrop': false});
+        },
+        unWaitBox: function () {
+            $('#waitBox_form').modal('hide');
+        }
+    };
+
+    $('#hintBox_form').on('hide.bs.modal', function() {
+        if($.bootstrapLoading) $.bootstrapLoading.end();
+        return;
+    });
+</script>
+

+ 24 - 0
public/web/commonAlert.js

@@ -0,0 +1,24 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Zhong
+ * @date 2018/6/15
+ * @version
+ */
+
+window.alert = function(str) {
+    /*$('#commonAlert').find('p').text(str);
+     $('#commonAlert').modal('show');*/
+    hintBox.infoBox('系统提示', str, 1);
+};
+
+$(document).ready(function() {
+    !function loadHintBox(){
+        $("body").append('<div id = "hintBox_container"></div>');
+        $("#hintBox_container").load("../../public/scHintBox.html");
+    }();
+
+})
+

+ 77 - 19
public/web/sheet/sheet_common.js

@@ -78,8 +78,9 @@ var sheetCommonObj = {
         var me = this, ch = GC.Spread.Sheets.SheetArea.colHeader;
         for (var i = 0; i < setting.header.length; i++) {
             sheet.setValue(0, i, setting.header[i].headerName, ch);
-            sheet.setColumnWidth(i, setting.header[i].headerWidth?setting.header[i].headerWidth:100);
+            sheet.setColumnWidth(i, setting.header[i].headerWidth ? setting.header[i].headerWidth : 100);
         }
+        if(setting.headerHeight)  sheet.setRowHeight(0, setting.headerHeight, GC.Spread.Sheets.SheetArea.colHeader);
     },
     cleanData: function (sheet, setting, rowCount) {
         sheet.suspendPaint();
@@ -118,13 +119,18 @@ var sheetCommonObj = {
             area.vAlign(GC.Spread.Sheets.VerticalAlign.center);
         }
     },
-    showData: function(sheet, setting, data) {
+    showData: function(sheet, setting, data,distTypeTree) {
         var me = this, ch = GC.Spread.Sheets.SheetArea.viewport;
         sheet.suspendPaint();
         sheet.suspendEvent();
         //sheet.addRows(row, 1);
 
         sheet.clear(0, 0, sheet.getRowCount(), sheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+        if(sheet.getRowCount()<data.length){
+            data.length<30? sheet.setRowCount(30):sheet.setRowCount(data.length);
+        }else if(sheet.getRowCount()==0){
+            sheet.setRowCount(30);
+        }
         for (var col = 0; col < setting.header.length; col++) {
             var hAlign = "left", vAlign = "center";
             if (setting.header[col].hAlign) {
@@ -135,9 +141,6 @@ var sheetCommonObj = {
             vAlign = setting.header[col].vAlign?setting.header[col].vAlign:vAlign;
             me.setAreaAlign(sheet.getRange(-1, col, -1, 1), hAlign, vAlign);
             if (setting.header[col].formatter) {
-                //var style = new GC.Spread.Sheets.Style();
-                //style.formatter = setting.header[col].formatter;
-                //sheet.setStyle(row,col,style,GC.Spread.Sheets.SheetArea.viewport);
                 sheet.setFormatter(-1, col, setting.header[col].formatter, GC.Spread.Sheets.SheetArea.viewport);
             }
             if(setting.header[col].cellType === "checkBox"||setting.header[col].cellType === "button"){//clear and reset
@@ -147,20 +150,16 @@ var sheetCommonObj = {
                 sheet.setValue(0, col, setting.header[col].headerName, header);
                 sheet.setColumnWidth(col, setting.header[col].headerWidth?setting.header[col].headerWidth:100);
             }
-            for (var row = 0; row < data.length; row++) {
-                //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
-                var val = data[row][setting.header[col].dataCode];
-                if(val&&setting.header[col].dataType === "Number"){
-                    if(setting.header[col].hasOwnProperty('tofix')){
-                        val =parseFloat(val).toFixed(setting.header[col].tofix);
-                    }else {
-                        val =parseFloat(val).toFixed(2);
-                    }
-                }
-                if(val!=null&&setting.header[col].cellType === "checkBox"){
-                    this.setCheckBoxCell(row,col,sheet,val)
-                }
-                sheet.setValue(row, col, val, ch);
+            if(setting.header[col].visible === false){
+                sheet.setColumnVisible(col,false);
+            }
+            sheet.getCell(0, col, GC.Spread.Sheets.SheetArea.colHeader).wordWrap(true);
+        }
+        for (var row = 0; row < data.length; row++) {
+            //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
+            this.showRowData(sheet,setting,row,data,distTypeTree);
+            if(setting.getStyle && setting.getStyle(data[row])){
+                sheet.setStyle(row, -1, setting.getStyle(data[row]));
             }
         }
         this.lockCells(sheet,setting);
@@ -168,6 +167,65 @@ var sheetCommonObj = {
         sheet.resumePaint();
         //me.shieldAllCells(sheet);
     },
+    showRowData:function (sheet,setting,row,data,distTypeTree=null) {
+        let ch = GC.Spread.Sheets.SheetArea.viewport;
+        for (var col = 0; col < setting.header.length; col++) {
+            //var cell = sheet.getCell(row, col, GC.Spread.Sheets.SheetArea.viewport);
+            var val = data[row][setting.header[col].dataCode];
+            if(val&&setting.header[col].dataType === "Number"){
+                if(setting.header[col].hasOwnProperty('tofix')){
+                    val =scMathUtil.roundToString(val,setting.header[col].tofix);
+                } else {
+                    val =val+'';
+                }
+            }
+            if(val!=null&&setting.header[col].cellType === "checkBox"){
+                this.setCheckBoxCell(row,col,sheet,val)
+            }
+            if(setting.header[col].cellType === "comboBox"){
+                this.setComboBox(row,col,sheet,setting.header[col].options,setting.header[col].editorValueType);
+            }
+            if(setting.header[col].getText){
+                val = setting.getText[setting.header[col].getText](data[row],val)
+            }
+            sheet.setValue(row, col, val, ch);
+        }
+        this.setRowStyle(row,sheet,data[row].bgColour);
+        if(setting.autoFit==true){
+            sheet.getRange(row, -1, 1, -1, GC.Spread.Sheets.SheetArea.viewport).wordWrap(true);
+            sheet.autoFitRow(row);
+        }
+    },
+    setCheckBoxCell(row,col,sheet,val){
+        var c = new GC.Spread.Sheets.CellTypes.CheckBox();
+        c.isThreeState(false);
+        sheet.setCellType(row, col,c,GC.Spread.Sheets.SheetArea.viewport);
+        sheet.getCell(row, col).value(val);
+        sheet.getCell(row, col).hAlign(GC.Spread.Sheets.HorizontalAlign.center);
+
+    },
+    setComboBox(row,col,sheet,options,editorValueType){
+        //let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        let dynamicCombo = sheetCommonObj.getDynamicCombo(true);
+        if(options){
+            dynamicCombo.itemHeight(options.length).items(options);
+            if(editorValueType==true){
+                dynamicCombo.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.value);
+            }
+        }
+        sheet.setCellType(row, col,dynamicCombo,GC.Spread.Sheets.SheetArea.viewport);
+    },
+    setRowStyle(row,sheet,bgColour) {
+        if(bgColour){
+            let style = new GC.Spread.Sheets.Style();
+            style.backColor = bgColour;
+            style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+            style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+            style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+            style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+            sheet.setStyle(row, -1, style);
+        }
+    },
     analyzePasteData: function(setting, pastedInfo) {
         var rst = [], propId = pastedInfo.cellRange.col, preStrIdx = 0, itemObj = {};
         for (var i = 0; i < pastedInfo.pasteData.text.length; i++) {

+ 1 - 0
public/web/sheet/sheet_data_helper.js

@@ -52,6 +52,7 @@ var SheetDataHelper = {
         spread.options.cutCopyIndicatorVisible = false;
         spread.options.allowCopyPasteExcelStyle = false;
         spread.options.allowUserDragDrop = false;
+        spread.options.allowContextMenu = false;
         spread.getActiveSheet().setRowCount(3);
         return spread;
     },

+ 13 - 62
web/maintain/common/css/main.css

@@ -74,6 +74,7 @@ body {
 }
 .main-side {
     border-right: 1px solid #ccc;
+    border-left: 1px solid #ccc;
     overflow:hidden;
 }
 .main-side .tab-bar {
@@ -270,76 +271,26 @@ body {
     max-height: 200px;
     overflow:auto;
 }
-.main-data-top,.main-data-bottom,.main-data{
+.main-data-top,.main-data-bottom,.main-data,.main-side,.main-data-side-q{
     overflow: hidden;
 }
 .modal-fixed-height {
     height:400px;
     overflow-y:auto;
 }
-.second_header{
-    background: #e1e1e1;
-}
-
-.input-group-addon{
-    padding: 6px 12px;
-    font-size: 14px;
-    font-weight: 400;
-    line-height: 1;
-    color: #555;
-    text-align: center;
-    background-color: #eee;
-    border: 1px solid #ccc;
-    border-radius: 4px;
-}
-
-.input-sm{
-    height: 30px;
-    padding: 5px 10px;
-    font-size: 12px;
-    line-height: 1.5;
-    border-radius: 3px;
-}
-
-.btn-default{
-    color: #333;
-    background-color: #fff;
-    border-color: #ccc;
-}
-.checkbox{
-    position: relative;
-    display: block;
-    margin-top: 10px;
-    margin-bottom: 10px;
+.modal-fixed-height2 {
+    height:368px;
+    overflow-y:auto;
 }
-input[type=checkbox]{
-    position: absolute;
-    margin-top: 5px;
-    margin-left: -20px;
+.btn.disabled, .btn:disabled {
+    cursor: not-allowed;
+    opacity: .65;
+    color:#666
 }
-.col-md-2{
-    position: relative;
-    min-height: 1px;
-    padding-right: 15px;
-    padding-left:15px;
-    width:16.66666667%
-}
-.checkbox label, .radio label {
-    min-height: 20px;
-    padding-left: 20px;
-    margin-bottom: 0;
-    font-weight: 400;
-    cursor: pointer;
-    width: 200px;
+.modal-lgx {
+    max-width: 1022px
 }
 
-.close{
-    float: right;
-    font-size: 21px;
-    font-weight: 700;
-    line-height: 1;
-    color: #000;
-    text-shadow: 0 1px 0 #fff;
-    filter: alpha(opacity=20);
-    opacity: .2;
+.second_header{
+    background: #e1e1e1;
 }

+ 4 - 3
web/maintain/common/html/edit_layout.html

@@ -9,6 +9,7 @@
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
     <link rel="stylesheet" href="/web/maintain/common/css/main.css">
     <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
+    <link rel="stylesheet" href="/lib/jquery-contextmenu/jquery.contextMenu.css" type="text/css">
     <!--spread-->
     <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.sc.css">
 </head>
@@ -24,7 +25,8 @@
 <script src="/lib/lodash/lodash.js"></script>
 <script src="/web/maintain/bills_lib/scripts/set_sheets.js"></script>
 <script src="/web/maintain/bills_lib/scripts/bills_lib_ajax.js"></script>
-
+<script src="/public/web/common_ajax.js"></script>
+<script src="/public/web/commonAlert.js"></script>
 
 
 <body>
@@ -32,7 +34,7 @@
     <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 ">
         <span class="header-logo px-2"><%= title%>编辑器</span>
         <div class="navbar-text">
-            <a href='<%= mainURL%>'>清单模板</a><i class='fa fa-angle-right fa-fw'></i><%= libName%>
+            <a href='<%= mainURL%>'><%= title%></a><i class='fa fa-angle-right fa-fw'></i><%= libName%>
         </div>
     </nav>
 </div>
@@ -42,5 +44,4 @@
 
 </body>
 
-
 </html>

+ 67 - 0
web/maintain/material_replace_lib/html/edit.html

@@ -0,0 +1,67 @@
+<nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0 second_header">
+    <ul class="nav nav-tabs" role="tablist">
+        <li class="nav-item">
+            <a class="nav-link active px-3" href="javascript: void(0);">材料替换</a>
+        </li>
+    </ul>
+</nav>
+
+<div class="main">
+    <div class="content" >
+        <div class="container-fluid" >
+            <div class="row">
+                <div class="tools-bar p-1 d-flex justify-content-between col-lg-12" style="background: #f7f7f7;">
+                    <div class="input-group input-group-sm col-lg-4">
+                        <input class="form-control" placeholder="搜索定位" type="text">
+                        <span class="input-group-btn">
+                              <button class="btn btn-secondary" type="button"><i class="fa fa-search"></i></button>
+                            </span>
+                        <a class="btn btn-sm btn-link"  style="color: #0275d8;cursor:pointer" data-toggle="modal" data-target="#guize"">关于规则</a>
+                    </div>
+                </div>
+                <div class="main-content col-lg-4 p-0">
+                    <div class="main-data" id="billsSpread"></div>
+                </div>
+                    <div class="col-lg-4 main-side p-0" id="materialSpread"></div>
+            </div>
+        </div>
+        <input type="hidden" id="libID" value="<%= libID %>">
+        <input type="hidden" id="gljLibID" value="<%= gljLibID %>">
+        <input type="hidden" id="billsLibId" value="<%= billsLibId %>">
+    </div>
+</div>
+
+<!--弹出关于规则-->
+<div class="modal fade" id="guize" data-backdrop="static">
+    <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">
+                <table class="table table-bordered">
+                    <thead><tr><th width="90">规则名称</th><th>规则解释</th></tr></thead>
+                    <tr><td>规则1</td><td>材料及规格:MU5烧结页岩空心砖</td></tr>
+                    <tr><td>规则2</td><td>1.混凝土种类:商品混凝土<br>2.混凝土强度等级:C30</td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript">
+    //自适应高度
+    $(".main-data").height($(window).height()-$(".header").height()-$(".tools-bar").height()-$(".navbar").height()-16);
+    let billsList = '<%- billsList %>';
+</script>
+<script type="text/javascript" src="/lib/jquery-contextmenu/jquery.contextMenu.js"></script>
+<script type="text/javascript" src="/public/web/sheet/sheet_common.js"></script>
+<script type="text/javascript" src="/public/web/sheet/sheet_data_helper.js"></script>
+
+<script type="text/javascript" src="/web/maintain/material_replace_lib/js/material_replace_edit.js"></script>

+ 2 - 2
web/maintain/material_replace_lib/html/main.html

@@ -9,7 +9,7 @@
                             <tbody id="showArea">
                             <% for(let lib of materialLibs){ %>
                             <tr class="libTr">
-                                <td id="<%= lib.ID%>"><a href="/materialReplace/editMaterial/<%= lib.ID%>"><%= lib.name%></a></td>
+                                <td id="<%= lib.ID%>"><a href="/materialReplace/edit/<%= lib.ID%>"><%= lib.name%></a></td>
                                 <td><%= lib.billsLibName%></td>
                                 <td><%= lib.compilationName%></td>
                                 <td><%= moment(lib.createDate).format('YYYY-MM-DD')%></td>
@@ -39,7 +39,7 @@
                 </button>
             </div>
             <div class="modal-body">
-                <form id="addLibForm" method="post" action="/materialReplace/add-lib" enctype="application/x-www-form-urlencoded21">
+                <form id="addLibForm" method="post" action="/materialReplace/addLib" enctype="application/x-www-form-urlencoded21">
                     <div class="form-group">
                         <label>库名称</label>
                         <input id="name" name="name" class="form-control" placeholder="请输入材料库名称" type="text">

+ 178 - 0
web/maintain/material_replace_lib/js/material_replace_edit.js

@@ -0,0 +1,178 @@
+/**
+ * Created by zhang on 2018/8/23.
+ */
+
+let materialOjb = {
+    billsSpread:null,
+    materialSpread:null,
+    billsList:JSON.parse(billsList),
+    billsSetting:{
+        header: [
+            {headerName: "清单编号", headerWidth: 180, dataCode: "code", dataType: "String",formatter: "@"},
+            {headerName: "清单名称", headerWidth: 240, dataCode: "name", dataType: "String"},
+            {headerName: "规则", headerWidth: 150, dataCode: "rule", hAlign: "left", dataType: "String",cellType:'comboBox',editorValueType:true,options:[{text:"规则1",value:1},{text:"规则2",value:2}]}
+        ],
+        view: {
+            lockColumns: [1]
+        },
+        headerHeight:45
+    },
+    materialSetting:{
+        header: [
+            {headerName: "材料编号", headerWidth: 180, dataCode: "code", dataType: "String"},
+            {headerName: "材料名称", headerWidth: 240, dataCode: "name", dataType: "String",cellType:'tipsCell'},
+            {headerName: "规格", headerWidth: 150, dataCode: "specs", hAlign: "left", dataType: "String",cellType:'tipsCell'}
+        ],
+        view: {
+            lockColumns: [1,2]
+        },
+        headerHeight:45
+    },
+    initSpread:function () {
+        if(!this.billsSpread){
+            this.billsSpread = SheetDataHelper.createNewSpread($("#billsSpread")[0]);
+        }
+        if(!this.materialSpread){
+            this.materialSpread = SheetDataHelper.createNewSpread($("#materialSpread")[0]);
+        }
+        this.billsSheet = this.billsSpread .getSheet(0);
+        sheetCommonObj.initSheet(this.billsSheet,this.billsSetting, 30);
+        this.billsSheet.name('billsSheet');
+        this.billsSheet.bind(GC.Spread.Sheets.Events.ValueChanged, this.onBillsValueChange);
+        this.billsSheet.bind(GC.Spread.Sheets.Events.SelectionChanged,this.onBillsSelectionChange);
+        this.initRightClick("billsSpread",this.billsSpread);
+
+
+        this.materialSheet = this.materialSpread .getSheet(0);
+        sheetCommonObj.initSheet(this.materialSheet,this.materialSetting, 30);
+        this.materialSheet.name('materialSheet');
+
+        this.refreshSheet();
+
+    },
+    initRightClick : function(id,spread) {
+        let me = this;
+        $.contextMenu({
+            selector: '#'+id,
+            build: function ($trigger, e) {
+                me.rightClickTarget = SheetDataHelper.safeRightClickSelection($trigger, e, spread);
+                return me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.viewport ||
+                    me.rightClickTarget.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+            },
+            items: {
+                "delete": {
+                    name: "删除",
+                    icon: 'fa-trash-o',
+                    disabled: function () {
+                        return false;
+                    },
+                    callback: function (key, opt) {
+                       console.log();
+                    }
+                }
+            }
+        });
+    },
+    refreshSheet:function(){
+        sheetCommonObj.showData(this.billsSheet,this.billsSetting,this.billsList);
+        this.billsSheet.setRowCount(this.billsList.length + 1);
+    },
+    onBillsSelectionChange:function (sander,args) {
+        args.sheet.repaint();
+    },
+
+    onBillsValueChange: function(sander,args){
+        let me = materialOjb;
+        let field = me.billsSetting.header[args.col].dataCode;
+        let code = null;
+        if(args.row < me.billsList.length){
+            code = me.billsList[args.row].code;
+        }
+        if(me.validateBills(field,args.newValue)){
+            let data = me.getUpdateData(field,args.newValue,code);
+            if (data){
+                me.saveBills([data]);
+                return;
+            }
+        }
+        me.refreshSheet();
+    },
+
+    validateBills:function (field,value) {
+        if(field == 'code'){
+            if(value.length !== 9){
+                alert("清单长度不正确");
+                return false;
+            }
+            if(_.find(this.billsList,{'code':value})) {
+                alert("清单已存在");
+                return false;
+            }
+        }
+        return true;
+    },
+    getUpdateData:function (field,newValue,code) {
+        if(field == 'code'){
+            if(!isDef(code) || code ==''&&newValue!=null){//说明是新增
+                return {
+                    type:'add',
+                    code:newValue.toString(),
+                    libID:$('#libID').val(),
+                    billsLibId:parseInt($('#billsLibId').val())
+                }
+            }else {//说明是替换
+                return {
+                    type:'update',
+                    oldCode:code.toString(),
+                    newCode:newValue.toString(),
+                    libID:$('#libID').val(),
+                    billsLibId:parseInt($('#billsLibId').val())
+                }
+            }
+        }else if(isDef(code)){
+            let updateData = {};
+            updateData[field] = newValue;
+            return {
+                type:'update',
+                oldCode:code.toString(),
+                libID:$('#libID').val(),
+                updateData:updateData
+            }
+        }
+    },
+    saveBills:async function (datas) {
+        try {
+            let result = await ajaxPost("/materialReplace/saveBills",datas);
+            let missCodes = [];
+            for(let r of result){
+                if(r.missCodes && r.missCodes.length >0) missCodes =missCodes.concat(r.missCodes);
+                if(r.type == 'add'){
+                    this.billsList = this.billsList.concat(r.list);
+                }if(r.type == 'update'){
+                    for(let l of r.list){
+                        this.updateCache(l.code,l.updateData)
+                    }
+                }if(r.type == 'delete'){
+
+                }
+            }
+            if(missCodes.length > 0) alert(`没有找到清单:${missCodes.join("、")}`);
+            this.refreshSheet();
+        }catch (err){
+            console.log(err);
+        }
+    },
+    updateCache:function (code,updateData) {
+        let bill = _.find(this.billsList,{'code':code});
+        for(let key of updateData){
+            bill[key] = updateData[key]
+        }
+    }
+};
+
+function isDef(obj) {
+    return obj!==undefined && obj!==null;
+}
+
+materialOjb.initSpread();
+