瀏覽代碼

Merge branch 'master' of http://smartcost.f3322.net:3000/SmartCost/ConstructionOperation

TonyKang 7 年之前
父節點
當前提交
4f02cc2771
共有 31 個文件被更改,包括 1194 次插入503 次删除
  1. 2 0
      Dockerfile
  2. 15 0
      Dockerfile_qa
  3. 4 14
      config/config.js
  4. 5 2
      modules/common/base/base_controller.js
  5. 64 0
      modules/common/const/bills_fixed.js
  6. 56 0
      modules/common/std/schemas/std_fee_rate_libs.js
  7. 50 0
      modules/common/std/std_fee_rate_libs_model.js
  8. 16 7
      modules/std_glj_lib/models/gljModel.js
  9. 0 53
      modules/templates/controllers/bills_template_controller.js
  10. 0 94
      modules/templates/models/bills_template_model.js
  11. 0 48
      modules/templates/routes/bills_template_router.js
  12. 81 1
      modules/users/controllers/compilation_controller.js
  13. 75 0
      modules/users/models/bills_template_model.js
  14. 28 21
      modules/users/models/compilation_model.js
  15. 10 0
      modules/templates/models/schemas/bills_template.js
  16. 5 0
      modules/users/models/schemas/compilation.js
  17. 2 0
      modules/users/routes/compilation_route.js
  18. 1 1
      operation.js
  19. 5 0
      public/web/sheet/sheet_common.js
  20. 3 0
      public/web/tree_sheet/tree_sheet_helper.js
  21. 17 15
      web/maintain/ration_repository/js/ration_glj.js
  22. 1 1
      web/maintain/std_glj_lib/html/gongliao.html
  23. 159 76
      web/maintain/std_glj_lib/js/glj.js
  24. 98 80
      web/maintain/std_glj_lib/js/gljComponent.js
  25. 5 49
      web/maintain/templates/html/bills.html
  26. 69 37
      web/users/js/compilation.js
  27. 328 0
      web/users/js/template.js
  28. 22 4
      web/users/views/compilation/add.html
  29. 10 0
      web/users/views/compilation/modal.html
  30. 62 0
      web/users/views/compilation/template.html
  31. 1 0
      web/users/views/layout/layout.html

+ 2 - 0
Dockerfile

@@ -10,4 +10,6 @@ RUN cnpm install
 
 EXPOSE 6080
 
+ENV NODE_ENV=prod
+
 ENTRYPOINT babel-node operation.js

+ 15 - 0
Dockerfile_qa

@@ -0,0 +1,15 @@
+FROM server:2.0
+
+COPY . ConstructionOperation
+
+WORKDIR ConstructionOperation
+
+RUN mkdir tmp
+
+RUN cnpm install
+
+EXPOSE 6080
+
+ENV NODE_ENV=qa
+
+ENTRYPOINT babel-node operation.js

+ 4 - 14
config/config.js

