Browse Source

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

Chenshilong 7 years ago
parent
commit
18e8c739d5

+ 0 - 2
modules/complementary_glj_lib/controllers/gljController.js

@@ -128,8 +128,6 @@ class GljController extends BaseController{
     mixUpdateGljItems(req, res){
         let user_id = req.session.sessionUser.ssoId,
             compilation_id = req.session.sessionCompilation._id;
-        console.log(user_id);
-        console.log(compilation_id);
         let data = JSON.parse(req.body.data);
         let updateItems = data.updateItems,
             addItems = data.addItems,

+ 2 - 0
modules/pm/models/project_model.js

@@ -5,6 +5,7 @@ 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 billsQuantityDecimal from './project_property_bills_quantity_decimal';
 let FeeRateFiles = mongoose.model('fee_rate_file');
 let counter = require("../../../public/counter/counter.js");
 
@@ -103,6 +104,7 @@ ProjectsDAO.prototype.updateUserProjects = async function(userId, datas, callbac
                 }
                 if(data.updateData.projType === projectType.tender){
                     data.updateData.property.decimal = defaultDecimal;
+                    data.updateData.property.billsQuantityDecimal = billsQuantityDecimal;
                 }
                 newProject = new Projects(data.updateData);
                 // 查找同级是否存在同名数据

+ 51 - 0
modules/pm/models/project_property_bills_quantity_decimal.js

@@ -0,0 +1,51 @@
+/**
+ * Created by Zhong on 2017/11/16.
+ */
+/*
+* 单位工程清单工程量精度模板
+* */
+const billsQuantityDecimal = [
+    {unit: '其他未列单位', decimal: 2},
+    {unit: 't', decimal: 3},
+    {unit: '吨', decimal: 3},
+    {unit: 'km', decimal: 3},
+    {unit: '千米', decimal: 3},
+    {unit: 'm', decimal: 2},
+    {unit: '米', decimal: 2},
+    {unit: 'm2', decimal: 2},
+    {unit: '平方米', decimal: 2},
+    {unit: 'm3', decimal: 2},
+    {unit: '立方米', decimal: 2},
+    {unit: 'kg', decimal: 2},
+    {unit: '千克', decimal: 2},
+    {unit: '公斤', decimal: 2},
+    {unit: '元', decimal: 2},
+    {unit: '工日', decimal: 0},
+    {unit: '台班', decimal: 0},
+    {unit: '个', decimal: 0},
+    {unit: '件', decimal: 0},
+    {unit: '根', decimal: 0},
+    {unit: '组', decimal: 0},
+    {unit: '宗', decimal: 0},
+    {unit: '付', decimal: 0},
+    {unit: '处', decimal: 0},
+    {unit: '系统', decimal: 0},
+    {unit: '部', decimal: 0},
+    {unit: '台', decimal: 0},
+    {unit: '辆', decimal: 0},
+    {unit: '套', decimal: 0},
+    {unit: '株', decimal: 0},
+    {unit: '从', decimal: 0},
+    {unit: '缸', decimal: 0},
+    {unit: '支', decimal: 0},
+    {unit: '只', decimal: 0},
+    {unit: '块', decimal: 0},
+    {unit: '座', decimal: 0},
+    {unit: '对', decimal: 0},
+    {unit: '份', decimal: 0},
+    {unit: '樘', decimal: 0},
+    {unit: '攒', decimal: 0},
+    {unit: '榀', decimal: 0}
+];
+
+export default billsQuantityDecimal;

+ 248 - 18
modules/reports/util/rpt_construct_data_util.js

@@ -12,6 +12,14 @@ let treeUtil = require('../../../public/treeUtil');
 let projectConst = consts.projectConst;
 let projectConstList = consts.projectConstList;
 
+const GLJ_TYPE = {
+    Labour: 1,
+    Material: 2,
+    Machine: 3,
+    Main_Material: 4,
+    Equipment: 5
+}
+
 class Rpt_Common{
     initialize(rpt_tpl, currentDataObj) {
         this.template = rpt_tpl;
@@ -152,7 +160,7 @@ class Rpt_Data_Extractor {
                 let srcData = getModuleDataByKey(rawDataObj.prjData, preHandle[JV.PROP_DATA_KEY]);
                 switch(preHandle[JV.PROP_HANDLE_TYPE]) {
                     case JV.PROP_HANDLE_TYPE_SORT:
-                        sortData(srcData, preHandle);
+                        sortData(srcData, preHandle, rawDataObj.prjData);
                         break;
                     case JV.PROP_HANDLE_TYPE_FILTER:
                         filterData(srcData, preHandle, rawDataObj.prjData);
@@ -160,6 +168,12 @@ class Rpt_Data_Extractor {
                     case JV.PROP_HANDLE_TYPE_SUM:
                         summaryData(srcData, preHandle, rawDataObj.prjData);
                         break;
+                    case JV.PROP_HANDLE_TYPE_ADD_DUMMY:
+                        addDummyData(srcData, preHandle);
+                        break;
+                    case JV.PROP_HANDLE_TYPE_ADJUST:
+                        adjustData(srcData, preHandle);
+                        break;
                     default:
                         break;
                 }
@@ -320,7 +334,133 @@ function filterData(sourceData, handleCfg, prjData) {
     // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/filteredRst.js");
 }
 
-function sortData(sourceData, sortCfg) {
+function adjustData(sourceData, adjustCfg) {
+    let rstArr = [];
+    for (let item of sourceData.data) {
+        if (item._doc) {
+            rstArr.push(item._doc);
+        } else {
+            rstArr.push(item);
+        }
+    }
+    for (let item of adjustCfg[JV.PROP_ADJUST_COLLECTION]) {
+        for (let rec of rstArr) {
+            if (item[JV.PROP_ADJUST_ACTION] === "prefix") {
+                rec[item.key] = item[JV.PROP_ADJUST_ACTION_VAL] + rec[item.key];
+            } else if (item[JV.PROP_ADJUST_ACTION] === "suffix") {
+                rec[item.key] = rec[item.key] + item[JV.PROP_ADJUST_ACTION_VAL];
+            }
+        }
+    }
+    delete sourceData.data;
+    sourceData.data = rstArr;
+}
+
+function getDupGrpKeyVals(sourceData, segKeys) {
+    let rst = [];
+    function pushKeyVal(item) {
+        let tr = {};
+        for (let i = 0; i < segKeys.length; i++) {
+            tr[segKeys[i]] = item[segKeys[i]];
+        }
+        rst.push(tr);
+    }
+    for (let idx = 0; idx < sourceData.length; idx++) {
+        let itemRec = sourceData[idx];
+        if (idx === 0) {
+            pushKeyVal(itemRec);
+            continue;
+        }
+        let hasDiff = false;
+        for (let i = 0; i < segKeys.length; i++) {
+            if (itemRec[segKeys[i]] !== sourceData[idx - 1][segKeys[i]]) {
+                hasDiff = true;
+                break;
+            }
+        }
+        if (hasDiff) {
+            pushKeyVal(itemRec);
+        }
+    }
+    return rst;
+}
+
+function addDummyData(sourceData, addCfg) {
+    let rstArr = [], tempRstArr = [];
+    for (let item of sourceData.data) {
+        if (item._doc) {
+            tempRstArr.push(item._doc);
+        } else {
+            tempRstArr.push(item);
+        }
+    }
+    for (let item of addCfg[JV.PROP_DUMMY_COLLECTION]) {
+        let newRecStr = JSON.stringify(item[JV.PROP_DUMMY_VAL]), cacheGrpKeyRecs = null;
+        if (item[JV.PROP_FREQUENCY] === "OncePerGrp") {
+            if (!cacheGrpKeyRecs) {
+                cacheGrpKeyRecs = {};
+            }
+            let cacheKey = "";
+            for (let key of item[JV.PROP_GRP_KEYS]) {
+                cacheKey += "_" + key;
+            }
+            if (!cacheGrpKeyRecs[cacheKey]) {
+                cacheGrpKeyRecs[cacheKey] = getDupGrpKeyVals(tempRstArr, item[JV.PROP_GRP_KEYS]);
+            }
+            for (let kv of cacheGrpKeyRecs[cacheKey]) {
+                let rec = JSON.parse(newRecStr);
+                for (let key of item[JV.PROP_GRP_KEYS]) {
+                    rec[key] = kv[key];
+                }
+                rstArr.push(rec);
+            }
+
+        } else if (item[JV.PROP_FREQUENCY] === "Once") {
+            rstArr.push(JSON.parse(newRecStr));
+        }
+    }
+    rstArr = rstArr.concat(tempRstArr);
+    delete sourceData.data;
+    sourceData.data = rstArr;
+}
+
+function getGLJBizType(orgType, orgCode, orgName) {
+    let rst = orgType;
+    if (orgName.indexOf("其他材料费") >= 0) {
+        rst = 299;
+    } else if (orgType === GLJ_TYPE.Labour) {
+        rst = 11;
+        if (orgCode === "000000") rst = 10;
+    } else if (orgType === GLJ_TYPE.Main_Material || orgType === GLJ_TYPE.Equipment) {
+        //未计价材料(主材 + 设备)
+        rst = 30 + orgType;
+    } else if (orgType === GLJ_TYPE.Material || (orgType >= 200 && orgType < 300)) {
+        //材料
+        if (orgCode === "000000") {
+            rst = 20; //2.材料
+        } else if (orgCode === "000000_1") {
+            rst = 30; //(1) 未计价材料
+        } else if (orgCode === "000000_2") {
+            rst = 40; //(2) 辅助材料
+        } else if (orgCode === "000000_3") {
+            rst = 50; //(3) 其他材料费
+        } else {
+            rst = 45; //到这里就只有辅助材料没有预处理了
+        }
+    } else if (orgType === GLJ_TYPE.Machine || (orgType >= 300 && orgType < 400)) {
+        //机械
+        if (orgCode === "000000") {
+            rst = 300; //3.机械
+        } else if (orgType === GLJ_TYPE.Machine) {
+            rst = 300.5;
+        } else {
+            rst = orgType;
+        }
+    }
+    return rst;
+}
+
+function sortData(sourceData, sortCfg, prjData) {
     let rst = sourceData.data, tempRstArr = [];
     let sortType = sortCfg[JV.PROP_SORT_TYPE];
     for (let item of sourceData.data) {
@@ -330,6 +470,56 @@ function sortData(sourceData, sortCfg) {
             tempRstArr.push(item);
         }
     }
+    function private_normal_sort(destArr, sortKeys) {
+        destArr.sort(function(a, b){
+            let compRst = 0;
+            for (let comp of sortKeys) {
+                let reverse = (comp.order === 'ascend')?1:(-1);
+                //
+                if (a[comp.key] > b[comp.key]) {
+                    compRst = reverse;
+                    break;
+                } else if (a[comp.key] < b[comp.key]) {
+                    compRst = -reverse;
+                    break;
+                }
+            }
+            return compRst;
+        });
+    }
+    function private_parent_sort(parentArr, parentKeys, childArr, childKeys) {
+        let tmpRst = {}, rst = [];
+        for (let pItem of parentArr) {
+            let pKey = "key";
+            for (let key of parentKeys) {
+                pKey += "_" + pItem[key];
+            }
+            tmpRst[pKey] = [];
+        }
+        for (let cItem of childArr) {
+            let cKey = "key";
+            for (let key of childKeys) {
+                cKey += "_" + cItem[key];
+            }
+            if (tmpRst[cKey]) {
+                tmpRst[cKey].push(cItem);
+            } else {
+                //unknown child value! should be filtered!
+            }
+        }
+        // childArr.splice(0);
+        for (let pItem of parentArr) {
+            let pKey = "key";
+            for (let key of parentKeys) {
+                pKey += "_" + pItem[key];
+            }
+            rst.push(tmpRst[pKey]);
+            // for (let rItem of tmpRst[pKey]) {
+            //     childArr.push(rItem);
+            // }
+        }
+        return rst;
+    }
     switch (sortType) {
         case "tree":
             rst = treeUtil.buildTreeNodeDirectly(tempRstArr);
@@ -340,23 +530,59 @@ function sortData(sourceData, sortCfg) {
             // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/sortedAndFlattedRst.js");
             break;
         case "normal":
-            tempRstArr.sort(function(a, b){
-                let compRst = 0;
-                for (let comp of sortCfg[JV.PROP_SORT_KEYS]) {
-                    let reverse = (comp.order === 'ascend')?1:(-1);
-                    if (a[comp.key] > b[comp.key]) {
-                        compRst = reverse;
-                        break;
-                    } else if (a[comp.key] < b[comp.key]) {
-                        compRst = -reverse;
-                        break;
+            private_normal_sort(tempRstArr, sortCfg[JV.PROP_SORT_KEYS]);
+            delete sourceData.data;
+            sourceData.data = tempRstArr;
+            // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/normalSortedRst.js");
+            break;
+        case "accord_to_parent":
+            let pcKey = sortCfg[JV.PROP_PARENT_CHILD_SORT_KEY];
+            let parentSrcData = getModuleDataByKey(prjData, pcKey[JV.PROP_PARENT_DATA_KEY]);
+            if (parentSrcData) {
+                let tempParentArr = [];
+                for (let item of parentSrcData.data) {
+                    if (item._doc) {
+                        tempParentArr.push(item._doc);
+                    } else {
+                        tempParentArr.push(item);
                     }
                 }
-                return compRst;
-            });
+                let sortedRstArr = private_parent_sort(tempParentArr, pcKey[JV.PROP_PARENT_SORT_KEYS], tempRstArr, pcKey[JV.PROP_CHILD_SORT_KEYS]);
+                if (sortCfg[JV.PROP_OTHER_SUB_SORT] && sortCfg[JV.PROP_OTHER_SUB_SORT].length > 0) {
+                    for (let sort of sortCfg[JV.PROP_OTHER_SUB_SORT]) {
+                        if (sort[JV.PROP_SORT_TYPE] === 'normal') {
+                            for (let subArr of sortedRstArr) {
+                                private_normal_sort(subArr, sort[JV.PROP_SORT_KEYS]);
+                            }
+                        } else if (sort[JV.PROP_SORT_TYPE] === 'self_define') {
+                            for (let subArr of sortedRstArr) {
+                                console.log(subArr);
+                                let selfDefFunc = null;
+                                eval('selfDefFunc = ' + sort[JV.PROP_SORT_TYPE_SELF_DEFINE_LOGIC]);
+                                subArr.sort(selfDefFunc);
+                                console.log(subArr);
+                            }
+                        }
+                    }
+                }
+                tempRstArr.splice(0);
+                for (let item of sortedRstArr) {
+                    for (let subItem of item) {
+                        tempRstArr.push(subItem);
+                    }
+                }
+            }
+            delete sourceData.data;
+            sourceData.data = tempRstArr;
+            break;
+        case "self_define":
+            if (sortCfg[JV.PROP_SORT_TYPE_SELF_DEFINE_LOGIC]) {
+                let selfDefFunc = null;
+                eval('selfDefFunc = ' + sortCfg[JV.PROP_SORT_TYPE_SELF_DEFINE_LOGIC]);
+                tempRstArr.sort(selfDefFunc);
+            }
             delete sourceData.data;
             sourceData.data = tempRstArr;
-            // fsUtil.wirteObjToFile(sourceData.data, "D:/GitHome/ConstructionCost/tmp/normalSortedRst.js");
             break;
         default:
             //
@@ -583,7 +809,7 @@ function ext_getArrayItemByKey(arrayKey, itemKey, itemKeyValue, itemRstKey){
 
 }
 
-function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey) {
+function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey, dftValIfNotFound) {
     let rst = [], parentObj = this;
     let IdKey = (adHocIdKey)?adHocIdKey:"ID";
     let dtObj = parentObj["myOwnRawDataObj"];
@@ -618,7 +844,9 @@ function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey) {
                         break;
                     }
                 }
-                // if (!isFound) rst.push[null];
+                if (!isFound) {
+                    rst.push(dftValIfNotFound);
+                }
             }
         } else {
             for (let item of dtObj.data) {
@@ -628,7 +856,9 @@ function ext_getPropertyByForeignId(foreignIdVal, adHocIdKey, propKey) {
                     break;
                 }
             }
-            // if (!isFound) rst.push[null];
+            if (!isFound) {
+                rst.push(dftValIfNotFound);
+            }
         }
     }
     return rst;

+ 9 - 0
public/stringUtil.js

@@ -136,6 +136,15 @@ module.exports = {
         }
         return rst;
     },
+    trim: function(str) {
+        return str.replace(/(^\s*)|(\s*$)/g, "");
+    },
+    leftTrim: function(str) {
+        return str.replace(/(^\s*)/g,"");
+    },
+    rightTrim: function(rst) {
+        return str.replace(/(\s*$)/g,"");
+    },
     convertNumToChinese : function(num, isCurrency) {
         if (!/^\d*(\.\d*)?$/.test(num)) { return "Number is wrong!"; }
         let AA, BB;

+ 16 - 0
public/web/rpt_value_define.js

@@ -47,6 +47,11 @@ const JV = {
 
     NODE_MAP_DATA_HANDLE_INFO: "映射数据预处理",
     PROP_DATA_KEY: "映射数据对象",
+    PROP_PARENT_DATA_KEY: "父映射数据对象",
+    PROP_PARENT_CHILD_SORT_KEY: "父子排序键",
+    PROP_PARENT_SORT_KEYS: "父排序键值集",
+    PROP_CHILD_SORT_KEYS: "子排序键值集",
+    PROP_OTHER_SUB_SORT: "其他子排序",
     PROP_HANDLE_TYPE: "预处理类型",
     PROP_FILTER_KEY: "过滤键值集",
     PROP_FILTER_COMPARE_OBJ: "compareObjKey",
@@ -56,7 +61,18 @@ const JV = {
     PROP_HANDLE_TYPE_FILTER: "过滤",
     PROP_HANDLE_TYPE_SUM: "合计",
     PROP_HANDLE_TYPE_SORT: "排序",
+    PROP_HANDLE_TYPE_ADD_DUMMY: "增加Dummy数据",
+    PROP_HANDLE_TYPE_ADJUST: "数据调整",
+
+    PROP_ADJUST_COLLECTION: "数据调整集",
+    PROP_ADJUST_ACTION: "action",
+    PROP_ADJUST_ACTION_VAL: "actionValue",
+    PROP_DUMMY_COLLECTION: "Dummy数据集",
+    PROP_DUMMY_VAL: "Dummy数据对象值",
+    PROP_FREQUENCY: "频率",
+    PROP_GRP_KEYS: "GrpKeyIds",
     PROP_SORT_TYPE: "排序方式",
+    PROP_SORT_TYPE_SELF_DEFINE_LOGIC: "自定义逻辑",
     PROP_SORT_KEYS: "排序键值集",
     PROP_SUM_GROUP_KEYS: "分组键值集",
     PROP_SUM_SUM_KEYS: "统计键值集",

+ 7 - 0
public/web/scMathUtil.js

@@ -60,6 +60,13 @@ let scMathUtil = {
         let result = bin;
         let iDot = bin.indexOf('.');
         if (iDot < 0){return result};
+        // 二进制浮点数带小数位数最大长度
+        let floatLength = 53;
+        let iLength = bin.length;
+        // 长度小于53说明该二进制数尾数长度小于Double类型限制,未被截断,无需进位
+        if (iLength < floatLength) {
+            return result;
+        }
         let iLength = bin.length;
         for (let i = iLength - 1; i > iDot; i--){
             let num = Number(bin[i]);

+ 3 - 2
test/unit/reports/test_tpl_09_1.js

@@ -39,7 +39,8 @@ let demoPrjId = - 1;
 let demoRptId = 226, pagesize = "A4";
 
 let userId_Leng = 1142; //小冷User Id
-demoPrjId = 720; //QA: DW3
+// demoPrjId = 720; //QA: DW3
+demoPrjId = 838; //QA:
 /*/
 let userId_Dft = userId_Leng;
 /*/
@@ -90,7 +91,7 @@ test('测试 - 获取project部分数据: ', function (t) {
                     //     newData.push(JSON.stringify(item));
                     // }
                     // fsUtil.writeArrayToFile(newData, "D:/GitHome/ConstructionCost/tmp/getProjectData_partial.js");
-                    fsUtil.wirteObjToFile(prjObj, "D:/GitHome/ConstructionCost/tmp/getProjectObjectNew.js");
+                    // fsUtil.wirteObjToFile(prjObj, "D:/GitHome/ConstructionCost/tmp/getProjectObjectNew.js");
                     fsUtil.wirteObjToFile(results, "D:/GitHome/ConstructionCost/tmp/getProjectData_partialNew.js");
                     t.pass('pass succeeded!');
                     t.end();

+ 4 - 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 = scMathUtil.roundTo(consumeAmt, -3);
+                let roundCons = scMathUtil.roundTo(parseFloat(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 = scMathUtil.roundTo(items[i].consumeAmt, 3);
+                        let roundCons = scMathUtil.roundTo(parseFloat(items[i].consumeAmt), 3);
                         isChange = true;
                         currentObj.consumeAmt = roundCons;
                         for(let j = 0; j < component.length; j++){
@@ -496,8 +496,8 @@ let gljComponentOprObj = {
     reCalGljBasePrc: function (component) {
         let me = gljComponentOprObj, gljBasePrc = 0;
         for(let i = 0; i < component.length; i++){
-            let roundBasePrc = scMathUtil.roundTo(component[i].basePrice, -2);
-            gljBasePrc += scMathUtil.roundTo(roundBasePrc * component[i].consumeAmt, -2);
+            let roundBasePrc = scMathUtil.roundTo(parseFloat(component[i].basePrice), -2);
+            gljBasePrc += scMathUtil.roundTo(roundBasePrc * parseFloat(component[i].consumeAmt), -2);
         }
         return gljBasePrc;
     }

+ 4 - 3
web/building_saas/main/html/main.html

@@ -264,7 +264,7 @@
                                 <li class="nav-item"><a class="nav-link" data-toggle="pill" href="#poj-settings-2" role="tab">工程特征</a></li>
                                 <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-billsQuanDecimal" id="tab_poj-settings-bqDecimal" 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>
@@ -337,8 +337,8 @@
                                     </div>
                                 </div>
                                 <!--清单工程精度-->
-                                <div class="tab-pane fade" id="poj-settings-5" role="tabpanel">
-                                    <div class="modal-auto-height" id="quantityPrecision">
+                                <div class="tab-pane fade" id="poj-settings-billsQuanDecimal" role="tabpanel">
+                                    <div class="modal-auto-height" style="overflow: hidden;" id="billsQuanDecimal">
                                     </div>
                                 </div>
                                 <!--小数位数-->
@@ -663,6 +663,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_bills_quantity_decimal.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>

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

@@ -28,6 +28,7 @@ var projectInfoObj = {
                 that.projectInfo = data;
                 //init decimal
                 setDecimal(decimalObj, data.property.decimal);
+                billsQuanDecimal.datas = data.property.billsQuantityDecimal;
                 $('#fullpath').html(that.getFullPathHtml(that.projectInfo));
             }
         });

+ 348 - 0
web/building_saas/main/js/views/project_property_bills_quantity_decimal.js

@@ -0,0 +1,348 @@
+/**
+ * Created by Zhong on 2017/11/15.
+ */
+/*
+* 清单工程量精度
+* */
+let billsQuanDecimal = {
+    datas: [],
+    //根据单位获取精度, 需要用清单工程量精度的调用此接口
+    decimal: function (unit) {
+        for(let i = 0, len = this.datas.length; i < len; i++){
+            if(unit === this.datas[i].unit){
+                return this.datas[i].decimal;
+            }
+        }
+        return this.datas[0].decimal;
+    }
+};
+
+//点了确定才更新数据
+let billsDecimalView = {
+    default: {min: 0, max: 4, decimal: 3},//以防后面修改,小数数位的取值范围, default.decimal为新增时默认的小数位
+    angleDecimal: {unit: '其他未列单位', decimal: 2},//防止出现工程没有初始默认的清单工程量精度模板(正常不会出现)
+    cache: [],//temp
+    workBook: null,
+    setting:{
+        header: [
+            {name: '计量单位', dataCode: 'unit', width: 120, vAlign: 'center', hAlign: 'left'},
+            {name: '小数位数', dataCode: 'decimal', width: 80, vAlign: 'center', hAlign: 'center'}
+        ],
+        options: {
+            tabStripVisible:  false,
+            allowCopyPasteExcelStyle : false,
+            allowUserDragDrop : false,
+            allowUserDragFill: false,
+            scrollbarMaxAlign : true
+        }
+    },
+
+    renderSheetFuc: function (sheet, fuc) {
+        sheet.suspendPaint();
+        sheet.suspendEvent();
+        fuc();
+        sheet.resumePaint();
+        sheet.resumeEvent();
+    },
+
+    setOptions: function (workbook, opts) {
+        for(let opt in opts){
+            workbook.options[opt] = opts[opt];
+        }
+    },
+
+    getComboItems: function (min, max) {
+        let rst = [];
+        while (min <= max){
+            rst.push(min);
+            min ++;
+        }
+        return rst;
+    },
+    
+    setComboBox: function (sheet, items) {
+        let combo = new GC.Spread.Sheets.CellTypes.ComboBox();
+        combo.items(items);
+        combo.editable(true);
+        sheet.getRange(-1, 1, -1, 1).cellType(combo);
+    },
+
+    buildHeader: function (sheet, headers) {
+        let me = billsDecimalView;
+        let fuc = function () {
+            sheet.setColumnCount(headers.length);
+            sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader);
+            me.setComboBox(sheet, me.getComboItems(me.default.min, me.default.max));
+            for(let i = 0, len = headers.length; i < len; i++){
+                sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
+                sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
+            }
+        };
+        me.renderSheetFuc(sheet, fuc);
+    },
+
+    buildSheet: function () {
+        if(!this.workBook){
+            this.workBook = new GC.Spread.Sheets.Workbook($('#billsQuanDecimal')[0], {sheetCount: 1});
+            this.setOptions(this.workBook, this.setting.options);
+            this.buildHeader(this.workBook.getActiveSheet(), this.setting.header);
+            this.bindEvent(this.workBook);
+            this.onContextmenuOpr();
+        }
+    },
+
+    bindEvent: function (workBook) {
+        const _events = GC.Spread.Sheets.Events;
+        let sheet = workBook.getActiveSheet();
+        sheet.bind(_events.EditStarting, this.onEditStarting);
+        sheet.bind(_events.EditEnded, this.onEditEnded);
+        sheet.bind(_events.ClipboardPasting, this.onClipboardPasting);
+        sheet.bind(_events.ClipboardPasted, this.onClipboardPasted);
+    },
+
+    showData(datas){
+        let sheet = this.workBook.getActiveSheet();
+        let cols = this.setting.header;
+        let fuc = function () {
+            sheet.setRowCount(datas.length);
+            for(let col = 0, cLen = cols.length; col < cLen; col++){
+                sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[cols[col]['hAlign']]);
+                sheet.getRange(-1, col, -1, 1).hAlign(GC.Spread.Sheets.VerticalAlign[cols[col]['vAlign']]);
+                for(let row = 0, rLen = datas.length; row < rLen; row++){
+                    sheet.setValue(row, col, datas[row][cols[col]['dataCode']]);
+                }
+            }
+        };
+        this.renderSheetFuc(sheet, fuc);
+    },
+
+    onEditStarting: function (sender, args) {
+        if(args.col === 0 && args.row === 0){//其他未列单位不可编辑
+            args.cancel = true;
+        }
+    },
+
+    onEditEnded: function (sender, args) {
+        let me = billsDecimalView;
+        let v =  args.editingText ? args.editingText.toString().trim() : '';
+        if(v.length === 0){
+            return;
+        }
+        if(args.col === 0){
+            if(me.hasUnit(me.cache, v)){
+                alert('已存在此计量单位');
+                args.sheet.setValue(args.row, args.col, me.isDef(me.cache[args.row]) ? me.cache[args.row].unit : '');
+            }
+            else {
+                me.cache[args.row].unit = v;
+                if(!me.isValidDecimal(me.cache[args.row].decimal)){
+                    me.cache[args.row].decimal = me.default.decimal;
+                    args.sheet.setValue(args.row, 1, me.default.decimal);
+                }
+            }
+        }
+        else if(args.col === 1){
+            if(!me.isValidDecimal(v)){
+                alert('小数位数只能是'+ me.default.min + '-' + me.default.max + '的整数');
+                args.sheet.setValue(args.row, args.col, me.isDef(me.cache[args.row]) ? me.cache[args.row].decimal : '');
+            }
+            else {
+                me.cache[args.row].decimal = v;
+            }
+        }
+    },
+
+    onClipboardPasting: function (sender, args) {
+        if(args.cellRange.row === 0 && args.cellRange.col === 0){
+            args.cancel = true;
+            alert('不可更改其他未列计量单位');
+        }
+    },
+
+    onClipboardPasted: function (sender, args) {
+        let me = billsDecimalView;
+        let items = sheetCommonObj.analyzePasteData(me.setting, args);
+        for(let i = 0, len = items.length; i < len; i++){
+            let row = args.cellRange.row + i;
+            if(me.isDef(me.cache[row])){
+                if(me.isDef(items[i].unit) && !me.hasUnit(me.cache, items[i].unit)){
+                    me.cache[row].unit = items[i].unit;
+                }
+                if(me.isValidDecimal(items[i].decimal)){
+                    me.cache[row].decimal = items[i].decimal;
+                }
+            }
+        }
+        me.showData(me.cache);
+    },
+
+    onContextmenuOpr: function () {//右键菜单
+        let me = billsDecimalView;
+        $.contextMenu({
+            selector: '#billsQuanDecimal',
+            build: function($triggerElement, e){
+                //控制允许右键菜单在哪个位置出现
+                let target = SheetDataHelper.safeRightClickSelection($triggerElement, e, me.workBook);
+                let sheet = me.workBook.getSheet(0);
+                if(target.hitTestType === 3 && typeof target.row !== 'undefined'){//在表格内
+                    let delDis = false;
+                    let insertDis = false;
+                    //控制按钮是否可用
+                    sheet.setActiveCell(target.row, target.col);
+                    if(target.row === 0){//不可删除其他未列单位行
+                        delDis = true;
+                    }
+                    if(!me.isDef(me.cache) || target.row >= me.cache.length){//有数据才能删除
+                        delDis = true;
+                    }
+                    return {
+                        callback: function(){},
+                        items: {
+                            "insert": {name: "添加", disabled: insertDis, icon: "fa-sign-in", callback: function (key, opt) {
+                                //插入空行
+                                me.addRow(me.cache, sheet, target.row);
+                            }},
+                            "delete": {name: "删除", disabled: delDis, icon: "fa-remove", callback: function (key, opt) {
+                               me.deleteRow(me.cache, sheet, target.row);
+                            }}
+                        }
+                    };
+                }
+                else{
+                    return false;
+                }
+            }
+        });
+    },
+    //新增一空白行
+    addRow: function (all, sheet, row) {
+        let func = function () {
+            let data = Object.create(null);
+            data.unit = '';
+            data.decimal = '';
+            //新增空行
+            if(row === 0){//保证其他未列单位在第一行
+                row = 1;
+            }
+            //新增空白数据
+            all.splice(row, 0, data);
+            sheet.addRows(row, 1);
+        };
+        this.renderSheetFuc(sheet, func);
+    },
+
+    deleteRow: function (all, sheet, row) {
+        let func = function () {
+            all.splice(row, 1);
+            sheet.deleteRows(row, 1);
+        };
+        this.renderSheetFuc(sheet, func);
+    },
+
+    isDef: function (v) {
+        return v !== undefined && v !== null;
+    },
+
+    isData: function (v) {
+        return this.isDef(v) && v.toString().trim().length > 0;
+    },
+
+    isValidDecimal: function (v) {
+        return this.isData(v) && !isNaN(v) && parseInt(v) % 1 === 0 && parseInt(v) >= this.default.min && parseInt(v) <= this.default.max;
+    },
+
+    hasUnit: function (all, v) {
+        for(let i = 0, len = all.length; i < len; i++){
+            if(all[i].unit === v){
+                return true;
+            }
+        }
+        return false;
+    },
+
+    rowHasData: function (sheet, row) {
+        let v = sheet.getValue(row, 0);
+        if(v && v.toString().trim().length > 0) {
+            return true;
+        }
+        return false;
+    },
+
+    toUpdate: function (orgV, newV) {//org: billsDecimal.datas, newV: toBillsDecimalDatas(cache)
+        for(let i = 0, len = orgV.length; i < len; i++){
+            if(!this.isDef(newV[i])){
+                return true;
+            }
+            else {
+                if(orgV[i].unit !== newV[i].unit || orgV[i].decimal !== newV[i].decimal){
+                    return true;
+                }
+            }
+        }
+        return false;
+    },
+
+    //将tempDatas提取出新的请单工程量精度数据
+    toBillsDecimalDatas: function (datas) {
+        let rst = [];
+        for(let i = 0, len = datas.length; i < len; i++){
+            if(this.isData(datas[i].unit) && this.isValidDecimal(datas[i].decimal)){
+                datas[i].decimal = parseInt(datas[i].decimal);
+                rst.push(datas[i]);
+            }
+        }
+        return rst;
+    },
+    
+    a_update: function (datas) {
+        let url = '/pm/api/updateProjects';
+        let updateData = {
+            updateType: 'update',
+            updateData: {
+                ID: parseInt(scUrlUtil.GetQueryString('project')),
+                'property.billsQuantityDecimal': datas
+            }
+        };
+        let postData = {
+            user_id: userID,
+            updateData: [updateData]
+        };
+        CommonAjax.post(url, postData, function () {
+            billsQuanDecimal.datas = datas;
+        });
+    }
+};
+
+$(document).ready(function () {
+    $('#poj-set').on('shown.bs.modal', function (e) {
+        //init Spread
+        if(billsDecimalView.isDef(billsQuanDecimal.datas)){
+            billsDecimalView.cache = billsDecimalView.cache.concat(billsQuanDecimal.datas);
+        }
+        if(billsDecimalView.cache.length === 0){
+            billsDecimalView.cache.push(billsDecimalView.angleDecimal);
+        }
+        billsDecimalView.buildSheet();
+        billsDecimalView.showData(billsDecimalView.cache);
+    });
+
+    $('#poj-set').on('hidden.bs.modal', function (e) {
+        //destroy Spread
+        if(billsDecimalView.workBook){
+            billsDecimalView.workBook.destroy();
+            billsDecimalView.workBook = null;
+        }
+        billsDecimalView.cache = [];
+    });
+
+    $('#tab_poj-settings-bqDecimal').on('shown.bs.tab', function () {
+        billsDecimalView.workBook.refresh();
+    });
+
+    $('#property_ok').bind('click', function () {
+        let newBillsDecimalDatas = billsDecimalView.toBillsDecimalDatas(billsDecimalView.cache);
+        if(billsDecimalView.toUpdate(billsQuanDecimal.datas, newBillsDecimalDatas)){
+            billsDecimalView.a_update(newBillsDecimalDatas);
+        }
+    });
+});