Browse Source

Merge branch 'master' of http://192.168.1.12:3000/SmartCost/ConstructionCost

TonyKang 8 năm trước cách đây
mục cha
commit
e5eacbde68
54 tập tin đã thay đổi với 2438 bổ sung713 xóa
  1. 1 0
      config/gulpConfig.js
  2. 17 1
      modules/common/base/base_model.js
  3. 23 0
      modules/complementary_glj_lib/models/gljModel.js
  4. 1 1
      modules/complementary_glj_lib/models/schemas.js
  5. 2 1
      modules/fee_rates/facade/fee_rates_facade.js
  6. 1 0
      modules/fee_rates/models/fee_rates.js
  7. 21 0
      modules/glj/controllers/glj_controller.js
  8. 250 92
      modules/glj/models/glj_list_model.js
  9. 1 1
      modules/glj/models/mix_ratio_model.js
  10. 11 3
      modules/glj/models/schemas/glj.js
  11. 17 4
      modules/glj/models/schemas/mix_ratio.js
  12. 20 3
      modules/glj/models/schemas/unit_price.js
  13. 2 1
      modules/glj/models/schemas/unit_price_file.js
  14. 11 0
      modules/glj/models/unit_price_file_model.js
  15. 111 22
      modules/glj/models/unit_price_model.js
  16. 1 0
      modules/glj/routes/glj_router.js
  17. 62 26
      modules/pm/controllers/pm_controller.js
  18. 164 33
      modules/pm/models/project_model.js
  19. 1 4
      modules/pm/routes/pm_route.js
  20. 20 2
      modules/ration_glj/controllers/ration_glj_controller.js
  21. 5 5
      modules/ration_glj/facade/glj_calculate_facade.js
  22. 4 6
      modules/ration_glj/facade/quantity_detail_facade.js
  23. 1 0
      modules/ration_glj/facade/ration_coe_facade.js
  24. 89 115
      modules/ration_glj/facade/ration_glj_facade.js
  25. 8 4
      modules/ration_glj/models/ration_glj.js
  26. 1 0
      modules/ration_glj/routes/ration_glj_route.js
  27. 34 25
      public/calc_util.js
  28. 10 0
      public/web/sheet/sheet_data_helper.js
  29. 1 0
      web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html
  30. 33 2
      web/building_saas/complementary_glj_lib/js/glj.js
  31. 5 4
      web/building_saas/complementary_glj_lib/js/gljComponent.js
  32. 1 0
      web/building_saas/glj/js/composition.js
  33. 10 2
      web/building_saas/glj/js/composition_spread.js
  34. 16 10
      web/building_saas/glj/js/project_glj.js
  35. 10 5
      web/building_saas/glj/js/project_glj_spread.js
  36. 69 0
      web/building_saas/main/html/main.html
  37. 7 1
      web/building_saas/main/js/calc/bills_calc.js
  38. 281 0
      web/building_saas/main/js/models/calc_program.js
  39. 13 0
      web/building_saas/main/js/models/main_consts.js
  40. 88 1
      web/building_saas/main/js/models/project_glj.js
  41. 35 1
      web/building_saas/main/js/models/ration.js
  42. 44 40
      web/building_saas/main/js/models/ration_glj.js
  43. 1 1
      web/building_saas/main/js/views/calc_program_manage.js
  44. 23 20
      web/building_saas/main/js/views/calc_program_view.js
  45. 3 2
      web/building_saas/main/js/views/character_content_view.js
  46. 79 47
      web/building_saas/main/js/views/glj_view.js
  47. 16 0
      web/building_saas/main/js/views/glj_view_contextMenu.js
  48. 2 0
      web/building_saas/main/js/views/project_info.js
  49. 216 0
      web/building_saas/main/js/views/project_property_decimal_view.js
  50. 62 34
      web/building_saas/main/js/views/project_view.js
  51. 5 0
      web/building_saas/pm/html/project-management-Recycle.html
  52. 23 2
      web/building_saas/pm/html/project-management.html
  53. 210 120
      web/building_saas/pm/js/pm_gc.js
  54. 296 72
      web/building_saas/pm/js/pm_main.js

+ 1 - 0
config/gulpConfig.js

@@ -87,6 +87,7 @@ module.exports = {
         'web/building_saas/main/js/views/project_info.js',
         'web/building_saas/main/js/views/project_view.js',
         'web/building_saas/main/js/views/options_view.js',
+        'web/building_saas/main/js/views/project_property_decimal_view.js',
         'web/building_saas/main/js/main_ajax.js',
         'web/building_saas/main/js/main.js',
         'web/building_saas/main/js/controllers/project_controller.js',

+ 17 - 1
modules/common/base/base_model.js

@@ -61,7 +61,13 @@ class BaseModel {
         if (indexBy !== null && !singleData && result.length > 0) {
             let tmpResult = {};
             for(let tmp of result) {
-                tmpResult[tmp[indexBy]] = tmp;
+                let key="";
+                if (indexBy instanceof Array){
+                    key = this.getIndex(tmp,indexBy);
+                }else {
+                    key =tmp[indexBy]?tmp[indexBy]:"";
+                }
+                tmpResult[key] = tmp;
             }
             result = tmpResult;
         }
@@ -125,6 +131,16 @@ class BaseModel {
 
         return result.ok !== undefined && result.ok === 1;
     }
+    getIndex(obj,pops){
+        let t_index = '';
+        let k_arr=[];
+        for(let p of pops){
+            let tmpK = (obj[p]==undefined||obj[p]==null||obj[p]=='')?'null':obj[p];
+            k_arr.push(tmpK);
+        }
+        t_index=k_arr.join("|-|");
+        return t_index;
+    }
 
 }
 

+ 23 - 0
modules/complementary_glj_lib/models/gljModel.js

@@ -37,8 +37,29 @@ class GljDao {
             else callback(false, data);
         })
     };