@@ -3,20 +3,10 @@ module.exports = {
     local: {server: "localhost", port: "27017"},
     qa: {server: "192.168.1.184", port: "60666"},
     prod: {server: "", port: ""},
-    setToLocalDb: function() {
-        var me = this;
-        me.current.server = me.local.server;
-        me.current.port = me.local.port;
-    },
-    setToQaDb: function() {
-        var me = this;
-        me.current.server = me.qa.server;
-        me.current.port = me.qa.port;
-    },
-    setToProdDb: function() {
-        var me = this;
-        me.current.server = me.prod.server;
-        me.current.port = me.prod.port;
+    setupDb:function (env="local") {
+        let me = this;
+        me.current.server = me[env].server;
+        me.current.port = me[env].port;
     },
     pageSize: 30
 }

+ 5 - 2
modules/common/base/base_controller.js

@@ -25,9 +25,12 @@ class BaseController {
      * @return {void}
      */
     constructor() {
-        if (new.target === BaseController) {
+        /**
+         * 使BaseController可以实例化,可作为中间件使用
+         */
+        /*if (new.target === BaseController) {
             throw new Error('BaseController不能实例化,只能继承使用。');
-        }
+        }*/
     }
 
     /**

+ 64 - 0
modules/common/const/bills_fixed.js

@@ -0,0 +1,64 @@
+/**
+ * Created by Mai on 2017/8/29.
+ */
+
+const fixedFlag = {
+    // 分部分项工程
+    SUB_ENGINERRING: 1,
+    // 措施项目
+    MEASURE: 2,
+    // 施工技术措施项目
+    CONSTRUCTION_TECH: 3,
+    // 安全文明施工按实计算费用
+    SAFETY_CONSTRUCTION_ACTUAL: 4,
+    // 施工组织措施专项费用
+    CONSTRUCTION_ORGANIZATION: 5,
+    // 安全文明施工专项费用
+    SAFETY_CONSTRUCTION: 6,
+    // 其他项目
+    OTHER: 7,
+    // 暂列金额
+    PROVISIONAL: 8,
+    // 暂估价
+    ESTIMATE: 9,
+    // 材料(工程设备)暂估价
+    MATERIAL_PROVISIONAL: 10,
+    // 专业工程暂估价
+    ENGINEERING_ESITIMATE: 11,
+    // 计日工
+    DAYWORK: 12,
+    // 总承包服务费
+    TURN_KEY_CONTRACT: 13,
+    // 索赔与现场签证
+    CLAIM_VISA: 14,
+    // 规费
+    CHARGE: 15,
+    // 社会保险费及住房公积金 Social insurance fee and housing accumulation fund
+    SOCIAL_INSURANCE_HOUSING_FUND: 16,
+    // 工程排污费 charges for disposing pollutants
+    POLLUTANTS: 17,
+    // 税金
+    TAX: 18
+};
+const fixedFlagList = [
+    {name: "分部分项工程", value: fixedFlag.SUB_ENGINERRING},
+    {name: "措施项目", value: fixedFlag.MEASURE},
+    {name: "施工技术措施项目", value: fixedFlag.CONSTRUCTION_TECH},
+    {name: "安全文明施工按实计算费用", value: fixedFlag.SAFETY_CONSTRUCTION_ACTUAL},
+    {name: "施工组织措施专项费用", value: fixedFlag.CONSTRUCTION_ORGANIZATION},
+    {name: "安全文明施工专项费用", value: fixedFlag.SAFETY_CONSTRUCTION},
+    {name: "其他项目", value: fixedFlag.OTHER},
+    {name: "暂列金额", value: fixedFlag.PROVISIONAL},
+    {name: "暂估价", value: fixedFlag.ESTIMATE},
+    {name: "材料(工程设备)暂估价", value: fixedFlag.MATERIAL_PROVISIONAL},
+    {name: "专业工程暂估价", value: fixedFlag.ENGINEERING_ESITIMATE},
+    {name: "计日工", value: fixedFlag.DAYWORK},
+    {name: "总承包服务费", value: fixedFlag.TURN_KEY_CONTRACT},
+    {name: "索赔与现场签证", value: fixedFlag.CLAIM_VISA},
+    {name: "规费", value: fixedFlag.CHARGE},
+    {name: "社会保险费及住房公积金", value: fixedFlag.SOCIAL_INSURANCE_HOUSING_FUND},
+    {name: "工程排污费", value: fixedFlag.POLLUTANTS},
+    {name: "税金", value: fixedFlag.TAX}
+];
+
+export {fixedFlag as default, fixedFlagList as List};

+ 56 - 0
modules/common/std/schemas/std_fee_rate_libs.js

@@ -0,0 +1,56 @@
+/**
+ * 费率标准库数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/8/30
+ * @version
+ */
+import mongoose from "mongoose";
+
+let Schema = mongoose.Schema;
+let collectionName = 'std_fee_rate_libs';
+
+let optionSchema = new Schema({
+    name:String,
+    value:String,
+    selected:Boolean
+}, {_id: false});
+
+let recordSchema = new Schema({
+    ID:Number,
+    name:String,
+    optionList:[optionSchema]
+}, {_id: false});
+
+let valueMapSchema = new Schema({
+    ID:String,
+    value:Number
+}, {_id: false});
+
+let subFeeRatesSchema = new Schema({
+    recodes :[recordSchema],
+    valueMaps:[valueMapSchema],
+}, {_id: false});
+
+let ratesSchema = new Schema({
+    ID: Number,
+    ParentID: Number,
+    name: String,
+    rate: Number,
+    memo: String,
+    subFeeRate:subFeeRatesSchema
+}, {_id: false});
+
+let modelSchema = {
+    // 自增id
+    ID: String,
+    // 工程所在地
+    region: String,
+    // 标准名称
+    libName: String,
+    // 费率数据
+    rates: [ratesSchema]
+};
+
+let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
+export {model as default, collectionName as collectionName};

+ 50 - 0
modules/common/std/std_fee_rate_libs_model.js

@@ -0,0 +1,50 @@
+/**
+ * 费率文件标准库业务逻辑
+ *
+ * @author CaiAoLin
+ * @date 2017/8/30
+ * @version
+ */
+import BaseModel from "../base/base_model";
+import STDFeeRateLibsSchema from "./schemas/std_fee_rate_libs";
+
+class STDFeeRateLibsModel extends BaseModel {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = STDFeeRateLibsSchema;
+        parent.init();
+    }
+
+    /**
+     * 获取费率列表
+     *
+     * @return {Promise}
+     */
+    async getFeeRateList() {
+        let result = [];
+        let field = {ID: 1, libName: 1};
+        let feeRateList = await this.findDataByCondition({ID: {$ne: ''}}, field, false);
+
+        if (feeRateList === null) {
+            return result;
+        }
+        // 整理数据
+        for(let feeRate of feeRateList) {
+            let tmpData = {
+                id: feeRate.ID,
+                name: feeRate.libName
+            };
+            result.push(tmpData);
+        }
+        return result;
+    }
+
+}
+
+export default STDFeeRateLibsModel;

+ 16 - 7
modules/std_glj_lib/models/gljModel.js

@@ -124,17 +124,26 @@ class GljDao  extends OprDao{
     mixUpdateGljItems (repId, lastOpr, updateItems, addItems, rIds, callback) {
         if (updateItems.length == 0 && rIds.length == 0) {
             GljDao.addGljItems(repId, lastOpr, addItems, callback);
-        } else if (rIds.length > 0) {
-            GljDao.removeGljItems(repId, lastOpr, rIds, function(err, message, docs) {
-            });
-            gljModel.remove({ID: {$in: rIds}}, function (err) {
+        }
+        else if(rIds.length > 0 && updateItems.length > 0){
+            async.parallel([
+                function (cb) {
+                    GljDao.removeGljItems(repId, lastOpr, rIds, cb);
+                },
+                function (cb) {
+                    GljDao.updateGljItems(repId, lastOpr, updateItems, cb);
+                }
+            ], function (err) {
                 if(err){
-                    callback(err, '删除错误', null);
+                    callback(true, "Fail to update and delete", false)
                 }
                 else{
-                    callback(null, '删除成功', null);
+                    callback(false, "Save successfully", false);
                 }
-            });
+            })
+        }
+        else if (rIds.length > 0 && updateItems.length === 0) {
+            GljDao.removeGljItems(repId, lastOpr, rIds, callback);
         }
         else if(updateItems.length > 0 || addItems.length > 0){
             GljDao.updateGljItems(repId, lastOpr, updateItems, function(err, results){

+ 0 - 53
modules/templates/controllers/bills_template_controller.js

@@ -1,53 +0,0 @@
-/**
- * 清单模板控制器
- * Created by Mai on 2017/4/17.
- */
-/*
-var BillsTemplateData = require('../models/bills_template_model');
-
-var callback = function(req, res, err, message, data){
-    res.json({error: err, message: message, data: data});
-};
-/!*
-class BillsTemplateController {
-    /!**
-     * 清单模板编辑页面
-     * @param req
-     * @param res
-     *!/
-    async index(req, res) {
-        let id = req.query.id;
-        let
-    }
-};*!/
-
-module.exports = {
-    getBillsTemplate: function(req, res){
-        var data = JSON.parse(req.body.data);
-        BillsTemplateData.getTemplate(data.tempType, function(err, message, templates){
-            if (templates) {
-                callback(req, res, err, message, templates);
-            } else {
-                callback(req, res, err, message, null);
-            }
-        });
-    },
-    updateBillsTemplate: function (req, res) {
-        var data = JSON.parse(req.body.data);
-        BillsTemplateData.updateTemplate(data.user_id, data.tempType, data.updateData, function (err, message, data) {
-            if (err === 0) {
-                callback(req, res, err, message, data);
-            } else {
-                callback(req, res, err, message, null);
-            }
-        });
-    },
-    getNewBillsTemplateID: function (req, res) {
-        var data = JSON.parse(req.body.data);
-        BillsTemplateData.getNewBillsTemplateID(data.count, function (err, message, data) {
-            callback(req, res, err, message, data);
-        });
-    }
-}
-
-export default BillsTemplateController;*/

+ 0 - 94
modules/templates/models/bills_template_model.js

@@ -1,94 +0,0 @@
-/**
- * Created by Mai on 2017/4/14.
- * 清单模板,新建项目使用
- */
-import BaseModel from "../../common/base/base_model";
-import BillsTemplateSchema from "./schemas/bills_template";
-
-class BillsTemplateModel extends BaseModel {
-    /**
-     * 构造函数
-     *
-     * @return {void}
-     */
-    constructor() {
-        let parent = super();
-        parent.model = BillsTemplateSchema;
-        parent.init();
-    }
-
-    async getTemplateData (valuationId) {
-        // 筛选字段
-        let field = {_id: 1, valuationId: 1, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1};
-        let data = await this.findDataByCondition({valuationId: valuationId}, field, false);
-
-        return data === null ? [] : data;
-    }
-};
-
-export default BillsTemplateModel;
-/*
-var BillsTemplateDAO = function(){};
-
-BillsTemplateDAO.prototype.getTemplate = function (type, callback) {
-    if (callback) {
-        BillsTemplates.find({'$or': [{tempType: type, deleteInfo: null}, {tempType: type, 'deleteInfo.deleted': {$in: [null, false]}}]}, '-_id').exec()
-            .then(function (result, err) {
-                if (err) {
-                    callback(1, '找不到模板', null);
-                } else {
-                    callback(0, '', result);
-                }
-            });
-        return null;
-    } else {
-        return BillsTemplates.find({'$or': [{tempType: type, deleteInfo: null}, {tempType: type, 'deleteInfo.deleted': {$in: [null, false]}}]}, '-_id').exec();
-    }
-};
-BillsTemplateDAO.prototype.updateTemplate = function (userID, tempType, datas, callback) {
-    var data, project, updateLength = 0, hasError = false, deleteInfo = null;
-    var updateAll = function (err) {
-        if (!err){
-            updateLength += 1;
-            if (updateLength === datas.length) {
-                callback(0, '', datas);
-            }
-        } else {
-            hasError = true;
-            callback(1, '升级数据出错', null);
-        }
-    };
-    if (datas){
-        for (var i = 0; i < datas.length && !hasError; i++){
-            data = datas[i];
-            if (data.type === 'update') {
-                BillsTemplates.update({tempType: tempType, ID: data.data.ID}, data.data, updateAll)
-            } else if (data.type === 'new') {
-                data.data['tempType'] = tempType;
-                newProject = new BillsTemplates(data.data);
-                newProject.save(updateAll);
-            } else if (data.type === 'delete') {
-                deleteInfo = {};
-                deleteInfo['deleted'] = true;
-                deleteInfo['deleteDateTime'] = new Date();
-                deleteInfo['deleteBy'] = userID;
-                BillsTemplates.update({ID: data.data.ID}, {deleteInfo: deleteInfo}, updateAll);
-            } else {
-                hasError = true;
-                callback(1, '升级数据出错', null)
-            }
-        }
-    }
-};
-BillsTemplateDAO.prototype.getNewBillsTemplateID = function (count, callback) {
-    counter.counterDAO.getIDAfterCount(counter.moduleName.template_bills, count, function (err, result) {
-        var highID = result.value.sequence_value;
-        if (!err) {
-            callback(0, '', {lowID: highID - count + 1, highID: highID});
-        } else {
-            callback(1, '获取主键失败', null);
-        }
-    });
-};
-
-module.exports = new BillsTemplateDAO();*/

+ 0 - 48
modules/templates/routes/bills_template_router.js

@@ -1,48 +0,0 @@
-/**
- * Created by Mai on 2017/4/17.
- */
-
-var express = require('express');
-/*
-var billsTemplateController = require('./../controllers/bills_template_controller');*/
-
-module.exports = function (app) {
-    app.get('/template/bills/:id', function (req, res) {
-        let checkAdmin = function (userAccount) {
-            return true;
-        }
-        if (checkAdmin(req.session.userAccount)) {
-            res.render('maintain/templates/html/bills.html',
-                {userAccount: req.session.userAccount,
-                    userID: req.session.userID});
-        } else {
-            res.redirect('/pm');
-        }
-    });
-
-    app.use('/template/bills/api', function (req, res, next) {
-        let checkAdmin = function (userAccount) {
-            return true;
-        }
-        if (checkAdmin(req.session.sessionUser)) {
-            next();
-        } else {
-            res.json({error: 1, message: '对不起,您无权限操作清单模板。', data: null});
-        }
-    });
-
-/*    var billsTemplateRouter = express.Router();
-
-    billsTemplateRouter.post('/getBillsTemplate', billsTemplateController.getBillsTemplate);
-    billsTemplateRouter.post('/updateBillsTemplate', billsTemplateController.updateBillsTemplate);
-    billsTemplateRouter.post('/getNewBillsTemplateID', billsTemplateController.getNewBillsTemplateID);
-    app.use('/template/bills/api', billsTemplateRouter);*/
-}
-
-
-
-
-
-
-
-

+ 81 - 1
modules/users/controllers/compilation_controller.js

@@ -10,8 +10,10 @@ import CompilationModel from "../models/compilation_model";
 import STDRationLibMapModel from "../../common/std/std_ration_lib_map_model";
 import STDBillLibListsModel from "../../common/std/std_bills_lib_lists_model";
 import STDGLJLibMapModel from "../../common/std/std_glj_lib_map_model";
+import STDFeeRateLibsModel from "../../common/std/std_fee_rate_libs_model";
 import {default as EngineeringConst, List as EngineeringList} from "../../common/const/engineering";
-import BillsTemplateModel from "../../templates/models/bills_template_model";
+import BillsTemplateModel from "../models/bills_template_model";
+import {default as BillsFixedFlagConst, List as BillsFixedFlagList} from "../../common/const/bills_fixed.js";
 
 class CompilationController extends BaseController {
 
@@ -118,6 +120,7 @@ class CompilationController extends BaseController {
         let valuationList = {};
         let gljList = [];
         let billsTemplateData = [];
+        let feeRateList = [];
         try {
             let compilationModel = new CompilationModel();
             compilationList = await compilationModel.getCompilationList();
@@ -134,6 +137,10 @@ class CompilationController extends BaseController {
             let stdGLJLibMapModel = new STDGLJLibMapModel();
             gljList = await stdGLJLibMapModel.getGLJLibList(selectedCompilation._id);
 
+            // 获取费率库
+            let stdFeeRateLibsModel = new STDFeeRateLibsModel();
+            feeRateList = await stdFeeRateLibsModel.getFeeRateList();
+
             // 获取对应的计价规则数据
             [valuationData, valuationList] = await compilationModel.getValuation(selectedCompilation._id, valuationId, section);
             if (Object.keys(valuationData).length <= 0) {
@@ -153,6 +160,7 @@ class CompilationController extends BaseController {
             billList: JSON.stringify(billList),
             rationList: JSON.stringify(rationList),
             gljList: JSON.stringify(gljList),
+            feeRateList: JSON.stringify(feeRateList),
             mainTreeCol: JSON.stringify(valuationData.main_tree_col),
             billsTemplateData: JSON.stringify(billsTemplateData),
             engineeringList: EngineeringList,
@@ -324,6 +332,78 @@ class CompilationController extends BaseController {
         response.json(responseData);
     }
 
+    /**
+     * 模板设置页面
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {void}
+     */
+    async template(request, response) {
+        let billList = {};
+        let valuationList = {};
+        let valuationData = {};
+        let compilationList = [];
+        let billsTemplateData = [];
+
+        let selectedCompilation = request.session.selectedCompilation;
+        let valuationId = request.params.id;
+        let section = request.params.section;
+
+        try {
+            let compilationModel = new CompilationModel;
+            // 获取对应的计价规则数据
+            [valuationData, valuationList] = await compilationModel.getValuation(selectedCompilation._id, valuationId, section);
+            compilationList = await compilationModel.getCompilationList();
+
+            // 获取标准清单
+            let stdBillLibListsModel = new STDBillLibListsModel();
+            billList = await stdBillLibListsModel.getBillList(selectedCompilation._id);
+
+            // 获取清单模板数据
+            let billsTemplateModel = new BillsTemplateModel();
+            billsTemplateData = await billsTemplateModel.getTemplateData(valuationId);
+        } catch (error) {
+            console.log(error);
+        }
+
+        let renderData = {
+            billList: JSON.stringify(billList),
+            billsTemplateData: JSON.stringify(billsTemplateData),
+            billsFixedFlagList: JSON.stringify(BillsFixedFlagList),
+            valuationData: valuationData,
+            valuationList: valuationList,
+            selectedCompilation: selectedCompilation,
+            compilationList: compilationList,
+            valuationId: valuationId,
+            section: section,
+            layout: 'users/views/layout/layout'
+        };
+        response.render('users/views/compilation/template', renderData);
+    }
+
+    /**
+     * 清单模板,更新数据操作
+     *
+     * @param request
+     * @param response
+     */
+    async updateBillsTemplate(request, response) {
+        let valuationId = request.params.id;
+        let section = request.params.section;
+        let data = JSON.parse(request.body.data);
+
+        let billsTemplateModel = new BillsTemplateModel();
+        let result = await billsTemplateModel.updateTemplate(valuationId, data);
+
+        if (result) {
+            response.json({error: 0, message: '', data: data});
+        } else {
+            response.json({error: 1, message: '更新数据错误', data: null});
+        }
+
+    }
+
 }
 
 export default CompilationController;

+ 75 - 0
modules/users/models/bills_template_model.js

@@ -0,0 +1,75 @@
+/**
+ * Created by Mai on 2017/4/14.
+ * 清单模板,新建项目使用
+ */
+import BaseModel from "../../common/base/base_model";
+import BillsTemplateSchema from "./schemas/bills_template";
+
+class BillsTemplateModel extends BaseModel {
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        let parent = super();
+        parent.model = BillsTemplateSchema;
+        parent.init();
+    }
+
+    /**
+     * 获取计价类别对应的清单模板
+     * @param valuationId
+     * @returns {*}
+     */
+    async getTemplateData (valuationId) {
+        // 筛选字段
+        let field = {_id: 1, valuationId: 1, ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let data = await this.findDataByCondition({valuationId: valuationId}, field, false);
+
+        return data === null ? [] : data;
+    }
+
+    /**
+     * 新建项目时,获取计价类别对应的清单模板
+     * @param valuationId
+     * @returns {*}
+     */
+    async getTemplateDataForNewProj (valuationId) {
+        // 筛选字段
+        let field = {ID: 1, ParentID: 1, NextSiblingID: 1, code: 1, name: 1, unit: 1, flags: 1};
+        let data = await this.findDataByCondition({valuationId: valuationId}, field, false);
+
+        return data === null ? [] : data;
+    }
+
+    async updateTemplate (valuationId, datas) {
+        try {
+            for (let data of datas) {
+                data.data.valuationId = valuationId;
+                let condition = {valuationId: valuationId, ID: data.data.ID}, result;
+                if (data.type === 'update') {
+                    result = await this.db.update(condition, data.data);
+                    if (result === undefined || result.ok ===undefined || !result.ok) {
+                        throw '更新数据错误';
+                    }
+                } else if (data.type === 'new') {
+                    result = await this.db.create(data.data);
+                    if (!result) {
+                        throw '新增数据错误';
+                    }
+                } else if (data.type === 'delete') {
+                    result = await this.db.delete(condition);
+                    if (result === undefined || result.ok ===undefined || !result.ok) {
+                        throw '删除数据错误';
+                    }
+                }
+            }
+            return true;
+        } catch (error) {
+            console.log(error);
+        }
+    }
+};
+
+export default BillsTemplateModel;

+ 28 - 21
modules/users/models/compilation_model.js

@@ -73,6 +73,7 @@ class CompilationModel extends BaseModel {
 
         this.setScene('add');
         data.create_time = new Date().getTime();
+        console.log(data);
         result = this.db.create(data);
         return result;
     }
@@ -123,6 +124,7 @@ class CompilationModel extends BaseModel {
         updateData[sectionString + ".$.bill_lib"] = data.bill_lib;
         updateData[sectionString + ".$.ration_lib"] = data.ration_lib;
         updateData[sectionString + ".$.glj_lib"] = data.glj_lib;
+        updateData[sectionString + ".$.fee_lib"] = data.fee_lib;
         updateData[sectionString + ".$.name"] = data.name;
         updateData[sectionString + ".$.engineering"] = data.engineering;
         updateData[sectionString + ".$.main_tree_col"] = JSON.parse(data.main_tree_col);
@@ -178,35 +180,40 @@ class CompilationModel extends BaseModel {
         }
         data.engineering = parseInt(data.engineering);
         // 判断标准清单
-        if (data.bill_lib === undefined || data.bill_lib === '') {
-            throw '判断标准清单不能为空';
-        }
-        data.bill_lib = data.bill_lib instanceof Array ? data.bill_lib : [data.bill_lib];
-        for(let tmp in data.bill_lib) {
-            data.bill_lib[tmp] = JSON.parse(data.bill_lib[tmp]);
-        }
+        data.bill_lib = this._validLib(data.bill_lib);
+
         // 判断定额库
-        if (data.ration_lib === undefined || data.ration_lib === '') {
-            throw '判断标准清单不能为空';
-        }
-        data.ration_lib = data.ration_lib instanceof Array ? data.ration_lib : [data.ration_lib];
-        for(let tmp in data.ration_lib) {
-            data.ration_lib[tmp] = JSON.parse(data.ration_lib[tmp]);
-        }
+        data.ration_lib = this._validLib(data.ration_lib);
 
         // 判断工料机库
-        if (data.glj_lib === undefined || data.glj_lib === '') {
-            throw '判断工料机库不能为空';
-        }
-        data.glj_lib = data.glj_lib instanceof Array ? data.glj_lib : [data.glj_lib];
-        for(let tmp in data.glj_lib) {
-            data.glj_lib[tmp] = JSON.parse(data.glj_lib[tmp]);
-        }
+        data.glj_lib = this._validLib(data.glj_lib);
+
+        // 判断费率库
+        data.fee_lib = this._validLib(data.fee_lib);
 
         return data;
     }
 
     /**
+     * 校验库数据
+     *
+     * @param {Object} libData
+     * @return {Object}
+     */
+    _validLib(libData) {
+        let result = [];
+        // 判断费率库
+        if (libData === undefined || libData === '') {
+            throw '判断费率库不能为空';
+        }
+        libData = libData instanceof Array ? libData : [libData];
+        for(let tmp in libData) {
+            result[tmp] = JSON.parse(libData[tmp]);
+        }
+        return result;
+    }
+
+    /**
      * 获取计价规则数据
      *
      * @param {String} compilationId

+ 10 - 0
modules/templates/models/schemas/bills_template.js

@@ -6,6 +6,11 @@ let Schema = mongoose.Schema;
 
 let collectionName = 'temp_bills';
 
+// 标记字段
+let flagsSchema = new Schema({
+    fieldName: String,
+    flag: Number
+});
 let BillsTemplateSchema = {
     // 树结构所需ID
     ID: Number,
@@ -17,6 +22,11 @@ let BillsTemplateSchema = {
     name: String,
     // 单位
     unit: String,
+    // 标记
+    flags:{
+        type: [flagsSchema],
+        default: []
+    },
     // 所属计价ID
     valuationId: String
 };

+ 5 - 0
modules/users/models/schemas/compilation.js

@@ -51,6 +51,11 @@ let childrenSchema = new Schema({
             "headRowHeight":[],
             "cols":[]
         }
+    },
+    // 费率标准库
+    fee_lib: {
+        type: Schema.Types.Mixed,
+        default: []
     }
 });
 let modelSchema = {

+ 2 - 0
modules/users/routes/compilation_route.js

@@ -16,12 +16,14 @@ module.exports = function (app) {
     router.get('/', compilationController.auth, compilationController.init, compilationController.index);
     router.get('/valuation/:section/:id', compilationController.auth, compilationController.init, compilationController.editValuation);
     router.get('/valuation/:section/delete/:id', compilationController.auth, compilationController.init, compilationController.deleteValuation);
+    router.get('/template/:section/:id', compilationController.auth, compilationController.init, compilationController.template);
 
     router.post('/release', compilationController.auth, compilationController.init, compilationController.release);
     router.post('/add', compilationController.auth, compilationController.init, compilationController.addCompilation);
     router.post('/add-valuation', compilationController.auth, compilationController.init, compilationController.addValuation);
     router.post('/save-valuation', compilationController.auth, compilationController.init, compilationController.saveValuation);
     router.post('/valuation/:section/enable', compilationController.auth, compilationController.init, compilationController.enableSwitch);
+    router.post('/template/:section/:id/update', compilationController.auth, compilationController.init, compilationController.updateBillsTemplate)
 
     app.use("/compilation", router);
 };

+ 1 - 1
operation.js

@@ -1,7 +1,7 @@
 let express = require('express');
 
 let config = require("./config/config.js");
-config.setToQaDb();
+config.setupDb(process.env.NODE_ENV);
 let dbm = require("./config/db/db_manager");
 
 let path = require('path');

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

@@ -136,6 +136,9 @@ var sheetCommonObj = {
                 else {
                     sheet.setValue(row, col, data[row][setting.header[col].dataCode], ch);
                     sheet.setTag(row, 0, data[row].ID, ch);
+                    if(typeof setting.owner === 'undefined'){
+                        sheet.getCell(row, 0, GC.Spread.Sheets.SheetArea.viewport).locked(true);
+                    }
                 }
             }
             for(let i = data.length; i < sheet.getRowCount(); i++){
@@ -203,6 +206,7 @@ var sheetCommonObj = {
                                     me.unLockAllCells(sheet);
                                     me.reLockSomeCodes(sheet, 0, repositoryGljObj.currentCache.length);
                                     sheet.setValue(row, 0, orgCode);
+                                    sheet.getCell(row, 0).formatter("@");
                                     sheet.setActiveCell(row, 0);
                                 });
                                 $('#codAleClose').click(function () {
@@ -210,6 +214,7 @@ var sheetCommonObj = {
                                     me.unLockAllCells(sheet);
                                     me.reLockSomeCodes(sheet, 0, repositoryGljObj.currentCache.length);
                                     sheet.setValue(row, 0, orgCode);
+                                    sheet.getCell(row, 0).formatter("@");
                                     sheet.setActiveCell(row, 0);
                                 });
                                 // sheet.setValue(row, col, orgCode);

+ 3 - 0
public/web/tree_sheet/tree_sheet_helper.js

@@ -109,6 +109,9 @@ var TREE_SHEET_HELPER = {
                 } else {
                     cell.value(getFieldText2());
                 }
+                if (colSetting.data.cellType) {
+                    cell.cellType(colSetting.data.cellType);
+                }
             });
             if (recursive) {
                 TREE_SHEET_HELPER.refreshTreeNodeData(setting, sheet, node.children, recursive);

+ 17 - 15
web/maintain/ration_repository/js/ration_glj.js

@@ -190,7 +190,8 @@ var rationGLJOprObj = {
                         let maxCount = info.cellRange.row + info.cellRange.rowCount -1 > me.cache["_GLJ_" + me.currentRationItem.ID].length -1 ?
                         me.cache["_GLJ_" + me.currentRationItem.ID].length - info.cellRange.row : info.cellRange.rowCount;
                         for(let i = 0; i < maxCount; i++){
-                            me.cache["_GLJ_" + me.currentRationItem.ID][info.cellRange.row + i].consumeAmt = tempConsumes[i].consumeAmt;
+                            let roundCons = me.round(tempConsumes[i].consumeAmt, 3);
+                            me.cache["_GLJ_" + me.currentRationItem.ID][info.cellRange.row + i].consumeAmt = roundCons;
                         }
                         me.updateRationItem();
                         if(info.cellRange.row + info.cellRange.rowCount -1 >= me.cache["_GLJ_" + me.currentRationItem.ID].length -1){
@@ -235,7 +236,8 @@ var rationGLJOprObj = {
                     }
                     else{
                         args.sheet.setValue(args.row, args.col, parseNum);
-                        editGlj["consumeAmt"] = parseNum;
+                        let roundNum = me.round(parseNum, 3);
+                        editGlj["consumeAmt"] = roundNum;
                         me.updateRationItem();
                     }
                 }
@@ -349,26 +351,25 @@ var rationGLJOprObj = {
             }
         })
     },
-
+    round(v, e){
+        var t=1;
+        for(;e>0;t*=10,e--);
+        for(;e<0;t/=10,e++);
+        return Math.round(v*t)/t;
+    },
     rationCal: function () {
-        let me = this;
+        let me = rationGLJOprObj;
         let price = {gljType1: [], gljType2: [], gljType3: []}, rst = {labourPrice: 0, materialPrice: 0, machinePrice: 0}, rationBasePrc = 0;
-        function round(v,e){
-            var t=1;
-            for(;e>0;t*=10,e--);
-            for(;e<0;t/=10,e++);
-            return Math.round(v*t)/t;
-        }
         if(me.currentRationItem && me.cache['_GLJ_' + me.currentRationItem.ID]){
             let cacheArr = me.cache['_GLJ_' + me.currentRationItem.ID];
             cacheArr.forEach(function (gljData) {
                 if(gljData.gljType && gljData.basePrice && gljData.consumeAmt){
                     let parent = me.distTypeTree.distTypes[me.distTypeTree.prefix + gljData.gljType].parent;
                     if(parent && parent.data.ID <= 3){
-                        price['gljType' + parent.data.ID].push(round( gljData.basePrice * gljData.consumeAmt, 3));//取三位
+                        price['gljType' + parent.data.ID].push(me.round( gljData.basePrice * gljData.consumeAmt, 3));//取三位
                     }
                     if(!parent && gljData.gljType <= 3){
-                        price['gljType' + gljData.gljType].push(round( gljData.basePrice * gljData.consumeAmt, 3));//取三位
+                        price['gljType' + gljData.gljType].push(me.round( gljData.basePrice * gljData.consumeAmt, 3));//取三位
                     }
                 }
             });
@@ -377,7 +378,7 @@ var rationGLJOprObj = {
                 price.gljType1.forEach(function (singlePrc) {
                     labourPrice += singlePrc;
                 });
-                let roundPrice = round(labourPrice, 2);
+                let roundPrice = me.round(labourPrice, 2);
                 rst.labourPrice = roundPrice;
                 rationBasePrc += roundPrice;
             }
@@ -386,7 +387,7 @@ var rationGLJOprObj = {
                 price.gljType2.forEach(function (singlePrc) {
                     materialPrice += singlePrc;
                 });
-                let roundPrice = round(materialPrice, 2);
+                let roundPrice = me.round(materialPrice, 2);
                 rst.materialPrice = roundPrice;
                 rationBasePrc += roundPrice;
             }
@@ -395,7 +396,7 @@ var rationGLJOprObj = {
                 price.gljType3.forEach(function (singlePrc) {
                     machinePrice += singlePrc;
                 });
-                let roundPrice = round(machinePrice, 2);
+                let roundPrice = me.round(machinePrice, 2);
                 rst.machinePrice = roundPrice;
                 rationBasePrc += roundPrice;
             }
@@ -460,6 +461,7 @@ var rationGLJOprObj = {
                 cache:false,
                 timeout:5000,
                 success:function(result){
+                    console.log(result);
                     sheetCommonObj.cleanSheet(me.sheet, me.setting, -1);
                     if (result) {
                         var cacheArr = [];

+ 1 - 1
web/maintain/std_glj_lib/html/gongliao.html

@@ -213,7 +213,7 @@
     <script type="text/javascript" src="/public/web/storageUtil.js"></script>
     <SCRIPT type="text/javascript">
         let userAccount = '<%=userAccount %>';
-        var gljSetting = {
+        let gljSetting = {
             view: {
                 //addHoverDom: gljTypeTreeOprObj.addHoverDom,
                 //removeHoverDom: gljTypeTreeOprObj.removeHoverDom,

+ 159 - 76
web/maintain/std_glj_lib/js/glj.js

@@ -31,6 +31,7 @@ let repositoryGljObj = {
     parentNodeIds: {},
     gljList: [],
     allowComponent: [202, 203, 204, 301],//可带组成物类型:混凝土、砂浆、配合比、机械台班
+    componentGljType: [201, 302, 303],//可成为组成物的工料机类型: 普通材料、 机械组成物、 机上人工
     distTypeTree: null,//add
     setting: {
 
@@ -231,13 +232,51 @@ let repositoryGljObj = {
         });
         return rst;
     },
+    //获得引用了组成物id为componentId的工料机,和重新变化组成物数组、重新计算单价
+    getUpdateGljs: function (rObj, isDelete) {
+        let me = repositoryGljObj, that = gljComponentOprObj,
+            rst = {updateArr: [], updateBasePrcArr: []};
+        //改变单价,以便reCalGljBasePrc方法可行
+        if(!isDelete){
+            for(let i = 0; i < me.gljList.length; i++){
+                if(me.gljList[i].ID === rObj.ID){
+                    me.gljList[i].basePrice = rObj.basePrice;
+                    break;
+                }
+            }
+        }
+        for(let i = 0; i < me.gljList.length; i++){
+            let thisComponent = me.gljList[i].component, isChange = false;
+            for(let j = 0; j < thisComponent.length; j++){
+                if(thisComponent[j].ID === rObj.ID){
+                    //删除
+                    isChange = true;
+                    if(isDelete){
+                        thisComponent.splice(j--, 1);
+                    }
+                    else {
+                        break;
+                    }
+                }
+            }
+            if(isChange){//引用了此组成物
+                let gljBasePrc = that.reCalGljBasePrc(me.getCurrentComponent(thisComponent));
+                if(me.gljList[i].basePrice !== gljBasePrc){
+                    me.gljList[i].basePrice = gljBasePrc;
+                    rst.updateBasePrcArr.push({gljId: me.gljList[i].ID, gljType: me.gljList[i].gljType, basePrice: me.gljList[i].basePrice});
+                }
+                rst.updateArr.push(me.gljList[i]);
+            }
+        }
+        return rst;
+    },
     reshowGljBasePrc: function (glj) {
         let me = repositoryGljObj;
-
-        for(let i = 0; i < me.gljList.length; i++){
-            if(glj.ID === me.gljList[i].ID){
-                me.gljList[i].basePrice = glj.basePrice;
-                me.workBook.getSheet(0).setValue(me.activeRow, 4, glj.basePrice);
+        let cacheSection = me.currentCache;
+        for(let i = 0; i < cacheSection.length; i++){
+            if(glj.ID === cacheSection[i].ID){
+                cacheSection[i].basePrice = glj.basePrice;
+                me.workBook.getSheet(0).setValue(i, 4, glj.basePrice);
                 break;
             }
         }
@@ -247,7 +286,6 @@ let repositoryGljObj = {
         //混凝土202、砂浆203、配合比204、机械3
         if(info.oldSelections.length === 0 && info.newSelections.length > 0 || info.oldSelections[0].row !== info.newSelections[0].row){
             let row = info.newSelections[0].row;
-            me.activeRow = row;
             sheetCommonObj.lockCells(that.workBook.getSheet(0), that.setting);
             that.workBook.getSheet(0).getRange(-1, 0 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
             that.workBook.getSheet(0).getRange(-1, 4 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);
@@ -339,9 +377,12 @@ let repositoryGljObj = {
                             let field = me.setting.header[col].dataCode;
                             if(field === 'gljType'){
                                 me.workBook.getSheet(0).getCell(me.editingRowIdx, col).value(
-                                    me.distTypeTree.distTypes[me.distTypeTree.prefix + me.currentEditingGlj[field]].data.fullName);
+                                    typeof me.currentEditingGlj[field] !== 'undefined'?
+                                        me.distTypeTree.distTypes[me.distTypeTree.prefix + me.currentEditingGlj[field]].data.fullName
+                                        : '');
                             }
                             else{
+                                me.workBook.getSheet(0).getCell(me.editingRowIdx, 0).formatter("@");
                                 me.workBook.getSheet(0).getCell(me.editingRowIdx, col).value(me.currentEditingGlj[me.setting.header[col].dataCode]);
                             }
                         }
@@ -368,31 +409,65 @@ let repositoryGljObj = {
         }
     },
     onCellEditEnd: function(sender, args) {
-        let me = repositoryGljObj, rObj = sheetCommonObj.combineRowData(me.workBook.getSheet(0), me.setting, args.row, me),
+        let me = repositoryGljObj, that = gljComponentOprObj,
+            rObj = sheetCommonObj.combineRowData(me.workBook.getSheet(0), me.setting, args.row, me),
             updateArr = [], addArr = [], updateBasePrcArr = [];
         me.editingRowIdx = args.row;
         rObj.basePrice = rObj.basePrice ? rObj.basePrice : 0;
+        //更新
         if (me.currentEditingGlj["ID"]) {
             rObj["ID"] = me.currentEditingGlj["ID"];
             rObj.gljClass = me.currentEditingGlj.gljClass;
-            for(let col =0; col< me.setting.header.length; col++){
-                if(me.currentEditingGlj[me.setting.header[col].dataCode] !== rObj[me.setting.header[col].dataCode]){
-                    me.addGljObj = rObj;
-                    if(rObj[me.setting.header[0].dataCode] && rObj[me.setting.header[1].dataCode] && rObj[me.setting.header[5].dataCode]){
-                        if(rObj.gljType !== me.currentEditingGlj.gljType){//修改了工料机类型,组成物清空
+                if(me.currentEditingGlj[me.setting.header[args.col].dataCode] !== rObj[me.setting.header[args.col].dataCode]){
+                    if(rObj[me.setting.header[0].dataCode] && rObj[me.setting.header[1].dataCode] && rObj[me.setting.header[5].dataCode] &&
+                        rObj[me.setting.header[0].dataCode].toString().trim().length !== 0 && rObj[me.setting.header[1].dataCode].toString().trim().length !== 0 && rObj[me.setting.header[5].dataCode].toString().trim().length !== 0){
+                        if(rObj.gljType !== me.currentEditingGlj.gljType){//修改了工料机类型
                             if(me.currentGlj){
                                 me.currentGlj.component = [];
                             }
                             if(me.allowComponent.indexOf(rObj.gljType) !== -1){
                                 rObj.basePrice = 0;
                             }
+                            if(me.componentGljType.indexOf(me.currentEditingGlj.gljType) !== -1 &&
+                                !(me.currentEditingGlj.gljType === 302 && rObj.gljType === 303) && !(me.currentEditingGlj.gljType === 303 && rObj.gljType === 302)){//修改了原本是组成物的工料机
+                               //寻找所有引用了此组成物的工料机,并从组成物中删去此工料机,并重算单价
+                                let updateGljs = me.getUpdateGljs(rObj);
+                                if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
+                                    for(let i = 0; i < updateGljs.updateArr.length; i++){
+                                        updateArr.push(updateGljs.updateArr[i]);
+                                    }
+                                    for(let i = 0; i < updateGljs.updateBasePrcArr.length; i++){
+                                        updateArr.push(updateGljs.updateBasePrcArr[i]);
+                                    }
+                                }
+                            }
+                        }
+                        else if(rObj.basePrice !== me.currentEditingGlj.basePrice){//修改了单价,可修改单价的必为可成为组成物的
+                            //寻找所有引用了此组成物的工料机,并从组成物中删去此工料机,并重算单价
+                            let updateGljs = me.getUpdateGljs(rObj);
+                            if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
+                                for(let i = 0; i < updateGljs.updateArr.length; i++){
+                                    updateArr.push(updateGljs.updateArr[i]);
+                                }
+                                for(let i = 0; i < updateGljs.updateBasePrcArr.length; i++){
+                                    updateArr.push(updateGljs.updateBasePrcArr[i]);
+                                }
+                            }
+                            rObj.basePrice = !isNaN(parseFloat(rObj.basePrice)) && (rObj.basePrice && typeof rObj.basePrice !== 'undefined') ? that.round(parseFloat(rObj.basePrice), 2) : 0;
                         }
                         rObj.component = me.currentGlj.component;
                         updateArr.push(rObj);
-                        break;
+                    }
+                    else{
+                        if(me.setting.header[args.col].dataCode === 'gljType'){
+                            let distTypeVal =  me.distTypeTree.distTypes[me.distTypeTree.prefix + me.currentEditingGlj[me.setting.header[args.col].dataCode]].data.fullName;
+                            args.sheet.setValue(args.row, args.col, distTypeVal);
+                        }
+                        else{
+                            args.sheet.setValue(args.row, args.col, me.currentEditingGlj[me.setting.header[args.col].dataCode]);
+                        }
                     }
                 }
-            }
             //--------------------------------------
             if(me.currentEditingGlj.basePrice !== rObj.basePrice){
                 //update basePrice of ration when editting basePrice of glj
@@ -422,20 +497,25 @@ let repositoryGljObj = {
                 }
             }
             //-----------------------------------------------------------
-        } else {
-            me.addGljObj = rObj;
-            let isCanSav = true;
-            if(!rObj[me.setting.header[0].dataCode] || !rObj[me.setting.header[1].dataCode] || !rObj[me.setting.header[5].dataCode]){
-                isCanSav = false;
-            }
-            if(isCanSav){
-                me.addGljObj = null;
-                rObj.component = [];
-                //如果类型为混凝土、砂浆、配合比、机械台班时,添加时填写的单价清空
-                if(me.allowComponent.indexOf(rObj.gljType) !== -1){
-                    rObj.basePrice = 0;
+        }
+        //新增
+        else {
+            if(typeof rObj.code !== 'undefined'){
+                me.addGljObj = rObj;
+                let isCanSav = true;
+                if(!rObj[me.setting.header[0].dataCode] || !rObj[me.setting.header[1].dataCode] || !rObj[me.setting.header[5].dataCode]){
+                    isCanSav = false;
+                }
+                if(isCanSav){
+                    me.addGljObj = null;
+                    rObj.component = [];
+                    //如果类型为混凝土、砂浆、配合比、机械台班时,添加时填写的单价清空
+                    if(me.allowComponent.indexOf(rObj.gljType) !== -1){
+                        rObj.basePrice = 0;
+                    }
+                    rObj.basePrice = !isNaN(parseFloat(rObj.basePrice)) && (rObj.basePrice && typeof rObj.basePrice !== 'undefined') ? parseFloat(rObj.basePrice) : 0;
+                    addArr.push(rObj);
                 }
-                addArr.push(rObj);
             }
         }
         if(me.gljCurTypeId !== 732){
@@ -463,6 +543,17 @@ let repositoryGljObj = {
                     if(sels[i].colCount === me.setting.header.length){
                         for(let j = 0; j < sels[i].rowCount; j++){
                             if(sels[i].row + j < cacheSection.length){
+                                //删除了已被引用成组成物的工料机,重新计算所有引用此组成物的工料机的单价、组成物数组
+                                let updateGljs = me.getUpdateGljs(cacheSection[sels[i].row + j], true);
+                                if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
+                                    for(let i = 0; i < updateGljs.updateArr.length; i++){
+                                        updateArr.push(updateGljs.updateArr[i]);
+                                    }
+                                    for(let i = 0; i < updateGljs.updateBasePrcArr.length; i++){
+                                        updateBasePrcArr.push(updateGljs.updateBasePrcArr[i]);
+                                    }
+                                }
+                                console.log(updateArr);
                                 removeArr.push(cacheSection[sels[i].row + j].ID);
                                 //tempRemoveArr.push({ID: cacheSection[sels[i].row + j].ID, code: cacheSection[sels[i].row + j].code});
                                 //删除后重新计算引用了此工料机的定额单价
@@ -518,45 +609,6 @@ let repositoryGljObj = {
                         }
                     }
                 }
-                //提取已被引用工料机
-               /* if(tempRemoveArr.length > 0){
-                    for(let i = 0; i < tempRemoveArr.length; i++){
-                        if(me.rationGljIds.indexOf(tempRemoveArr[i].ID) !== -1){
-                            refGljCodes.push(tempRemoveArr[i].code);
-                            tempRemoveArr.splice(i--, 1);
-                        }
-                        else{
-                            removeArr.push(tempRemoveArr[i].ID);
-                        }
-                    }
-                }*/
-                //提示
-              /*  if(refGljCodes.length > 0){
-                    let alertText;
-                    if(refGljCodes.length > 3){
-                        alertText = "编号: " + refGljCodes[0]+" 、" + refGljCodes[1] + " 、" + refGljCodes[2] + "...等工料机已有定额引用,删除失败!";
-                    }
-                    else {
-                        let alertCode = " ";
-                        for(let i=0; i< refGljCodes.length; i++){
-                            alertCode += refGljCodes[i] + " 、";
-                        }
-                        alertText = "编号:" + alertCode + "工料机已有定额引用,删除失败!"
-                    }
-                    $('#alertText').text(alertText);
-                    $('#codeAlertBtn').click();
-                    sheet.options.isProtected = true;
-                    $('#codAleConfBtn').click(function () {
-                        sheetCommonObj.lockSomeCodes(sheet, 0, cacheSection.length);
-                        if(removeArr.length > 0){
-                            me.mixUpdateRequest(updateArr, [], removeArr);
-                        }
-                    });
-                    $('#codAleClose').click(function () {
-                        sheetCommonObj.lockSomeCodes(sheet, 0, cacheSection.length);
-                        me.mixUpdateRequest(updateArr, [], removeArr);
-                    });
-                }*/
                 if(removeArr.length > 0 || updateArr.length > 0){
                     //删除警告
                     $('#alertGljTxt').text('可能已有定额引用了当前工料机,导致定额查找不到此工料机。确定要删除吗?');
@@ -577,8 +629,9 @@ let repositoryGljObj = {
         me.workBook.commandManager().setShortcutKey('repositoryGljDel', GC.Spread.Commands.Key.del, false, false, false, false);
     },
     validUpdateObj: function (pasteObj, rowIdx) {
-        let rst = {}, backUpObj = {},
+        let rst = {updateGlj: [], updateBasePrcArr: []}, backUpObj = {},
             me = repositoryGljObj,
+            that = gljComponentOprObj,
             tempObj = me.currentCache[rowIdx],
             reCalBasePrc = false,
             isValid = true;
@@ -618,25 +671,53 @@ let repositoryGljObj = {
                 if(pasteObj.gljType === me.distTypeTree.comboDatas[i].text){
                     isExsit = true;
                     reCalBasePrc = true;
+                    //
+                    if(me.componentGljType.indexOf(tempObj.gljType) !== -1 &&
+                        !(tempObj.gljType === 302 && pasteObj.gljType === 303) && !(tempObj.gljType === 303 && pasteObj.gljType === 302)){//修改了原本是组成物的工料机
+                        //寻找所有引用了此组成物的工料机,并从组成物中删去此工料机,并重算单价
+                        let updateGljs = me.getUpdateGljs(tempObj, true);
+                        if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
+                            for(let i = 0; i < updateGljs.updateArr.length; i++){
+                                rst.updateGlj.push(updateGljs.updateArr[i]);
+                            }
+                            for(let i = 0; i < updateGljs.updateBasePrcArr.length; i++){
+                                rst.updateBasePrcArr.push(updateGljs.updateBasePrcArr[i]);
+                            }
+                        }
+                    }
                     tempObj.gljType = me.distTypeTree.comboDatas[i].value;
                     tempObj.shortName = me.distTypeTree.distTypes[me.distTypeTree.prefix + tempObj.gljType].data.shortName;
-
+                    if(me.allowComponent.indexOf(tempObj.gljType) !== -1){
+                        tempObj.basePrice = 0;
+                    }
+                    break;
                 }
             }
             if(!isExsit) isValid = false;
         }
         //
-        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? parseFloat(pasteObj.basePrice) :
+        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? that.round(parseFloat(pasteObj.basePrice), 2) :
             me.currentCache[rowIdx].basePrice;
         if(pasteObj.basePrice !== me.currentCache[rowIdx].basePrice){
             reCalBasePrc = true;
             tempObj.basePrice = pasteObj.basePrice;
+            let updateGljs = me.getUpdateGljs(tempObj, false);
+            if(updateGljs.updateArr.length > 0 || updateGljs.updateBasePrcArr.length > 0){
+                for(let i = 0; i < updateGljs.updateArr.length; i++){
+                    rst.updateGlj.push(updateGljs.updateArr[i]);
+                }
+                for(let i = 0; i < updateGljs.updateBasePrcArr.length; i++){
+                    rst.updateBasePrcArr.push(updateGljs.updateBasePrcArr[i]);
+                }
+            }
         }
         if(isValid){
-            rst.updateGlj = tempObj;
+            rst.updateGlj.push(tempObj);
             if(reCalBasePrc){
                 //重新计算定额基价对象
-                rst.updateBasePrc = {gljId: tempObj.ID, gljType: tempObj.gljType, basePrice: tempObj.basePrice};
+                //rst.updateBasePrc = {gljId: tempObj.ID, gljType: tempObj.gljType, basePrice: tempObj.basePrice};
+                let newReObj = {gljId: tempObj.ID, gljType: tempObj.gljType, basePrice: tempObj.basePrice};
+                rst.updateBasePrcArr.push(newReObj);
             }
         }
         else {
@@ -679,6 +760,7 @@ let repositoryGljObj = {
         return true;
     },
     onClipboardPasting: function(sender, args) {
+        console.log(`oncp`);
         let me = repositoryGljObj;
         /*if (args.cellRange.colCount != me.setting.header.length || me.gljCurTypeId < 0 || me.parentNodeIds["_pNodeId_" + me.gljCurTypeId]) {
          args.cancel = true;
@@ -705,9 +787,9 @@ let repositoryGljObj = {
                 for(let i = 0; i < items.length; i++){
                     let updateObj = me.validUpdateObj(items[i], info.cellRange.row + i);
                     if(updateObj && typeof updateObj.updateGlj !== 'undefined'){
-                        updateArr.push(updateObj.updateGlj);
+                        updateArr = updateObj.updateGlj;
                         if(typeof updateObj.updateBasePrc !== 'undefined'){
-                            updateBasePrcArr.push(updateObj.updateBasePrc);
+                            updateBasePrcArr = updateObj.updateBasePrc;
                         }
                     }
                     else{
@@ -720,9 +802,9 @@ let repositoryGljObj = {
                 for(let i = 0; i < updateCount; i++){
                     let updateObj = me.validUpdateObj(items[i], info.cellRange.row + i);
                     if(updateObj && typeof updateObj.updateGlj !== 'undefined'){
-                        updateArr.push(updateObj.updateGlj);
+                        updateArr = updateObj.updateGlj;
                         if(typeof updateObj.updateBasePrc !== 'undefined'){
-                            updateBasePrcArr.push(updateObj.updateBasePrc);
+                            updateBasePrcArr = updateObj.updateBasePrc;
                         }
                     }
                     else{
@@ -938,6 +1020,7 @@ let gljTypeTreeOprObj = {
             that = gljComponentOprObj,
             gljTypeId = treeNode.ID;
         me.gljCurTypeId = treeNode.ID;
+        me.addGljObj = null;
         //me.currentCache = me.getCache();
         sheetCommonObj.cleanSheet(that.workBook.getSheet(0), that.setting, 5);
         that.workBook.getSheet(0).getRange(-1, 0 , -1, 1, GC.Spread.Sheets.SheetArea.viewport).locked(true);

+ 98 - 80
web/maintain/std_glj_lib/js/gljComponent.js

@@ -143,105 +143,121 @@ let gljComponentOprObj = {
     onCellEditEnd: function (sender, args) {
         let me = gljComponentOprObj, that = repositoryGljObj, updateBasePrc = [];
         let gljList = that.gljList, updateArr = [], materialComponent = [202, 203, 204], machineComponent = [302, 303];
-        if(args.editingText !== me.currentEditingComponent.code){
-            if(args.col === 0 && args.editingText && args.editingText.trim().length > 0){
-                let component = that.currentGlj.component, hasCode = false;
-                for(let i = 0; i < gljList.length; i++){
-                    if(gljList[i].code === args.editingText){//有效的组成物
-                        hasCode = true;
-                        if((materialComponent.indexOf(that.currentGlj.gljType) !== -1 && gljList[i].gljType === 201)
-                            || (that.currentGlj.gljType === 301 && machineComponent.indexOf(gljList[i].gljType) !== -1 )){//普通材料
-                            //是否与原有组成物不同
-                            let isExist = false;
-                            for(let j = 0; j < component.length; j++){
-                                if(component[j].ID === gljList[i].ID){
-                                    isExist = true;
-                                    break;
-                                }
+        //if(args.editingText !== me.currentEditingComponent.code){
+        if(args.col === 0 && args.editingText && args.editingText.trim().length > 0 && args.editingText !== me.currentEditingComponent.code){
+            let component = that.currentGlj.component, hasCode = false;
+            for(let i = 0; i < gljList.length; i++){
+                if(gljList[i].code === args.editingText){//有效的组成物
+                    hasCode = true;
+                    if((materialComponent.indexOf(that.currentGlj.gljType) !== -1 && gljList[i].gljType === 201)
+                        || (that.currentGlj.gljType === 301 && machineComponent.indexOf(gljList[i].gljType) !== -1 )){//普通材料
+                        //是否与原有组成物不同
+                        let isExist = false;
+                        for(let j = 0; j < component.length; j++){
+                            if(component[j].ID === gljList[i].ID){
+                                isExist = true;
+                                break;
                             }
-                            if(!isExist){
-                                let rObj = {};
-                                rObj.ID = gljList[i].ID;
-                                //rObj.basePrice = gljList[i].basePrice;
-                                if(typeof that.currentComponent[args.row] !== 'undefined'){
-                                    rObj.consumeAmt = that.currentComponent[args.row].consumeAmt;
-                                    let index;
-                                    for(let j = 0; j < component.length; j++){
-                                        if(component[j].ID === that.currentComponent[args.row].ID){
-                                            index = j;
-                                            break;
-                                        }
+                        }
+                        if(!isExist){
+                            let rObj = {};
+                            rObj.ID = gljList[i].ID;
+                            //rObj.basePrice = gljList[i].basePrice;
+                            if(typeof that.currentComponent[args.row] !== 'undefined'){
+                                rObj.consumeAmt = that.currentComponent[args.row].consumeAmt;
+                                let index;
+                                for(let j = 0; j < component.length; j++){
+                                    if(component[j].ID === that.currentComponent[args.row].ID){
+                                        index = j;
+                                        break;
                                     }
-                                    component.splice(index, 1);
-                                    component.splice(index, 0, rObj);
-                                    updateArr.push(that.currentGlj);
                                 }
-                                else{
-                                    rObj.consumeAmt = 0;
-                                    component.push(rObj);
-                                    updateArr.push(that.currentGlj);
+                                component.splice(index, 1);
+                                component.splice(index, 0, rObj);
+                                //计算工料机单价
+                                let gljBasePrc = me.reCalGljBasePrc(that.getCurrentComponent(component));
+                                if(gljBasePrc !== that.currentGlj.basePrice){
+                                    that.currentGlj.basePrice = gljBasePrc;
+                                    that.reshowGljBasePrc(that.currentGlj);
+                                    //工料机单价改变,重算引用了该工料机的定额单价
+                                    updateBasePrc.push({gljId: that.currentGlj.ID, gljType: that.currentGlj.gljType, basePrice: that.currentGlj.basePrice});
                                 }
-                                break;
+                                updateArr.push(that.currentGlj);
                             }
                             else{
-                                //已存在
-                                alert("已存在!");
-                                args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
-                                    me.currentEditingComponent[me.setting.header[args.col].dataCode]: '');
+                                rObj.consumeAmt = 0;
+                                component.push(rObj);
+                                //计算工料机单价
+                                let gljBasePrc = me.reCalGljBasePrc(that.getCurrentComponent(component));
+                                if(gljBasePrc !== that.currentGlj.basePrice){
+                                    that.currentGlj.basePrice = gljBasePrc;
+                                    that.reshowGljBasePrc(that.currentGlj);
+                                    //工料机单价改变,重算引用了该工料机的定额单价
+                                    updateBasePrc.push({gljId: that.currentGlj.ID, gljType: that.currentGlj.gljType, basePrice: that.currentGlj.basePrice});
+                                }
+                                updateArr.push(that.currentGlj);
                             }
-
+                            break;
                         }
                         else{
-                            if(materialComponent.indexOf(that.currentGlj.gljType) === 1){
-                                alert("该组成物只能是普通材料!");
-                            }
-                            else if(that.currentGlj.gljType === 3){
-                                alert("该组成物只能是机械组成物或机上人工!")
-                            }
+                            //已存在
+                            alert("已存在!");
                             args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
                                 me.currentEditingComponent[me.setting.header[args.col].dataCode]: '');
-                            //无效
                         }
+
                     }
-                }
-                if(!hasCode){
-                    alert("不存在此工料机,请先添加!");
-                    args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
-                        me.currentEditingComponent[me.setting.header[args.col].dataCode] : '');
-                    //不存在
-                }
-            }
-            else if(args.col === 4 && me.currentEditingComponent.code && args.editingText && args.editingText.trim().length > 0){//消耗量
-                let consumeAmt = parseFloat(args.editingText);
-                if(!isNaN(consumeAmt) && consumeAmt !== me.currentEditingComponent.consumeAmt){
-                    let component = that.currentGlj.component;
-                    for(let i = 0; i < component.length; i++){
-                        if(component[i].ID === that.currentComponent[args.row].ID){
-                            component[i].consumeAmt = consumeAmt
+                    else{
+                        if(materialComponent.indexOf(that.currentGlj.gljType) === 1){
+                            alert("该组成物只能是普通材料!");
+                        }
+                        else if(that.currentGlj.gljType === 301){
+                            alert("该组成物只能是机械组成物或机上人工!")
                         }
+                        args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
+                            me.currentEditingComponent[me.setting.header[args.col].dataCode]: '');
+                        //无效
                     }
-                    that.currentComponent[args.row].consumeAmt = consumeAmt;
-                    //计算工料机单价
-                    let gljBasePrc = me.reCalGljBasePrc(that.currentComponent);
-                    if(gljBasePrc !== that.currentGlj.basePrice){
-                        that.currentGlj.basePrice = gljBasePrc;
-                        that.reshowGljBasePrc(that.currentGlj);
-                        //工料机单价改变,重算引用了该工料机的定额单价
-                        updateBasePrc.push({gljId: that.currentGlj.ID, gljType: that.currentGlj.gljType, basePrice: that.currentGlj.basePrice});
+                }
+            }
+            if(!hasCode){
+                alert("不存在此工料机,请先添加!");
+                args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
+                    me.currentEditingComponent[me.setting.header[args.col].dataCode] : '');
+                //不存在
+            }
+        }
+        else if(args.col === 4 && me.currentEditingComponent.code && args.editingText && args.editingText.trim().length > 0){//消耗量
+            let consumeAmt = parseFloat(args.editingText);
+            if(!isNaN(consumeAmt) && consumeAmt !== me.currentEditingComponent.consumeAmt){
+                let roundCons = me.round(consumeAmt, 3);
+                let component = that.currentGlj.component;
+                for(let i = 0; i < component.length; i++){
+                    if(component[i].ID === that.currentComponent[args.row].ID){
+                        component[i].consumeAmt = roundCons;
                     }
-                    updateArr.push(that.currentGlj);
                 }
-                else{
-                    //只能输入数值
-                    args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
-                        me.currentEditingComponent[me.setting.header[args.col].dataCode]: 0);
-
+                that.currentComponent[args.row].consumeAmt = roundCons;
+                //计算工料机单价
+                let gljBasePrc = me.reCalGljBasePrc(that.currentComponent);
+                if(gljBasePrc !== that.currentGlj.basePrice){
+                    that.currentGlj.basePrice = gljBasePrc;
+                    that.reshowGljBasePrc(that.currentGlj);
+                    //工料机单价改变,重算引用了该工料机的定额单价
+                    updateBasePrc.push({gljId: that.currentGlj.ID, gljType: that.currentGlj.gljType, basePrice: that.currentGlj.basePrice});
                 }
+                updateArr.push(that.currentGlj);
             }
             else{
-                args.sheet.setValue(args.row, args.col, '');
+                //只能输入数值
+                args.sheet.setValue(args.row, args.col, me.currentEditingComponent[me.setting.header[args.col].dataCode] ?
+                    me.currentEditingComponent[me.setting.header[args.col].dataCode]: 0);
+
             }
         }
+        else{
+            args.sheet.setValue(args.row, args.col, '');
+        }
         if(updateArr.length > 0){
             me.updateComponent(updateArr);
             if(updateBasePrc.length > 0){
@@ -317,7 +333,7 @@ let gljComponentOprObj = {
             }
             if(isChange){
                 //计算工料机单价
-                let gljBasePrc = me.reCalGljBasePrc(that.currentComponent);
+                let gljBasePrc = me.reCalGljBasePrc(that.getCurrentComponent(component));
                 if(gljBasePrc !== that.currentGlj.basePrice){
                     that.currentGlj.basePrice = gljBasePrc;
                     that.reshowGljBasePrc(that.currentGlj);
@@ -333,8 +349,9 @@ let gljComponentOprObj = {
                 if(row + i < that.currentComponent.length){
                     let currentObj = that.currentComponent[row + i];
                     if(items[i].consumeAmt.trim().length > 0 && items[i].consumeAmt !== currentObj.consumeAmt){
+                        let roundCons = me.round(items[i].consumeAmt, 3);
                         isChange = true;
-                        currentObj.consumeAmt = items[i].consumeAmt;
+                        currentObj.consumeAmt = roundCons;
                         for(let j = 0; j < component.length; j++){
                             if(component[j].ID === currentObj.ID){
                                 component[j].consumeAmt = currentObj.consumeAmt;
@@ -395,7 +412,8 @@ let gljComponentOprObj = {
     reCalGljBasePrc: function (component) {
         let me = gljComponentOprObj, gljBasePrc = 0;
         for(let i = 0; i < component.length; i++){
-            gljBasePrc += me.round(component[i].basePrice * component[i].consumeAmt, 2);
+            let roundBasePrc = me.round(component[i].basePrice, 2);
+            gljBasePrc += me.round(roundBasePrc * component[i].consumeAmt, 2);
         }
         return gljBasePrc;
     }

+ 5 - 49
web/maintain/templates/html/bills.html

@@ -1,41 +1,3 @@
-<!DOCTYPE html>
-<html>
-<head lang="en">
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-    <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title>模板清单-Smartcost</title>
-    <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
-    <link rel="stylesheet" href="/web/maintain/templates/css/main.css">
-    <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
-    <!--SpreadJs-->
-    <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css" type="text/css">
-    <script>
-        // 这里的变量供页面调用
-        var userAccount = '<%- userAccount %>';
-        var userID = '<%- userID %>';
-    </script>
-</head>
-<body>
-    <div class="header">
-        <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 justify-content-between">
-            <span class="header-logo px-2">Smartcost</span>
-            <div class="float-lg-right navbar-text pt-0">
-                <div class="dropdown d-inline-block">
-                    <button class="btn btn-link btn-sm dropdown-toggle" type="button" data-toggle="dropdown"><%- userAccount %></button>
-                    <div class="dropdown-menu dropdown-menu-right">
-                        <a class="dropdown-item" href="/user/info" target="_blank">账号资料</a>
-                        <a class="dropdown-item" href="/user/buy" target="_blank">产品购买</a>
-                        <a class="dropdown-item" href="/user/set" target="_blank">偏好设置</a>
-                    </div>
-                </div>
-                    <span class="btn btn-link btn-sm new-msg">
-                        <i class="fa fa-envelope-o" aria-hidden="true"></i>&nbsp;2
-                    </span>
-                    <a class="btn btn-link btn-sm" href="/logout">注销</a>
-            </div>
-        </nav>
-    </div>
 <div class="main">
     <div class="content">
         <div class="toolsbar px-1 d-flex justify-content-between">
@@ -82,15 +44,10 @@
         </div>
     </div>
 </div>
-</body>
-<!-- JS. -->
-<script src="/lib/jquery/jquery.min.js"></script>
-<script src="/lib/tether/tether.min.js"></script>
-<script src="/lib/bootstrap/bootstrap.min.js"></script>
-<script src="/web/maintain/templates/js/global.js"></script>
-<!-- SpreadJs -->
-<script type="text/javascript" src="/lib/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js"></script>
-<script>GC.Spread.Sheets.LicenseKey = "559432293813965#A0y3iTOzEDOzkjMyMDN9UTNiojIklkI1pjIEJCLi4TPB9mM5AFNTd4cvZ7SaJUVy3CWKtWYXx4VVhjMpp7dYNGdx2ia9sEVlZGOTh7NRlTUwkWR9wEV4gmbjBDZ4ElR8N7cGdHVvEWVBtCOwIGW0ZmeYVWVr3mI0IyUiwCMzETN8kzNzYTM0IicfJye&Qf35VfiEzRwEkI0IyQiwiIwEjL6ByUKBCZhVmcwNlI0IiTis7W0ICZyBlIsIyNyMzM5ADI5ADNwcTMwIjI0ICdyNkIsIibj9SbvNmL4N7bjRnch56ciojIz5GRiwiI8+Y9sWY9QmZ0Jyp96uL9v6L0wap9biY9qiq95q197Wr9g+89iojIh94Wiqi";</script>
+<script>
+    let valuationId = '<%- valuationId%>';
+    let billsTemplateData = '<%- billsTemplateData %>'
+</script>
 <!-- Models -->
 <script type="text/javascript" src="/public/web/id_tree.js"></script>
 <!-- Controller -->
@@ -102,5 +59,4 @@
 <script type="text/javascript" src="/public/web/common_ajax.js"></script>
 <script>
     autoFlashHeight();
-</script>
-</html>
+</script>

+ 69 - 37
web/users/js/compilation.js

@@ -24,15 +24,14 @@ $(document).ready(function() {
     // 新增编办
     $("#add-compilation").click(function() {
         try {
-            let [name, standardBill, rationLib, gljLib, standardBillString, rationLibString, gljLibString] = getAndValidData(model);
-
+            let data = getAndValidData(model);
             let url = '/compilation/add';
             if (model === 'all') {
                 // 新增编办操作
                 $.ajax({
                     url: url,
                     type: 'post',
-                    data: {name: name},
+                    data: {name: data.name},
                     error: function() {
                         isAdding = false;
                     },
@@ -52,22 +51,27 @@ $(document).ready(function() {
             } else {
                 // 新增标准清单/定额库
                 let addLib = {
-                    name: '',
-                    id: ''
+                    name: data[model].name,
+                    id: data[model].id
                 };
-                switch (model) {
-                    case 'bill':
-                        addLib.name = standardBillString;
-                        addLib.id = standardBill;
-                        break;
-                    case 'ration':
-                        addLib.name = rationLibString;
-                        addLib.id = rationLib;
-                        break;
-                    case 'glj':
-                        addLib.name = gljLibString;
-                        addLib.id = gljLib;
-                }
+                // switch (model) {
+                //     case 'bill':
+                //         addLib.name = standardBillString;
+                //         addLib.id = standardBill;
+                //         break;
+                //     case 'ration':
+                //         addLib.name = rationLibString;
+                //         addLib.id = rationLib;
+                //         break;
+                //     case 'glj':
+                //         addLib.name = gljLibString;
+                //         addLib.id = gljLib;
+                //         break;
+                //     case 'fee':
+                //         addLib.name = gljLibString;
+                //         addLib.id = gljLib;
+                //         break;
+                // }
                 // 判断是否有重复的数据
                 if ($("input:hidden[name='"+ model +"_lib'][data-id='"+ addLib.id +"']").length > 0) {
                     alert('重复添加数据!');
@@ -127,34 +131,27 @@ $(document).ready(function() {
     // 添加
     $(".add-compilation").click(function() {
         model = $(this).data('model');
+        $("#addcompilation .modal-body > div").hide();
         switch (model) {
             case 'all':
                 $("#name-area").show();
-                $("#bill-area").hide();
-                $("#ration-area").hide();
-                $("#glj-area").hide();
                 $("#add-compilation-title").text('添加新编办');
                 break;
             case 'bill':
-                $("#name-area").hide();
                 $("#bill-area").show();
-                $("#ration-area").hide();
-                $("#glj-area").hide();
                 $("#add-compilation-title").text('添加标准清单');
                 break;
             case 'ration':
-                $("#name-area").hide();
-                $("#bill-area").hide();
                 $("#ration-area").show();
-                $("#glj-area").hide();
                 $("#add-compilation-title").text('添加定额库');
                 break;
             case 'glj':
-                $("#name-area").hide();
-                $("#bill-area").hide();
-                $("#ration-area").hide();
                 $("#glj-area").show();
                 $("#add-compilation-title").text('添加定额库');
+                break;
+            case 'fee':
+                $("#fee-area").show();
+                $("#add-compilation-title").text('添加费率库');
         }
 
         $("#addcompilation").modal('show');
@@ -168,7 +165,7 @@ $(document).ready(function() {
     });
 
     // 移除操作
-    $(".bill-list, .ration-list").on("click", ".remove-lib", function() {
+    $(".bill-list, .ration-list, .glj-list, .fee-list").on("click", ".remove-lib", function() {
         $(this).parent().remove();
     });
 
@@ -245,10 +242,8 @@ function initCompilation() {
     let billListData = billList === undefined ? [] : JSON.parse(billList);
     let rationLibData = rationList === undefined ? [] : JSON.parse(rationList);
     let gljLibData = gljList === undefined ? [] : JSON.parse(gljList);
+    let feeLibData = feeRateList === undefined ? [] : JSON.parse(feeRateList);
 
-    if (billListData.length <= 0 || rationLibData.length <= 0 || gljLibData.length <= 0) {
-        return false;
-    }
     // 初始化 造价书列设置
     colSpread = TREE_SHEET_HELPER.createNewSpread($('#main-tree-col')[0]);
     let billsTemplateTree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1});
@@ -256,6 +251,10 @@ function initCompilation() {
     TREE_SHEET_HELPER.loadSheetHeader(JSON.parse(mainTreeCol), colSpread.getActiveSheet());
     TREE_SHEET_HELPER.showTreeData(JSON.parse(mainTreeCol), colSpread.getActiveSheet(), billsTemplateTree);
 
+    if (billListData.length <= 0 || rationLibData.length <= 0 || gljLibData.length <= 0) {
+        return false;
+    }
+
     // 标准清单
     let html = '';
     for(let tmp of billListData) {
@@ -280,19 +279,28 @@ function initCompilation() {
     }
     $("select[name='glj_lib']").children("option").first().after(html);
 
+    // 费率库
+    html = '';
+    for(let tmp of feeLibData) {
+        let tmpHtml = '<option value="' + tmp.id + '">' + tmp.name + '</option>';
+        html += tmpHtml;
+    }
+    $("select[name='fee_lib']").children("option").first().after(html);
+
 }
 
 /**
  * 校验数据
  *
  * @param {String} model
- * @return {Array}
+ * @return {Object}
  */
 function getAndValidData(model) {
     let name = $("input[name='compilation_name']").val();
     let standardBill = $("select[name='standard_bill']").children("option:selected").val();
     let rationLib = $("select[name='ration_lib']").children("option:selected").val();
     let gljLib = $("select[name='glj_lib']").children("option:selected").val();
+    let feeLib = $("select[name='fee_lib']").children("option:selected").val();
 
     if (name === '' && model === 'all') {
         throw '编办名字不能为空';
@@ -310,11 +318,35 @@ function getAndValidData(model) {
         throw '请选择工料机库';
     }
 
+    if (model === 'fee' && (feeLib === '' || feeLib === undefined)) {
+        throw '请选择费率库';
+    }
+
     let standardBillString = $("select[name='standard_bill']").children("option:selected").text();
     let rationLibString = $("select[name='ration_lib']").children("option:selected").text();
     let gljLibString = $("select[name='glj_lib']").children("option:selected").text();
-
-    return [name, standardBill, rationLib, gljLib, standardBillString, rationLibString, gljLibString];
+    let feeLibString = $("select[name='fee_lib']").children("option:selected").text();
+
+    let result = {
+        name: name,
+        standardBill: {
+            id: standardBill,
+            name: standardBillString
+        },
+        ration: {
+            id: rationLib,
+            name: rationLibString
+        },
+        glj: {
+            id: gljLib,
+            name: gljLibString
+        },
+        fee: {
+            id: feeLib,
+            name: feeLibString
+        }
+    };
+    return result;
 }
 
 /**

+ 328 - 0
web/users/js/template.js

@@ -0,0 +1,328 @@
+/**
+ * Created by Mai on 2017/8/22.
+ */
+
+let TEMPLATE_BILLS_SETTING = {
+    "emptyRows":1,
+    "headRows":1,
+    "headRowHeight":[35],
+    "treeCol": 0,
+    "cols":[{
+        "width":200,
+        "readOnly":false,
+        "head":{
+            "titleNames":["编号"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"code",
+            "vAlign":0,
+            "hAlign":3,
+            "font":"Arail"
+        }
+    }, {
+        "width":300,
+        "readOnly":false,
+        "head":{
+            "titleNames":["名称"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"name",
+            "vAlign":0,
+            "hAlign":3,
+            "font":"Arail"
+        }
+    }, {
+        "width":50,
+        "readOnly":false,
+        "head":{
+            "titleNames":["单位"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"unit",
+            "vAlign":0,
+            "hAlign":1,
+            "font":"Arail"
+        }
+    }, {
+        "width":200,
+        "readOnly":false,
+        "head":{
+            "titleNames":["清单固定类别"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"flagsIndex.fixed.flag",
+            "vAlign":0,
+            "hAlign":3,
+            "font":"Arail",
+        }
+    }, {
+        "width":50,
+        "readOnly":true,
+        "head":{
+            "titleNames":["ID"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"ID",
+            "vAlign":0,
+            "hAlign":1,
+            "font":"Arail"
+        }
+    }, {
+        "width":50,
+        "readOnly":true,
+        "head":{
+            "titleNames":["ParentID"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"ParentID",
+            "vAlign":0,
+            "hAlign":1,
+            "font":"Arail"
+        }
+    }, {
+        "width":50,
+        "readOnly":true,
+        "head":{
+            "titleNames":["NextSiblingID"],
+            "spanCols":[1],
+            "spanRows":[1],
+            "vAlign":[1],
+            "hAlign":[1],
+            "font":["Arial"]
+        },
+        "data":{
+            "field":"NextSiblingID",
+            "vAlign":0,
+            "hAlign":1,
+            "font":"Arail"
+        }
+    }]
+};
+
+$(document).ready(function () {
+    let RefreshBaseActn = function (tree) {
+        let showButton = function (show, btn) {
+            if (show) {
+                btn.show();
+            } else {
+                btn.hide();
+            }
+        };
+        showButton(tree.selected && tree.selected.canUpLevel(), $('#upLevel'));
+        showButton(tree.selected && tree.selected.canDownLevel(), $('#downLevel'));
+        showButton(tree.selected && tree.selected.canUpMove(), $('#upMove'));
+        showButton(tree.selected && tree.selected.canDownMove(), $('#downMove'));
+        showButton(tree.selected ? true : false, $('#delete'));
+    }
+    let RefreshBillsData = function (datas) {
+        datas.forEach(function (data) {
+            let node = tree.findNode(data.data.ID);
+            if (node) {
+                $.extend(true, node.data, data.data);
+            }
+        });
+    };
+    let getFixedFlagCellType = function () {
+        let billsFixedFlagData = JSON.parse(billsFixedFlagList);
+        let comboItems = [];
+        for (let data of billsFixedFlagData) {
+            comboItems.push({text: data.name, value: data.value});
+        }
+        let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        combo.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.value)
+            .items(comboItems);
+        return combo;
+    };
+    let setFee = function (data, fullField, value) {
+        let fields = fullField.split('.'), valueField = data;
+        for (let i in fields) {
+            if (valueField[fields[i]]) {
+                if (i == fields.length - 1) {
+                    valueField[fields[i]] = value;
+                } else {
+                    valueField = valueField[fields[i]];
+                }
+            } else {
+                if (i == fields.length - 1) {
+                    valueField[fields[i]] = value;
+                } else {
+                    valueField[fields[i]] = {};
+                };
+                valueField = valueField[fields[i]];
+            }
+        }
+    }
+
+    let templateData = JSON.parse(billsTemplateData);
+    for (let data of templateData) {
+        if (data.flags) {
+            data.flagsIndex = {};
+            for (let flag of data.flags) {
+                data.flagsIndex[flag.fieldName] = flag;
+            }
+        }
+    }
+
+    for (col of TEMPLATE_BILLS_SETTING.cols) {
+        if (col.data.field === 'flagsIndex.fixed.flag' && TEMPLATE_BILLS_SETTING.cols.indexOf(col) !== TEMPLATE_BILLS_SETTING.treeCol) {
+            col.data.cellType = getFixedFlagCellType();
+        }
+    }
+
+    let tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
+    let billsSpread = TREE_SHEET_HELPER.createNewSpread($('#billsSpread')[0]);
+    let controller = TREE_SHEET_CONTROLLER.createNew(tree, billsSpread.getActiveSheet(), TEMPLATE_BILLS_SETTING);
+
+    controller.bind('refreshBaseActn', RefreshBaseActn);
+
+    billsSpread.bind(GC.Spread.Sheets.Events.EditEnded, function (sender, info) {
+        var node = controller.tree.items[info.row];
+        var fieldName = controller.setting.cols[info.col].data.field;
+        var data = {type: 'update', data: {ID: node.getID()}};
+        if (/flagsIndex/.test(fieldName)) {
+            data.data.flags = [];
+            let flagField = fieldName.split('.');
+            data.data.flags.push({fieldName: flagField[1], flag: info.editingText});
+        } else {
+            setFee(data.data, fieldName, info.editingText);
+        }
+        var updateData = [data];
+        CommonAjax.post(updateUrl, updateData, function (data) {
+            setFee(node.data, fieldName, info.editingText);
+            controller.refreshTreeNode([node], false);
+        }, function () {
+            controller.refreshTreeNode([node], false);
+        });
+    });
+    billsSpread.bind(GC.Spread.Sheets.Events.ClipboardPasted, function (e, info) {
+        console.log("ClipboardPasted");
+        var node, iRow, iCol, curRow, curCol, datas = [], data, fieldName, updateData;
+        for (iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
+            curRow = info.cellRange.row + iRow;
+            node = controller.tree.items[curRow];
+            if (node) {
+                data = {type: 'update', data: {ID: node.getID()}};
+                for (iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                    curCol = info.cellRange.col + iCol;
+                    fieldName = controller.setting.cols[curCol].data.field;
+
+                    if (/flagsIndex/.test(fieldName)) {
+                        data.data.flags = [];
+                        let flagField = fieldName.split('.');
+                        data.data.flags.push({fieldName: flagField[1],flag: info.sheet.getText(curRow, curCol)});
+                    } else {
+                        setFee(data.data, fieldName, info.sheet.getText(curRow, curCol));
+                    }
+                }
+                datas.push(data);
+            }
+        };
+        CommonAjax.post(updateUrl, datas, function (data) {
+            RefreshBillsData(data);
+            controller.showTreeData();
+        }, function () {
+            controller.showTreeData();
+        });
+    });
+
+    tree.loadDatas(templateData);
+    controller.showTreeData();
+    RefreshBaseActn(tree);
+
+    $('#insert').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = controller.tree.getInsertData(selected.getParentID(), selected.getNextSiblingID());
+        } else {
+            updateData = controller.tree.getInsertData();
+        }
+        if (updateData.length > 0) {
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.insert();
+                controller.showTreeData();
+            });
+        } else {
+            alert('新增节点失败, 请重试.');
+        }
+    });
+    $('#delete').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = controller.tree.getDeleteData(selected);
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.delete();
+                controller.showTreeData();
+            });
+        }
+    });
+    $('#upLevel').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = selected.getUpLevelData();
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.upLevel();
+                controller.showTreeData();
+            });
+        }
+    });
+    $('#downLevel').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = selected.getDownLevelData();
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.downLevel();
+                controller.showTreeData();
+            });
+        }
+    });
+    $('#upMove').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = selected.getUpMoveData();
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.upMove();
+                controller.showTreeData();
+            });
+        }
+    });
+    $('#downMove').click(function () {
+        var selected = controller.tree.selected, updateData;
+        if (selected) {
+            updateData = selected.getDownMoveData();
+            CommonAjax.post(updateUrl, updateData, function (data) {
+                controller.downMove();
+                controller.showTreeData();
+            });
+        }
+    });
+});

+ 22 - 4
web/users/views/compilation/add.html

@@ -100,12 +100,29 @@
                             </div>
                             <a href="#" class="btn btn-link btn-sm add-compilation" data-model="glj">添加</a>
                         </div>
+                        <div class="form-group">
+                            <label>费率库</label>
+                            <div class="fee-list">
+                                <% if (valuationData.fee_lib.length > 0) { %>
+                                <% valuationData.fee_lib.forEach(function (fee, index){ %>
+                                <p class="form-control-static">
+                                    <a class="pull-right text-danger remove-lib" data-model="fee" title="移除" data-id="<%= fee.id %>">
+                                        <span class="glyphicon glyphicon-remove"></span>
+                                    </a>
+                                    <input type="hidden" name="fee_lib" data-id="<%= fee.id %>" value="<%= JSON.stringify({id: fee.id, name: fee.name}) %>">
+                                    <% if (index === 0) {%><i class="glyphicon glyphicon-flag"></i>&nbsp;<% } %><%= fee.name %>
+                                </p>
+                                <% }) %>
+                                <% } %>
+                            </div>
+                            <a href="#" class="btn btn-link btn-sm add-compilation" data-model="fee">添加</a>
+                        </div>
                     </div>
                     <div class="col-md-8">
-                        <legend>清单模板 / 造价书列
-                            <a href="javascript:void(0)" data-toggle="modal" data-target="#set-column" class="btn btn-primary btn-sm pull-right">列设置</a>
-                            <a href="/template/bills/<%= valuationId %>" data-toggle="modal" data-target="" class="btn btn-primary btn-sm pull-right" style="margin-right:5px">模板设置</a>
-                        </legend>
+                        <legend>
+                            清单模板 / 造价书列
+                            <a href="javascript:void(0)" data-toggle="modal" data-target="#set-column" class="btn btn-primary btn-sm pull-right">设置</a>
+                            <a href="/compilation/template/<%= section %>/<%= valuationId %>" data-toggle="modal" data-target="" class="btn btn-primary btn-sm pull-right" style="margin-right:5px">模板设置</a>
                         <input type="hidden" name="main_tree_col" value="<%= mainTreeCol %>">
                         <div id="main-tree-col">
                         </div>