+    _exist(data, attr){
+        return data && data[attr] !== 'undefined' && data[attr];
+    }
+
+    sortToNumber(datas){
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i]._doc;
+            if(this._exist(data, 'basePrice')){
+                data['basePrice'] = parseFloat(data['basePrice']);
+            }
+            if(this._exist(data, 'component')){
+                for(let j = 0, jLen = data['component'].length; j < jLen; j++){
+                    let comGljObj = data['component'][j]._doc;
+                    if(this._exist(comGljObj, 'consumeAmt')){
+                        comGljObj['consumeAmt'] = parseFloat(comGljObj['consumeAmt']);
+                    }
+                }
+            }
+        }
+    }
     //获得用户的补充工料机和用户当前所在编办的标准工料机
     getGljItems (stdGljLibId, userId, compilationId, callback){
+        let me = this;
         let rst = {stdGljs: [], complementaryGljs: []};
         async.parallel([
             function (cb) {
@@ -47,6 +68,7 @@ class GljDao {
                         cb(err);
                     }
                     else{
+                        me.sortToNumber(stdGljs);
                         rst.stdGljs = stdGljs;
                         cb(null);
                     }
@@ -58,6 +80,7 @@ class GljDao {
                         cb(err);
                     }
                     else{
+                        me.sortToNumber(complementaryGljs);
                         rst.complementaryGljs = complementaryGljs;
                         cb(null);
                     }

+ 1 - 1
modules/complementary_glj_lib/models/schemas.js

@@ -9,7 +9,7 @@ let comGjlComponentSchema = new Schema(
     {
         isStd: Boolean, //组成物里的工料机是否是标准的,否则是补充的
         ID: Number,
-        consumeAmt: Number
+        consumeAmt: String
     },
     {_id: false},
     {versionKey: false}

+ 2 - 1
modules/fee_rates/facade/fee_rates_facade.js

@@ -262,7 +262,7 @@ async function changeFeeRateStandard(jdata){
     return doc;
 }
 
-async function newFeeRateFile(updateData){
+async function newFeeRateFile(userId, updateData){
     if(updateData.property !== null){
         let property = updateData.property;
         logger.info("Create new feeRate file for project :"+updateData.ID);
@@ -280,6 +280,7 @@ async function newFeeRateFile(updateData){
             let temA = feeFile.id.split("-");
             let libID = temA[1];
             let doc={
+                userID: userId,
                 rootProjectID:rootProjectID,
                 name:name
             };

+ 1 - 0
modules/fee_rates/models/fee_rates.js

@@ -49,6 +49,7 @@ mongoose.model('fee_rates', feeRatesSchema, 'fee_rates');
 let feeRateFileSchema = new Schema({
     ID: String,
     rootProjectID:Number,//顶层项目ID
+    userID:Number,
     name:String,
     libID: String,
     libName: String,

+ 21 - 0
modules/glj/controllers/glj_controller.js

@@ -11,6 +11,7 @@ import GLJListModel from "../models/glj_list_model";
 import UnitPriceModel from "../models/unit_price_model";
 import MixRatioModel from "../models/mix_ratio_model";
 import UnitPriceFileModel from "../models/unit_price_file_model";
+let logger = require("../../../logs/log_helper").logger;
 
 const ProjectModel = require('../../pm/models/project_model').project;
 class GLJController extends BaseController {
@@ -565,6 +566,26 @@ class GLJController extends BaseController {
         }
         response.end('success');
     }
+
+    async  updateUnitPrice(req, res){
+        let result={
+            error:0
+        }
+        try {
+            let data = req.body.data;
+            data = JSON.parse(data);
+            let unitPriceModel = new UnitPriceModel();
+            // 更新数据
+            let datas = await unitPriceModel.updateUnitPrice(data);
+            result.data=datas;
+        }catch (err){
+            logger.err(err);
+            result.error=1;
+            result.message = err.message;
+        }
+        res.json(result);
+    }
+
 }
 
 export default GLJController;

+ 250 - 92
modules/glj/models/glj_list_model.js

@@ -107,47 +107,50 @@ class GLJListModel extends BaseModel {
             // 整理数据
             for (let tmp of quantityData) {
                 let tmpNum = parseFloat(tmp.rationQuantity);
-                tmpNum = isNaN(tmpNum) ? 1 : tmpNum;
+                tmpNum = isNaN(tmpNum) ||tmpNum==0? 1 : tmpNum;
                 if (quantityList[tmp.projectGLJID] === undefined) {
                     quantityList[tmp.projectGLJID] = tmp.quantity * tmpNum;
                 } else {
                     quantityList[tmp.projectGLJID] += tmp.quantity * tmpNum;
                 }
             }
-            // 整理获取有组成物的项目工料机编码
-            let projectGLJCode = [];
+            // 整理获取有组成物的项目工料机的数据
+            let connect_keys = [];
             for(let tmp of gljData) {
                 // 有组成物的类型且消耗量大于0才查找
                 if (quantityList[tmp.id] !== undefined) {
-                    projectGLJCode.push(tmp.code);
+                   let key = this.getIndex(tmp,['code','name','specs','unit','type']);
+                    connect_keys.push(key);
                 }
             }
             // 查找组成物的消耗量
             let totalComposition = {};
             let mixRatioData = {};
-            if (projectGLJCode.length > 0) {
+            if (connect_keys.length > 0) {
                 let mixRatioModel = new MixRatioModel();
-                condition = {connect_code: {"$in": projectGLJCode}, unit_price_file_id: unitPriceFileId};
+                condition = {connect_key: {"$in": connect_keys}, unit_price_file_id: unitPriceFileId};
                 let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
                 for (let tmp of mixRatioList) {
-                    totalComposition[tmp.connect_code] = totalComposition[tmp.connect_code] === undefined ? tmp.consumption :
-                        totalComposition[tmp.connect_code] + tmp.consumption;
-                    totalComposition[tmp.connect_code] = scMathUtil.roundTo(totalComposition[tmp.connect_code], -4);
-
-                    if (mixRatioData[tmp.glj_id] !== undefined) {
-                        mixRatioData[tmp.glj_id].push(tmp);
+                   let t_index = tmp.connect_key;
+                    let consumption=parseFloat(tmp.consumption);
+                    totalComposition[t_index] = totalComposition[t_index] === undefined ? consumption :
+                        totalComposition[t_index] + consumption;
+                    totalComposition[t_index] = scMathUtil.roundTo(totalComposition[t_index], -4);
+
+                    if (mixRatioData[t_index] !== undefined) {
+                        mixRatioData[t_index].push(tmp);
                     } else {
-                        mixRatioData[tmp.glj_id] = [tmp];
+                        mixRatioData[t_index] = [tmp];
                     }
-                    if(mixRationMap[tmp.connect_code]!=undefined){
-                        mixRationMap[tmp.connect_code].push(tmp);
+                    if(mixRationMap[t_index]!=undefined){
+                        mixRationMap[t_index].push(tmp);
                     }else {
-                        mixRationMap[tmp.connect_code]=[tmp];
+                        mixRationMap[t_index]=[tmp];
                     }
                     if (mixRatioConnectData[tmp.glj_id] !== undefined) {
-                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_code);
+                        mixRatioConnectData[tmp.glj_id].push(tmp.connect_key);
                     } else {
-                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_code];
+                        mixRatioConnectData[tmp.glj_id] = [tmp.connect_key];
                     }
                 }
             }
@@ -167,7 +170,6 @@ class GLJListModel extends BaseModel {
 
         return [gljData, mixRatioConnectData,mixRationMap];
     }
-
     /**
      * 组合工料机数据和单价文件数据
      *
@@ -184,8 +186,9 @@ class GLJListModel extends BaseModel {
         if (Object.keys(mixRatioData).length > 0 && Object.keys(totalComposition).length > 0) {
             for(let index in mixRatioData) {
                 for(let tmp of mixRatioData[index]) {
-                    compositionConsumption[tmp.glj_id] = compositionConsumption[tmp.glj_id] === undefined ? tmp.consumption :
-                        compositionConsumption[tmp.glj_id] + tmp.consumption;
+                    let t_index = this.getIndex(tmp,['code','name','specs','unit','type']);//取做为组成物的工料机的总消耗量
+                    compositionConsumption[t_index] = compositionConsumption[t_index] === undefined ? tmp.consumption :
+                        compositionConsumption[t_index] + tmp.consumption;
                 }
 
             }
@@ -197,44 +200,47 @@ class GLJListModel extends BaseModel {
             if (glj.code === undefined) {
                 continue;
             }
-            glj.unit_price = unitPriceList !== null && unitPriceList[glj.code + glj.name] !== undefined ? unitPriceList[glj.code + glj.name] : null;
+            let g_index = this.getIndex(glj,['code','name','specs','unit','type']);
+            glj.unit_price = unitPriceList !== null && unitPriceList[g_index] !== undefined ? unitPriceList[g_index] : null;
 
             if (glj.unit_price === null) {
                 continue;
             }
-
             let gljId = glj.glj_id + '';
             let projectGljId = glj.id + '';
-
             // 消耗量赋值
             glj.quantity = quantityList[projectGljId] !== undefined ? quantityList[projectGljId] : 0;
-            glj.quantity = totalComposition[glj.code] !== undefined ? totalComposition[glj.code] : glj.quantity;
-            glj.quantity = compositionConsumption[gljId] !== undefined ?  glj.quantity + compositionConsumption[gljId] : glj.quantity;
+            glj.quantity = totalComposition[g_index] !== undefined ? totalComposition[g_index] : glj.quantity;
+            glj.quantity = compositionConsumption[g_index] !== undefined ?  glj.quantity + compositionConsumption[g_index] : glj.quantity;
             glj.quantity = scMathUtil.roundTo(parseFloat(glj.quantity), -3);
 
             // 组成物数据
-            gljList[index].ratio_data = mixRatioData[gljId] !== undefined ? mixRatioData[gljId] : [];
-
-            glj.unit_price.base_price = scMathUtil.roundTo(parseFloat(glj.unit_price.base_price), -2);
-            glj.unit_price.market_price = scMathUtil.roundTo(parseFloat(glj.unit_price.market_price), -2);
-            // 计算调整基价
-            switch (glj.unit_price.type + '') {
-                // 人工: 调整基价=基价单价*调整系数
-                case GLJTypeConst.LABOUR:
-                    glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj.unit_price.base_price), -2);
-                    break;
-                // 机械类型的算法
-                case GLJTypeConst.MACHINE:
-                    console.log('机械');
-                    break;
-                // 材料、主材、设备
-                default:
-                    glj.adjust_price = glj.unit_price.base_price;
-            }
+            gljList[index].ratio_data = mixRatioData[g_index] !== undefined ? mixRatioData[g_index] : [];
+            //因为schema中设置base_price 为string 类型,所以要通过中间变量转换为数字再做计算,不然会自动变成字符串类型
+            this.getGLJPrice(glj);
             result.push(glj);
         }
         return result;
     }
+    getGLJPrice(glj){
+        let glj_basePrice = scMathUtil.roundTo(parseFloat(glj.unit_price.base_price), -2);
+        glj.unit_price.base_price = glj_basePrice;
+        glj.unit_price.market_price = scMathUtil.roundTo(parseFloat(glj.unit_price.market_price), -2);
+        // 计算调整基价
+        switch (glj.unit_price.type + '') {
+            // 人工: 调整基价=基价单价*调整系数
+            case GLJTypeConst.LABOUR:
+                glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj_basePrice), -2);
+                break;
+            // 机械类型的算法
+            case GLJTypeConst.MACHINE:
+                console.log('机械');
+                break;
+            // 材料、主材、设备
+            default:
+                glj.adjust_price = glj.unit_price.base_price;
+        }
+    }
 
     /**
      * 新增项目工料机数据(包括新增单价文件) 定额工料机新增时调用
@@ -248,8 +254,15 @@ class GLJListModel extends BaseModel {
             if (Object.keys(data).length <= 0) {
                 throw '新增数据为空';
             }
-            // 首先查找是否有同编码同名称的工料机数据
-            let projectGljData = await this.findDataByCondition({code: data.code, project_id: data.project_id});
+            let condition={
+                code: data.code,
+                project_id: data.project_id,
+                name:data.name,
+                specs:data.specs,
+                type:data.type,
+                unit:data.unit
+            };
+            let projectGljData = await this.findDataByCondition(condition);
             let isAddProjectGLJ = false;
             // 如果找不到数据则新增
             if (!projectGljData) {
@@ -267,12 +280,17 @@ class GLJListModel extends BaseModel {
             if (unitPriceFileId <= 0) {
                 throw '没有对应的单价文件';
             }
-
+            let CompositionGLJ=[];
             // 判断类型,如果是混凝土、砂浆或者配合比则查找对应的组成物(前提是没有对应的项目工料机数据)
-            if (isAddProjectGLJ && (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
-                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE)) {
-                this.compositionInit(data, unitPriceFileId);
+            if (data.type === GLJTypeConst.CONCRETE || data.type === GLJTypeConst.MORTAR ||
+                data.type === GLJTypeConst.MIX_RATIO || data.type === GLJTypeConst.GENERAL_MACHINE) {
+                //如果是新增
+                if(isAddProjectGLJ ){
+                    await this.compositionInit(data, unitPriceFileId);
+                }
+                CompositionGLJ=await this.getCompositionGLJByData(data,unitPriceFileId);
             }
+            projectGljData.subList=CompositionGLJ;
 
             // 新增单价文件
             let unitPriceModel = new UnitPriceModel();
@@ -319,6 +337,87 @@ class GLJListModel extends BaseModel {
     }
 
     /**
+     * 修改名称、规格型号、单位、市场单价等
+     * @param data
+     * @returns {Promise.<void>}
+     */
+    async modifyGLJ(data,ration_glj){
+        let unitPriceFileModel = new UnitPriceFileModel();
+        let unitPriceFile = await unitPriceFileModel.getDataByProject(data.project_id);
+        if (!unitPriceFile) {
+            throw '没有对应的单价文件';
+        }
+        //查找单价信息,有则返回,没有则新增并返回
+        let unitPriceFileId = unitPriceFile.id;
+        let unitPriceModel = new UnitPriceModel();
+        let [unitPriceData, isAdd] = await unitPriceModel.addUnitPrice(data, unitPriceFileId);
+        let gljData={};
+        if(isAdd){ //如果是新增,则新增一条新的项目工料机
+            data.code = unitPriceData.code;
+            gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
+        }else { //如果不是新增,则查找是否有对应的项目工料机,有则返回,没有则新增
+            let condition = {
+                project_id:data.project_id,
+                original_code: data.original_code,
+                name:data.name,
+                specs:data.specs,
+                type:data.type,
+                unit:data.unit
+            }
+            gljData = await this.findDataByCondition(condition,{_id: 0});
+            if(!gljData){
+                data.code = unitPriceData.code;
+                gljData  = await this.insertGLJWhenIsAdd(data,ration_glj,unitPriceFileId);
+            }
+        }
+        gljData.unit_price = unitPriceData;
+        return gljData
+    }
+
+    //修改属性后插入项目工料机
+    async insertGLJWhenIsAdd(glj,ration_glj,unitPriceFileId){
+        //新增项目工料机
+        let gljData = await this.add(glj);
+        //查看是否是有配合比的工料机类型
+        if (glj.type === GLJTypeConst.CONCRETE || glj.type === GLJTypeConst.MORTAR ||
+            glj.type === GLJTypeConst.MIX_RATIO || glj.type === GLJTypeConst.GENERAL_MACHINE){
+            // 配合比数据插入
+            let connect_key =this.getIndex(ration_glj,['code','name','specs','unit','type']);
+            let connect_key_n =this.getIndex(glj,['code','name','specs','unit','type']);
+            //先查找配合比数据是否已经存在
+            let mixRatioModel = new MixRatioModel();
+            let mixRatios_o = await mixRatioModel.findDataByCondition({connect_key: connect_key_n, unit_price_file_id: unitPriceFileId}, {_id: 0}, false);
+            if(mixRatios_o&&mixRatios_o.length>0){//已经存在,不用再插入配合比数据,直接返回
+                return gljData;
+            }
+            //不存在,则查找原始的配合比数据,并为新工料机增加配合比数据
+            let mixRatios = await mixRatioModel.findDataByCondition({connect_key: connect_key, unit_price_file_id: unitPriceFileId}, {_id: 0}, false);
+            let mixInsertResult ={};
+            if(mixRatios&&mixRatios.length>0){
+                let newMixRatioData = [];
+                for(let m of mixRatios){
+                    let tem ={
+                        consumption: m.consumption,
+                        glj_id: m.glj_id,
+                        unit_price_file_id: m.unit_price_file_id,
+                        connect_key: connect_key_n,
+                        type: m.type,
+                        code: m.code,
+                        specs:m.specs,
+                        name:m.name,
+                        unit:m.unit
+                    };
+                    newMixRatioData.push(tem);
+                }
+                mixInsertResult= mixRatioModel.add(newMixRatioData);
+            }
+        }
+        return gljData;
+
+    }
+
+
+    /**
      * 根据工料机id修改市场单价
      *
      * @param {Object} updateData
@@ -432,21 +531,25 @@ class GLJListModel extends BaseModel {
             throw '参数错误';
         }
         let fromTable = data.from === undefined ? 'std' : data.from;
-
         // 查找对应组成物的项目工料机数据
-        let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, 'name', fromTable);
+        let indexs=['code','name','specs','unit','type'];
+        let [projectGljList, compositionGljList] = await this.getCompositionGLJList(gljId, projectId, indexs, fromTable);
 
         // 整理配合比待插入数据
         let mixRatioInsertData = [];
         for (let tmp of compositionGljList) {
             // 配合比数据插入
+            var connect_key =this.getIndex(data,['code','name','specs','unit','type']);
             let mixRatioData = {
                 consumption: tmp.consumption,
                 glj_id: tmp.ID,
                 unit_price_file_id: unitPriceFileId,
-                connect_code: tmp.connectCode,
-                glj_type: tmp.gljType,
+                connect_key: connect_key,
+                type: tmp.gljType,
                 code: tmp.code,
+                specs:tmp.specs,
+                name:tmp.name,
+                unit:tmp.unit
             };
             mixRatioInsertData.push(mixRatioData);
         }
@@ -459,15 +562,16 @@ class GLJListModel extends BaseModel {
             throw '组成物插入单价数据失败!';
         }
         // 如果已经存在则后续操作停止
-        if(projectGljList.length === compositionGljList.length) {
-            return;
+        if(Object.getOwnPropertyNames(projectGljList).length === compositionGljList.length) {
+            return
         }
 
         // 整理插入的数据
         let gljInsertData = [];
         let unitPriceInsertData = [];
         for(let tmp of compositionGljList) {
-            if (projectGljList[tmp.name] !== undefined) {
+            let key = this.getIndex(tmp,['code','name','specs','unit','gljType']);
+            if (projectGljList[key] !== undefined) {
                 continue;
             }
             // 项目工料机插入的数据
@@ -478,24 +582,29 @@ class GLJListModel extends BaseModel {
                 name: tmp.name,
                 specs: tmp.specs,
                 unit: tmp.unit === undefined ? '' : tmp.unit,
+                type: tmp.gljType,
+                original_code:tmp.code
             };
             gljInsertData.push(gljData);
 
+            let basePrice = scMathUtil.roundTo(tmp.basePrice,-6);
             // 单价文件插入的数据
             let unitPriceData = {
-                base_price: tmp.basePrice,
+                base_price: basePrice,
                 // 初始市场价=基价
-                market_price: tmp.basePrice,
+                market_price: basePrice,
                 code: tmp.code,
                 name: tmp.name,
                 unit_price_file_id: unitPriceFileId,
                 type: tmp.gljType,
                 short_name: tmp.shortName === undefined ? '' : tmp.shortName,
                 glj_id: tmp.ID,
+                specs: tmp.specs,
+                unit: tmp.unit === undefined ? '' : tmp.unit,
+                original_code:tmp.code
             };
             unitPriceInsertData.push(unitPriceData);
         }
-
         // 整理完后开始插入数据
         let addResult = await this.add(gljInsertData);
         if (!addResult) {
@@ -508,6 +617,7 @@ class GLJListModel extends BaseModel {
             throw '组成物插入单价数据失败!';
         }
 
+        return
     }
 
     /**
@@ -521,7 +631,7 @@ class GLJListModel extends BaseModel {
         let result = [];
         try {
             // 查找对应的项目工料机数据
-            let projectGLJData = await this.getDataById(projectGLJId);
+            let projectGLJData = await this.getDataById(projectGLJId,unitPriceFileId);
 
             let allowType = [GLJTypeConst.MIX_RATIO, GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR,
                 GLJTypeConst.GENERAL_MACHINE];
@@ -530,39 +640,13 @@ class GLJListModel extends BaseModel {
                 throw '找不到相关项目工料机';
             }
 
-            // 查找对应的项目工料机数据
-            let [gljData, compositionList] = await this.getCompositionGLJList(projectGLJData.glj_id, projectGLJData.project_id);
+            // 查找对应的项目工料机数据配合比,单价数据
+            let [gljData, mixRatioData,unitPriceData] = await this.getCompositionListByGLJ(projectGLJData, unitPriceFileId);
 
             if (gljData.length <= 0) {
                 throw '没有对应的组成物项目工料机';
             }
 
-            // 整理出code和name查找相关单价数据
-            let codeList = [];
-            let nameList = [];
-            let gljIdList = [];
-            for(let tmp of gljData) {
-                codeList.push(tmp.code);
-                nameList.push(tmp.name);
-                gljIdList.push(tmp.glj_id);
-            }
-
-            // 查找对应的单价数据
-            let unitPriceModel = new UnitPriceModel();
-            let condition = {code: {"$in": codeList}, name: {"$in": nameList}, unit_price_file_id: unitPriceFileId};
-            let unitPriceList = await unitPriceModel.findDataByCondition(condition, {_id: 0}, false);
-
-            // 查找对应的配合比数据
-            let mixRatioModel = new MixRatioModel();
-            condition = {glj_id: {"$in": gljIdList}, connect_code: projectGLJData.code, unit_price_file_id: unitPriceFileId};
-            let mixRatioData = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false, 'glj_id');
-
-            // 整理数据
-            let unitPriceData = {};
-            for(let tmp of unitPriceList) {
-                unitPriceData[tmp.code + tmp.name] = tmp;
-            }
-
             gljData = this.combineData(gljData, unitPriceData, [], mixRatioData);
 
             // 排序
@@ -598,25 +682,91 @@ class GLJListModel extends BaseModel {
 
         let codeList = [];
         let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
         for(let tmp of componentGljList) {
             codeList.push(tmp.code);
             nameList.push(tmp.name);
+            specsList.push(tmp.specs);
+            typeList.push(tmp.gljType);
+            unitList.push(tmp.unit);
         }
 
         // 查找对应的项目工料机数据
-        let condition = {code: {"$in": codeList}, name: {"$in": nameList}, project_id: projectId};
+        let condition = {project_id: projectId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
         let gljData = await this.findDataByCondition(condition, {_id: 0}, false, indexBy);
 
         return [gljData, componentGljList];
     }
 
+    async getCompositionGLJByData(glj,unitPriceFileId){
+        let [gljData,mixRatioData,unitPriceData] = await this.getCompositionListByGLJ(glj,unitPriceFileId);
+        gljData = this.combineData(gljData, unitPriceData, [], mixRatioData);
+        return gljData;
+    }
+
+
+
+    /**
+     * 反回组成物工料机和配合比数据
+     * @param glj
+     * @param unitPriceFileId
+     * @returns {Promise.<void>}
+     */
+    async getCompositionListByGLJ(glj,unitPriceFileId){
+        let t_index = this.getIndex(glj,['code','name','specs','unit','type']);
+        // 查找对应的配合比数据
+        let mixRatioModel = new MixRatioModel();
+        let condition = {connect_key: t_index, unit_price_file_id: unitPriceFileId};
+        let mixRatios = await mixRatioModel.findDataByCondition(condition, {_id: 0}, false);
+        let codeList = [];
+        let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
+        if(mixRatios.length<=0){
+            throw  '不存在对应的组成物';
+        }
+        let mixRatioData={};
+        for(let tmp of mixRatios) {
+            codeList.push(tmp.code);
+            nameList.push(tmp.name);
+            specsList.push(tmp.specs);
+            typeList.push(tmp.type);
+            unitList.push(tmp.unit);
+            let m_index = this.getIndex(tmp,['code','name','specs','unit','type']);
+            mixRatioData[m_index]=tmp;
+        }
+        // 查找对应的项目工料机数据
+        let gcondition = {project_id: glj.project_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList} };
+        let gljData = await this.findDataByCondition(gcondition, {_id: 0}, false);
+
+        // 查找对应的单价数据
+        let unitPriceModel = new UnitPriceModel();
+        let ucondition = { unit_price_file_id: unitPriceFileId,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        let unitPriceList = await unitPriceModel.findDataByCondition(ucondition, {_id: 0}, false);
+
+
+        // 整理数据
+        let unitPriceData = {};
+        for(let tmp of unitPriceList) {
+            let u_index = this.getIndex(tmp,['code','name','specs','unit','type'])
+            unitPriceData[u_index] = tmp;
+        }
+
+
+        return [gljData,mixRatioData,unitPriceData];
+
+    }
+
     /**
      * 根据条件获取对应项目工料机数据
      *
      * @param {Number} id
      * @return {Promise}
      */
-    async getDataById(id) {
+    async getDataById(id,unitPriceFileId) {
 
         // 查找对应的项目工料机数据
         let projectGLJData = await this.findDataByCondition({id: id});
@@ -626,7 +776,15 @@ class GLJListModel extends BaseModel {
 
         // 查找对应的单价数据
         let unitPriceModel = new UnitPriceModel();
-        let unitPrice = await unitPriceModel.findDataByCondition({code: projectGLJData.code, name: projectGLJData.name});
+        let condition={
+            unit_price_file_id:unitPriceFileId,
+            code: projectGLJData.code,
+            name:projectGLJData.name,
+            specs:projectGLJData.specs,
+            type:projectGLJData.type,
+            unit:projectGLJData.unit
+        }
+        let unitPrice = await unitPriceModel.findDataByCondition(condition);
 
         projectGLJData.unit_price = unitPrice;
 

+ 1 - 1
modules/glj/models/mix_ratio_model.js

@@ -35,7 +35,7 @@ class MixRatioModel extends BaseModel {
                 this.model.schema.path('glj_id').required(true);
                 this.model.schema.path('consumption').required(true);
                 this.model.schema.path('unit_price_file_id').required(true);
-                this.model.schema.path('connect_code').required(true);
+                this.model.schema.path('connect_key').required(true);
                 break;
         }
     }

+ 11 - 3
modules/glj/models/schemas/glj.js

@@ -21,6 +21,11 @@ let modelSchema = {
         type: String,
         index: true
     },
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     // 名称
     name: {
         type: String,
@@ -62,21 +67,24 @@ let modelSchema = {
         type: Number,
         default: 1
     },
+
     // 规格型号
     specs: {
         type: String,
         default: ''
     },
+    // 类型
+    type: Number,
     // 单位
     unit: String,
     // 显示调整基价
-    adjust_price: Number,
+    adjust_price: String,
     // 显示关联单价文件的字段
     unit_price: Schema.Types.Mixed,
     // 显示关联的消耗量
-    quantity: Number,
+    quantity: String,
     // 显示组成物的消耗量
-    consumption: Number,
+    consumption: String,
     // 显示关联配合比的id
     mix_ratio_id: Number,
     // 显示关联父级工料机code(组合物用)

+ 17 - 4
modules/glj/models/schemas/mix_ratio.js

@@ -14,7 +14,7 @@ let modelSchema = {
     id: Number,
     // 消耗量(有别于总消耗,此字段记录配合比的消耗量)
     consumption: {
-        type: Number,
+        type: String,
         default: 0
     },
     // 工料机总库对应id (关联用)
@@ -24,15 +24,28 @@ let modelSchema = {
     },
     // 单价文件表id (因为选择单价文件后配合比数据也需要同步,所以记录单价文件id)
     unit_price_file_id: Number,
-    // 关联项目工料机的code 不能关联id,因为单价文件导入别的项目后项目工料机id不同
-    connect_code: {
+    // 关联项目工料机的key 不能关联id,因为单价文件导入别的项目后项目工料机id不同
+    connect_key: {
         type: String,
         index: true
     },
+    // 规格型号
+    specs: {
+        type: String,
+        default: ''
+    },
+    // 单位
+    unit: String,
+    // 名称
+    name: {
+        type: String,
+        index: true,
+        default: ''
+    },
     // 对应工料机code
     code: String,
     // 工料机类型
-    glj_type: Number
+    type: Number
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 20 - 3
modules/glj/models/schemas/unit_price.js

@@ -13,19 +13,31 @@ let modelSchema = {
     // 自增ID
     id: Number,
     // 基价单价
-    base_price: Number,
+    base_price: String,
     // 市场单价
-    market_price: Number,
+    market_price: String,
     // 编码
     code: {
         type: String,
         index: true
     },
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     // 名称
     name: {
         type: String,
         index: true
     },
+    // 规格型号
+    specs: {
+        type: String,
+        default: ''
+    },
+    // 单位
+    unit: String,
     // 类型
     type: Number,
     // 类型简称
@@ -33,7 +45,12 @@ let modelSchema = {
     // 单价文件表id
     unit_price_file_id: Number,
     // 对应标准库工料机id
-    glj_id: Number
+    glj_id: Number,
+    //是否新增1为是,0为否
+    is_add:{
+        type: Number,
+        default: 0
+    }
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 2 - 1
modules/glj/models/schemas/unit_price_file.js

@@ -6,7 +6,7 @@
  * @version
  */
 import mongoose from "mongoose";
-
+let deleteSchema = require('../../../../public/models/delete_schema');
 let Schema = mongoose.Schema;
 let collectionName = 'unit_price_file';
 let modelSchema = {
@@ -23,6 +23,7 @@ let modelSchema = {
     user_id: Number,
     // 顶层projectId
     root_project_id: Number,
+    deleteInfo: deleteSchema
 };
 let model = mongoose.model(collectionName, new Schema(modelSchema, {versionKey: false, collection: collectionName}));
 export {model as default, collectionName as collectionName};

+ 11 - 0
modules/glj/models/unit_price_file_model.js

@@ -127,6 +127,17 @@ class UnitPriceFileModel extends BaseModel {
     }
 
     /**
+     * 根据建设项目id获取单价文件数据
+     *
+     * @param {Number} root_project_id
+     * @return {Promise}
+     */
+    async getDataByRootProject(root_project_id){
+        let condition = {root_project_id: root_project_id, deleteInfo: null};
+        return  await this.findDataByCondition(condition, null, false);
+    }
+
+    /**
      * 根据用户获取对应被删除的单价文件
      *
      * @param {String} userID

+ 111 - 22
modules/glj/models/unit_price_model.js

@@ -8,7 +8,10 @@
 import BaseModel from "../../common/base/base_model"
 import GLJTypeConst from "../../common/const/glj_type_const"
 import CounterModel from "./counter_model"
+import MixRatioModel from "./mix_ratio_model";
 import {default as UnitPriceSchema, collectionName as collectionName} from "./schemas/unit_price";
+import _ from "lodash";
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 class UnitPriceModel extends BaseModel {
 
@@ -43,12 +46,12 @@ class UnitPriceModel extends BaseModel {
         // 整理数据
         let result = {};
         for(let tmp of unitPriceList) {
-            result[tmp.code + tmp.name] = tmp;
+            let index = this.getIndex(tmp,['code','name','specs','unit','type'])
+            result[index] = tmp;
         }
 
         return result;
     }
-
     /**
      * 设置场景
      *
@@ -78,19 +81,16 @@ class UnitPriceModel extends BaseModel {
      * @return {Promise} 返回数据以及是否新增
      */
     async addUnitPrice(data, unitPriceFileId, gljCount = 0) {
-        if (data.code === undefined || data.project_id === undefined || data.name === undefined
+        if (data.original_code===undefined||data.code === undefined || data.project_id === undefined || data.name === undefined
             || data.market_price === undefined) {
             return [null, false];
         }
-
-        // 先查找是否有同code的单价记录
-        let unitPriceData = await this.findDataByCondition({code: data.code, unit_price_file_id: unitPriceFileId}, null, false);
-
-        // 如果有记录,判断是否存在一样的市场单价,有则直接返回数据
-        data.market_price = parseFloat(data.market_price);
-        let unitPriceIndex = this.isPriceIncluded(unitPriceData, data.market_price);
-        if (unitPriceData && unitPriceIndex >= 0) {
-            return [unitPriceData[unitPriceIndex], false];
+        // 先查找是否有原始code相同的记录
+        let unitPriceData = await this.findDataByCondition({original_code: data.original_code, unit_price_file_id: unitPriceFileId}, null, false);
+        // 如果有记录,判断是否存在一样的名称,单位...等,有则直接返回数据
+        let unitPrice =  this.isPropertyInclude(unitPriceData,['name','specs','unit','type'],data);
+        if(unitPrice){
+            return [unitPrice, false];
         }
 
         // 如果不存在基价单价,则在数据源中获取
@@ -100,24 +100,25 @@ class UnitPriceModel extends BaseModel {
             data.type = firstUnitPrice.type !== undefined ? firstUnitPrice.type : 0;
         }
 
-        // 更改名称
-        if (gljCount > 0) {
-            let regular = /\(\d+\)/;
-            let changeString = '(' + gljCount + ')';
-            data.name = regular.test(data.name) ? data.name.replace(regular, changeString) :
-                data.name + changeString;
-        }
         let insertData = {
             code: data.code,
             base_price: data.base_price,
             market_price: data.market_price,
             unit_price_file_id: unitPriceFileId,
             name: data.name,
+            specs:data.specs,
+            original_code:data.original_code,
+            unit:data.unit,
             type: data.type,
             short_name: data.shortName !== undefined ? data.shortName : '',
             glj_id: data.glj_id
         };
 
+        // 如果原始编码能找到,但不存在一样的编号,名称,单位.更改 等,更改code和添加新增标记
+        if (unitPriceData&&unitPriceData.length>0) {
+            insertData.code = data.original_code+"-"+unitPriceData.length;
+            insertData.is_add=1;
+        }
         let addPriceResult = await this.add(insertData);
         return [addPriceResult, true];
     }
@@ -161,10 +162,25 @@ class UnitPriceModel extends BaseModel {
                 break;
             }
         }
-
         return index;
     }
 
+    isPropertyInclude(data,pops,obj){
+        let condition={};
+        if (data.length <= 0) {
+            return null;
+        }
+        if(pops instanceof  Array){
+            for(let p of pops){
+                if(obj[p]){
+                    condition[p]=obj[p]
+                }
+            }
+        }else {
+            condition[pops]=obj[pops]
+        }
+        return _.find(data,condition);
+    }
     /**
      * 更新市场单价
      *
@@ -183,14 +199,14 @@ class UnitPriceModel extends BaseModel {
             throw '找不到对应的单价数据';
         }
 
-        // 基价单价的计算
+       /* // 基价单价的计算-----先不考虑同步
         switch (unitPriceData.type) {
             // 主材、设备自动赋值基价单价=市场单价
             case GLJTypeConst.MAIN_MATERIAL:
             case GLJTypeConst.EQUIPMENT:
                 updateData.base_price = updateData.market_price;
                 break;
-        }
+        }*/
 
         // 额外更新数据
         if (extend !== '') {
@@ -214,6 +230,79 @@ class UnitPriceModel extends BaseModel {
         return result.ok !== undefined && result.ok === 1;
     }
 
+    async updateUnitPrice(data){
+        //查找并更新单价
+        let doc={};
+        doc[data.field]=data.newval;
+        let unitPrice = await this.db.findAndModify({id:data.id},doc);
+        if(!unitPrice){
+            throw "没有找到对应的单价";
+        }
+        //查找是否是属于某个项目工料机的组成物
+        let mixRatioModel = new MixRatioModel();
+        let condition = {unit_price_file_id:unitPrice.unit_price_file_id, code:unitPrice.code,name: unitPrice.name, specs: unitPrice.specs,unit:unitPrice.unit,type:unitPrice.type};
+        let mixRatioList = await mixRatioModel.findDataByCondition(condition, null, false);
+        let connectKeyMap={};
+        //找到则计算项目工料机组成物的价格并更新
+        let rList= [];
+        if(mixRatioList&&mixRatioList.length>0){
+            for(let m of mixRatioList){
+                if(!connectKeyMap.hasOwnProperty(m.connect_key)){//为了去重复,组成物会与其它项目同步,所以有可能重复。
+                    rList.push(await this.updateParentUnitPrice(m,data.field));
+                    connectKeyMap[m.connect_key]=true;
+                }
+            }
+        }
+        return rList;
+    }
+
+    async updateParentUnitPrice(mixRatio,fieid){
+        //查找该工料机所有组成物
+        let indexList = ['code','name','specs','unit','type'];
+        let mixRatioModel = new MixRatioModel();
+        let mixRatioMap = await mixRatioModel.findDataByCondition({unit_price_file_id:mixRatio.unit_price_file_id,connect_key:mixRatio.connect_key}, null, false,indexList);
+        //查找对应的价格
+        let codeList = [];
+        let nameList = [];
+        let specsList= [];
+        let typeList = [];
+        let unitList = [];
+        for(let mk in mixRatioMap){
+            codeList.push(mixRatioMap[mk].code);
+            nameList.push(mixRatioMap[mk].name);
+            specsList.push(mixRatioMap[mk].specs);
+            typeList.push(mixRatioMap[mk].type);
+            unitList.push(mixRatioMap[mk].unit);
+        }
+        let condition = {unit_price_file_id: mixRatio.unit_price_file_id,code: {"$in": codeList}, name: {"$in": nameList},specs:{"$in": specsList},type:{"$in": typeList},unit:{"$in": unitList}};
+        let priceMap = await this.findDataByCondition(condition, {_id: 0}, false, indexList);
+        let sumPrice=0;
+        for(let pk in priceMap){
+            let price = parseFloat(priceMap[pk][fieid]);
+            let consumption = parseFloat(mixRatioMap[pk].consumption);
+            sumPrice +=scMathUtil.roundTo(price*consumption,-6);
+        }
+        sumPrice= scMathUtil.roundTo(sumPrice,-6);
+        if(sumPrice<=0){
+            return null;
+        }
+        //更新父价格
+        let keyList = mixRatio.connect_key.split("|-|");
+        let pcondition = {
+            unit_price_file_id:mixRatio.unit_price_file_id,
+            code:keyList[0]
+        };
+        for(let i = 1;i<keyList.length;i++){
+            if(keyList[i]!='null'){
+                pcondition[indexList[i]]=keyList[i];
+            }
+        }
+        let doc={};
+        doc[fieid]=sumPrice;
+        let uprice =  await this.db.findAndModify(pcondition,doc);
+        uprice[fieid]=sumPrice;
+        return uprice;
+    }
     /**
      * 复制单价文件数据
      *

+ 1 - 0
modules/glj/routes/glj_router.js

@@ -21,6 +21,7 @@ router.post('/get-project-info', gljController.init, gljController.getProjectInf
 router.post('/change-file', gljController.init, gljController.changeUnitPriceFile);
 router.post('/save-as', gljController.init, gljController.unitPriceSaveAs);
 router.post('/get-composition', gljController.init, gljController.getComposition);
+router.post('/updatePrice', gljController.init, gljController.updateUnitPrice);
 
 router.get('/test', gljController.init, gljController.test);
 router.get('/testModify', gljController.init, gljController.testModify);

+ 62 - 26
modules/pm/controllers/pm_controller.js

@@ -58,6 +58,13 @@ module.exports = {
             }
         });
     },
+    updateFiles: async function(req, res){
+        let data = JSON.parse(req.body.data);
+        let updateDatas = data.updateDatas;
+        await ProjectsData.udpateUserFiles(req.session.sessionUser.ssoId, updateDatas, function (err, message, data) {
+            callback(req, res, err, message, data);
+        });
+    },
     copyProjects: function (req, res) {
         let data = JSON.parse(req.body.data);
         ProjectsData.copyUserProjects(req.session.sessionUser.ssoId, data.updateData, function (err, message, data) {
@@ -145,14 +152,14 @@ module.exports = {
             if (isNaN(projectId) && projectId <= 0) {
                 throw {msg: 'id数据有误!', err: 1};
             }
-            // 获取对应建设项目下所有的单位工程id
+            /*// 获取对应建设项目下所有的单位工程id
             let idList = await ProjectsData.getTenderByProjectId(projectId);
             if (idList.length <= 0) {
                 throw {msg: '不存在对应单位工程', err: 0};
-            }
+            }*/
             // 获取对应的单价文件
             let unitPriceFileModel = new UnitPriceFileModel();
-            let unitPriceFileData = await unitPriceFileModel.getDataByTenderId(idList);
+            let unitPriceFileData = await unitPriceFileModel.getDataByRootProject(projectId);
 
             if (unitPriceFileData === null) {
                 throw {msg: '不存在对应单价文件', err: 0};
@@ -194,50 +201,79 @@ module.exports = {
     },
 
     getGCDatas: async function(request, response) {
-        let userID = req.session.sessionUser.ssoId;
+        let userID = request.session.sessionUser.ssoId;
         let rst = [];
         let _projs = Object.create(null), _engs = Object.create(null), prefix = 'ID_';
         try{
             let gc_unitPriceFiles = await ProjectsData.getGCFiles(fileType.unitPriceFile, userID);
             let gc_feeRateFiles = await ProjectsData.getGCFiles(fileType.feeRateFile, userID);
             let gc_tenderFiles = await ProjectsData.getGCFiles(projType.tender, userID);
-                for(let i = 0, len = gc_unitPriceFiles.length; i < len; i++){
-                    let gc_uf = gc_unitPriceFiles[i];
-                    let theProj = _projs[prefix + gc_uf.root_project_id] || null;
-                    if(!theProj){
-                        theProj = _projs[prefix + gc_uf.root_project_id] = await ProjectsData.getProjectsByIds([gc_uf.root_project_id]);
+            for(let i = 0, len = gc_unitPriceFiles.length; i < len; i++){
+                let gc_uf = gc_unitPriceFiles[i];
+                let theProj = _projs[prefix + gc_uf.root_project_id] || null;
+                if(!theProj){
+                    let tempProj = await ProjectsData.getProjectsByIds([gc_uf.root_project_id]);
+                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                        theProj = _projs[prefix + gc_uf.root_project_id] = tempProj[0]._doc;
                         buildProj(theProj);
                     }
+                }
+                if(theProj){
                     theProj.unitPriceFiles.push(gc_uf);
                 }
-                for(let i = 0, len = gc_feeRateFiles.length; i < len; i++){
-                    let gc_ff = gc_feeRateFiles[i];
-                    let theProj = _projs[prefix + gc_ff.rootProjectID] || null;
-                    if(!theProj){
-                        theProj = _projs[prefix + gc_ff.rootProjectID] = await ProjectsData.getProjectsByIds([gc_ff.rootProjectID]);
+            }
+            for(let i = 0, len = gc_feeRateFiles.length; i < len; i++){
+                let gc_ff = gc_feeRateFiles[i];
+                let theProj = _projs[prefix + gc_ff.rootProjectID] || null;
+                if(!theProj){
+                    let tempProj = await ProjectsData.getProjectsByIds([gc_ff.rootProjectID]);
+                    if(tempProj.length > 0 && tempProj[0].projType !== projType.folder){
+                        theProj = _projs[prefix + gc_ff.rootProjectID] = tempProj[0]._doc;
                         buildProj(theProj);
                     }
+                }
+                if(theProj) {
                     theProj.feeRateFiles.push(gc_ff);
                 }
+            }
             if(gc_tenderFiles.length > 0){
                 for(let i = 0, len = gc_tenderFiles.length; i < len; i++){
                     let gc_t = gc_tenderFiles[i];
                     let theEng = _engs[prefix + gc_t.ParentID] || null;
                     if(!theEng){
-                        theEng = _engs[prefix + gc_t.ParentID] = await ProjectsData.getProjectsByIds([gc_t.ParentID]);
-                        theEng.children = [];
+                        let tempEngs = await ProjectsData.getProjectsByIds([gc_t.ParentID]);
+                        if(tempEngs.length > 0 && tempEngs[0].projType === projType.engineering){
+                            theEng = _engs[prefix + gc_t.ParentID] = tempEngs[0]._doc;
+                            theEng.children = [];
+                        }
                     }
-                    theEng.children.push(gc_t);
-                    let theProj = _projs[prefix + theEng.ParentID] || null;
-                    if(!theProj){
-                        theProj = _projs[prefix + theEng.ParentID] = await ProjectsData.getProjectsByIds([theEng.ParentID]);
-                        buildProj(theProj);
+                    if(theEng) {
+                        theEng.children.push(gc_t);
+                        let theProj = _projs[prefix + theEng.ParentID] || null;
+                        if(!theProj){
+                            let tempProj = await ProjectsData.getProjectsByIds([theEng.ParentID]);
+                            if(tempProj.length > 0 && tempProj[0].projType === projType.project){
+                                theProj = _projs[prefix + theEng.ParentID] = tempProj[0]._doc;
+                                buildProj(theProj);
+                            }
+                        }
+                        if(theProj) {
+                            let isExist = false;
+                            for(let j = 0, jLen = theProj.children.length; j < jLen; j++){
+                                if(theProj.children[j].ID === theEng.ID){
+                                    isExist = true;
+                                    break;
+                                }
+                            }
+                            if(!isExist){
+                                theProj.children.push(theEng);
+                            }
+                        }
                     }
-                    theProj.children.push(theEng);
                 }
             }
-            for(let i of _projs){
-                rst.push(i);
+            for(let i in _projs){
+                rst.push(_projs[i]);
             }
             function buildProj(proj){
                 proj.children = [];
@@ -253,10 +289,10 @@ module.exports = {
 
     recGC: function(request, response){
         let userID = request.session.sessionUser.ssoId;
-        let nodes = JSON.parse(request.body.nodes);
+        let data = JSON.parse(request.body.data);
+        let nodes = data.nodes;
         ProjectsData.recGC(userID, nodes, function (err, msg, data) {
             callback(request, response, err, msg, data);
         });
     }
-
 };

+ 164 - 33
modules/pm/models/project_model.js

@@ -1,10 +1,10 @@
 /**
  * Created by Mai on 2017/1/18.
  */
-import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
+import mongoose from 'mongoose';
 import async_c from 'async';
+import UnitPriceFileModel from "../../glj/models/unit_price_file_model";
 import UnitPriceFiles from '../../glj/models/schemas/unit_price_file';
-import mongoose from 'mongoose';
 let FeeRateFiles = mongoose.model('fee_rate_file');
 let counter = require("../../../public/counter/counter.js");
 
@@ -22,11 +22,17 @@ let projectType = {
     project: 'Project',
     engineering: 'Engineering',
 };
-//回收站恢复级别
 let fileType = {
     unitPriceFile: 'UnitPriceFile',
     feeRateFile: 'FeeRateFile'
 };
+//默认的小数位数,用于定义用户可编辑的字段(入库),用户不可编辑的字段在前端defaultDecima._def中定义即可
+const defaultDecimal = {
+    bills: {unitPrice: 2, totalPrice: 2},
+    ration: {quantity: 3, unitPrice: 2, totalPrice: 2},
+    glj: {quantity: 3, unitPrice: 2},
+    feeRate: 2,
+};
 
 let ProjectsDAO = function(){};
 
@@ -93,7 +99,10 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                         callback(1, '新增单价文件失败.', null);
                         return;
                     }
-                    data.updateData.property.unitPriceFile.id = addResult.id + '';
+                    data.updateData.property.unitPriceFile.id = addResult.id;
+                }
+                if(data.updateData.projType === projectType.tender){
+                    data.updateData.property.decimal = defaultDecimal;
                 }
                 newProject = new Projects(data.updateData);
                 // 查找同级是否存在同名数据
@@ -103,7 +112,7 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                     return;
                 }
                 if(data.updateData.projType==='Tender'){
-                    let  feeRateFileID = await feeRateFacade.newFeeRateFile(data.updateData);
+                    let  feeRateFileID = await feeRateFacade.newFeeRateFile(userId, data.updateData);
                     newProject.property.feeFile = feeRateFileID?feeRateFileID:-1;
 
                     // 新建人工系数文件 CSL, 2017.10.13
@@ -126,7 +135,54 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                 deleteInfo['deleteDateTime'] = new Date();
                 deleteInfo['deleteBy'] = userId;
                 data.updateData['deleteInfo'] = deleteInfo;
-                Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                //Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                //update
+                try{
+                    if(data.updateData.projType === projectType.project){
+                        let engineerings = await Projects.find({userID: userId, ParentID: data.updateData.ID});
+                        let isExist = false;
+                        if(engineerings.length > 0){
+                            for(let j = 0, jLen = engineerings.length; j < jLen; j++){
+                                let e_tenders = await Projects.find({userID: userId, ParentID: engineerings[j].ID});
+                                if(e_tenders.length > 0){
+                                    isExist = true;
+                                    break;
+                                }
+                            }
+                        }
+                        if(isExist){//fake
+                            await UnitPriceFiles.update({user_id: userId, root_project_id: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                            await FeeRateFiles.update({userID: userId, rootProjectID: data.updateData.ID}, {$set: {deleteInfo: deleteInfo}}, {multi: true});
+                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                        }
+                        else {//true
+                            await UnitPriceFiles.remove({user_id: userId, root_project_id: data.updateData.ID});
+                            await FeeRateFiles.remove({userID: userId, rootProjectID: data.updateData.ID});
+                            //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
+                            await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                        }
+                    }
+                    else if(data.updateData.projType === projectType.engineering){
+                        let tenders = await Projects.find({userID: userId, ParentID: data.updateData.ID});
+                        if(tenders.length > 0){//fake
+                            await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                        }
+                        else {//true
+                            //await Projects.update({userID: userId, NextSiblingID: data.updateData.ID, deleteInfo: null}, {$set: {NextSiblingID: data.NextSiblingID}});
+                            await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                        }
+                    }
+                    else if(data.updateData.projType === projectType.tender){//fake
+                        await Projects.update({userID: userId, ID: data.updateData.ID}, data.updateData, updateAll);
+                    }
+                    else if(data.updateData.projType === projectType.folder){//true
+                        await Projects.remove({userID: userId, ID: data.updateData.ID}, updateAll);
+                    }
+                    else throw '未知文件类型,删除失败!';
+                }
+                catch (error){
+                    callback(1, error, null);
+                }
             } else {
                 hasError = true;
                 callback(1, '提交数据错误.', null);
@@ -134,6 +190,42 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
         }
     }
 };
+
+ProjectsDAO.prototype.udpateUserFiles = async function (userId, datas, callback){
+    let updateType = {update: 'update', delete: 'delete'};
+    let deleteInfo = Object.create(null);
+    deleteInfo.deleted = true;
+    deleteInfo.deleteBy = userId;
+    deleteInfo.deleteDateTime = new Date();
+    try{
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i];
+            if(data.updateType === updateType.update && data.fileType === fileType.unitPriceFile){
+                await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
+                await Projects.update({userID: userId, 'property.unitPriceFile.id': data.updateData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.update && data.fileType === fileType.feeRateFile){
+                await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
+                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.ID}, {$set: {'property.feeFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.delete && data.fileType === fileType.unitPriceFile){
+                data.updateData.deleteInfo = deleteInfo;
+                await UnitPriceFiles.update({user_id: userId, id: parseInt(data.updateData.id)}, data.updateData);
+                await Projects.update({userID: userId, 'property.feeFile.id': data.updateData.id}, {$set: {'property.feeFile.name': data.updateData.name}});
+            }
+            else if(data.updateType === updateType.delete && data.fileType === fileType.feeRateFile){
+                data.updateData.deleteInfo = deleteInfo;
+                await FeeRateFiles.update({userID: userId, ID: data.updateData.ID}, data.updateData);
+            }
+            else throw '未知文件类型,删除失败'
+        }
+        callback(false, '删除成功', null);
+    }
+    catch(error){
+        callback(true, '删除失败', null);
+    }
+};
+
 ProjectsDAO.prototype.copyUserProjects = function (userId, datas, callback) {
     let data, project, updateLength = 0, hasError = false, deleteInfo = null, tempType = 1, i;
     let updateAll = function (err) {
@@ -250,41 +342,45 @@ ProjectsDAO.prototype.getGCFiles = async function (fileType, userID){
     }
     else {
         let isExist = false;
-        for(let type of projectType){
-            if(type === fileType) isExist = true;
-            break;
+        for(let type in projectType){
+            if(projectType[type] === fileType) {
+                isExist = true;
+                break;
+            }
         }
         if(!isExist) throw '不存在此项目类型!';
-        rst = await Projects.find({projType: fileType, 'deleteInfo.deleted': true});
+        rst = await Projects.find({userID: userID, projType: fileType, 'deleteInfo.deleted': true});
     }
     return rst;
 };
 
-ProjectsDAO.prototype.getFirstNodeID = async function (userID, projType) {
-    if(projType !== projectType.project) throw '只能为建设项目层';
-    let nodes = await Projects.find({userID: userID, projType: projType, deleteInfo: null});
+ProjectsDAO.prototype.getFirstNodeID = async function (userID, pid) {
+    let nodes = await Projects.find({userID: userID, ParentID: pid, deleteInfo: null});
     if(nodes.length === 0){
         return -1;
     }
     else {
+        let prefix = 'ID_';
         let chain = Object.create(null);
         for(let i = 0, len = nodes.length; i < len; i++){
+            let nodeDoc = nodes[i]._doc;
             let node = Object.create(null);
-            node.ID = nodes[i].ID;
-            node.NextSiblingID = nodes[i].NextSiblingID;
-            chain[node.ID] = node;
+            node.ID = nodeDoc.ID;
+            node.NextSiblingID = nodeDoc.NextSiblingID;
+            chain[prefix + node.ID] = node;
         }
         for(let i =0, len = nodes.length; i < len; i++){
-            let next = nodes[i].NextSiblingID > 0 ? chain[nodes[i].NextSiblingID] : null;
-            chain[nodes[i].ID].next = next;
+            let nodeDoc = nodes[i]._doc;
+            let next = nodeDoc.NextSiblingID > 0 ? chain[prefix + nodeDoc.NextSiblingID] : null;
+            chain[prefix + nodeDoc.ID].next = next;
             if(next){
-                next.pre = chain[nodes[i].ID]
+                next.pre = chain[prefix + nodeDoc.ID]
             }
         }
-        for(let node of chain){
-            let pre = node.pre || null;
+        for(let node in chain){
+            let pre = chain[node].pre || null;
             if(!pre){
-                return node.ID;
+                return chain[node].ID;
             }
         }
     }
@@ -292,34 +388,69 @@ ProjectsDAO.prototype.getFirstNodeID = async function (userID, projType) {
 
 ProjectsDAO.prototype.recGC = async function(userID, datas, callback){
     let functions = [];
+    let updateDatas = [];
     for(let i = 0, len = datas.length; i < len; i++){
-        if(datas[i].updateType === projectType.project && datas[i].updateData.NextSiblingID === undefined){//则为待查询NextSiblingID,前端无法查询
-            let firstNodeID = await this.getFirstNodeID(userID, projectType.project);
-            datas[i].updateData.NextSiblingID = firstNodeID;
+        if(datas[i].findData.ParentID !== undefined && datas[i].findData.NextSiblingID === -1 && !datas[i].findData.deleteInfo){//维护项目管理树
+            let findNode = await Projects.find(datas[i].findData);
+            if(findNode.length > 0){
+                datas[i].findData = Object.create(null);
+                datas[i].findData.ID = findNode[0].ID;
+                updateDatas.push(datas[i]);
+            }
         }
+        else {
+            if(datas[i].updateType === projectType.project && datas[i].updateData.NextSiblingID === undefined){//则为待查询NextSiblingID,前端无法查询
+                let projData = await Projects.find({userID: userID, ID: datas[i].findData.ID});//建设项目原本可能属于某文件夹、文件夹的存在需要判断
+                let projPid = projData[0].ParentID;
+                if(projPid !== -1){
+                    let projFolder = await Projects.find({userID: userID, ID: projPid});
+                    if(projFolder.length === 0){//文件夹已不存在
+                        projPid = -1;
+                        datas[i].updateData.ParentID = -1;
+                    }
+                }
+                let firstNodeID = await this.getFirstNodeID(userID, projPid);
+                datas[i].updateData.NextSiblingID = firstNodeID;
+            }
+            updateDatas.push(datas[i])
+        }
+    }
+    for(let i = 0, len = updateDatas.length; i < len; i ++){
         functions.push((function(data){
                 return function (cb) {
                     if(data.updateType === fileType.unitPriceFile){
-                        UnitPriceFiles.update(data.findData, data.updateData, function (err) {
+                        UnitPriceFiles.update({id: parseInt(data.findData.id)}, data.updateData, function (err) {
                             if(err) cb(err);
-                            else cb(false);
+                            else {
+                                Projects.update({userID: userID, 'property.unitPriceFile.id': data.findData.id}, {$set: {'property.unitPriceFile.name': data.updateData.name}}, function (err) {
+                                    if(err) cb(err);
+                                    else cb(false);
+                                });
+                            }
                         })
                     }
                     else if(data.updateType === fileType.feeRateFile){
                         FeeRateFiles.update(data.findData, data.updateData, function (err) {
                             if(err) cb(err);
-                            else cb(false);
+                            else {
+                                Projects.update({userID: userID, 'property.feeFile.id': data.findData.ID}, {$set: {'property.feeFile.name': data.updateData.name}}, function (err) {
+                                    if(err) cb(err);
+                                    else cb(false);
+                                });
+                            }
                         });
                     }
                     else{
-                        Projects.update(data.findData, data.updateData, function (err) {
-                            if(err)cb(err);
-                            else cb(false);
-                        });
+                        if(data){
+                            Projects.update(data.findData, data.updateData, function (err) {
+                                if(err)cb(err);
+                                else cb(false);
+                            });
+                        }
                     }
                 }
             }
-        )(datas[i]));
+        )(updateDatas[i]));
     }
     async_c.parallel(functions, function (err, results) {
         if(err) callback(err, 'fail', null);

+ 1 - 4
modules/pm/routes/pm_route.js

@@ -11,10 +11,6 @@ module.exports = function (app) {
 
     app.get('/pm', baseController.init, pmController.index);
 
-    app.get('/pm/gc',baseController.init, function (req, res) {
-        res.render('building_saas/pm/html/project-management-Recycle.html');
-    });
-
     let pmRouter = express.Router();
 
     pmRouter.use(function (req, res, next) {
@@ -47,6 +43,7 @@ module.exports = function (app) {
     pmRouter.post('/getNewProjectID', pmController.getNewProjectID);
     pmRouter.post('/getUnitFile', pmController.getUnitFileList);
     pmRouter.post('/getFeeRateFile', pmController.getFeeRateFileList);
+    pmRouter.post('/updateFiles', pmController.updateFiles);
     //GC
     pmRouter.post('/getGCDatas', pmController.getGCDatas);
     pmRouter.post('/recGC', pmController.recGC);

+ 20 - 2
modules/ration_glj/controllers/ration_glj_controller.js

@@ -12,7 +12,8 @@ module.exports={
     getGLJData:getGLJData,
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
-    mReplaceGLJ:mReplaceGLJ
+    mReplaceGLJ:mReplaceGLJ,
+    updateRationGLJByEdit:updateRationGLJByEdit
 }
 function createRationGLJ() {
     let gls = mongoose.model('ration_glj');
@@ -42,7 +43,6 @@ async function getGLJData(req, res) {
     }
     try {
         let info = await ration_glj_facade.getLibInfo(req);
-        console.log(info);
         ration_glj_facade.getGLJData(info,function (err,datas) {
             if(err){
                 result.error=1;
@@ -111,4 +111,22 @@ async function mReplaceGLJ(req, res){
         result.message = err.message;
     }
     res.json(result);
+}
+
+async  function updateRationGLJByEdit(req, res) {
+    let result={
+        error:0
+    }
+    try {
+        let data = req.body.data;
+        data = JSON.parse(data);
+        let uresult= await ration_glj_facade.updateRationGLJByEdit(data);
+        result.data=uresult;
+    }catch (err){
+        logger.err(err);
+        result.error=1;
+        result.message = err.message;
+    }
+    res.json(result);
+    
 }

+ 5 - 5
modules/ration_glj/facade/glj_calculate_facade.js

@@ -10,7 +10,7 @@ let ration = mongoose.model('ration');
 let ration_coe = mongoose.model('ration_coe');
 let std_ration_lib_ration_items = mongoose.model('std_ration_lib_ration_items');
 let glj_type_util = require('../../../public/cache/std_glj_type_util');
-
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 module.exports={
     calculateQuantity:calculateQuantity,
@@ -131,7 +131,7 @@ function calculateAss(quantity,assList,glj) {
             }
         }
     }
-    return quantity;
+    return scMathUtil.roundTo(quantity,-6);
 }
 
 function generateAdjustState(glj,coeList,adjustState,index) {
@@ -187,7 +187,7 @@ function calculateTimes(ass){
     if(r){
         times=times*-1;
     }
-    return times;
+    return scMathUtil.roundTo(times,-6);
 }
 
 function calculateQuantityByCoes(quantity,coeList,glj){
@@ -197,7 +197,7 @@ function calculateQuantityByCoes(quantity,coeList,glj){
             coeQuantity = everyCoe(coeQuantity,coeList[i],glj);
         }
     }
-    return coeQuantity;
+    return scMathUtil.roundTo(coeQuantity,-6);
 }
 
 function everyCoe(quantity,coe,glj) {
@@ -213,7 +213,7 @@ function everyCoe(quantity,coe,glj) {
             }
         }
     }
-    return coeQuantity;
+    return scMathUtil.roundTo(coeQuantity,-6);
 }
 
 

+ 4 - 6
modules/ration_glj/facade/quantity_detail_facade.js

@@ -14,7 +14,7 @@ let quantity_detail_model = mongoose.model('quantity_detail');
 const uuidV1 = require('uuid/v1');
 let ration_model = mongoose.model('ration');
 let bill_model=mongoose.model("bills");
-
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 module.exports={
     save:save,
@@ -238,7 +238,6 @@ function normalUpdate(user_id,datas) {
 }
 function updateQuantityRegex(user_id,datas) {
     return function(callback){
-        console.log(datas);
         doRegexUpdate(datas).then(function (result) {
             if(result.err){
                 callback(null,{
@@ -422,8 +421,9 @@ function  getEvalResult(referenceIndexs,detailList,regex) {
         console.log('replace all C reference -----'+regex);
         regex =replaceSqr(regex);
         console.log('replace all sqar reference -----'+regex);
-        return _.round(eval(regex), 4);
+        return scMathUtil.roundTo(eval(regex), -4);
     }catch (error){
+        console.log(error);
         throw new Error('输入的表达式有误,请重新输入!');
     }
 
@@ -523,7 +523,6 @@ function update_quantity_detail(user_id,datas) {
 function delete_quantity_detail(user_id,datas) {
     return function (callback) {
         doQuantityDelete(datas.doc).then(function (result) {
-            console.log(result);
             if(result.err){
                 callback(result.err,'')
             }else {
@@ -533,8 +532,7 @@ function delete_quantity_detail(user_id,datas) {
     }
 }
 
-async function doQuantityDelete(doc) {
-   console.log(doc) ;
+async function doQuantityDelete(doc) {;
     let result={
         err:null
     }

+ 1 - 0
modules/ration_glj/facade/ration_coe_facade.js

@@ -11,6 +11,7 @@ let async_n = require("async");
 let coeMolde = mongoose.model('std_ration_lib_coe_list');
 let ration_coe = mongoose.model('ration_coe');
 let glj_calculate_facade = require('./glj_calculate_facade');
+const scMathUtil = require('../../../public/scMathUtil').getUtil();
 
 
 module.exports={

+ 89 - 115
modules/ration_glj/facade/ration_glj_facade.js

@@ -32,7 +32,8 @@ module.exports={
     getGLJData:getGLJData,
     addGLJ:addGLJ,
     replaceGLJ:replaceGLJ,
-    mReplaceGLJ:mReplaceGLJ
+    mReplaceGLJ:mReplaceGLJ,
+    updateRationGLJByEdit:updateRationGLJByEdit
 }
 
 let operationMap={
@@ -41,9 +42,7 @@ let operationMap={
     'ut_delete':delete_ration_glj
 };
 let updateFunctionMap = {
-    'normalUpdate':normalUpdate,
-    'marketPriceAdjustUpdate':marketPriceAdjustUpdate,
-    'customQuantityUpdate':customQuantityUpdate
+    'normalUpdate':normalUpdate
 };
 
 
@@ -82,7 +81,7 @@ function combineQuantity(results,rations) {
     _.forEach(results,function (data) {
         let tmp = {
             projectGLJID:data.projectGLJID,
-            quantity: data.quantity
+            quantity: Number(data.quantity)
         }
         let ration=_.find(rations,{ID:data.rationID});
         if(ration){
@@ -117,6 +116,7 @@ function get_lib_glj_info(ration_glj) {
             }else if(glj){
                 ration_glj.name = glj.name;
                 ration_glj.code = glj.code;
+                ration_glj.original_code=glj.code;
                 ration_glj.unit = glj.unit;
                 ration_glj.specs = glj.specs;
                 ration_glj.basePrice = glj.basePrice;
@@ -152,6 +152,7 @@ function createNewRecord(ration_glj) {
     newRecoed.quantity=ration_glj.quantity;
     newRecoed.name = ration_glj.name;
     newRecoed.code = ration_glj.code;
+    newRecoed.original_code=ration_glj.original_code;
     newRecoed.unit = ration_glj.unit;
     newRecoed.specs = ration_glj.specs;
     newRecoed.from=ration_glj.from?ration_glj.from:undefined;
@@ -174,6 +175,9 @@ async function getInfoFromProjectGLJ(ration_glj) {
          ration_glj.basePrice=result.unit_price.base_price;
          ration_glj.projectGLJID=result.id;
          ration_glj.isEstimate=result.is_evaluate;
+         if(result.hasOwnProperty('subList')&&result.subList.length>0){
+             ration_glj.subList=getMixRatioShowDatas(result.subList);
+         }
          return ration_glj;
      } catch (err) {
          logger.err(err);
@@ -182,6 +186,29 @@ async function getInfoFromProjectGLJ(ration_glj) {
 
  }
 
+ function getMixRatioShowDatas(subList) {
+     var temRationGLJs = [];
+     for(let pg of subList){
+         var tem = {
+             projectGLJID:pg.id,
+             code:pg.code,
+             name:pg.name,
+             specs:pg.specs,
+             unit:pg.unit,
+             shortName:pg.unit_price.short_name,
+             rationItemQuantity:pg.ratio_data.consumption,
+             basePrice:pg.unit_price.base_price,
+             marketPrice:pg.unit_price.market_price,
+             adjustPrice:pg.adjust_price,
+             isEstimate:pg.is_evaluate,
+             isMixRatio:true
+         }
+         temRationGLJs.push(tem);
+     }
+     temRationGLJs=_.sortBy(temRationGLJs,'code');
+     return temRationGLJs;
+ }
+
 function create_ration_glj(user_id,datas) {
     return function (callback) {
         let ration_glj_list=datas.ration_glj_list;
@@ -223,7 +250,6 @@ function create_ration_glj(user_id,datas) {
                     logger.info("can't find gljs")
                     callback(null,null)
                 }
-
             }
         })
     }
@@ -258,122 +284,15 @@ function normalUpdate(user_id,datas){
     }
 }
 
-function customQuantityUpdate(user_id,datas){
-    return function(callback) {
-        doCustomQuantityUpdate(datas).then((result)=>{
-            if(result.err){
-                callback(result.err,'');
-            }else {
-                let ration_glj_data ={
-                    moduleName:'ration_glj',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        quantityRefresh:true,
-                        glj_result:result.cal_result.glj_result
-                    }
-                };
-                let ration_data ={
-                    moduleName:'ration',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        stateRefresh:true,
-                        rationID:result.cal_result.rationID,
-                        adjustState:result.cal_result.adjustState
-                    }
-                };
-                callback(null,[ration_glj_data,ration_data]);
-            }
-        })
-    }
-}
-
 async function doCustomQuantityUpdate(datas){
-    let result = {
-        err:null
-    }
-    try{
-        await ration_glj.update(datas.query,datas.doc);
+        let result =  await ration_glj.findOneAndUpdate(datas.query,datas.doc);
         let cal_result = await glj_calculate_facade.calculateQuantity({projectID:datas.query.projectID,rationID:datas.query.rationID});
-
         cal_result.glj_result.forEach(function (item) {
            if(!item.doc.hasOwnProperty('customQuantity')){
                item.doc.customQuantity=null;
            }
         });
-        result.cal_result =cal_result;
-        console.log(result);
-        return result;
-    }catch (err){
-        result.err = err;
-        console.log(err);
-        return result;
-    }
-}
-
-
-function marketPriceAdjustUpdate(user_id,datas) {
-    return function (callback) {
-        updateprojectGljAndRationGLJ(datas.query,datas.doc).then((result)=>{
-            if(result.err){
-                callback(result.err,'');
-            }else {
-                let returndata ={
-                    moduleName:'ration_glj',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        query:datas.query,
-                        doc:result.doc
-                    }
-                };
-                let ration_data ={
-                    moduleName:'ration',
-                    data:{
-                        updateTpye:commonConsts.UT_UPDATE,
-                        stateRefresh:true,
-                        rationID:datas.query.rationID,
-                        adjustState:result.adjustState
-                    }
-                }
-                callback(null,[returndata,ration_data]);
-            }
-        })
-
-    }
-}
-
-async function updateprojectGljAndRationGLJ(query,doc) {
-    let returnresult={};
-    try {
-        let gljListModel = new GLJListModel();
-        let temp = doc.market_price;
-        if(doc.market_price==null){
-            doc.market_price = doc.base_price;
-        }
-        delete doc.base_price;
-        let result = await gljListModel.modifyMarketPrice(doc);
-        let updateDoc ={
-            marketPrice:result.unit_price.market_price,
-            marketPriceAdjust:temp,
-            projectGLJID:result.id,
-            isEstimate:result.is_evaluate,
-            name : result.name,
-            code:result.code
-        };
-        let updateresult = await ration_glj.findOneAndUpdate(query, updateDoc);
-        let stateResult =  await glj_calculate_facade.calculateQuantity({projectID:query.projectID,rationID:query.rationID},true);
-
-         returnresult ={
-            err:null,
-            doc :updateDoc,
-            adjustState:stateResult.adjustState
-        }
-        return returnresult;
-
-        } catch (error) {
-           returnresult.err = error;
-            console.log(error);
-            return returnresult
-         }
+        return cal_result;
 }
 
 
@@ -622,6 +541,7 @@ function getGLJSearchInfo(ration_glj) {
         glj_id: ration_glj.GLJID,
         project_id: ration_glj.projectID,
         code: ration_glj.code,
+        original_code:ration_glj.original_code,
         name: ration_glj.name,
         shortName:ration_glj.shortName,
         specs: ration_glj.specs,
@@ -633,6 +553,9 @@ function getGLJSearchInfo(ration_glj) {
         repositoryId:ration_glj.repositoryId,
         from:ration_glj.from?ration_glj.from:'std'//std:标准工料机库, cpt:补充工料机库
     };
+     if(data.from=='cpt'){//从补充工料机来的数据即为新增数据
+         data.is_add = 1;
+     }
     return data;
 }
 
@@ -648,6 +571,9 @@ async function addGLJ(rgList) {
        g.projectGLJID=result.id;
        g.isEstimate=result.is_evaluate;
        g.ID=uuidV1();
+       if(result.hasOwnProperty('subList')&&result.subList.length>0){
+           g.subList=getMixRatioShowDatas(result.subList);
+       }
        newRecodes.push(createNewRecord(g));
    }
     await ration_glj.insertMany(newRecodes);
@@ -693,6 +619,54 @@ async function mReplaceGLJ(data) {
     return mresult
 }
 
+async function updateRationGLJByEdit(data) {
+    var doc = data.doc;
+    var result;
+    if(doc.hasOwnProperty('customQuantity')){
+      result = await doCustomQuantityUpdate(data)
+    }else {
+       result = await doRationGLJUpdate(data);
+    }
+    return result;
+}
+
+async function doRationGLJUpdate(data){
+    let resutl = {};
+    let doc = data.doc;
+    let priceInfo = data.priceInfo;
+    let rg =  await ration_glj.findOne(data.query);
+    let gljListModel = new GLJListModel();
+    let projectGLJ= getGLJSearchInfo(rg);
+    for(let key in doc){
+        projectGLJ[key]=doc[key]
+    }
+    projectGLJ.base_price = priceInfo.base_price;
+    projectGLJ.market_price = priceInfo.market_price;
+    let projcetGLJ_n = await gljListModel.modifyGLJ(projectGLJ,rg);
+    doc.code = projcetGLJ_n.code;
+    doc.projectGLJID=projcetGLJ_n.id;
+    if(projcetGLJ_n.unit_price.is_add==1){
+        doc.createType='replace';
+        doc.rcode=projcetGLJ_n.original_code;
+    }else {
+        doc.createType='normal';
+        doc.rcode='';
+    }
+    await ration_glj.findOneAndUpdate(data.query,doc);
+    //取价格
+    gljListModel.getGLJPrice(projcetGLJ_n);
+    doc.basePrice=projcetGLJ_n.unit_price.base_price;
+    doc.marketPrice=projcetGLJ_n.unit_price.market_price;
+    doc.adjustPrice=projcetGLJ_n.adjust_price;
+    doc.isAdd = projcetGLJ_n.unit_price.is_add;
+    resutl.doc = doc;
+    let stateResult = await glj_calculate_facade.calculateQuantity({projectID:data.query.projectID,rationID:data.query.rationID});
+    resutl.adjustState= stateResult.adjustState;
+    return resutl;
+}
+
+
+
 async function changAdjustState(data,rationList) {
     let stateList=[];
     for(let r of rationList){

+ 8 - 4
modules/ration_glj/models/ration_glj.js

@@ -13,16 +13,20 @@ var ration_glj = new Schema({
     projectGLJID:Number,
     name:String,
     code:String,
+    //原始的编码
+    original_code: {
+        type: String,
+        index: true
+    },
     rcode:String,
     specs:String,
     unit:String,
     shortName:String,
     billsItemID: Number,
     type:Number,
-    quantity:Number,
-    customQuantity:Number,
-    rationItemQuantity:Number,
-    marketPriceAdjust:Number,
+    quantity:String,
+    customQuantity:String,
+    rationItemQuantity:String,
     createType: {type: String,default:'normal'},//normal、add、replace  正常、添加工料机、替换工料机
     from:{type: String,default:'std'}//std, cpt  来自标准工料机库、补充工料机库
 },{versionKey:false});

+ 1 - 0
modules/ration_glj/routes/ration_glj_route.js

@@ -12,6 +12,7 @@ module.exports = function (app) {
     rgRouter.post('/addGLJ',rgController.addGLJ);
     rgRouter.post('/replaceGLJ',rgController.replaceGLJ);
     rgRouter.post('/mReplaceGLJ',rgController.mReplaceGLJ);
+    rgRouter.post('/updateRationGLJByEdit',rgController.updateRationGLJByEdit);
     app.use('/rationGlj',rgRouter);
 }
 

+ 34 - 25
public/calc_util.js

@@ -231,9 +231,16 @@ class Calculation {
     // 先编译公用的基础数据
     compilePublics(feeRates, labourCoes, feeTypes, calcBases){
         let me = this;
+        me.compiledFeeRates = {};
+        me.compiledLabourCoes = {};
+        me.compiledTemplates = {};
+        me.compiledFeeTypes = {};
+        me.compiledFeeTypeNames = [];
+        me.compiledCalcBases = {};
+        me.saveForReports = [];
+
         let private_compile_feeRateFile = function() {
             if (feeRates) {
-                me.compiledFeeRates = {};
                 for (let rate of feeRates) {
                     me.compiledFeeRates["feeRate_" + rate.ID] = rate;
                 }
@@ -241,7 +248,6 @@ class Calculation {
         };
         let private_compile_labourCoeFile = function() {
             if (labourCoes) {
-                me.compiledLabourCoes = {};
                 for (let coe of labourCoes) {
                     me.compiledLabourCoes["LabourCoe_" + coe.ID] = coe;
                 }
@@ -249,8 +255,6 @@ class Calculation {
         };
         let private_compile_feeType = function() {
             if (feeTypes) {
-                me.compiledFeeTypes = {};
-                me.compiledFeeTypeNames = [];
                 for (let ft of feeTypes) {
                     me.compiledFeeTypes[ft.type] = ft.name;
                     me.compiledFeeTypes[ft.name] = ft.type;    // 中文预编译,可靠性有待验证
@@ -260,7 +264,6 @@ class Calculation {
         };
         let private_compile_calcBase = function() {
             if (calcBases) {
-                me.compiledCalcBases = {};
                 for (let cb of calcBases) {
                     me.compiledCalcBases[cb.dispName] = cb;         // 中文预编译,可靠性有待验证
                 }
@@ -271,8 +274,6 @@ class Calculation {
         private_compile_labourCoeFile();
         private_compile_feeType();
         private_compile_calcBase();
-        me.compiledTemplates = {};
-        me.saveForReports = [];
     };
 
     compileTemplate(template){
@@ -382,8 +383,6 @@ class Calculation {
         };
     };
 
-
-
     calculate($treeNode){
         let me = this;
         let templateID = $treeNode.data.programID;
@@ -400,6 +399,7 @@ class Calculation {
             if (!$treeNode.data.fees) {
                 $treeNode.data.fees = [];
                 $treeNode.data.feesIndex = {};
+                $treeNode.changed = true;
             };
 
             for (let idx of template.compiledSeq) {
@@ -415,23 +415,32 @@ class Calculation {
 
                 // 费用同步到定额
                 // 引入小麦的字段检测后,快速切换定额出现计算卡顿现象,过多的循环造成。这里把她的代码拆出来,减少微循环。
-                if (!$treeNode.data.feesIndex[calcItem.fieldName]){
-                    let fee = {
-                        'fieldName': calcItem.fieldName,
-                        'unitFee': calcItem.unitFee,
-                        'totalFee': calcItem.totalFee,
-                        'tenderUnitFee': 0,
-                        'tenderTotalFee': 0
-                    };
-                    $treeNode.data.fees.push(fee);
-                    $treeNode.data.feesIndex[calcItem.fieldName] = fee;
-                }
-                else{
-                    $treeNode.data.feesIndex[calcItem.fieldName].unitFee = calcItem.unitFee;
-                    $treeNode.data.feesIndex[calcItem.fieldName].totalFee = calcItem.totalFee;
-                }
-            }
+                if (calcItem.fieldName != '') {
+                    if (!$treeNode.data.feesIndex[calcItem.fieldName]){
+                        let fee = {
+                            'fieldName': calcItem.fieldName,
+                            'unitFee': calcItem.unitFee,
+                            'totalFee': calcItem.totalFee,
+                            'tenderUnitFee': 0,
+                            'tenderTotalFee': 0
+                        };
+                        $treeNode.data.fees.push(fee);
+                        $treeNode.data.feesIndex[calcItem.fieldName] = fee;
+                        $treeNode.changed = true;
+                    }
+                    else{
+                        if ($treeNode.data.feesIndex[calcItem.fieldName].unitFee != calcItem.unitFee){
+                            $treeNode.data.feesIndex[calcItem.fieldName].unitFee = calcItem.unitFee;
+                            $treeNode.changed = true;
+                        };
 
+                        if ($treeNode.data.feesIndex[calcItem.fieldName].totalFee != calcItem.totalFee){
+                            $treeNode.data.feesIndex[calcItem.fieldName].totalFee = calcItem.totalFee;
+                            $treeNode.changed = true;
+                        };
+                    }
+                };
+            };
         }
     }
 };

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

@@ -280,5 +280,15 @@ var SheetDataHelper = {
             sheet.setSelection(range.row, range.col, range.rowCount, range.colCount);
         }
         return target;
+    },
+    /**
+     * 在sheet中使用delete键,触发EndEdited事件
+     * @param sheet
+     */
+    deleteBind: function (sheet, fun) {
+        sheet.addKeyMap(46, false, false, false, false, function () {
+            let selections = sheet.getSelections();
+            
+        });
     }
 };

+ 1 - 0
web/building_saas/complementary_glj_lib/html/tools-gongliaoji.html

@@ -188,6 +188,7 @@
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.core.js"></script>
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.excheck.js"></script>
     <script type="text/javascript" src="/lib/ztree/jquery.ztree.exedit.js"></script>
+    <script type="text/javascript" src="/public/web/scMathUtil.js"></script>
     <script type="text/javascript" src="/public/web/treeDataHelper.js"></script>
     <script type="text/javascript" src="/public/web/QueryParam.js"></script>
     <script type="text/javascript" src="/web/building_saas/complementary_glj_lib/js/glj.js"></script>

+ 33 - 2
web/building_saas/complementary_glj_lib/js/glj.js

@@ -442,6 +442,12 @@ let repositoryGljObj = {
                         }
                         else if(rObj.basePrice !== me.currentEditingGlj.basePrice){//修改了单价,可修改单价的必为可成为组成物的
                             //寻找所有引用了此组成物的工料机,并从组成物中删去此工料机,并重算单价
+                            if(isNaN(parseFloat(rObj.basePrice))){
+                                alert('单价只能为数值!');
+                                args.sheet.setValue(args.row, args.col, me.currentEditingGlj.basePrice ? me.currentEditingGlj.basePrice : 0);
+                                return;
+                            }
+                            rObj.basePrice = !isNaN(parseFloat(rObj.basePrice))? scMathUtil.roundTo(parseFloat(rObj.basePrice), -2) : 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++){
@@ -451,7 +457,7 @@ let repositoryGljObj = {
                                     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.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);
@@ -698,7 +704,7 @@ let repositoryGljObj = {
             if(!isExsit) isValid = false;
         }
         //
-        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? that.round(parseFloat(pasteObj.basePrice), 2) :
+        pasteObj.basePrice = !isNaN(parseFloat(pasteObj.basePrice)) && (pasteObj.basePrice && typeof pasteObj.basePrice !== 'undefined') ? scMathUtil.roundTo(parseFloat(pasteObj.basePrice), 2) :
             me.currentCache[rowIdx].basePrice;
         if(pasteObj.basePrice !== me.currentCache[rowIdx].basePrice){
             reCalBasePrc = true;
@@ -951,6 +957,12 @@ let repositoryGljObj = {
     },*/
     mixUpdateRequest: function(updateArr, addArr, removeIds) {
         let me = repositoryGljObj;
+        if(updateArr.length > 0){
+            me.saveInString(updateArr);
+        }
+        if(addArr.length > 0){
+            me.saveInString(addArr);
+        }
         $.ajax({
             type:"POST",
             url:"complementartGlj/api/mixUpdateGljItems",
@@ -985,6 +997,25 @@ let repositoryGljObj = {
             }
         })
     },
+    saveInString: function (datas) {
+        for(let i = 0, len = datas.length; i < len; i++){
+            let data = datas[i];
+            if(_exist(data, 'basePrice')){
+                data['basePrice'] = data['basePrice'].toString();
+            }
+            if(_exist(data, 'component')){
+                for(let j = 0, jLen = data['component'].length; j < jLen; j++){
+                    let comGljObj = data['component'][j];
+                    if(_exist(comGljObj, 'consumeAmt')){
+                        comGljObj['consumeAmt'] = comGljObj['consumeAmt'].toString();
+                    }
+                }
+            }
+        }
+        function _exist(data, attr){
+            return data && data[attr] !== undefined && data[attr];
+        }
+    },
     getParentCache: function (nodes) {
         let me = repositoryGljObj, rst = [];
         for(let i = 0; i < me.complementaryGljList.length; i++){

+ 5 - 4
web/building_saas/complementary_glj_lib/js/gljComponent.js

@@ -309,7 +309,7 @@ let gljComponentOprObj = {
          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 roundCons = scMathUtil.roundTo(consumeAmt, -3);
                 let component = that.currentGlj.component;
                 for(let i = 0; i < component.length; i++){
                     if(component[i].ID === that.currentComponent[args.row].ID){
@@ -430,7 +430,7 @@ 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);
+                        let roundCons = scMathUtil.roundTo(items[i].consumeAmt, 3);
                         isChange = true;
                         currentObj.consumeAmt = roundCons;
                         for(let j = 0; j < component.length; j++){
@@ -468,6 +468,7 @@ let gljComponentOprObj = {
     },
     updateComponent: function (updateArr) {
         let me = gljComponentOprObj, that = repositoryGljObj;
+        that.saveInString(updateArr);
         $.ajax({
             type: 'post',
             url: 'complementartGlj/api/updateComponent',
@@ -495,8 +496,8 @@ let gljComponentOprObj = {
     reCalGljBasePrc: function (component) {
         let me = gljComponentOprObj, gljBasePrc = 0;
         for(let i = 0; i < component.length; i++){
-            let roundBasePrc = me.round(component[i].basePrice, 2);
-            gljBasePrc += me.round(roundBasePrc * component[i].consumeAmt, 2);
+            let roundBasePrc = scMathUtil.roundTo(component[i].basePrice, -2);
+            gljBasePrc += scMathUtil.roundTo(roundBasePrc * component[i].consumeAmt, -2);
         }
         return gljBasePrc;
     }

+ 1 - 0
web/building_saas/glj/js/composition.js

@@ -26,6 +26,7 @@ $(document).ready(function() {
 
             // 筛选数据显示(显示混凝土、砂浆、配合比)
             projectGLJSheet.filterData('unit_price.type', [GLJTypeConst.CONCRETE, GLJTypeConst.MORTAR, GLJTypeConst.MIX_RATIO]);
+
             projectGLJSheet.selectRow(projectGLJSpread.firstMixRatioRow);
             projectGLJId = projectGLJSheet.getActiveDataByField('id');
 

+ 10 - 2
web/building_saas/glj/js/composition_spread.js

@@ -46,12 +46,20 @@ CompositionSpread.prototype.init = function(target) {
     let codeColumn = this.sheetObj.getFieldColumn('code');
     let unitColumn = this.sheetObj.getFieldColumn('unit');
     let consumptionColumn = this.sheetObj.getFieldColumn('consumption');
-
+    let basePriceCol = this.sheetObj.getFieldColumn('unit_price.base_price');
+    let adjustPriceCol = this.sheetObj.getFieldColumn('adjust_price');
+    let marketPriceCol = this.sheetObj.getFieldColumn('unit_price.market_price');
     // 居中样式
     let centerStyleSetting = {hAlign: 1};
     this.sheetObj.setStyle(-1, codeColumn, centerStyleSetting);
     this.sheetObj.setStyle(-1, unitColumn, centerStyleSetting);
 
+    //靠右设置
+    let rightStyleSetting={hAlign: 2};
+    this.sheetObj.setStyle(-1, basePriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, adjustPriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, marketPriceCol, rightStyleSetting);
+    this.sheetObj.setStyle(-1, consumptionColumn, rightStyleSetting);
 
     // 设置可编辑列
     this.sheetObj.setColumnEditable(consumptionColumn);
@@ -125,7 +133,7 @@ CompositionSpread.prototype.getRatioData = function(projectGLJid) {
         success: function(response) {
             if (response.err === 0) {
                 response.data = JSON.parse(response.data);
-
+                console.log(response.data);
                 // 设置数据
                 self.sheetObj.setData(response.data);
                 self.specialColumn(response.data);

+ 16 - 10
web/building_saas/glj/js/project_glj.js

@@ -205,16 +205,7 @@ function init() {
         if (jsonData.length <= 0) {
             // 赋值
             jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
-            if (jsonData.length > 0) {
-                // 不显示消耗量为0的数据
-                let tmpData = [];
-                for(let data of jsonData) {
-                    if (data.quantity !== 0) {
-                        tmpData.push(data);
-                    }
-                }
-                jsonData = tmpData;
-            }
+            jsonData= filterProjectGLJ(jsonData);
             mixRatioConnectData = data.mixRatioConnectData !== undefined ? data.mixRatioConnectData : mixRatioConnectData;
             mixRatioMap = data.mixRatioMap !== undefined ? data.mixRatioMap : mixRatioMap;
             host = data.constData.hostname !== undefined ? data.constData.hostname : '';
@@ -355,3 +346,18 @@ function socketInit() {
         $("#notify").slideDown('fast');
     });
 }
+
+//过滤消耗量为0的项目工料机
+function filterProjectGLJ(jsonData) {
+    if (jsonData.length > 0) {
+        // 不显示消耗量为0的数据
+        let tmpData = [];
+        for(let data of jsonData) {
+            if (data.quantity !== 0&&data.quantity !=='0') {
+                tmpData.push(data);
+            }
+        }
+        jsonData = tmpData;
+    }
+    return jsonData;
+}

+ 10 - 5
web/building_saas/glj/js/project_glj_spread.js

@@ -41,7 +41,7 @@ ProjectGLJSpread.prototype.init = function () {
     selectBox.editorValueType(GC.Spread.Sheets.CellTypes.EditorValueType.text);
     let header = [
         {name: '编码', field: 'code', visible: true,width:80},
-        {name: '名称', field: 'name', visible: true,width:100},
+        {name: '名称', field: 'name', visible: true,width:160},
         {name: '规格型号', field: 'specs', visible: true,width:120},
         {name: '单位', field: 'unit', visible: true,width:45},
         {name: '类型', field: 'unit_price.short_name', visible: true,width:45},
@@ -57,7 +57,7 @@ ProjectGLJSpread.prototype.init = function () {
             visible: true,
             cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
             validator: 'boolean'
-            ,width:40
+            ,width:60
         },
         {name: '供货方式', field: 'supply', visible: true, cellType: selectBox,width:80},
         {name: '甲供数量', field: 'supply_quantity', visible: true,width:100},
@@ -69,7 +69,7 @@ ProjectGLJSpread.prototype.init = function () {
             visible: true,
             cellType: new GC.Spread.Sheets.CellTypes.CheckBox(),
             validator: 'boolean',
-            width:40
+            width:55
         },
         {name: 'UID', field: 'unit_price.id', visible: false},
         {name: '工料机ID', field: 'glj_id', visible: false},
@@ -261,6 +261,11 @@ ProjectGLJSpread.prototype.specialColumn = function (sourceData) {
             // 锁定该单元格
             activeSheet.getCell(rowCounter, isEvaluateColumn, GC.Spread.Sheets.SheetArea.viewport).locked(true);
             activeSheet.setValue(rowCounter, isEvaluateColumn, '');
+        }else {
+            let checkBox = new GC.Spread.Sheets.CellTypes.CheckBox();
+            activeSheet.setCellType(rowCounter, isEvaluateColumn, checkBox, GC.Spread.Sheets.SheetArea.viewport);
+            activeSheet.getCell(rowCounter, isEvaluateColumn, GC.Spread.Sheets.SheetArea.viewport).locked(false);
+            activeSheet.setValue(rowCounter, isEvaluateColumn, data.is_evaluate);
         }
         // 设置供货方式列是否可选
         if (this.supplyReadonlyType.indexOf(data.unit_price.type) >= 0) {
@@ -393,14 +398,14 @@ ProjectGLJSpread.prototype.priceCalculate = function(info) {
     // 获取类型
     let type = activeSheet.getValue(row, typeColumn);
 
-    // 基价单价计算
+  /*  // 基价单价计算
     switch (type) {
         // 主材、设备自动赋值基价单价=市场单价
         case GLJTypeConst.MAIN_MATERIAL:
         case GLJTypeConst.EQUIPMENT:
             activeSheet.setValue(info.row, basePriceColumn, info.newValue);
             break;
-    }
+    }*/
 
     // 调整基价计算
     switch (type) {

+ 69 - 0
web/building_saas/main/html/main.html

@@ -265,6 +265,7 @@
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-3" role="tab">指标信息</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-4" role="tab">关于计算</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-5" role="tab">清单工程精度</a></li>
+                                <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-decimal" role="tab" id="tab_poj-settings-decimal">小数位数</a></li>
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-6" role="tab" id="tab_poj-settings-6">人工单价调整</a></li>
                             </ul>
                         </div>
@@ -341,6 +342,73 @@
                                         清单工程精度
                                     </div>
                                 </div>
+                                <!--小数位数-->
+                                <div class="tab-pane fade" id="poj-settings-decimal" role="tabpanel">
+                                    <div class="modal-auto-height">
+                                        <fieldset class="form-group">
+                                            <h5>清单</h5>
+                                            <div class="row m-0">
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">单价</div>
+                                                        <input type="number" name="bills-unitPrice" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">合价</div>
+                                                        <input type="number" name="bills-totalPrice" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <h5 class="mt-3">定额</h5>
+                                            <div class="row m-0">
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">工程量</div>
+                                                        <input type="number" name="ration-quantity" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">单价</div>
+                                                        <input type="number" name="ration-unitPrice" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">合价</div>
+                                                        <input type="number" name="ration-totalPrice" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <h5 class="mt-3">工料机</h5>
+                                            <div class="row m-0">
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">工程量</div>
+                                                        <input type="number" name="glj-quantity" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">单价</div>
+                                                        <input type="number" name="glj-unitPrice" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                            </div>
+                                            <h5 class="mt-3">费率</h5>
+                                            <div class="row m-0">
+                                                <div class="col-sm-3">
+                                                    <div class="input-group input-group-sm mb-2">
+                                                        <div class="input-group-addon">费率</div>
+                                                        <input type="number" name="feeRate" class="form-control" value="2" step="1" max="6" min="0">
+                                                    </div>
+                                                </div>
+                                            </div>
+                                        </fieldset>
+                                    </div>
+                                </div>
                                 <!--人工单价调整-->
                                 <div class="tab-pane fade" id="poj-settings-6" role="tabpanel">
                                     <div class="row px-3">
@@ -596,6 +664,7 @@
         <script type="text/javascript" src="/web/building_saas/main/js/views/project_info.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/project_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/views/options_view.js"></script>
+        <script type="text/javascript" src="/web/building_saas/main/js/views/project_property_decimal_view.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/main_ajax.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/main.js"></script>
         <script type="text/javascript" src="/web/building_saas/main/js/controllers/project_controller.js"></script>

+ 7 - 1
web/building_saas/main/js/calc/bills_calc.js

@@ -323,6 +323,12 @@ class BillsCalcHelper {
             this.setTotalFee(node, field, value, isIncre);
         }
     };
+    clearFeeFields(node, fields, isIncre) {
+        for (let field of fields) {
+            node.data.feesIndex[field.type].unitFee = 0;
+            this.setTotalFee(node, field, 0, isIncre);
+        }
+    }
     calcNode(node, isIncre) {
         if (node.source.children.length > 0) {
             this.calcParent(node, this.project.calcFields, isIncre);
@@ -334,7 +340,7 @@ class BillsCalcHelper {
                     this.calcVolumePriceLeaf(node, this.project.calcFields, isIncre);
                 }
             } else {
-
+                this.clearFeeFields(node, this.project.calcFields, isIncre);
             }
         }
     };

+ 281 - 0
web/building_saas/main/js/models/calc_program.js

@@ -5,10 +5,102 @@
  *  用到费率的规则必须有feeRateID属性,当有该属性时,会自动显示费率值。
  */
 
+let defaultBillTemplate = [
+    {
+        ID: 1,
+        serialNo: '一',
+        code: "A",
+        name: "定额直接费",
+        dispExpr: "A1+A2+A3",
+        statement: "人工费+材料费+机械费",
+        feeRate: null,
+        memo: ''
+    },
+    {
+        ID: 2,
+        serialNo: '1',
+        code: "A1",
+        name: "人工费",
+        dispExpr: "HJ",
+        statement: "合计",
+        feeRate: 50,
+        fieldName: 'labour',
+        memo: ''
+    },
+    {
+        ID: 3,
+        serialNo: '2',
+        code: "A2",
+        name: "材料费",
+        dispExpr: "HJ",
+        statement: "合计",
+        feeRate: 30,
+        fieldName: 'material',
+        memo: ''
+    },
+    {
+        ID: 4,
+        serialNo: '3',
+        code: "A3",
+        name: "机械费",
+        dispExpr: "HJ",
+        statement: "合计",
+        feeRate: 20,
+        fieldName: 'machine',
+        memo: ''
+    },
+    {
+        ID: 5,
+        serialNo: '二',
+        code: "A4",
+        name: "管理费",
+        dispExpr: "A",
+        statement: "定额直接费",
+        feeRate: null,
+        fieldName: 'manage',
+        memo: ''
+    },
+    {
+        ID: 6,
+        serialNo: '三',
+        code: "B",
+        name: "利润",
+        dispExpr: "A",
+        statement: "定额直接费",
+        feeRate: null,
+        fieldName: 'profit',
+        memo: ''
+    },
+    {
+        ID: 7,
+        serialNo: '四',
+        code: "C",
+        name: "风险费用",
+        dispExpr: "",
+        statement: "",
+        feeRate: null,
+        fieldName: 'risk',
+        memo: ''
+    },
+    {
+        ID: 8,
+        serialNo: '',
+        code: "",
+        name: "综合单价",
+        dispExpr: "A+B",
+        statement: "定额直接费+利润",
+        feeRate: null,
+        fieldName: 'common',
+        memo: ''
+    }
+];
+
 class CalcProgram {
     constructor(project){
         this.project = project;
         this.datas = [];
+        this.digit = 2;
+        this.digitDefault = 6;
         this.calc = new Calculation();
         project.registerModule(ModuleNames.calc_program, this);
     };
@@ -50,6 +142,7 @@ class CalcProgram {
     };
 
     calculate(treeNode){
+        let me = this;
         if (treeNode.sourceType === this.project.Ration.getSourceType()) {
             treeNode.data.gljList = this.project.ration_glj.getGljArrByRation(treeNode.data.ID);
         }
@@ -59,6 +152,194 @@ class CalcProgram {
         };
 
         this.calc.calculate(treeNode);
+
+        // 存储、刷新本结点、所有父结点
+        if (treeNode.changed) {
+            me.saveAndCalcParents(treeNode);
+            delete treeNode.changed;
+        };
+
+    };
+
+    saveAndCalcParents(treeNode) {
+        if (treeNode.parent) {
+            projectObj.converseCalculateBills(treeNode.parent);
+        };
+
+        let data = {ID: treeNode.data.ID, projectID: projectObj.project.ID(), fees: treeNode.data.fees};
+        let newDta = {'updateType': 'ut_update', 'updateData': data};
+        let newDataArr = [];
+        newDataArr.push(newDta);
+        projectObj.project.pushNow('', treeNode.sourceType, newDataArr);
         projectObj.mainController.refreshTreeNode([treeNode]);
+    };
+
+    initFees(treeNode){
+        if (!treeNode.data.fees) {
+            treeNode.data.fees = [];
+            treeNode.data.feesIndex = {};
+            treeNode.changed = true;
+        };
+    };
+
+    checkFee(treeNode, ftObj){
+        if (!treeNode.data.feesIndex[ftObj.fieldName]){
+            let fee = {
+                'fieldName': ftObj.fieldName,
+                'unitFee': ftObj.unitFee,
+                'totalFee': ftObj.totalFee,
+                'tenderUnitFee': 0,
+                'tenderTotalFee': 0
+            };
+            treeNode.data.fees.push(fee);
+            treeNode.data.feesIndex[ftObj.fieldName] = fee;
+            treeNode.changed = true;
+        }
+        else{
+            if (treeNode.data.feesIndex[ftObj.fieldName].unitFee != ftObj.unitFee){
+                treeNode.data.feesIndex[ftObj.fieldName].unitFee = ftObj.unitFee;
+                treeNode.changed = true;
+            };
+
+            if (treeNode.data.feesIndex[ftObj.fieldName].totalFee != ftObj.totalFee){
+                treeNode.data.feesIndex[ftObj.fieldName].totalFee = ftObj.totalFee;
+                treeNode.changed = true;
+            };
+        };
+    };
+
+    gatherFeeTypes(treeNode, gatherType){
+        let me = this;
+        let rst = [];
+
+        if (treeNode.sourceType === this.project.Bills.getSourceType()) {
+            me.initFees(treeNode);
+
+            let objsArr = [];
+            if (gatherType == CP_GatherType.rations){
+                objsArr = this.project.Ration.getRationsByNode(treeNode);
+            }else if (gatherType == CP_GatherType.bills){
+                objsArr = treeNode.children;
+            };
+
+            for (let ft of feeType) {
+                let ftObj = {};
+                ftObj.fieldName = ft.type;
+                ftObj.name = ft.name;
+                let uf = 0, tf = 0, tuf = 0, ttf = 0;
+                for (let item of objsArr) {
+                    let data = {};
+                    if (gatherType == CP_GatherType.rations){
+                        data = item;
+                    }else if (gatherType == CP_GatherType.bills){
+                        data = item.data;
+                    };
+                    if (data.feesIndex && data.feesIndex[ft.type]) {
+                        uf = (uf + parseFloat(data.feesIndex[ft.type].unitFee)).toDecimal(me.digitDefault);
+                        tf = (tf + parseFloat(data.feesIndex[ft.type].totalFee)).toDecimal(me.digitDefault);
+                        tuf = (tuf + parseFloat(data.feesIndex[ft.type].tenderUnitFee)).toDecimal(me.digitDefault);
+                        ttf = (ttf + parseFloat(data.feesIndex[ft.type].tenderTotalFee)).toDecimal(me.digitDefault);
+                    };
+                };
+                ftObj.unitFee = uf.toDecimal(me.digit);
+                ftObj.totalFee = tf.toDecimal(me.digit);
+                ftObj.tenderUnitFee = tuf.toDecimal(me.digit);
+                ftObj.tenderTotalFee = ttf.toDecimal(me.digit);
+
+                me.checkFee(treeNode, ftObj);
+
+                rst.push(ftObj);
+            };
+
+            if (treeNode.changed) {
+                me.saveAndCalcParents(treeNode);
+                delete treeNode.changed;
+            };
+        };
+
+        return rst;
+    };
+
+    calcDefaultBillTemp(treeNode, totalPrice){
+        let me = this;
+        let rst = [];
+        if (treeNode.sourceType != this.project.Bills.getSourceType()){return rst};
+        me.initFees(treeNode);
+        for (let item of defaultBillTemplate) {
+            let num = totalPrice;
+            item.dispExprUser = item.dispExpr;
+            item.displayFieldName = me.calc.compiledFeeTypes[item.fieldName];
+
+            if (item.feeRate)
+                item.unitFee = (totalPrice * item.feeRate * 0.01).toDecimal(me.digit)
+            else
+                item.unitFee = 0;
+
+            let quantity = treeNode.data.quantity;
+            if (!quantity) quantity = 0;
+            item.totalFee = (item.unitFee * quantity).toDecimal(me.digit);
+            item.tenderUnitFee = 0;
+            item.tenderTotalFee = 0;
+
+            me.checkFee(treeNode, item);
+        };
+
+        if (treeNode.changed) {
+            me.saveAndCalcParents(treeNode);
+            delete treeNode.changed;
+        };
+
+        rst = defaultBillTemplate;
+        return rst;
+    };
+
+    getCalcDatas(treeNode){
+        let me = this;
+        let rst = [];
+        let isRation = treeNode.sourceType === me.project.Ration.getSourceType();
+        let isBill = treeNode.sourceType === me.project.Bills.getSourceType();
+        let isLeafBill = isBill && treeNode.source.children && treeNode.source.children.length === 0;
+        let isBillPriceCalc = me.project.projSetting.billsCalcMode === billsPrice;
+
+        if (isRation) {                 // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算。
+            me.calculate(treeNode);
+            rst = treeNode.data.calcTemplate.calcItems;
+        }
+        else if (isLeafBill) {
+            let ct = '';
+            if (treeNode.children && treeNode.children.length > 0){
+                if (treeNode.children[0].sourceType == me.project.Ration.getSourceType()){
+                    ct = childrenType.ration;
+                }
+                else if (treeNode.children[0].sourceType == me.project.VolumePrice.getSourceType()){
+                    ct = childrenType.volumePrice;
+                };
+            }
+            else{
+                ct = childrenType.formula;
+            };
+
+            if (ct == childrenType.ration){
+                if (isBillPriceCalc){   // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算。
+                    me.calculate(treeNode);
+                    rst = treeNode.data.calcTemplate.calcItems;
+                }else{                  // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
+                    rst = me.gatherFeeTypes(treeNode, CP_GatherType.rations);
+                };
+            }
+            else if (ct == childrenType.volumePrice){
+                let totalPrice = 10000;
+                rst = me.calcDefaultBillTemp(treeNode, totalPrice);
+            }
+            else if (ct == childrenType.formula){
+                let totalPrice = 20000;
+                rst = me.calcDefaultBillTemp(treeNode, totalPrice);
+            };
+        }
+        else if (isBill){    // 父清单:汇总子清单的费用类别
+            rst = me.gatherFeeTypes(treeNode, CP_GatherType.bills);
+        };
+
+        return rst;
     }
 }

+ 13 - 0
web/building_saas/main/js/models/main_consts.js

@@ -25,6 +25,7 @@ const CP_Col_Width = {          // 多处计算程序界面的列宽统一设置
     rowHeader: 30,
     colHeader: 30,              // 这个是标题栏高度不是宽度,也写在一起
     code: 70,
+    serialNo: 50,
     name: 200,
     dispExprUser: 180,
     feeRate: 60,
@@ -33,4 +34,16 @@ const CP_Col_Width = {          // 多处计算程序界面的列宽统一设置
     memo: 110,
     unitFee: 90,
     totalFee: 90
+};
+
+const CP_GatherType = {
+    rations: 'rations',
+    bills: 'bills'
+};
+
+const childrenType = {
+    ration: 'ration',
+    bill: 'bill',
+    volumePrice: 'volumePrice',
+    formula: 'formula'
 };

+ 88 - 1
web/building_saas/main/js/models/project_glj.js

@@ -133,6 +133,93 @@ ProjectGLJ.prototype.loadCacheData = function() {
         return;
     }
     jsonData = data.gljList !== undefined && data.gljList.length > 0 ? data.gljList : [];
+    jsonData=filterProjectGLJ(jsonData);
     projectGLJSheet.setData(jsonData);
     projectGLJSpread.specialColumn(jsonData);
-};
+};
+
+ProjectGLJ.prototype.updatePriceFromRG=function(recode,updateField,newval){
+    if(updateField=='marketPrice'){
+        this.updateBasePriceFromRG(recode,"market_price",newval);
+    }
+    if(updateField=='basePrice'){
+        this.updateBasePriceFromRG(recode,"base_price",newval);
+    }
+};
+
+ProjectGLJ.prototype.updateBasePriceFromRG=function(recode,updateField,newval){
+    let me = this;
+    let projectGljs = this.datas.gljList;
+    let glj = _.find(projectGljs,{'id':recode.projectGLJID});
+    if(glj){
+        let data = {id:glj.unit_price.id,field:updateField,newval:newval};
+        let callback =function (data) {
+            if(updateField=='base_price'){
+                glj.unit_price.base_price=newval;
+                me.setAdjustPrice(glj);
+            }else {
+                glj.unit_price.market_price=newval;
+            }
+            //更新项目工料机价格
+            me.refreshProjectGLJPrice(data);
+            me.refreshRationGLJPrice(glj);
+            gljOprObj.showRationGLJSheetData();
+            $.bootstrapLoading.end();
+        }
+        $.bootstrapLoading.start();
+        CommonAjax.post("/glj/updatePrice",data,callback,function (err) {
+            $.bootstrapLoading.end();
+        });
+    }else {
+        gljOprObj.showRationGLJSheetData();
+    }
+}
+
+ProjectGLJ.prototype.refreshRationGLJPrice=function (glj) {
+    for(let ration_glj of gljOprObj.sheetData){
+        if(ration_glj.projectGLJID ==glj.id){
+            ration_glj.basePrice=glj.unit_price.base_price;
+            ration_glj.marketPrice=glj.unit_price.market_price;
+            ration_glj.adjustPrice=glj.adjust_price;
+        }
+    }
+
+
+}
+ProjectGLJ.prototype.refreshProjectGLJPrice=function(data){
+    let projectGljs = this.datas.gljList;
+    let indexList = ['code','name','specs','unit','type'];
+    for(let d of data){
+        if(d){
+            let condition = {};
+            for(let index of indexList){
+                if(d[index]!=null&&d[index]!=undefined&&d[index]!=''){
+                    condition[index]=d[index]
+                }
+            }
+            let glj = _.find(projectGljs,condition);
+            if(glj){
+                glj.unit_price.base_price = d.base_price;
+                glj.unit_price.market_price = d.market_price;
+                this.setAdjustPrice(glj);
+                this.refreshRationGLJPrice(glj);
+            }
+        }
+    }
+}
+
+ProjectGLJ.prototype.setAdjustPrice=function(glj){
+    switch (glj.unit_price.type + '') {
+        // 人工: 调整基价=基价单价*调整系数
+        case GLJTypeConst.LABOUR:
+            glj.adjust_price = scMathUtil.roundTo(parseFloat(glj.adjustment * glj.unit_price.base_price), -2);
+            break;
+        // 机械类型的算法
+        case GLJTypeConst.MACHINE:
+            console.log('机械');
+            break;
+        // 材料、主材、设备
+        default:
+            glj.adjust_price = glj.unit_price.base_price;
+    }
+}

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

@@ -107,7 +107,7 @@ var Ration = {
             return newData;
         };
 
-        ration.prototype.getBillsSortRation = function (billsID) {
+        ration.prototype.getBillsSortRation = function (billsID) {     // 该方法只适用于叶子清单
             var rations = this.datas.filter(function (data) {
                 return data[project.masterField.ration] === billsID;
             });
@@ -117,6 +117,40 @@ var Ration = {
             return rations;
         };
 
+        // CSL, 2017-11-13 取任何清单(父清单、叶子清单)下的所有定额
+        ration.prototype.getRationsByNode = function (billNode) {
+            let rations = [];
+            let sBills = 'bills';
+            if (billNode.sourceType != sBills) return rations;
+
+            let IDs = [];
+            function getSubBillsIDs(node) {
+                if (!node) return;
+                if (node.sourceType != sBills) return;
+                if (!node.children || node.children.length == 0 || node.children[0].sourceType != sBills)
+                    IDs.push(node.data.ID)
+                else
+                    getSubBillsIDs(node.children[0]);
+                getSubBillsIDs(node.nextSibling);
+            };
+
+            if (billNode.source.children.length == 0)
+                IDs.push(billNode.data.ID)
+            else
+                getSubBillsIDs(billNode.children[0]);
+
+            for (let id of IDs){
+                let subRations = this.datas.filter(function (data) {
+                    return data[project.masterField.ration] === id;
+                });
+                rations.push(...subRations);
+            };
+            rations.sort(function (x, y) {
+                return x.serialNo - y.serialNo;
+            });
+            return rations;
+        };
+
         ration.prototype.getInsertRationData = function (billsID, preRation) {
             var br = this.getBillsSortRation(billsID);
             var updateData = [];

+ 44 - 40
web/building_saas/main/js/models/ration_glj.js

@@ -76,7 +76,9 @@ var ration_glj = {
                         sameGlj.quantity = sameGlj.quantity + (glj.quantity * ration.quantity).toDecimal(4);
                     }
                 }
-            }
+            };
+
+            result = gljOprObj.combineWithProjectGlj(result);
             return result;
         }
 
@@ -106,7 +108,7 @@ var ration_glj = {
             }else {
                 projectObj.project.ration_glj.datas = neRecodes;
             }
-            gljOprObj.showRationGLJSheetData();
+            gljOprObj.showRationGLJSheetData(true);
         };
         ration_glj.prototype.refreshAfterUpdate=function(data){
             var me = this;
@@ -117,7 +119,7 @@ var ration_glj = {
             }else {
                 me.refreshEachItme(data.doc,data.query);
             }
-            gljOprObj.showRationGLJSheetData();
+            gljOprObj.showRationGLJSheetData(true);
         };
         ration_glj.prototype.refreshEachItme = function (doc,query) {
             var glj_list = projectObj.project.ration_glj.datas;
@@ -268,50 +270,52 @@ var ration_glj = {
             updateData.push(newobj);
             return updateData;
         };
-        ration_glj.prototype.customQuantityUpdate = function(recode,newVal,text){
-            newVal = _.round(newVal,3);
+        ration_glj.prototype.updateRationGLJByEdit=function (recode,updateField,newval) {
+            var me=this;
+            $.bootstrapLoading.start();
+            var callback=function (data) {
+                if(updateField=='customQuantity'){
+                    me.refreshAfterQuantityUpdate(data);
+                }else {
+                    var doc = data.doc;
+                    for(var key in doc){
+                        recode[key] = doc[key];
+                    }
+                    if(data.hasOwnProperty('adjustState')){//更新定额调整状态
+                        me.updateRationAdjustState(data.adjustState);
+                    }
+                }
+                gljOprObj.showRationGLJSheetData();
+                projectObj.project.projectGLJ.loadData(function () {//等项目工料机加载完成后再给用户编辑
+                    $.bootstrapLoading.end();
+                });
+            }
             var query = {
                 'ID':recode.ID,
                 'projectID': recode.projectID,
                 'rationID':recode.rationID
             };
-            var doc;
-            if(text===null){
-                doc ={
-                    customQuantity:null
-                };
-            }else {
-                doc ={
-                    customQuantity:newVal,
-                    quantity:newVal
-                };
+            var priceInfo={
+                base_price: recode.basePrice,
+                market_price: recode.marketPrice
             }
-
-
-            var updateData = this.getUpdateData('ut_update',query,doc,'customQuantityUpdate');
-            project.pushNow('updateRationGLJ',[this.getSourceType()],updateData);
-
+            var doc = {};
+            doc[updateField]=newval;
+            CommonAjax.post("/rationGlj/updateRationGLJByEdit",{query:query,doc:doc,priceInfo:priceInfo},callback,function (err) {
+                $.bootstrapLoading.end();
+            });
+        }
+        ration_glj.prototype.refreshAfterQuantityUpdate=function (data) {
+            var me=this;
+            data.glj_result.forEach(function (item) {
+                me.refreshEachItme(item.doc,item.query);
+            })
+            me.updateRationAdjustState(data.adjustState);
         };
-        ration_glj.prototype.marketPriceAdjustUpdate = function (recode,newVal,text) {
-            newVal = _.round(newVal,2);
-            if(text===null){
-                newVal=null;
-            }
-            var query = {
-                'ID':recode.ID,
-                'projectID': recode.projectID,
-                'rationID':recode.rationID
-            };
-            var doc ={
-                base_price:Number(recode.basePrice),
-                market_price:newVal,
-                code:recode.code,
-                name:recode.name,
-                project_id:recode.projectID,
-                repositoryId:recode.repositoryId
-            };
-            var updateData = this.getUpdateData('ut_update',query,doc,'marketPriceAdjustUpdate');
-            project.pushNow('updateRationGLJ',[this.getSourceType()],updateData);
+        ration_glj.prototype.updateRationAdjustState=function(adjustState){
+            var selected = projectObj.project.mainTree.selected;
+            selected.data.adjustState=adjustState;
+            projectObj.mainController.refreshTreeNode([selected]);
         };
         ration_glj.prototype.getGLJData = function(cb){
             CommonAjax.get('/rationGlj/getGLJData', function (data) {

+ 1 - 1
web/building_saas/main/js/views/calc_program_manage.js

@@ -33,7 +33,7 @@ let rationPM = {
         ],
         view:{
             comboBox:[],
-            lockColumns:[0,1,2,3,5,6],
+            lockColumns:[0,1,2,4,5,6],
             colHeaderHeight: CP_Col_Width.colHeader,
             rowHeaderWidth: CP_Col_Width.rowHeader
         }

+ 23 - 20
web/building_saas/main/js/views/calc_program_view.js

@@ -205,15 +205,15 @@ let calcProgramObj = {
             {headerName: "费用名称", headerWidth: CP_Col_Width.name, dataCode: "name", dataType: "String"},
             {headerName: "计算基数", headerWidth: CP_Col_Width.dispExprUser, dataCode: "dispExprUser", dataType: "String"},
             {headerName: "费率", headerWidth: CP_Col_Width.feeRate, dataCode: "feeRate", dataType: "Number"},   // precision: 3
-            {headerName:"费用类别", headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
-            {headerName: "基数说明", headerWidth: CP_Col_Width.statement, dataCode: "statement", dataType: "String"},
             {headerName: "单价", headerWidth: CP_Col_Width.unitFee, dataCode: "unitFee", dataType: "Number"},  // execRst
             {headerName: "合价", headerWidth: CP_Col_Width.totalFee, dataCode: "totalFee", dataType: "Number"},
+            {headerName:"费用类别", headerWidth:CP_Col_Width.displayFieldName, dataCode:"displayFieldName", dataType: "String", hAlign: "center"},
+            {headerName: "基数说明", headerWidth: CP_Col_Width.statement, dataCode: "statement", dataType: "String"},
             {headerName: "备注", headerWidth: CP_Col_Width.memo, dataCode: "memo", dataType: "String"}
         ],
         view: {
             comboBox: [],
-            lockColumns: [0,1,2,3,4,5,6,7,8],
+            lockColumns: [0,1,2,3,4,5,6,7,8,9],
             colHeaderHeight: CP_Col_Width.colHeader,
             rowHeaderWidth: CP_Col_Width.rowHeader
         }
@@ -228,25 +228,28 @@ let calcProgramObj = {
     showData: function (treeNode) {
         var me = this;
         me.treeNode = treeNode;
+        me.datas = projectObj.project.calcProgram.getCalcDatas(treeNode);
 
-        let isRation = treeNode.sourceType === projectObj.project.Ration.getSourceType();
-        let isBill = treeNode.sourceType === projectObj.project.Bills.getSourceType();
-        let isLeafBill = isBill && treeNode.source.children && treeNode.source.children.length === 0;
-        let isBillPriceCalc = projectObj.project.projSetting.billsCalcMode === billsPrice;
-
-        if (isRation || (isLeafBill && isBillPriceCalc)) {
-            projectObj.project.calcProgram.calculate(treeNode);
-            if (treeNode.parent) {
-                projectObj.converseCalculateBills(treeNode.parent);
+        function checkSerialNo() {     // 检测序号列。隐藏列的方式焦点难控制,体验不佳,这里动态添加删除。
+            let srlCol = {headerName: "序号", headerWidth: CP_Col_Width.serialNo, dataCode: "serialNo", dataType: "String", hAlign: "center"};
+            if (me.datas.length > 0 && me.datas[0].serialNo){
+                if (me.setting.header[0].dataCode != "serialNo"){
+                    me.setting.header.splice(0, 0, srlCol);
+                };
             }
-            me.datas = me.treeNode.data.calcTemplate.calcItems;
-            sheetCommonObj.initSheet(me.sheet, me.setting, me.datas.length);
-            sheetCommonObj.showData(me.sheet, me.setting, me.datas);
-        }
-        else if (isBill) {
-            SheetDataHelper.loadSheetHeader(calcProgramSetting, me.sheet);
-            SheetDataHelper.loadSheetData(calcProgramSetting, me.sheet, baseCalcField);
-        }
+            else {
+                if (me.setting.header[0].dataCode == "serialNo"){
+                    me.setting.header.splice(0, 1);
+                };
+            }
+        };
+
+        checkSerialNo();
+        sheetCommonObj.initSheet(me.sheet, me.setting, me.datas.length);
+        sheetCommonObj.showData(me.sheet, me.setting, me.datas);
+
+        // SheetDataHelper.loadSheetHeader(calcProgramSetting, me.sheet);
+        //  SheetDataHelper.loadSheetData(calcProgramSetting, me.sheet, baseCalcField);
     },
 
     clearData: function (){

+ 3 - 2
web/building_saas/main/js/views/character_content_view.js

@@ -746,8 +746,9 @@ let pageCCOprObj = {
                     selectedNode.data[txtObj.field] = txtObj.text;
                     me.showData(oprObj.workBook.getSheet(0), oprObj.setting, oprObj.currentCache);//刷新特征及内容Spread
                     if(updateCol){
-                        projectObj.mainSpread.getActiveSheet().setValue(me.mainActiveCell.row, updateCol, txtObj.text + ''); //刷新输出显示
-                        projectObj.mainSpread.getActiveSheet().autoFitRow(me.mainActiveCell.row);
+                        let activeCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];
+                        projectObj.mainSpread.getActiveSheet().setValue(activeCell.row, updateCol, txtObj.text + ''); //刷新输出显示
+                        projectObj.mainSpread.getActiveSheet().autoFitRow(activeCell.row);
                     }
                 }
             }

+ 79 - 47
web/building_saas/main/js/views/glj_view.js

@@ -17,26 +17,29 @@ var gljOprObj = {
     GLJSelection:[],
     parentNodeIds:{},
     activeTab:'#linkGLJ',
+    decimalSetting:{
+        marketPrice:2,
+        customQuantity:3
+    },
     setting: {
         header: [
             {headerName: "编码", headerWidth: 100, dataCode: "code", dataType: "String", formatter: "@"},
-            {headerName: "名称", headerWidth: 120, dataCode: "name", dataType: "String"},
-            {headerName: "规格型号", headerWidth: 80, dataCode: "specs", dataType: "String", hAlign: "center"},
-            {headerName: "单位", headerWidth: 60, dataCode: "unit", dataType: "String", hAlign: "center"},
-            {headerName: "类别", headerWidth: 50, dataCode: "shortName", dataType: "String", hAlign: "center"},
+            {headerName: "名称", headerWidth: 160, dataCode: "name", dataType: "String"},
+            {headerName: "规格型号", headerWidth: 120, dataCode: "specs", dataType: "String", hAlign: "left"},
+            {headerName: "单位", headerWidth: 45, dataCode: "unit", dataType: "String", hAlign: "center"},
+            {headerName: "类型", headerWidth: 45, dataCode: "shortName", dataType: "String", hAlign: "center"},
             {headerName: "定额消耗量", headerWidth: 80, dataCode: "rationItemQuantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},    // dataType: "Number", formatter: "0.00"
             {headerName: "自定义消耗量", headerWidth: 80, dataCode: "customQuantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},
             {headerName: "消耗量", headerWidth: 80, dataCode: "quantity", dataType: "Number", hAlign: "right",formatter:"0.000",tofix:3},
             {headerName: "基价单价", headerWidth: 80, dataCode: "basePrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
             {headerName: "调整基价", headerWidth: 80, dataCode: "adjustPrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
             {headerName: "市场单价", headerWidth: 80, dataCode: "marketPrice", dataType: "Number", hAlign: "right",formatter:"0.00"},
-            {headerName: "市场单价调整", headerWidth: 80, dataCode: "marketPriceAdjust", dataType: "Number", hAlign: "right",formatter:"0.00"},
-            {headerName: "是否暂估", headerWidth: 80, dataCode: "isEstimate", dataType: "String", hAlign: "center",vAlign:"center",cellType:"checkBox"}
+            {headerName: "是否暂估", headerWidth: 65, dataCode: "isEstimate", dataType: "String", hAlign: "center",vAlign:"center",cellType:"checkBox"}
         ],
         view: {
             comboBox: [{row: -1, col: 12, rowCount: -1, colCount: 1}],
             lockedCells: [{row: -1, col: 3, rowCount: -1, colCount: 1}],
-            lockColumns:[0,1,2,3,4,5,7,8,9,10,12]
+            lockColumns:[0,4,5,7,8,9,11]
         },
         notEditedType: ['砼','桨','配比','机']
     },
@@ -231,15 +234,7 @@ var gljOprObj = {
             me.sheet.getCell(args.row, args.col).value(null);
             return;
         }
-        var ration_glj = projectObj.project.ration_glj;
-        var updateFunction =null;
-        if(me.setting.header[args.col].dataCode=='marketPriceAdjust'){//市场单价调整
-            updateFunction = ration_glj.marketPriceAdjustUpdate;
-        }
-        if(me.setting.header[args.col].dataCode=='customQuantity'){//自定义消耗量
-            updateFunction = ration_glj.customQuantityUpdate;
-        }
-        me.updateRationGLJ(args,updateFunction);
+        me.updateRationGLJ(args);
     },
     onEditAssSheet:function(args){
         var me = gljOprObj;
@@ -302,15 +297,7 @@ var gljOprObj = {
         if(args.row>=me.sheetData.length){
             return;
         }
-        if(me.setting.header[args.col]&&me.setting.header[args.col].dataCode=='marketPriceAdjust'){//市场单价调整
-            var type = me.sheetData[args.row].shortName;
-            var index= _.indexOf(me.setting.notEditedType,type);
-            if(index!=-1){
-                me.sheet.getCell(args.row, args.col, GC.Spread.Sheets.SheetArea.viewport).locked(true);
-            }else {
-                me.sheet.getCell(args.row, args.col, GC.Spread.Sheets.SheetArea.viewport).locked(false);
-            }
-        }
+        me.editChecking(args);
     },
     onCoeCellClick:function (sender,args) {
         var me = gljOprObj;
@@ -427,6 +414,25 @@ var gljOprObj = {
         return result;
 
     },
+    editChecking:function (args) {
+        var me = gljOprObj;
+        var header = me.setting.header;
+        var disable = null;
+        if(header[args.col]&&header[args.col].dataCode=='marketPrice'){
+            var type = me.sheetData[args.row].shortName;
+            var index= _.indexOf(me.setting.notEditedType,type);
+            disable=index==-1?false:true;
+        }
+        if(header[args.col]&&header[args.col].dataCode=='basePrice'){
+            var isAdd= me.sheetData[args.row].isAdd;
+            var type_b = me.sheetData[args.row].shortName;
+            var index_b= _.indexOf(me.setting.notEditedType,type);
+            disable=isAdd==1&&index_b==-1?false:true
+        }
+        if(disable!=null){
+            me.sheet.getCell(args.row, args.col, GC.Spread.Sheets.SheetArea.viewport).locked(disable);
+        }
+    },
     addDropDownList:function () {
         var sheet = this.coeSheet;
         sheet.suspendPaint();
@@ -528,8 +534,8 @@ var gljOprObj = {
         }
     },
     showRationGLJSheetData:function (init) {
-        this.sheet.getRange(0,-1,this.sheet.getRowCount(),-1).visible(true);
         if(init){
+            this.sheet.getRange(0,-1,this.sheet.getRowCount(),-1).visible(true);
             this.sheetData=_.sortBy(this.sheetData,'type');
             this.addMixRatioToShow();
             this.initRationTree();
@@ -560,12 +566,13 @@ var gljOprObj = {
     },
     addMixRatioToShow:function () {
         var newList=[];
-      for(var i =0;i<this.sheetData.length;i++){
-          newList.push(this.sheetData[i]);
-          if(this.sheetData[i].hasOwnProperty('subList')){
+        _.remove(this.sheetData,{'isMixRatio':true});
+        for(var i =0;i<this.sheetData.length;i++){
+            newList.push(this.sheetData[i]);
+            if(this.sheetData[i].hasOwnProperty('subList')){
                 newList = newList.concat(this.sheetData[i].subList);
-          }
-      }
+            }
+        }
         this.sheetData= newList;
     },
     combineWithProjectGlj:function (ration_gljs) {
@@ -580,8 +587,10 @@ var gljOprObj = {
                     ration_gljs[i].marketPrice=glj.unit_price.market_price;
                     ration_gljs[i].adjustPrice=glj.adjust_price;
                     ration_gljs[i].isEstimate=glj.is_evaluate;
-                    if(mixRatioMap.hasOwnProperty(glj.code)){
-                        var mixRatios = this.getMixRationShowDatas(mixRatioMap[glj.code],projectGljs);
+                    ration_gljs[i].isAdd=glj.unit_price.is_add;
+                    var connect_index = this.getIndex(glj,['code','name','specs','unit','type'])
+                    if(mixRatioMap.hasOwnProperty(connect_index)){
+                        var mixRatios = this.getMixRationShowDatas(mixRatioMap[connect_index],projectGljs);
                         ration_gljs[i].subList = mixRatios;
                     }
                 }
@@ -589,11 +598,22 @@ var gljOprObj = {
         }
         return ration_gljs;
     },
+    getIndex(obj,pops){
+        let t_index = '';
+        let k_arr=[];
+        for(let p of pops){
+            let tmpK = (obj[p]==undefined||obj[p]==null||obj[p]=='')?'null':obj[p];
+            k_arr.push(tmpK);
+        }
+        t_index=k_arr.join("|-|");
+        return t_index;
+    },
     getMixRationShowDatas:function (mixRatioList,projectGljs) {
         var temRationGLJs = [];
         for(var i =0;i<mixRatioList.length;i++){
-            var pg =  _.find(projectGljs,{'code':mixRatioList[i].code});
+            var pg =  _.find(projectGljs,{'code':mixRatioList[i].code,'name':mixRatioList[i].name,'specs':mixRatioList[i].specs,'type':mixRatioList[i].type,'unit':mixRatioList[i].unit});//改关联关系
             var tem = {
+                projectGLJID:pg.id,
                 code:pg.code,
                 name:pg.name,
                 specs:pg.specs,
@@ -605,7 +625,8 @@ var gljOprObj = {
                 marketPrice:pg.unit_price.market_price,
                 adjustPrice:pg.adjust_price,
                 isEstimate:pg.is_evaluate,
-                isMixRatio:true
+                isMixRatio:true,
+                isAdd:pg.unit_price.is_add
             }
             temRationGLJs.push(tem);
         }
@@ -669,20 +690,31 @@ var gljOprObj = {
     lockRationGLJCell:function(){
         sheetCommonObj.lockCells(this.sheet,this.setting);
     },*/
-    updateRationGLJ:function (args,updateFunction) {
-        if(!updateFunction){
-            return
-        }
-        var me = gljOprObj;
-        var newval = this.numberValueChecking(args.editingText);
+    updateRationGLJ:function (args) {
+        var me=this;
+        var updateField = me.setting.header[args.col].dataCode;
         var recode = me.sheetData[args.row];
-        if(args.editingText===null){
-            updateFunction.apply(projectObj.project.ration_glj,[recode,null,args.editingText]);
-        }else if(newval!=null){
-            updateFunction.apply(projectObj.project.ration_glj,[recode,newval,args.editingText]);
+        var newval;
+        if(updateField=='marketPrice'||updateField=='customQuantity'||updateField=='basePrice'){
+            newval = number_util.checkNumberValue(args.editingText,this.decimalSetting[updateField]);
+            if(!newval){
+                me.sheet.getCell(args.row, args.col).value(recode[updateField]);
+                return;
+            }
         }else {
-            newval = recode[me.setting.header[args.col].dataCode];
-            me.sheet.getCell(args.row, args.col).value(newval);
+             if(updateField=='name'||updateField=='unit'){
+                 if(args.editingText===null){
+                     alert(me.setting.header[args.col].headerName +'不能为空!');
+                     me.sheet.getCell(args.row, args.col).value(recode[updateField]);
+                     return;
+                 }
+             }
+            newval=args.editingText==null?"":args.editingText;
+        }
+        if(updateField=='marketPrice'||updateField=='basePrice'){
+            projectObj.project.projectGLJ.updatePriceFromRG(recode,updateField,newval);
+        } else {
+            projectObj.project.ration_glj.updateRationGLJByEdit(recode,updateField,newval);
         }
     },
     updateRationAss:function (args) {

+ 16 - 0
web/building_saas/main/js/views/glj_view_contextMenu.js

@@ -76,6 +76,22 @@ var gljContextMenu = {
                     callback: function () {
                         getGLJData('m_replace');
                     }
+                },
+                "add_to_lib": {
+                    name: '保存到我的工料机库',
+                    icon: 'fa-sign-in',
+                    disabled: function () {
+                        var sheetData = gljOprObj.sheetData;
+                        var disable = true;
+                        if(subSpread.getActiveSheetIndex()==0&&sheetData!=null&&sheetData.length>0&&gljContextMenu.selectedRow<sheetData.length){
+                            var recode = sheetData[gljContextMenu.selectedRow];
+                            disable= recode.isMixRatio!=true&&recode.isAdd==true?false:true
+                        }
+                        return disable;
+                    },
+                    callback: function () {
+                        getGLJData('m_replace');
+                    }
                 }
             }
         });

+ 2 - 0
web/building_saas/main/js/views/project_info.js

@@ -26,6 +26,8 @@ var projectInfoObj = {
         CommonAjax.post('/pm/api/getProject', {"user_id": userID, "proj_id": scUrlUtil.GetQueryString('project')}, function (data) {
             if (data) {
                 that.projectInfo = data;
+                //init decimal
+                setDecimal(decimalObj, data.property.decimal);
                 $('#fullpath').html(that.getFullPathHtml(that.projectInfo));
             }
         });

+ 216 - 0
web/building_saas/main/js/views/project_property_decimal_view.js

@@ -0,0 +1,216 @@
+/**
+ * Created by Zhong on 2017/11/13.
+ */
+//default setting
+let defaultDecimal = {
+    min: 0,
+    max: 6,
+    _def: {//定义往这加, editable: 开放给用户编辑的(入库),定义editable为true的字段时,要在后端project_model.js defaultDecimal中添加定义,html添加input
+        bills: {editable: true, data: {unitPrice: 2, totalPrice: 2}},
+        ration: {editable: true, data: {quantity: 3, unitPrice: 2, totalPrice: 2}},
+        glj: {editable: true, data: {quantity: 3, unitPrice: 2}},
+        feeRate: {editable: true, data: 2},
+        quantity_detail: {editable: false, data: 4},
+        process: {editable: false, data: 6}
+    }
+};
+
+let decimalObj = Object.create(null);
+
+function isUndef(v) {
+    return v === undefined || v === null;
+}
+
+function isDef(v){
+    return v !== undefined && v !== null;
+}
+
+function isObj(v){
+    return isDef(v) && typeof v === 'object';
+}
+
+function isNum(v){
+    return isDef(v) && !isNaN(v);
+}
+
+function isInt(v){
+    return isNum(v) && v % 1 === 0;
+}
+
+function isValidDigit(v){
+    return isInt(v) && v >= defaultDecimal.min && v <= defaultDecimal.max;
+}
+
+//newV用户可编辑数据
+function toUpdateDecimal(orgV, newV){
+    let rst = false;
+    let len = Object.keys(newV).length;
+    reCompare(orgV, newV);
+    function reCompare(orgV, newV){
+        for(let attr in newV){
+            if(Object.keys(newV[attr]).length > 0){
+                if(isUndef(orgV[attr])){
+                    rst =  true;
+                    return;
+                }
+                else{
+                    reCompare(orgV[attr], newV[attr]);
+                }
+            }
+            else {
+                if(isUndef(orgV[attr]) || parseInt(newV[attr]) !== parseInt(orgV[attr])){
+                    rst = true;
+                    return;
+                }
+            }
+        }
+    }
+    return rst;
+}
+
+function setDecimal(_digits, data){
+    if(isDef(data)){
+        for(let attr in data){//设置入库的数据
+            _digits[attr] = data[attr] || defaultDecimal['_def'][attr]['data'];
+        }
+        for(let attr in defaultDecimal['_def']){//设置不入库的数据
+            if(!defaultDecimal['_def'][attr]['editable']){
+                _digits[attr] = defaultDecimal['_def'][attr]['data'];
+            }
+        }
+    }
+    else {
+        for(let attr in defaultDecimal['_def']){
+            _digits[attr] = defaultDecimal['_def'][attr]['data'];
+        }
+    }
+}
+
+//获取decimalPanel中要展示的数据
+function m_getInitData(data){
+    let rst = Object.create(null);
+    for(let attr in defaultDecimal['_def']){
+        if(defaultDecimal['_def'][attr]['editable'] && isDef(data[attr])){
+            rst[attr] = data[attr];
+        }
+    }
+    return rst;
+}
+//获取小数位数panel里的数据
+function m_getDecimalData(inputs){
+    let rst = Object.create(null);
+    for(let i = 0, len = inputs.length ; i < len; i++){
+       let name = $(inputs[i]).attr('name');
+        let attrs = name.split('-');
+        if(attrs.length > 1){
+            if(isUndef(rst[attrs[0]])){
+                rst[attrs[0]] = Object.create(null);
+            }
+            if(isDef(rst[attrs[0]])) {
+                rst[attrs[0]][attrs[1]] = parseInt($(inputs[i]).val());
+            }
+        }
+        else {
+            rst[attrs[0]] = parseInt($(inputs[i]).val());
+        }
+    }
+    return rst;
+}
+
+function v_initPanel(data){
+    if(this.isDef(data)){
+        for(let attr in data){
+            if(this.isObj(data[attr])){
+                for(let subAttr in data[attr]){
+                    let str = attr + '-' + subAttr;
+                    let jqs = 'input[name="' + str + '"]';
+                    $(jqs).val(data[attr][subAttr]);
+                    $(jqs).attr('min', defaultDecimal.min);
+                    $(jqs).attr('max', defaultDecimal.max);
+                }
+            }
+            else {
+                let str = attr + '';
+                let jqs = 'input[name="' + str + '"]';
+                $(jqs).val(data[attr]);
+                $(jqs).attr('min', defaultDecimal.min);
+                $(jqs).attr('max', defaultDecimal.max);
+            }
+        }
+    }
+}
+
+function e_validIn(inputs){
+    for(let i = 0, len = inputs.length; i < len; i++){
+        let orgV = $(inputs[i]).val();
+        $(inputs[i]).keydown(function () {
+            let v = $(this).val();
+            if(v.trim().length > 0 && isValidDigit(v)){
+                orgV = v;
+            }
+        });
+        $(inputs[i]).keyup(function () {
+            let v = $(this).val();
+            if(v.trim().length === 0 || !isValidDigit(v)){
+                alert('小数位数范围在0-6!');
+                $(this).val(orgV);
+            }
+            else{
+                //let newV = parseInt(v);
+            }
+        });
+    }
+}
+
+function e_bindCof(btn){
+    btn.bind('click', function () {
+        //获取更新的数据
+        let updateDecimal = m_getDecimalData($('input', '#poj-settings-decimal'));
+        if(toUpdateDecimal(decimalObj, updateDecimal)){
+            a_updateDigits(updateDecimal);
+        }
+    });
+}
+
+function e_unbindCof(btn){
+    btn.unbind();
+}
+
+function a_updateDigits(updateDecimal){
+    let url = '/pm/api/updateProjects';
+    let data = {
+        updateType: 'update',
+        updateData: {
+            ID: parseInt(scUrlUtil.GetQueryString('project')),
+            'property.decimal': updateDecimal
+        }
+    };
+    let postData = {
+        user_id: userID,
+        updateData: [data]
+    };
+    let scCaller = function (resultData) {
+        //update data
+        setDecimal(decimalObj, updateDecimal);
+        let v_data = m_getInitData(decimalObj);
+        v_initPanel(v_data);
+        console.log(decimalObj);
+    };
+    let errCaller = function () {
+        alert('更新小数位数失败!');
+        let v_data = m_getInitData(decimalObj);
+        v_initPanel(v_data);
+    };
+    CommonAjax.post(url, postData, scCaller, errCaller);
+}
+
+$(document).ready(function () {
+    $('#poj-set').on('shown.bs.modal', function (e) {
+        let v_data = m_getInitData(decimalObj);
+        v_initPanel(v_data);
+    });
+    //绑定确定按钮
+    e_bindCof($('#property_ok'));
+    //绑定小数位数输入控制
+    e_validIn($('input', '#poj-settings-decimal'));
+});

+ 62 - 34
web/building_saas/main/js/views/project_view.js

@@ -139,7 +139,9 @@ var projectObj = {
             }
             return nodes;
         }
-        value = value.toDecimal(projectObj.project.Decimal.common.quantity);
+        if (value) {
+            value = value.toDecimal(projectObj.project.Decimal.common.quantity);
+        }
         if (node.sourceType === projectObj.project.Bills.getSourceType()) {
             calcFees.setFee(node.data, fieldName, value);
             calc.calcNode(node, true);
@@ -250,15 +252,9 @@ var projectObj = {
             this.updateRationCode(node, value);
         }
     },
-    mainSpreadEditEnded: function (sender, info) {
-        let project = projectObj.project;
-        let node = project.mainTree.items[info.row];
-        let colSetting = projectObj.mainController.setting.cols[info.col];
-        let fieldName = projectObj.mainController.setting.cols[info.col].data.field;
-        // 检查输入类型等
-        let value = projectObj.checkSpreadEditingText(info.editingText, colSetting);
-
-        if (value && value !== calcFees.getFee(node.data, fieldName)) {
+    updateCellValue: function (node, value, colSetting) {
+        let project = projectObj.project, fieldName = colSetting.data.field;
+        if (value !== calcFees.getFee(node.data, fieldName)) {
             if (fieldName === 'code') {
                 projectObj.updateCode(node, value);
             } else if (fieldName === 'quantity' && project.quantity_detail.quantityEditChecking(value,node,fieldName)) {
@@ -276,7 +272,7 @@ var projectObj = {
                     project.VolumePrice.updateField(node.source, fieldName, value, true);
                 }
                 if (colSetting.data.wordWrap) {
-                    info.sheet.autoFitRow(info.row);
+                    info.sheet.autoFitRow(node.serialNo());
                 }
                 projectObj.mainController.refreshTreeNode([node]);
             }
@@ -286,6 +282,29 @@ var projectObj = {
             projectObj.mainController.refreshTreeNode([node], false);
         }
     },
+    mainSpreadEditEnded: function (sender, info) {
+        let project = projectObj.project;
+        let node = project.mainTree.items[info.row];
+        let colSetting = projectObj.mainController.setting.cols[info.col];
+        let fieldName = projectObj.mainController.setting.cols[info.col].data.field;
+        // 检查输入类型等
+        let value = projectObj.checkSpreadEditingText(info.editingText, colSetting);
+
+        projectObj.updateCellValue(node, value, colSetting);
+    },
+    mainSpreadRangeChanged: function (sender, info) {
+        let project = projectObj.project, setting = projectObj.mainController.setting;
+        if (info.changedCells.length > 0) {
+            for (let changedCell of info.changedCells) {
+                let cell = info.sheet.getCell(changedCell.row, changedCell.col);
+                let node = project.mainTree.items[changedCell.row];
+                let colSetting = setting.cols[changedCell.col];
+                let value = projectObj.checkSpreadEditingText(cell.text(), colSetting);
+
+                projectObj.updateCellValue(node, value, colSetting);
+            }
+        }
+    },
     checkMainSpread: function () {
         if (!this.mainSpread) {
             this.mainSpread = SheetDataHelper.createNewSpread($('#billsSpread')[0]);
@@ -338,6 +357,7 @@ var projectObj = {
                 that.mainController.bind(TREE_SHEET_CONTROLLER.eventName.treeSelectedChanged, that.treeSelectedChanged);
 
                 that.mainSpread.bind(GC.Spread.Sheets.Events.EditEnded, that.mainSpreadEditEnded);
+                that.mainSpread.bind(GC.Spread.Sheets.Events.RangeChanged, that.mainSpreadRangeChanged);
                 that.loadMainSpreadContextMenu();
             }
             else {
@@ -425,7 +445,7 @@ var projectObj = {
                         return !selected;
                     },
                     callback: function () {
-                        var selected = controller.tree.selected;
+                        var selected = controller.tree.selected, parent = selected.parent;
                         if (selected) {
                             if (selected.sourceType === project.Bills.getSourceType()) {
                                 project.Bills.deleteBills(selected.source);
@@ -437,6 +457,7 @@ var projectObj = {
                                 project.VolumePrice.delete(selected.source);
                                 controller.delete();
                             };
+                            projectObj.converseCalculateBills(parent);
                         }
                     }
                 },
@@ -452,16 +473,18 @@ var projectObj = {
     },
     // 计算node及node的所有父项
     converseCalculateBills: function (node) {
-        let calc = new BillsCalcHelper(this.project);
-        calc.calcNode(node, true);
-        let cur = node, nodes = [];
-        while (cur) {
-            nodes.push(cur);
-            cur = cur.parent;
+        if (node) {
+            let calc = new BillsCalcHelper(this.project);
+            calc.calcNode(node, true);
+            let cur = node, nodes = [];
+            while (cur) {
+                nodes.push(cur);
+                cur = cur.parent;
+            }
+            this.mainController.refreshTreeNode(nodes, false);
+            this.project.Bills.updateNodes(nodes, true);
+            calc = null;
         }
-        this.mainController.refreshTreeNode(nodes, false);
-        this.project.Bills.updateNodes(nodes, true);
-        calc = null;
     },
     // 计算全部清单
     calculateAll: function () {
@@ -487,7 +510,7 @@ $('#insert').click(function () {
 });
 $('#delete').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected;
+    var selected = controller.tree.selected, parent = selected.parent;
 
     if (selected) {
         if (selected.sourceType === project.Bills.getSourceType()) {
@@ -500,15 +523,17 @@ $('#delete').click(function () {
             project.VolumePrice.delete(selected.source);
             controller.delete();
         };
+        projectObj.converseCalculateBills(parent);
     }
 });
 $('#upLevel').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected;
+    var selected = controller.tree.selected, orgParent = selected.parent;
 
     if (selected && selected.sourceType === project.Bills.getSourceType()) {
         project.Bills.upLevelBills(selected.source);
         controller.upLevel();
+        projectObj.converseCalculateBills(orgParent);
     }
 });
 $('#downLevel').click(function () {
@@ -516,12 +541,13 @@ $('#downLevel').click(function () {
     var selected = controller.tree.selected;
     if (selected && selected.sourceType === project.Bills.getSourceType()) {
         project.Bills.downLevelBills(selected.source);
-        controller.downLevel();
+        controller.downLevel();       
+        projectObj.converseCalculateBills(selected.parent);
     }
 });
 $('#upMove').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
-    var selected = controller.tree.selected, pre, preSerialNo;
+    var selected = controller.tree.selected, pre = selected.preSibling, preSerialNo;
 
     if (selected.sourceType === project.Bills.getSourceType()) {
         project.Bills.upMoveBills(selected.source);
@@ -538,15 +564,17 @@ $('#downMove').click(function () {
     var controller = projectObj.mainController, project = projectObj.project;
     var selected = controller.tree.selected, next, nextSerialNo;
 
-    if (selected.sourceType === project.Bills.getSourceType()) {
-        project.Bills.downMoveBills(selected.source);
-        controller.downMove();
-    } else if (selected.sourceType === project.Ration.getSourceType()) {
-        project.Ration.changePos(selected.source, selected.nextSibling.source);
-        controller.downMove();
-    } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
-        project.VolumePrice.changePos(selected.source, selected.nextSibling.source);
-        controller.downMove();
+    if (selected) {
+        if (selected.sourceType === project.Bills.getSourceType()) {
+            project.Bills.downMoveBills(selected.source);
+            controller.downMove();
+        } else if (selected.sourceType === project.Ration.getSourceType()) {
+            project.Ration.changePos(selected.source, selected.nextSibling.source);
+            controller.downMove();
+        } else if (selected.sourceType === project.VolumePrice.getSourceType()) {
+            project.VolumePrice.changePos(selected.source, selected.nextSibling.source);
+            controller.downMove();
+        }
     }
 });
 

+ 5 - 0
web/building_saas/pm/html/project-management-Recycle.html

@@ -1,6 +1,11 @@
 <div class="toolsbar">
     <legend class="m-0 pb-1">全部</legend>
 </div>
+<div class="top-content">
+    <div class="main-data-top" id="gc_waiting">
+        <p style="text-align: center; margin-top: 30px;">正在加载数据...</p>
+    </div>
+</div>
 <div class="poj-list">
     <table class="table table-hover table-sm mb-5" id="gcTree">
         <thead>

+ 23 - 2
web/building_saas/pm/html/project-management.html

@@ -177,7 +177,7 @@
                     <div class="col-md-6">
                         <legend>单价文件</legend>
                         <table class="table table-bordered table-hover table-sm" id="summary-project-unit-price-table">
-                            <thead><th></th><th>名称</th></thead>
+                            <thead><th></th><th>名称</th><th width="50">使用</th></thead>
                             <tbody>
                             <tr><td>1</td><td>A单价文件</td></tr>
                             <tr><td>2</td><td>B单价文件</td></tr>
@@ -188,7 +188,7 @@
                     <div class="col-md-6">
                         <legend>费率文件</legend>
                         <table class="table table-bordered table-hover table-sm" id="summary-project-fee-table">
-                            <thead><th></th><th>名称</th></thead>
+                            <thead><th></th><th>名称</th><th width="50">使用</th></thead>
                             <tbody>
                             <tr><td>1</td><td>A费率文件</td></tr>
                             <tr><td>2</td><td>B费率文件</td></tr>
@@ -501,6 +501,27 @@
         </div>
     </div>
 </div>
+<!--弹出删除 单价/费率 文件-->
+<div class="modal fade" id="del-wj" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除确认</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <h5 class="text-danger">删除 "C单价文件" ?</h5>
+                <p class="">删除后,你可以在回收站找到它。</p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal" id="fileDelCancel">取消</button>
+                <a href="javascript:void(0);" class="btn btn-danger" id="fileDelConfirm">删除</a>
+            </div>
+        </div>
+    </div>
+</div>
 <!-- JS. -->
 <!-- inject:js -->
 <script src="/lib/jquery/jquery-3.2.1.min.js"></script>

+ 210 - 120
web/building_saas/pm/js/pm_gc.js

@@ -1,55 +1,10 @@
 /**
  * Created by Zhong on 2017/10/30.
  */
-//测试数据
-let deleteInfo = {deleteBy: 76075, deleteDateTime: '2017-10-31', deleted: true};
-let temp_gc_datas = [
-    {ID: 1, ParentID: -1, NextSiblingID: 7, name: 'testTreeA', projType: 'Project', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo,
-        unitPriceFiles: [{name: 'uA', id: 100, root_project_id: 1, user_id: 76075, deleteInfo: deleteInfo}, {name: 'uB', id: 101, root_project_id: 1, user_id: 76075, deleteInfo: deleteInfo}],
-        feeRateFiles: [{name: 'fA', ID: 'fr-1', rootProjectID: 1, userID: 76075, deleteInfo: deleteInfo}, {name: 'fB', ID: 'fr-2', rootProjectID: 1, userID: 76075, deleteInfo: deleteInfo}]},
-    {ID: 2, ParentID: 1, NextSiblingID: 3, name: 'enA', projType: 'Engineering', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo},
-    {ID: 3, ParentID: 1, NextSiblingID: -1, name: 'enB', projType: 'Engineering', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo},
-    {ID: 4, ParentID: 3, NextSiblingID: 5, name: 'tenderA', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'fr-1', name: 'fA'}, unitPriceFile: {id: 100, name: 'uA'}}},
-    {ID: 5, ParentID: 3, NextSiblingID: 6, name: 'tenderB', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'unD-1', name: 'unDF'}, unitPriceFile: {id: 100, name: 'uA'}}},
-    {ID: 6, ParentID: 3, NextSiblingID: -1, name: 'tenderC', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'fr-2', name: 'fB'}, unitPriceFile: {id: 101, name: 'uB'}}},
-    {ID: 7, ParentID: -1, NextSiblingID: 10, name: 'testTreeB', projType: 'Project', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo,
-        unitPriceFiles: [{name: 'uC', id: 102, root_project_id: 7, user_id: 76075, deleteInfo: deleteInfo}, {name: 'uD', id: 103, root_project_id: 7, user_id: 76075, deleteInfo: deleteInfo}],
-        feeRateFiles: [{name: 'fC', ID: 'fr-3', rootProjectID: 7, userID: 76075, deleteInfo: deleteInfo}]},
-    {ID: 8, ParentID: 7, NextSiblingID: -1, name: 'enC', projType: 'Engineering', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo},
-    {ID: 9, ParentID: 8, NextSiblingID: -1, name: 'tenderD', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'fr-3', name: 'fC'}, unitPriceFile: {id: 102, name: 'uC'}}},
-    {ID: 10, ParentID: -1, NextSiblingID: -1, name: 'testTreeC', projType: 'Project', userID: 76075, createDateTime: '2017-10-30', unitPriceFiles: [], feeRateFiles: [{name: 'fC', ID: 'fr-3', rootProjectID: 7, userID: 76075, deleteInfo: deleteInfo}]},
-    {ID: 11, ParentID: 10, NextSiblingID: -1, name: 'enD', projType: 'Engineering', userID: 76075, createDateTime: '2017-10-30'},
-    {ID: 12, ParentID: 11, NextSiblingID: -1, name: 'tenderE', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'fr-3', name: 'fC'}, unitPriceFile: {id: 103, name: 'uD'}}},
-    {ID: 13, ParentID: 2, NextSiblingID: 14, name: 'tenderF', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'unD-2', name: 'unDF2'}, unitPriceFile: {id: 102, name: 'uC'}}},
-    {ID: 14, ParentID: 2, NextSiblingID: 15, name: 'tenderG', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'fr-3', name: 'fC'}, unitPriceFile: {id: 102, name: 'uC'}}},
-    {ID: 15, ParentID: 2, NextSiblingID: -1, name: 'tenderH', projType: 'Tender', userID: 76075, createDateTime: '2017-10-30', deleteInfo: deleteInfo, property: {feeFile: {id: 'qwqw', name: 'qweq'}, unitPriceFile: {id: 300, name: 'u30'}}}
-
-];
-
-/*function getTestDatas(){
-    let rst = [];
-    for(let i = 0; i < 1000; i ++){
-        let nid = i + 1 < 1000 ? i + 1 : -1;
-        let obj = {ID: i, ParentID: -1, NextSiblingID: nid, name : 'test' + i, projType: 'Project', userID: 76075, createDateTime: '2017-11-3', deleteInfo: deleteInfo,
-            unitPriceFiles: [{name: 'up' + i, id: i + 10000, deleteInfo: deleteInfo}], feeRateFiles: [{name: 'ff' + i, id: 'fr-' + i, deleteInfo: deleteInfo}]};
-        rst.push(obj);
-    }
-    return rst;
-}*/
 let gcTree = null;
 let decDate = null;//恢复后的名称后缀(时间+恢复)
-/*let projectType = {
-    folder: 'Folder',
-    tender: 'Tender',
-    project: 'Project',
-    engineering: 'Engineering'
-};*/
-let fileType = {
-    unitPriceFile: 'UnitPriceFile',
-    feeRateFile: 'FeeRateFile'
-};
 //恢复路径t = tender, e = engineering, p = project
-let recPath = {t: 'T', t_e: 'T_E', t_e_p: 'T_E_P', e: 'E', e_p: 'E_P', p: 'P'};
+const recPath = {t: 'T', t_e: 'T_E', t_e_p: 'T_E_P', e: 'E', e_p: 'E_P', p: 'P'};
 let gcTreeSetting = {
     tree: {
         id: 'ID',
@@ -103,9 +58,13 @@ let gcTreeSetting = {
             event: {
                 getText: function (html, node, text) {
                     if (node.data.projType === projectType.tender) {
-                        html.push(deleted(node) ?
-                            node.data.deleteInfo.deleteDateTime : '');
-                     //   html.push(text ? new Date(text).Format('yyyy-MM-dd') : '');
+                        if(deleted(node)){
+                            let localDateTime = new Date(node.data.deleteInfo.deleteDateTime).toLocaleDateString();
+                            html.push(new Date(localDateTime).Format('yyyy-MM-dd'));
+                        }
+                        else{
+                            html.push('');
+                        }
                     }
                 }
             }
@@ -117,7 +76,13 @@ let gcTreeSetting = {
             event: {
                 getText: function (html, node, text) {
                     if (node.data.projType === projectType.tender) {
-                      html.push(text ? text : '');
+                        if(deleted(node)){
+                            let localDateTime = new Date(text).toLocaleDateString();
+                            html.push(new Date(localDateTime).Format('yyyy-MM-dd'));
+                        }
+                        else{
+                            html.push('');
+                        }
                     }
                 }
             }
@@ -148,11 +113,6 @@ let gcTreeSetting = {
                         $('p', '#rePoj .modal-body').remove();
                         $('#rePoj .modal-header').html(v_getTitle(node));
                         $('#rePoj .modal-body').html(v_getMoBody(node, tenderNodes));
-                        console.log(node);
-                        console.log(node.preSibling());
-                        //test 获取更新的数据
-                        /*let updateDatas = m_getRecDatas(node);
-                        console.log(updateDatas);*/
                     });
                 }
             }
@@ -202,6 +162,7 @@ let gcTreeSetting = {
 
 $(document).ready(function () {
     $('#tab_pm_gc').on('show.bs.tab', function () {
+        $('#gc_waiting').show();
         gc_init();
         Tree = null;
     });
@@ -213,9 +174,12 @@ function gc_init(){
     let table = $('#gcTree');
     $('thead', table).remove();
     $('tbody', table).remove();
-    m_buildVirtualTree(temp_gc_datas);
-    gcTree = $.fn.treeTable.init(table, gcTreeSetting, temp_gc_datas);
-    //gcTree = $.fn.treeTable.init(table, gcTreeSetting, getTestDatas());
+    a_getGC(function (datas) {
+        m_buildVirtualTree(datas);
+        let normalDatas = m_VTreeToDatas(datas);
+        gcTree = $.fn.treeTable.init(table, gcTreeSetting, normalDatas);
+        $('#gc_waiting').hide();
+    });
 }
 
 //项目恢复模态框标题
@@ -294,6 +258,7 @@ function v_recFiles(project, fileIds, type){
                 let id = projFiles[i].id || projFiles[i].ID || null;
                 if(id && id === fileIds[j]){
                     isExist = true;
+                    break;
                 }
             }
             if(!isExist){
@@ -313,7 +278,12 @@ function v_removeNode(node){
     gcTree.removeNode(node);
     let parent = node.parent || null;
     if(parent && parent.children.length === 0 && parent.data !== undefined){
-        v_removeNode(parent);
+        if(parent.data.projType === projectType.project && fileEmpty(parent)){
+            v_removeNode(parent);
+        }
+        else if(parent.data.projType !== projectType.project){
+            v_removeNode(parent);
+        }
     }
 }
 
@@ -331,6 +301,9 @@ function v_refreshNode(node){
 //将获取的回收站中的数据建虚拟树
 function m_buildVirtualTree(datas){
     for(let i = 0, len = datas.length; i < len; i++){
+        if(datas[i].projType === projectType.project){
+            datas[i].ParentID = -1;
+        }
         let children = datas[i].children || null;
         if(children){
             m_buildVirtualTree(children);
@@ -345,6 +318,25 @@ function m_buildVirtualTree(datas){
     }
 }
 
+function m_VTreeToDatas(datas){
+    let rst = [];
+    let projs = datas;
+    rst = rst.concat(projs);
+    for(let i = 0, len = projs.length; i < len; i++){
+        let p_engs = projs[i].children || null;
+        if(p_engs){
+            rst = rst.concat(p_engs);
+            for(let j = 0, jLen = p_engs.length; j < jLen; j ++){
+                let e_tenders = p_engs[j].children || null;
+                if(e_tenders){
+                    rst = rst.concat(e_tenders);
+                }
+            }
+        }
+    }
+    return rst;
+}
+
 //获得当前节点的tenders数据,模态提示框用
 function m_getTenders(node){
     if(node.data.projType === projectType.tender) return [node];
@@ -412,6 +404,7 @@ function m_getRecDatas(oprNode){
             for(let i = 0, len = tenders.length; i < len; i++){
                 rstProj = rstProj.concat(getUpdateDatas(projectType.tender, tenders[i], true, false));
             }
+            rstProj = deWeightName(rstProj);
             //恢复单价、费率文件
             rstFile = rstFile.concat(getUpdateFiles(tenders, project));
         }
@@ -427,42 +420,54 @@ function m_getRecDatas(oprNode){
         path = recPath.p;
         let engineerings = oprNode.children;
         if(engineerings.length > 0){
-            let allTenders = [];
+            let allTenders = [], rstEngs = [];
             for(let i = 0, len = engineerings.length; i < len; i++){
                 //恢复单项工程
-                rstProj = rstProj.concat(getUpdateDatas(projectType.engineering, engineerings[i], false, false));
+                rstEngs = rstEngs.concat(getUpdateDatas(projectType.engineering, engineerings[i], false, false));
                 let tenders = engineerings[i].children;
                 allTenders = allTenders.concat(tenders);
+                let rstTends = [];
                 for(let j = 0, jLen = tenders.length; j < jLen; j++){
                     //恢复单位工程
-                    rstProj = rstProj.concat(getUpdateDatas(projectType.tender, tenders[j], false, false));
+                    rstTends = rstTends.concat(getUpdateDatas(projectType.tender, tenders[j], false, false));
                 }
+                //去除重名
+                rstTends = deWeightName(rstTends);
+                rstProj = rstProj.concat(rstTends);
             }
+            //去除重名
+            rstEngs = deWeightName(rstEngs);
+            rstProj = rstProj.concat(rstEngs);
             //恢复单价、费率文件
             rstFile = rstFile.concat(getUpdateFiles(allTenders, oprNode));
+
         }
         //恢复建设项目
         rstProj = rstProj.concat(getUpdateDatas(projectType.project, oprNode, false, false));
     }
-    console.log(path);
     rst.proj = rstProj;
     rst.file = rstFile;
     return rst;
 }
 
 //获得勾选的单价、费率文件的id
-function m_getFilesIds(nodes){
-    let rstSet = new Set();
+function m_getFilesObjs(nodes){
+    let rst = [];
     for(let i = 0, len = nodes.length; i < len; i++){
         let fileId = $(nodes[i]).attr('fileId') || null;
         if(fileId){
+            let dispName = $('td:eq(0)', $(nodes[i])[0].parentNode.parentNode)[0].textContent;
+            let name = dispName.slice(0, dispName.length - 4);
             if($(nodes[i]).attr('fileType') === fileType.unitPriceFile){
                 fileId = parseInt(fileId);
             }
-            rstSet.add(fileId);
+            let obj = Object.create(null);
+            obj.id = fileId;
+            obj.name = name;
+            rst.push(obj);
         }
     }
-    return Array.from(rstSet);
+    return rst;
 }
 
 function m_project(node){
@@ -481,21 +486,40 @@ function m_project(node){
 //点击单价文件、费率文件下的恢复操作(确认)
 function e_recFiles(btn){
     btn.bind('click', function () {
-        let recIds = m_getFilesIds($('[name = "fileItems"]:checked'));
-        let fileType = $('[name = "fileItems"]:checked').attr('fileType');
+        decDate = '(' + new Date().Format('MM-dd hh:mm:ss') + '恢复)';
+        let recObjs = m_getFilesObjs($('[name = "fileItems"]:checked'));
+        let recIds = [];
+        for(let i of recObjs){
+            recIds.push(i.id);
+        }
+        let type = $('[name = "fileItems"]:checked').attr('fileType');
         let selected = gcTree.selected();
         //backend
         let updateDatas = [];
-        for(let i = 0, len = recIds.length; i < len; i++){
-            updateDatas.push(getUpdateObj(fileType, {id: recIds[i]}, {deleteInfo: null}));
-        }
-        //front
-        if(recIds.length > 0){
-            v_recFiles(selected, recIds, fileType);
-            if(deleted(selected)){
-                delete selected.data.deleteInfo;
+        for(let i = 0, len = recObjs.length; i < len; i++){
+            let findData = type === fileType.unitPriceFile ? {id: recObjs[i].id} : {ID: recObjs[i].id};
+            updateDatas.push(getUpdateObj(type, findData, {deleteInfo: null, name: delPostFix(recObjs[i].name) + decDate}));
+        }
+        //恢复建设项目
+        if(updateDatas.length > 0 && deleted(selected)){
+            updateDatas.push(getUpdateObj(projectType.project, {ID: selected.data.ID}, {deleteInfo: null, name: delPostFix(selected.data.name) + decDate}));
+        }
+        updateDatas = deWeightName(updateDatas);
+        a_rec(updateDatas, caller);
+        function caller(){
+            //front
+            if(recIds.length > 0){
+                v_recFiles(selected, recIds, type);
+                if(deleted(selected)){
+                    delete selected.data.deleteInfo;
+                }
+                if(fileEmpty(selected) && selected.children.length === 0){
+                    gcTree.removeNode(selected);
+                }
+                else {
+                    gcTree.refreshNodesDom([selected]);
+                }
             }
-            gcTree.refreshNodesDom([selected]);
         }
     });
 }
@@ -508,20 +532,21 @@ function e_recProj(btn){
         let updateObj = m_getRecDatas(selected);
         let updateDatas = updateObj.proj.concat(updateObj.file);
         let fileObj = getRecFileObj(updateObj.file);
-        console.log(updateObj);
         //保存成功后回调front
-        //front
-        let project = m_project(selected);
-        if(project){
-            if(fileObj[fileType.unitPriceFile].length > 0){
-                v_recFiles(project, fileObj[fileType.unitPriceFile], fileType.unitPriceFile);
-            }
-            if(fileObj[fileType.feeRateFile].length > 0){
-                v_recFiles(project, fileObj[fileType.feeRateFile], fileType.feeRateFile);
+        a_rec(updateDatas, caller);
+        function caller() {
+            let project = m_project(selected);
+            if(project){
+                if(fileObj[fileType.unitPriceFile].length > 0){
+                    v_recFiles(project, fileObj[fileType.unitPriceFile], fileType.unitPriceFile);
+                }
+                if(fileObj[fileType.feeRateFile].length > 0){
+                    v_recFiles(project, fileObj[fileType.feeRateFile], fileType.feeRateFile);
+                }
             }
+            v_removeNode(selected);
+            v_refreshNode(selected);
         }
-        v_removeNode(selected);
-        v_refreshNode(selected);
     });
 }
 
@@ -529,6 +554,7 @@ function a_getGC(callback){
     $.ajax({
         type: 'post',
         url: '/pm/api/getGCDatas',
+        data: {data: JSON.stringify({user_id: userID})},
         dataType: 'json',
         timeout: 5000,
         success: function (result) {
@@ -538,14 +564,14 @@ function a_getGC(callback){
                 }
             }
         }
-    })
+    });
 }
 
 function a_rec(nodes, callback){
     $.ajax({
         type: 'post',
         url: '/pm/api/recGC',
-        data: {nodes: JSON.stringify(nodes)},
+        data: {data: JSON.stringify({user_id: userID, nodes: nodes})},
         dataType: 'json',
         timeout: 5000,
         success: function (result) {
@@ -558,14 +584,67 @@ function a_rec(nodes, callback){
     })
 }
 
+//去除重名,回收站不处理重名,只保证恢复到项目管理中不出现重名
+function deWeightName(datas){
+    let rst = [];
+    let _deWeight = Object.create(null), prefix = 'name_';
+    //按同名分组
+    for(let i = 0, len = datas.length; i < len; i++){
+        let data = datas[i];
+        let _names = _deWeight[prefix + data.updateData.name] || null;
+        if(!_names){
+            _names = _deWeight[prefix + data.updateData.name] = [];
+        }
+        if(_names){
+            _names.push(data);
+        }
+    }
+    for(let _name in _deWeight){
+        let sameNameDatas = _deWeight[_name];
+        let count = 0;
+        for(let i = 0, len = sameNameDatas.length; i < len; i++){
+            let postFix = '(' + count + ')';
+            if(i > 0){
+                sameNameDatas[i].updateData.name = sameNameDatas[i].updateData.name + postFix;
+            }
+            count ++;
+            rst.push(sameNameDatas[i]);
+        }
+    }
+    return rst;
+}
+
+//去除名称后缀(Date恢复)
+function delPostFix (str){
+    let rst = '';
+    rst = delPostRecFix(delPostNameFix(str));
+    return rst;
+}
+function delPostRecFix (str){
+    let rst = '';
+    let re = /(+[0-9]{2}-[0-9]{2}\s+[0-9]{2}:[0-9]{2}:[0-9]{2}恢复)/g;
+    rst = str.replace(re, '');
+    return rst;
+}
+function delPostNameFix (str){
+    let rst = '';
+    let re = /(+[0-9][0-9]*)/g;
+    rst = str.replace(re, '');
+    return rst;
+}
+
 function deleted(node){
-    return node.data.deleteInfo !== undefined && node.data.deleteInfo.deleted;
+    return node.data.deleteInfo !== undefined && node.data.deleteInfo && node.data.deleteInfo.deleted;
+}
+
+function fileEmpty(node){
+    return node.data.unitPriceFiles.length === 0 && node.data.feeRateFiles.length === 0;
 }
 
 function fIsExist(files, id, type){
     let isExist = false;
     for(let i = 0, len = files.length; i < len; i++){
-        if((type === fileType.unitPriceFile && files[i].id === id) || (type === fileType.feeRateFile && files[i].ID === id)){
+        if((type === fileType.unitPriceFile && files[i].id === parseInt(id)) || (type === fileType.feeRateFile && files[i].ID === id)){
             isExist = true;
             break;
         }
@@ -577,12 +656,12 @@ function getRecFileObj(files){
     let rst = Object.create(null);
     let rst_UF = [], rst_FF = [];
     for(let i = 0, len = files.length; i < len; i++){
-        if(files[i].findData !== undefined && files[i].findData.id !== undefined){
-            if(files[i].updateType === fileType.unitPriceFile){
+        if(files[i].findData !== undefined){
+            if(files[i].updateType === fileType.unitPriceFile && files[i].findData.id !== undefined){
                 rst_UF.push(files[i].findData.id);
             }
-            else if(files[i].updateType === fileType.feeRateFile){
-                rst_FF.push(files[i].findData.id);
+            else if(files[i].updateType === fileType.feeRateFile && files[i].findData.ID !== undefined){
+                rst_FF.push(files[i].findData.ID);
             }
         }
     }
@@ -601,56 +680,67 @@ function getUpdateObj(updateType, findData, updateData){
 
 function getUpdateDatas(updateType, node, mtNID, mtPM){
     let rst = [];
-    if(!decDate){
-        decDate = '(' + new Date().Format('MM-dd hh:mm:ss') + '恢复)';
-    }
+    decDate = '(' + new Date().Format('MM-dd hh:mm:ss') + '恢复)';
+    let newName = delPostFix(node.data.name) + decDate;
     if(updateType === projectType.tender || updateType === projectType.engineering){
-        //维护回收站树
-     /*   if(mtGC){
-            rst.push(getUpdateObj(updateType, {NextSiblingID: node.data.ID, 'deleteInfo.deleted': true}, {NextSiblingID: node.data.NextSiblingID}));
-        }*/
         //维护项目管理树
         if(mtPM){
             rst.push(getUpdateObj(updateType, {ParentID: node.data.ParentID, NextSiblingID: -1, deleteInfo: null}, {NextSiblingID: node.data.ID}));
         }
         //恢复
         if(mtNID){
-            rst.push(getUpdateObj(updateType, {ID: node.data.ID, name: node.data.name + decDate}, {deleteInfo: null, NextSiblingID: -1}));
+            rst.push(getUpdateObj(updateType, {ID: node.data.ID}, {name: newName, deleteInfo: null, NextSiblingID: -1}));
         }
         else {
-            rst.push(getUpdateObj(updateType, {ID: node.data.ID, name: node.data.name + decDate}, {deleteInfo: null}));
+            rst.push(getUpdateObj(updateType, {ID: node.data.ID}, {name: newName, deleteInfo: null}));
         }
     }
     else if(updateType === projectType.project){
-       /* //维护回收站树
-        if(mtGC){
-            rst.push(getUpdateObj(updateType, {NextSiblingID: node.data.ID, 'deleteInfo.deleted': true}, {NextSiblingID: node.data.NextSiblingID}));
-        }*/
         //恢复
-        rst.push(getUpdateObj(updateType, {ID: node.data.ID, name: node.data.name + decDate}, {deleteInfo: null}));//NextSibling为undefined,后端处理
+        rst.push(getUpdateObj(updateType, {ID: node.data.ID}, {deleteInfo: null, name: newName}));//NextSibling为undefined,后端处理
     }
     return rst;
 }
 //unitPriceFile or feeRateFile
 function getUpdateFiles(tenders, project){
-    let unitFiles = [], feeFiles = [], rst = [];
+    let rstUF = [], rstFF = [], rst = [];
+    let _unitFiles = Object.create(null), _feeFiles = Object.create(null), prefix = '_id';
+    decDate = '(' + new Date().Format('MM-dd hh:mm:ss') + '恢复)';
     for(let i = 0, len = tenders.length; i < len; i++){
         //恢复单价文件
         if(project && project.data.unitPriceFiles.length > 0 && fIsExist(project.data.unitPriceFiles, tenders[i].data.property.unitPriceFile.id, fileType.unitPriceFile)){
-            unitFiles.push(tenders[i].data.property.unitPriceFile.id);
+            //unitFiles.push(tenders[i].data.property.unitPriceFile.id);
+            let propId = tenders[i].data.property.unitPriceFile.id;
+            let propName = tenders[i].data.property.unitPriceFile.name;
+            let _uf = _unitFiles[prefix + propId] || null;
+            if(!_uf){
+                let obj = Object.create(null);
+                obj.id = parseInt(propId);
+                obj.name = propName;
+                _unitFiles[prefix + propId] = obj;
+            }
         }
         //恢复费率文件
         if(project && project.data.feeRateFiles.length > 0 && fIsExist(project.data.feeRateFiles, tenders[i].data.property.feeFile.id, fileType.feeRateFile)){
-            feeFiles.push(tenders[i].data.property.feeFile.id);
+            //feeFiles.push(tenders[i].data.property.feeFile.id);
+            let propId = tenders[i].data.property.feeFile.id;
+            let propName = tenders[i].data.property.feeFile.name;
+            let _ff = _feeFiles[prefix + propId] || null;
+            if(!_ff){
+                let obj = Object.create(null);
+                obj.id = propId;
+                obj.name = propName;
+                _feeFiles[prefix + propId] = obj;
+            }
         }
     }
-    let ufIds = Array.from(new Set(unitFiles));
-    let ffIds = Array.from(new Set(feeFiles));
-    for(let i = 0, len = ufIds.length; i < len; i++){
-        rst.push(getUpdateObj(fileType.unitPriceFile, {id: ufIds[i]}, {deleteInfo: null}));
+    for(let uf in _unitFiles){
+        rstUF.push(getUpdateObj(fileType.unitPriceFile, {id: _unitFiles[uf].id}, {deleteInfo: null, name: delPostFix(_unitFiles[uf].name) + decDate}));
     }
-    for(let i = 0, len = ffIds.length; i < len; i++){
-        rst.push(getUpdateObj(fileType.feeRateFile, {id: ffIds[i]}, {deleteInfo: null}));
+    for(let ff in _feeFiles){
+        rstFF.push(getUpdateObj(fileType.feeRateFile, {ID: _feeFiles[ff].id}, {deleteInfo: null, name: delPostFix(_feeFiles[ff].name) + decDate}));
     }
+    rst = rst.concat(deWeightName(rstUF));
+    rst = rst.concat(deWeightName(rstFF));
     return rst;
 }

+ 296 - 72
web/building_saas/pm/js/pm_main.js

@@ -12,12 +12,17 @@ let engineering = [];
 let feeRateData = [];
 let isSaving = false;
 let projectProperty = [];
+let fileDelObj = null;
 let projectType = {
     folder: 'Folder',
     tender: 'Tender',
     project: 'Project',
     engineering: 'Engineering'
 };
+let fileType = {
+    unitPriceFile: 'UnitPriceFile',
+    feeRateFile: 'FeeRateFile'
+};
 let ProjTreeSetting = {
     tree: {
         id: 'ID',
@@ -64,7 +69,6 @@ let ProjTreeSetting = {
                         html.push('class="'+ aClassName +'" ');
                     }
                     if (node && node.data) {
-                        //html.push('href="/main?project=', node.id(), '"');
                         html.push('href="javacript:void(0);"');
                     }
                     html.push('>', icon, '&nbsp;', text, '<a>');
@@ -123,14 +127,19 @@ let ProjTreeSetting = {
                 case projectType.project:
                     $("#add-engineering-btn").removeClass("disabled");
                     $("#add-project-btn").removeClass("disabled");
+                    $("#del-btn").removeClass("disabled");
                     break;
                 case projectType.folder:
+                    if(!node.children || node.children.length === 0){
+                        $("#del-btn").removeClass("disabled");
+                    }
                     $("#add-folder-btn").removeClass("disabled");
                     $("#add-project-btn").removeClass("disabled");
                     break;
                 case projectType.engineering:
                     $("#add-tender-btn").removeClass("disabled");
                     $("#add-engineering-btn").removeClass("disabled");
+                    $("#del-btn").removeClass("disabled");
                     break;
                 case projectType.tender:
                     $("#add-tender-btn").removeClass("disabled");
@@ -138,9 +147,9 @@ let ProjTreeSetting = {
                     $("#copy-to-btn").removeClass("disabled");
                     $("#share-btn").removeClass("disabled");
                     $("#cooperate-btn").removeClass("disabled");
+                    $("#del-btn").removeClass("disabled");
                     break;
             }
-            $("#del-btn").removeClass("disabled");
             $("#rename-btn").removeClass("disabled");
             $('td:eq(0)', node.row).append($('<i class="fa fa-sort" data-toggle="tooltip" data-placement="top" title="长安拖动"></i>'));
         }
@@ -161,6 +170,28 @@ $(document).ready(function() {
         $(".slide-sidebar").animate({width:"800"}).addClass("open");
     });
 
+    //单价、费率文件删除确认
+    $('#fileDelConfirm').click(function () {
+        if(fileDelObj && fileDelObj.id){
+            let updateObj = Object.create(null);
+            updateObj.updateType = 'delete';
+            updateObj.updateData = Object.create(null);
+            if(fileDelObj.fileType === fileType.unitPriceFile){
+                updateObj.updateData.id = fileDelObj.id;
+            }
+            else if(fileDelObj.fileType === fileType.feeRateFile){
+                updateObj.updateData.ID = fileDelObj.id;
+            }
+            updateObj.fileType = fileDelObj.fileType;
+            console.log(updateObj);
+            a_updateFiles([updateObj], function () {
+                fileDelObj = null;
+                $('#fileDelCancel').click();
+                //refresh front?
+            });
+        }
+    });
+
     // 新增建设项目点击
     $('#add-project-btn').click(function () {
         let selectedItem = Tree.selected();
@@ -253,9 +284,30 @@ $(document).ready(function() {
         }
 
         // 获取单价文件数据
-        getUnitFile(projectInfo.data.ID);
+        getUnitFile(projectInfo.data.ID, function (response) {
+            if (response.data.length <= 0) {
+                return false;
+            }
+            let unitFileHtml = '';
+            for(let tmp of response.data) {
+                unitFileHtml += '<option value="'+ tmp.id +'">'+ tmp.name +'</option>';
+            }
+            $("#unit-price").children("option:not(':first')").remove();
+            $("#unit-price").children("option").first().after(unitFileHtml);
+        });
         // 获取费率文件数据
-        getFeeRateFile(projectInfo.data.ID);
+        getFeeRateFile(projectInfo.data.ID, function (response) {
+            if (response.data.length <= 0) {
+                return false;
+            }
+            var first = $("#tender-fee-rate").children("option").first();
+            $("#tender-fee-rate").empty();
+            $("#tender-fee-rate").append(first);
+            for(let tmp of response.data) {
+                var option =  $("<option>").val(tmp.ID).text(tmp.name);
+                $("#tender-fee-rate").append(option);
+            }
+        });
     });
 
     // 新增单位工程
@@ -330,6 +382,7 @@ $(document).ready(function() {
         let dialog = $('#del');
         if (Tree) {
             updateData = GetDeleteUpdateData(Tree.selected());
+            console.log(updateData);
             UpdateProjectData(updateData, function () {
                 dialog.modal('hide');
                 Tree.removeNode(Tree.selected());
@@ -875,10 +928,12 @@ function GetDeleteUpdateData(node) {
     let datas = [], updateData,
         pre = node.preSibling(),
         deleteNodeData = function (node) {
-            var data = {};
+            var data = Object.create(null);
             data['updateType'] = 'delete';
-            data['updateData'] = {};
+            //data['NextSiblingID'] = node.data.NextSiblingID;
+            data['updateData'] = Object.create(null);
             data['updateData'][Tree.setting.tree.id] = node.id();
+            data['updateData']['projType'] = node.data.projType;
             if (node.data.projType === 'Tender') {
                 data['updateData']['FullFolder'] = GetFullFolder(node.parent);
             }
@@ -1085,7 +1140,7 @@ function getProperty(projectInfo) {
  * @param {Number} parentID
  * @return {void}
  */
-function getUnitFile(parentID) {
+function getUnitFile(parentID, callback) {
     parentID = parseInt(parentID);
     if (isNaN(parentID) && parentID <= 0) {
         return;
@@ -1106,15 +1161,9 @@ function getUnitFile(parentID) {
             if (response.error === 1) {
                 alert('获取失败!');
             } else {
-                if (response.data.length <= 0) {
-                    return false;
+                if(callback){
+                    callback(response);
                 }
-                let unitFileHtml = '';
-                for(let tmp of response.data) {
-                    unitFileHtml += '<option value="'+ tmp.id +'">'+ tmp.name +'</option>';
-                }
-                $("#unit-price").children("option:not(':first')").remove();
-                $("#unit-price").children("option").first().after(unitFileHtml);
             }
         }
     });
@@ -1127,7 +1176,7 @@ function getUnitFile(parentID) {
  * @param {Number} parentID
  * @return {void}
  */
-function getFeeRateFile(parentID) {
+function getFeeRateFile(parentID, callback) {
     parentID = parseInt(parentID);
     if (isNaN(parentID) && parentID <= 0) {
         return;
@@ -1148,15 +1197,8 @@ function getFeeRateFile(parentID) {
             if (response.error === 1) {
                 alert('获取失败!');
             } else {
-                if (response.data.length <= 0) {
-                    return false;
-                }
-                var first = $("#tender-fee-rate").children("option").first();
-                $("#tender-fee-rate").empty();
-                $("#tender-fee-rate").append(first);
-                for(let tmp of response.data) {
-                    var option =  $("<option>").val(tmp.ID).text(tmp.name);
-                    $("#tender-fee-rate").append(option);
+                if(callback){
+                    callback(response);
                 }
             }
         }
@@ -1179,76 +1221,258 @@ function setDataToSideBar() {
     $(target).show();
     $(target + '-name').html(name);
 
-    if (selectedItem.children.length <= 0) {
+    /*if (selectedItem.children.length <= 0) {
         return;
-    }
-    // 建设项目相关
-    let counter = 1;
-    let html = '';
-    for(let tmp of selectedItem.children) {
+    }*/
+    if(selectedItem.children.length > 0){
+        // 建设项目相关
+        let counter = 1;
+        let html = '';
+        for(let tmp of selectedItem.children) {
+            html += '<tr>' +
+                '<td>'+ counter +'</td>' +
+                '<td>'+ counter +'</td>' +
+                '<td>'+ tmp.data.name +'</td>' +
+                '<td></td>' +
+                '<td></td>' +
+                '<td></td>' +
+                '<td></td>' +
+                '</tr>';
+
+        }
         html += '<tr>' +
-            '<td>'+ counter +'</td>' +
-            '<td>'+ counter +'</td>' +
-            '<td>'+ tmp.data.name +'</td>' +
+            '<td>'+ (counter + 1) +'</td>' +
+            '<td> </td>' +
+            '<td>合计</td>' +
             '<td></td>' +
             '<td></td>' +
             '<td></td>' +
             '<td></td>' +
             '</tr>';
-
+        $(target + '-table tbody').html(html);
     }
-    html += '<tr>' +
-        '<td>'+ (counter + 1) +'</td>' +
-        '<td> </td>' +
-        '<td>合计</td>' +
-        '<td></td>' +
-        '<td></td>' +
-        '<td></td>' +
-        '<td></td>' +
-        '</tr>';
-    $(target + '-table tbody').html(html);
 
     // 加载单价文件与费率文件
     if (selectedItem.data.projType === projectType.project) {
         let engineeringData = selectedItem.children !== null && selectedItem.children.children !== null ?
             selectedItem.children : [];
-        if (engineeringData.length <= 0) {
+        /*if (engineeringData.length <= 0) {
             return;
-        }
-        let unitPriceFileHtml = '';
-        let feeFileHtml = '';
-        let unitPriceFileCounter = 1;
-        let feeFileCounter = 1;
+        }*/
         let unitPriceFileList = [];
         let feeFileList = [];
-        for(let engineering of engineeringData) {
-            let tenderData = engineering.children !== null ? engineering.children : [];
-            if (tenderData.length <= 0) {
-                continue;
+        let poj_tenders = getProjTenders(selectedItem);
+        getUnitFile(selectedItem.data.ID, function (response) {
+            unitPriceFileList = unitPriceFileList.concat(response.data);
+            $(target + '-unit-price-table tbody').empty();
+            set_file_table(target, poj_tenders, unitPriceFileList, fileType.unitPriceFile);
+        });
+        getFeeRateFile(selectedItem.data.ID, function (response) {
+            feeFileList = feeFileList.concat(response.data);
+            $(target + '-fee-table tbody').empty();
+            set_file_table(target, poj_tenders, feeFileList, fileType.feeRateFile);
+        });
+    }
+}
+
+//获取建设项目的全部单位工程
+function getProjTenders(proj) {
+    let rst = [];
+    let engineerings = proj.children || null;
+    if (engineerings) {
+        for (let i = 0, len = engineerings.length; i < len; i++) {
+            let e_tenders = engineerings[i].children || null;
+            if (e_tenders && e_tenders.length > 0) {
+                rst = rst.concat(e_tenders);
             }
-            for(let tmp of tenderData) {
-                tmp = tmp.data.property !== null ? tmp.data.property : null;
-                if(tmp === null) {
-                    continue;
-                }
+        }
+    }
+    return rst;
+}
+//单价、费率文件汇总信息的使用次数、使用该文件的单位工程
+function getUsedObj(tenders, fileId, type){
+    let rst = Object.create(null), usedCount = 0, usedInfo = '';
+    rst.usedCount = 0;
+    rst.usedInfo = null;
+    for(let i = 0, len = tenders.length; i < len; i++){
+        let theFile = type === fileType.unitPriceFile ? tenders[i].data.property.unitPriceFile || null : tenders[i].data.property.feeFile || null;
+        if(theFile && theFile.id == fileId){
+            usedCount ++;
+            usedInfo += tenders[i].data.name + '<br>';
+        }
+    }
+    if(usedCount > 0){
+        usedInfo = usedInfo.slice(0, usedInfo.length - 4);
+        rst.usedCount = usedCount;
+        rst.usedInfo = usedInfo;
+    }
+    return rst;
+}
 
-                if (tmp.feeFile !== undefined && feeFileList.indexOf(tmp.feeFile.name) < 0) {
-                    feeFileHtml += '<tr><td>'+ feeFileCounter +'</td><td>'+ tmp.feeFile.name +'</td></tr>';
-                    feeFileCounter++;
-                    feeFileList.push(tmp.feeFile.name);
+//单价、费率文件汇总界面交互
+function bindEvents_file_table(jqS, usedObj, targetBody, type){
+    //悬浮显示删除和重命名按钮
+    $(jqS).hover(function(){
+        $('p',this).show();
+    },function(){
+        $('p', this).hide();
+    });
+    //删除
+    if(usedObj.usedCount > 0){//不可删除
+        $(jqS + ' p a:eq(0)').addClass('disabled');
+        $(jqS + ' p a:eq(0)').removeAttr('data-target');
+    }
+    else {
+        $(jqS + ' p a:eq(0)').on('click', function () {
+            let dispName = $(jqS + ' div:eq(0)')[0].childNodes[0].textContent;
+            //刷新删除弹出框内容
+            let delData = '删除"' + dispName + '"?';
+            $('#del-wj .modal-body h5').text(delData);
+            let attrId = $(jqS).attr('id');
+            let id = attrId.slice(5, attrId.length);
+            if(type === fileType.unitPriceFile){
+                id = parseInt(id);
+            }
+            let obj = Object.create(null);
+            obj.fileType = type;
+            obj.id = id;
+            fileDelObj = obj;
+        });
+    }
+    //重命名
+    $(jqS + ' p a:eq(1)').on('click', function () {
+        let fileObjs = getFileObj(targetBody, type);
+        let orgDispName = $(jqS + ' div:eq(0)')[0].childNodes[0].textContent;
+        let postFix = orgDispName.slice(orgDispName.length - 4, orgDispName.length);
+        let orgName = orgDispName.slice(0, orgDispName.length - 4);
+        $(jqS + ' input').val(orgName);
+        $(jqS + ' div:eq(0)').hide();
+        $(jqS + ' div:eq(1)').show();
+        //确认重命名
+        $(jqS + ' .btn-success').on('click', function () {
+            let attrId = $(jqS).attr('id');
+            let id = attrId.slice(5, attrId.length);
+            let newName = $(jqS + ' input').val().trim();
+            if(newName !== orgName){
+                if(hasThisFileName(fileObjs, newName)){
+                    alert('本建设项目已存在该文件名,请重新输入!');
+                    $(jqS + ' input').val(orgName);
+                    return;
                 }
-
-                if (tmp.unitPriceFile !== undefined && unitPriceFileList.indexOf(tmp.unitPriceFile.name) < 0) {
-                    unitPriceFileHtml += '<tr><td>'+ unitPriceFileCounter +'</td><td>'+ tmp.unitPriceFile.name +'单价文件</td></tr>';
-                    unitPriceFileCounter++;
-                    unitPriceFileList.push(tmp.unitPriceFile.name);
+                if(newName.trim().length === 0){
+                    alert('文件名不可为空!');
+                    $(jqS + ' input').val(orgName);
+                    return;
+                }
+                //ajax
+                let updateObj = Object.create(null);
+                updateObj.fileType = type;
+                updateObj.updateType = 'update';
+                updateObj.updateData = Object.create(null);
+                if(type === fileType.unitPriceFile){
+                    updateObj.updateData.id = parseInt(id);
+                    //updateObj.updateData.id = id;
+                }
+                else if(type === fileType.feeRateFile){
+                    updateObj.updateData.ID = id;
                 }
+                updateObj.updateData.name = newName;
+                a_updateFiles([updateObj], function () {
+                    let newDispName = newName + postFix;
+                    $(jqS + ' div:eq(0)')[0].childNodes[0].textContent = newDispName;
+                    $(jqS + ' div:eq(0)').show();
+                    $(jqS + ' div:eq(1)').hide();
+                });
+                console.log(updateObj);
             }
+            let newDispName = newName + postFix;
+            $(jqS + ' div:eq(0)')[0].childNodes[0].textContent = newDispName;
+            $(jqS + ' div:eq(0)').show();
+            $(jqS + ' div:eq(1)').hide();
+        });
+        //取消重命名
+        $(jqS + ' .btn-secondary').on('click', function () {
+            $(jqS + ' div:eq(0)').show();
+            $(jqS + ' div:eq(1)').hide();
+        });
+    });
+    //悬浮框显示使用该文件的单位工程
+    $($(jqS)[0].nextSibling).popover({
+            placement:"bottom",
+            html:true,
+            trigger:"hover", //| focus
+            content: usedObj.usedInfo ? usedObj.usedInfo : ''
+        }
+    );
+}
 
-            $(target + '-unit-price-table tbody').html(unitPriceFileHtml);
-            $(target + '-fee-table tbody').html(feeFileHtml);
+//更新单价、费率文件(删除、重命名)
+function a_updateFiles(updateDatas, callback){
+    $.ajax({
+        type: 'post',
+        url: '/pm/api/updateFiles',
+        data: {data: JSON.stringify({user_id: userID, updateDatas: updateDatas})},
+        dataType: 'json',
+        success: function (result){
+            if(!result.error && callback){
+                callback();
+            }
         }
+    })
+}
 
+function set_file_table(target, poj_tenders, fileList, type){
+    let fileCounter = 1;
+    for(let i = 0, len = fileList.length; i < len; i++){
+        let fileId = type === fileType.unitPriceFile ? fileList[i].id : fileList[i].ID;
+        let usedObj = getUsedObj(poj_tenders, fileId, type);
+        let usedHtml = usedObj.usedCount > 0 ?  '<td class="text-center"><a href="javascript:void(0);">' + usedObj.usedCount + '</a></td>' : '<td class="text-center">' + usedObj.usedCount + '</td>';
+        let hoverHtml = '<p style="display: none"><a class="btn btn-sm" href="javascript:void(0);" data-toggle="modal" data-target="#del-wj">删除</a><a class="btn btn-sm" href="javascript:void(0);">重命名</a></p></div>';
+        let renHtml = '<div class="input-group" style="display: none;">'
+            + '<input class="form-control form-control-sm" value="">'
+            + '<span class="input-group-btn">'
+            + '<button class="btn btn-success btn-sm" type="button"><i class="fa fa-check"></i></button>'
+            + '</span>'
+            + '<span class="input-group-btn">'
+            + '<button class="btn btn-secondary btn-sm" type="button"><i class="fa fa-remove"></i></button>'
+            + '</span>'
+            + '</div></td>';
+        let fileTypeStr = type === fileType.unitPriceFile ? '单价文件' : '费率文件';
+        let fileHtml = '<tr><td>' + fileCounter + '</td><td id="file_' + fileId + '"><div>' + fileList[i].name + fileTypeStr + hoverHtml + renHtml + usedHtml + '</tr>';
+        fileCounter++;
+        let targetBody = type === fileType.unitPriceFile ? target + '-unit-price-table tbody' : target + '-fee-table tbody';
+        $(targetBody).append(fileHtml);
+        let jqS = '#file_' + fileId;
+        bindEvents_file_table(jqS, usedObj, targetBody, type);
     }
+}
+
+function getFileObj(jqS, type){
+    let rst = [];
+    let trs = $('tr', jqS);
+    for(let i = 0, len = trs.length; i < len; i++){
+        let td = $('td:eq(1)', trs[i])[0];
+        let dispName = td.childNodes[0].childNodes[0].textContent;
+        let name = dispName.slice(0, dispName.length - 4);
+        let attrId = $(td).attr('id');
+        let id = attrId.slice(5, attrId.length);
+        if(type === fileType.unitPriceFile){
+            id = parseInt(id);
+        }
+        let obj = Object.create(null);
+        obj.id = id;
+        obj.name = name;
+        rst.push(obj);
+    }
+    return rst;
+    //let orgDispName = $(jqS + ' div:eq(0)')[0].childNodes[0].textContent;
+}
 
-}
+function hasThisFileName(fileObjs, name){
+    for(let i = 0, len = fileObjs.length; i < len; i++){
+        if(fileObjs[i].name === name){
+            return true;
+        }
+    }
+    return false;
+}