@@ -123,6 +140,7 @@
     let gljList = '<%- gljList %>';
     let mainTreeCol = '<%- mainTreeCol %>';
     let billsTemplateData = '<%- billsTemplateData %>';
+    let feeRateList = '<%- feeRateList %>';
     let colSpread = null;
     let colEditSpread = null;
 </script>

+ 10 - 0
web/users/views/compilation/modal.html

@@ -41,6 +41,16 @@
                         </div>
                     </div>
                 </div>
+                <div class="form-group" id="fee-area">
+                    <label>费率库</label>
+                    <div class="row">
+                        <div class="col-xs-12">
+                            <select class="form-control" name="fee_lib">
+                                <option value="">请选择费率库</option>
+                            </select>
+                        </div>
+                    </div>
+                </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>

+ 62 - 0
web/users/views/compilation/template.html

@@ -0,0 +1,62 @@
+<%include ./common.html %>
+<div class="panel-content">
+    <% if(Object.keys(selectedCompilation).length > 0) {%>
+    <div class="panel-title">
+        <div class="title-main">
+            <h2>
+                <%= selectedCompilation.name %>
+                <% if(selectedCompilation.is_release) {%>
+                <span class="text-muted" style="margin-left: 5px;">已发布 <%= moment(selectedCompilation.update_time).format('YYYY-MM-DD')%></span>
+                <% } %>
+                <a href="javascript:void(0);" data-id="<%= selectedCompilation._id %>" data-status="<%= selectedCompilation.is_release ? 0 : 1 %>" class="btn btn-primary btn-sm pull-right" id="release"><% if(selectedCompilation.is_release) {%>取消<% }else{ %>发布<% } %>编办</a>
+            </h2>
+        </div>
+    </div>
+    <% } %>
+    <div class="content-wrap">
+        <!--清单模板设置-->
+        <div class="c-header" style="padding:0">
+            <ul class="nav nav-tabs">
+                <% valuationList.forEach(function(valuation) { %>
+                <li role="presentation" <% if (valuation._id.toString() === valuationId) { %>class="active"<% } %>><a href="/compilation/valuation/<%= section %>/<%= valuation._id %>"><%= valuation.name %></a></li>
+                <% }) %>
+            </ul>
+        </div>
+        <div class="c-body">
+            <legend>清单模板设置<a href="/compilation/valuation/<%= section %>/<%= valuationId %>" class="btn btn-default btn-sm pull-right">返回</a><a href="#" class="btn btn-primary btn-sm pull-right" style="margin-right:5px">保存</a></legend>
+            <div class="row">
+                <div class="col-md-8">
+                    <div class="tools-btn btn-group align-top">
+                        <a href="" class="btn btn-sm"><i class="fa fa-files-o" aria-hidden="true"></i> 复制</a>
+                        <a href="" class="btn btn-sm"><i class="fa fa-scissors" aria-hidden="true"></i> 剪切</a>
+                        <a href="" class="btn btn-sm"><i class="fa fa-clipboard" aria-hidden="true"></i> 粘贴</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="insert"><i class="fa fa-sign-in" aria-hidden="true"></i> 插入</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="delete"><i class="fa fa-remove" aria-hidden="true"></i> 删除</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="upLevel"><i class="fa fa-arrow-left" aria-hidden="true"></i> 升级</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="downLevel"><i class="fa fa-arrow-right" aria-hidden="true"></i> 降级</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="downMove"><i class="fa fa-arrow-down" aria-hidden="true"></i> 下移</a>
+                        <a href="javascript:void(0)" class="btn btn-sm" id="upMove"><i class="fa fa-arrow-up" aria-hidden="true"></i> 上移</a>
+                    </div>
+                    <div class="mb-qd-height" id="billsSpread">
+                    </div>
+                </div>
+                <div class="col-md-4">
+                    <select class="form-control"><option>定额</option></select>
+                    <div class="mb-de-height">
+
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    let billsTemplateData = '<%- billsTemplateData %>';
+    let billsFixedFlagList = '<%- billsFixedFlagList %>';
+    let updateUrl = '/compilation/template/<%= section %>/<%= valuationId %>/update';
+</script>
+<script type="text/javascript" src="/public/web/id_tree.js"></script>
+<script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_helper.js"></script>
+<script type="text/javascript" src="/public/web/tree_sheet/tree_sheet_controller.js"></script>
+<script type="text/javascript" src="/public/web/common_ajax.js"></script>
+<script type="text/javascript" src="/web/users/js/template.js"></script>

+ 1 - 0
web/users/views/layout/layout.html

@@ -7,6 +7,7 @@
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="renderer" content="webkit">
     <meta name=copyright content="smartcost.com.cn">
+    <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
     <link rel="stylesheet" href="/web/users/css/bootstrap.min.css">
     <link rel="stylesheet" href="/web/users/css/style.css">
     <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.excel2013lightGray.10.0.1.css" type="text/css">