Browse Source

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

TonyKang 7 năm trước cách đây
mục cha
commit
69393c646e

+ 16 - 12
modules/complementary_glj_lib/controllers/gljController.js

@@ -13,6 +13,7 @@ let callback = function(req, res, err, message, data){
 
 class GljController extends BaseController{
     async redirectGlj(req, res){
+        console.log(req.s)
         let gljLibId = null, engineeringId, sessionCompilation = req.session.sessionCompilation,
             rationValuation = sessionCompilation.ration_valuation,
             billValuation = sessionCompilation.bill_valuation,
@@ -38,14 +39,15 @@ class GljController extends BaseController{
     getGljDistType (req, res) {
         let gljDistTypeCache = stdgljutil.getStdGljTypeCacheObj().toArray();
         if(gljDistTypeCache.length >0 ){
-            callback(req, res, null, '', gljDistTypeCache);
+            callback(req, res, 0, '', gljDistTypeCache);
         }
         else {
             callback(req, res, 1, 'Error', null);
         }
     }
     getGljTree(req,res){
-        let gljLibId = req.body.gljLibId;
+        let data = JSON.parse(req.body.data);
+        let gljLibId = data.gljLibId;
         gljDao.getGljTypes(gljLibId,function(err,data){
             callback(req,res,err, 'Get Tree', data)
         });
@@ -95,9 +97,10 @@ class GljController extends BaseController{
         });
     }
     getGljItems(req, res) {
-        let stdGljLibId = req.body.stdGljLibId,
-            userId = req.body.userId,
-            compilationId = req.body.compilationId;
+        let data = JSON.parse(req.body.data);
+        let stdGljLibId = data.stdGljLibId,
+            userId = data.userId,
+            compilationId = data.compilationId
             gljDao.getGljItems(stdGljLibId, userId, compilationId, function(err, data){
                 callback(req,res,err,'Get Items',data)
             });
@@ -123,16 +126,17 @@ class GljController extends BaseController{
         })
     }
     mixUpdateGljItems(req, res){
-        let userId = req.body.userId,
-            compilationId = req.body.compilationId,
-            updateItems = JSON.parse(req.body.updateItems),
-            addItems = JSON.parse(req.body.addItems),
-            removeIds = JSON.parse(req.body.removeIds);
-        gljDao.mixUpdateGljItems(userId, compilationId, updateItems, addItems, removeIds, function(err, message, rst){
+        let user_id = req.session.sessionUser.ssoId,
+            compilation_id = req.session.sessionCompilation._id;
+        let data = JSON.parse(req.body.data);
+        let updateItems = data.updateItems,
+            addItems = data.addItems,
+            removeIds = data.removeIds;
+        gljDao.mixUpdateGljItems(user_id, compilation_id, updateItems, addItems, removeIds, function(err, message, rst){
             if (err) {
                 callback(req, res, err, message, null);
             } else {
-                callback(req, res, err, message, rst);
+                callback(req, res, 0, message, rst);
             }
         });
     }

+ 4 - 4
modules/complementary_glj_lib/models/gljModel.js

@@ -10,7 +10,7 @@ class GljDao {
     getGljTypes (gljLibId, callback){
         gljClassModel.find({"repositoryId": gljLibId, "$or": [{"isDeleted": null}, {"isDeleted": false} ]},function(err,data){
             if(data.length) {
-                callback(false,data);
+                callback(0,data);
             }
             else  if(err) callback("获取工料机类型错误!",false);
         })
@@ -88,10 +88,10 @@ class GljDao {
             }
         ], function (err) {
             if(err){
-                callback(true, null);
+                callback(err, null);
             }
             else{
-                callback(false, rst);
+                callback(0, rst);
             }
         })
 
@@ -128,7 +128,7 @@ class GljDao {
                 callback(err, '更新组成物错误!', null);
             }
             else{
-                callback(null, '成功!', result);
+                callback(0, '成功!', result);
             }
         });
     }

+ 4 - 3
modules/main/controllers/bills_controller.js

@@ -42,9 +42,10 @@ module.exports = {
     },
     //zhong 2017-9-1
     updateCharacterContent: function (req, res) {
-        let findSet = JSON.parse(req.body.findSet),
-            updateObj = JSON.parse(req.body.updateObj),
-            txtObj = JSON.parse(req.body.txtObj);
+        let data = JSON.parse(req.body.data);
+        let findSet = data.findSet,
+            updateObj = data.updateObj,
+            txtObj = data.txtObj;
         billsData.updateCharacterContent(findSet, updateObj, txtObj, function (err, message) {
             callback(req, res, err, message, null);
         });

+ 5 - 4
modules/options/controllers/optionsController.js

@@ -10,7 +10,7 @@ let optionsDao = new OptionsDao();
 class OptionController extends BaseController {
     //获得所有选项类型的选项
     async getOptions(req, res){
-        let resJson = {error: null, message: '', data: []};
+        let resJson = {error: 0, message: '', data: []};
         let user_id = req.session.sessionUser.id,
             compilation_id = req.session.sessionCompilation._id;
         let defaultOpts = {
@@ -58,11 +58,12 @@ class OptionController extends BaseController {
     }
 
     async saveOptions(req, res){
-        let resJson = {error: null, message: '', data: null};
+        let resJson = {error: 0, message: '', data: null};
+        let data = JSON.parse(req.body.data);
         let user_id = req.session.sessionUser.id,
             compilation_id = req.session.sessionCompilation._id,
-            optsType = req.body.optsType,
-            opts = JSON.parse(req.body.opts);
+            optsType = data.optsType,
+            opts = data.opts;
         try{
             let hasThisOpts = false;
             for(let i in optionsTypes){

+ 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;

+ 138 - 69
public/calc_util.js

@@ -4,14 +4,6 @@
  * added by CSL, 2017-09-01 增加公式解析对象analyzer,用于解析用户修改公式、自定义表达式。
  */
 
-// 需求说小数位数固定为2位,这里预留缓冲接口,防止以后需求变卦。
-const Digit_Calc_Program = -2;              // 需求指定计算程序用到的小数位数。
-const Digit_Calc_Program_Default = -6;      // 需求末指定时默认用到的小数位数。
-function round(value, useDef = false) {
-    let digit = useDef ? Digit_Calc_Program_Default : Digit_Calc_Program;
-    return scMathUtil.roundTo(value, digit);
-};
-
 let executeObj = {
     treeNode: null,
     template: null,
@@ -52,12 +44,12 @@ let executeObj = {
                                if (base.gljTypes.indexOf(md.glj_type) >= 0) {
                                    price = md["base_price"];
                                    if (!price) price = 0;
-                                   mdSum = mdSum +  round(md["consumption"] * price);
-                                   mdSum = round(mdSum, true);
+                                   mdSum = mdSum + (md["consumption"] * price).toDecimal(me.digit);
+                                   mdSum = (mdSum).toDecimal(me.digitDefault);
                                }
                            };
-                           tmpSum = tmpSum + round(glj["quantity"] * mdSum, true);
-                           tmpSum = round(tmpSum, true);
+                           tmpSum = tmpSum + (glj["quantity"] * mdSum).toDecimal(me.digitDefault);
+                           tmpSum = (tmpSum).toDecimal(me.digitDefault);
                        }
                 };
             }else{
@@ -74,15 +66,20 @@ let executeObj = {
                             price = mprice - aprice;
                         };
                         if (!price) price = 0;
-                        tmpSum = round(tmpSum + round(glj["quantity"] * price, true), true);
+                        tmpSum = tmpSum + (glj["quantity"] * price).toDecimal(me.digitDefault);
+                        tmpSum = (tmpSum).toDecimal(me.digitDefault);
                     };
                 };
             };
 
-            rst = round(tmpSum);
+            rst = (tmpSum).toDecimal(me.digitDefault);
         };
 
         return rst;
+    },
+    HJ: function () {
+        let me = this;
+        return me.treeNode.data.baseTotalPrice;
     }
 };
 
@@ -238,6 +235,8 @@ class Calculation {
         me.compiledFeeTypeNames = [];
         me.compiledCalcBases = {};
         me.saveForReports = [];
+        me.digit = 2;
+        me.digitDefault = 6;
 
         let private_compile_feeRateFile = function() {
             if (feeRates) {
@@ -338,8 +337,13 @@ class Calculation {
             for (let idx of template.compiledSeq) {
                 let item = template.calcItems[idx];
                 item.dispExprUser = item.dispExpr;    // 用于界面显示。disExpr是公式模板,不允许修改:人工系数占位符被修改后变成数值,第二次无法正确替换。
-                item.compiledExpr = item.expression.split('@(').join('$CE.at(');
-                item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
+                if (item.expression == 'HJ')
+                    item.compiledExpr = '$CE.HJ()'
+                else{
+                    item.compiledExpr = item.expression.split('@(').join('$CE.at(');
+                    item.compiledExpr = item.compiledExpr.split('base(').join('$CE.base(');
+                };
+
                 if (item.labourCoeID){
                     let lc = me.compiledLabourCoes["LabourCoe_" + item.labourCoeID].coe;
                     item.dispExprUser = item.dispExpr.replace(/L/gi, lc.toString());
@@ -383,65 +387,130 @@ class Calculation {
         };
     };
 
-    calculate($treeNode){
-        let me = this;
-        let templateID = $treeNode.data.programID;
-        if (!templateID) templateID = 1;
-        let template = me.compiledTemplates[templateID];
-        $treeNode.data.calcTemplate = template;
-
-        if ($treeNode && template.hasCompiled) {
-            let $CE = executeObj;
-            $CE.treeNode = $treeNode;
-            $CE.template = template;
-            $CE.calcBase = me.compiledCalcBases;
-
-            if (!$treeNode.data.fees) {
-                $treeNode.data.fees = [];
-                $treeNode.data.feesIndex = {};
-                $treeNode.changed = true;
+    initFees(treeNode){
+        if (!treeNode.data.fees) {
+            treeNode.data.fees = [];
+            treeNode.data.feesIndex = {};
+            treeNode.changed = true;
+        };
+    };
+
+    checkFee(treeNode, feeObj){
+        if (feeObj.fieldName == '') return;
+
+        if (!treeNode.data.feesIndex[feeObj.fieldName]){
+            let fee = {
+                'fieldName': feeObj.fieldName,
+                'unitFee': feeObj.unitFee,
+                'totalFee': feeObj.totalFee,
+                'tenderUnitFee': 0,
+                'tenderTotalFee': 0
+            };
+            treeNode.data.fees.push(fee);
+            treeNode.data.feesIndex[feeObj.fieldName] = fee;
+            treeNode.changed = true;
+        }
+        else{
+            if (treeNode.data.feesIndex[feeObj.fieldName].unitFee != feeObj.unitFee){
+                treeNode.data.feesIndex[feeObj.fieldName].unitFee = feeObj.unitFee;
+                treeNode.changed = true;
             };
 
-            for (let idx of template.compiledSeq) {
-                let calcItem = template.calcItems[idx];
-
-                let feeRate = calcItem.feeRate;
-                if (!feeRate) feeRate = 100;    // 100%
-                calcItem.unitFee = round(eval(calcItem.compiledExpr) * feeRate * 0.01);   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
-
-                let quantity = $treeNode.data.quantity;
-                if (!quantity) quantity = 0;
-                calcItem.totalFee = round(calcItem.unitFee * quantity);
-
-                // 费用同步到定额
-                // 引入小麦的字段检测后,快速切换定额出现计算卡顿现象,过多的循环造成。这里把她的代码拆出来,减少微循环。
-                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[feeObj.fieldName].totalFee != feeObj.totalFee){
+                treeNode.data.feesIndex[feeObj.fieldName].totalFee = feeObj.totalFee;
+                treeNode.changed = true;
+            };
+        };
+    };
 
-                        if ($treeNode.data.feesIndex[calcItem.fieldName].totalFee != calcItem.totalFee){
-                            $treeNode.data.feesIndex[calcItem.fieldName].totalFee = calcItem.totalFee;
-                            $treeNode.changed = true;
-                        };
-                    }
+    calculate(treeNode){
+        let me = this;
+        let project = projectObj.project;
+
+        // 汇总定额或子清单的费用类别
+        if (treeNode.data.gatherType != undefined){
+            if (treeNode.sourceType != project.Bills.getSourceType()) return;
+
+            me.initFees(treeNode);
+
+            let objsArr = (treeNode.data.gatherType === CP_GatherType.rations) ? project.Ration.getRationsByNode(treeNode) : treeNode.children;
+            let rst = [];
+            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 = (treeNode.data.gatherType === CP_GatherType.rations) ? item : 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);
             };
+            treeNode.data.calcTemplate = {"calcItems": rst};
         }
+        else{
+            // 叶子清单的缺省计算程序需要提供总金额作为计算基数(不需要工料机),然后每条按比例(费率)计算,不需要工料机明细。
+            if (treeNode.data.baseTotalPrice != undefined){
+                if (treeNode.sourceType != project.Bills.getSourceType()) return;
+
+                delete treeNode.data.gljList;
+
+                if (treeNode.data.programID == undefined){
+                    treeNode.data.programID = defaultBillTemplate.ID;
+                };
+            }
+            else {
+                if (treeNode.sourceType === project.Ration.getSourceType()) {
+                    treeNode.data.gljList = project.ration_glj.getGljArrByRation(treeNode.data.ID);
+                }
+                else if (treeNode.sourceType === project.Bills.getSourceType()) {
+                    let rations = project.Ration.getBillsSortRation(treeNode.source.getID());
+                    treeNode.data.gljList = project.ration_glj.getGatherGljArrByRations(rations);
+                };
+
+                if (treeNode.data.programID == undefined){
+                    treeNode.data.programID = 1;
+                };
+            };
+
+            let template = me.compiledTemplates[treeNode.data.programID];
+            treeNode.data.calcTemplate = template;
+
+            if (treeNode && template.hasCompiled) {
+                let $CE = executeObj;
+                $CE.treeNode = treeNode;
+                $CE.template = template;
+                $CE.calcBase = me.compiledCalcBases;
+
+                me.initFees(treeNode);
+
+                for (let idx of template.compiledSeq) {
+                    let calcItem = template.calcItems[idx];
+
+                    let feeRate = calcItem.feeRate;
+                    if (!feeRate) feeRate = 100;    // 100%
+                    calcItem.unitFee = (eval(calcItem.compiledExpr) * feeRate * 0.01).toDecimal(me.digit);   // 如果eval()对清单树有影响,就换成小麦的Expression对象再试
+
+                    let quantity = treeNode.data.quantity;
+                    if (!quantity) quantity = 0;
+                    calcItem.totalFee = (calcItem.unitFee * quantity).toDecimal(me.digit);
+
+                    me.checkFee(treeNode, calcItem);
+                };
+            }
+        };
     }
 };
 

+ 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]);

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

@@ -188,7 +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/common_ajax.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>

+ 58 - 97
web/building_saas/complementary_glj_lib/js/glj.js

@@ -92,75 +92,46 @@ let repositoryGljObj = {
     },
     getGljDistType: function (callback) {
         let me = this;
-        $.ajax({
-            type: 'post',
-            url: "complementartGlj/api/getGljDistType",
-            dataType: 'json',
-            success: function (result) {
-                if(!result.error && callback){
-                    me.distTypeTree = me.getComboData(result.data);
-                    console.log(me.distTypeTree);
-                    callback();
-                }
+        CommonAjax.post('complementartGlj/api/getGljDistType', {}, function (rstData) {
+            if(callback){
+                me.distTypeTree = me.getComboData(rstData);
+                console.log(me.distTypeTree);
+                callback();
             }
-        })
+        });
     },
     getGljTree: function(gljLibId, callback) {
         let me = this;
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/getGljTree",
-            data:{"gljLibId": gljLibId},
-            dataType:"json",
-            cache:false,
-            timeout:20000,
-            success:function(result,textStatus,status){
-                if(status.status == 200) {
-                    zTreeHelper.createTree(result.data, gljSetting, "repositoryTree", me);
-                    zTreeHelper.createTree(result.data, componentSetting, "componentTree", componentOprObj);
-                    if (result.data && result.data.length > 0) {
-                        me.gljCurTypeId = result.data[0].ID;
-                    } else {
-                        //重新创建库?
-                        gljTypeTreeOprObj.addRootNode();
-                    }
-                    callback();
-                }
-            },
-            error:function(err){
-                alert(err.responseJSON.error);
+        CommonAjax.post('complementartGlj/api/getGljTree', {gljLibId: gljLibId}, function (rstData) {
+            zTreeHelper.createTree(rstData, gljSetting, "repositoryTree", me);
+            zTreeHelper.createTree(rstData, componentSetting, "componentTree", componentOprObj);
+            if (rstData && rstData.length > 0) {
+                me.gljCurTypeId = rstData[0].ID;
+            } else {
+                //重新创建库?
+               // gljTypeTreeOprObj.addRootNode();
+            }
+            if(callback){
+                callback();
             }
-        })
+        });
     },
     getGljItems: function(stdGljLibId, userId, compilationId) {
         let me = this;
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/getGljItems",
-            data:{stdGljLibId: stdGljLibId, userId: userId, compilationId: compilationId},
-            dataType:"json",
-            cache:false,
-            timeout:5000,
-            success:function(result){
-                if(!result.error) {
-                    me.stdGljList = result.data.stdGljs;
-                    me.complementaryGljList = result.data.complementaryGljs;
-                    me.workBook.getSheet(0).setRowCount(me.stdGljList.length);
-                    me.sortGlj(me.stdGljList);
-                    me.setProp('isStd', true, me.stdGljList);
-                    me.sortGlj(me.complementaryGljList);
-                    let rootNode = me.treeObj.getNodes()[0];
-                    if(rootNode && rootNode.isParent && rootNode.isFirstNode){
-                        componentOprObj.rootNode = rootNode;
-                        me.treeObj.selectNode(rootNode);
-                        gljTypeTreeOprObj.onClick(null, 'repositoryTree', rootNode);
-                    }
-                }
-            },
-            error:function(err){
-                alert(err.responseJSON.error);
+        CommonAjax.post('complementartGlj/api/getGljItems', {stdGljLibId: stdGljLibId, userId: userId, compilationId: compilationId}, function (rstData) {
+            me.stdGljList = rstData.stdGljs;
+            me.complementaryGljList = rstData.complementaryGljs;
+            me.workBook.getSheet(0).setRowCount(me.stdGljList.length);
+            me.sortGlj(me.stdGljList);
+            me.setProp('isStd', true, me.stdGljList);
+            me.sortGlj(me.complementaryGljList);
+            let rootNode = me.treeObj.getNodes()[0];
+            if(rootNode && rootNode.isParent && rootNode.isFirstNode){
+                componentOprObj.rootNode = rootNode;
+                me.treeObj.selectNode(rootNode);
+                gljTypeTreeOprObj.onClick(null, 'repositoryTree', rootNode);
             }
-        })
+        });
     },
     showGljItems: function(data, type) {
         let me = repositoryGljObj;
@@ -963,39 +934,29 @@ let repositoryGljObj = {
         if(addArr.length > 0){
             me.saveInString(addArr);
         }
-        $.ajax({
-            type:"POST",
-            url:"complementartGlj/api/mixUpdateGljItems",
-            data:{userId: pageOprObj.userId, compilationId: pageOprObj.compilationId, updateItems: JSON.stringify(updateArr), addItems: JSON.stringify(addArr), removeIds: JSON.stringify(removeIds)},
-            dataType:"json",
-            cache:false,
-            timeout:5000,
-            success:function(result){
-                if (result.error) {
-                    alert(result.message);
-                    me.getRationItems(me.currentRepositoryId);
-                } else {
-                    me.updateCache(addArr, updateArr, removeIds, result);
-                    me.sortGlj(me.complementaryGljList);
-                    if(me.currentOprParent === 1){
-                        me.currentCache = me.getParentCache(me.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
-                    }
-                    else{
-                        me.currentCache = me.getCache();
-                    }
-                    me.showGljItems(me.complementaryGljList, me.gljCurTypeId);
-                    //getCurrentGlj
-                    let row = me.workBook.getSheet(0).getSelections()[0].row;
-                    me.currentGlj = row < me.currentCache.length ? me.currentCache[row] : null;
-                    me.currentComponent = me.currentGlj ?  me.getCurrentComponent(me.currentGlj.component) : [];
-                    sheetOpr.cleanData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, -1);
-                    sheetOpr.showData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, me.currentComponent);
-                }
-            },
-            error:function(err){
-                alert("保存失败");
+        let url = 'complementartGlj/api/mixUpdateGljItems';
+        let post = {updateItems: updateArr, addItems: addArr, removeIds: removeIds};
+        let scCaller = function (rstData) {
+            me.updateCache(addArr, updateArr, removeIds, rstData);
+            me.sortGlj(me.complementaryGljList);
+            if(me.currentOprParent === 1){
+                me.currentCache = me.getParentCache(me.parentNodeIds["_pNodeId_" + me.gljCurTypeId]);
+            }
+            else{
+                me.currentCache = me.getCache();
             }
-        })
+            me.showGljItems(me.complementaryGljList, me.gljCurTypeId);
+            //getCurrentGlj
+            let row = me.workBook.getSheet(0).getSelections()[0].row;
+            me.currentGlj = row < me.currentCache.length ? me.currentCache[row] : null;
+            me.currentComponent = me.currentGlj ?  me.getCurrentComponent(me.currentGlj.component) : [];
+            sheetOpr.cleanData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, -1);
+            sheetOpr.showData(gljComponentOprObj.workBook.getSheet(0), gljComponentOprObj.setting, me.currentComponent);
+        }
+        let errCaller = function (err) {
+            alert('保存失败');
+        };
+        CommonAjax.post(url, post, scCaller, errCaller);
     },
     saveInString: function (datas) {
         for(let i = 0, len = datas.length; i < len; i++){
@@ -1040,7 +1001,7 @@ let repositoryGljObj = {
         }
         return rst;
     },
-    updateCache: function(addArr, updateArr, removeIds, result) {
+    updateCache: function(addArr, updateArr, removeIds, rstData) {
         let me = this, cacheSection = me.complementaryGljList;
         if (addArr.length > 0) {
             me.complementaryGljList = me.complementaryGljList.concat(addArr);
@@ -1053,11 +1014,11 @@ let repositoryGljObj = {
                 }
             }
         }
-        if (result && result.data && result.data.ops && result.data.ops.length > 0) {
-            for (let i = 0; i < result.data.ops.length; i++) {
+        if (rstData && rstData.ops && rstData.ops.length > 0) {
+            for (let i = 0; i < rstData.ops.length; i++) {
                 for (let j = 0; j < cacheSection.length; j++) {
-                    if (cacheSection[j][me.setting.header[0].dataCode] == result.data.ops[i][me.setting.header[0].dataCode]) {
-                        cacheSection[j]["ID"] = result.data.ops[i]["ID"];
+                    if (cacheSection[j][me.setting.header[0].dataCode] == rstData.ops[i][me.setting.header[0].dataCode]) {
+                        cacheSection[j]["ID"] = rstData.ops[i]["ID"];
                     }
                 }
             }

+ 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 - 2
web/building_saas/css/main.css

@@ -10,6 +10,9 @@ body {
 .btn.disabled, .btn:disabled {
   color:#999
 }
+.btn-link:focus, .btn-link:hover{
+  text-decoration: none
+}
 /*自定义css*/
 .header {
     background: #e1e1e1
@@ -306,6 +309,5 @@ body {
   border-bottom:1px solid #ddd
 }
 .navbar-crumb span{
-  max-width: 200px;
-  display: inline-block;
+  max-width: 200px
 }

+ 4 - 4
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,9 +337,8 @@
                                     </div>
                                 </div>
                                 <!--清单工程精度-->
-                                <div class="tab-pane fade" id="poj-settings-5" role="tabpanel">
-                                    <div class="modal-auto-height">
-                                        清单工程精度
+                                <div class="tab-pane fade" id="poj-settings-billsQuanDecimal" role="tabpanel">
+                                    <div class="modal-auto-height" style="overflow: hidden;" id="billsQuanDecimal">
                                     </div>
                                 </div>
                                 <!--小数位数-->
@@ -664,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>

+ 112 - 231
web/building_saas/main/js/models/calc_program.js

@@ -5,102 +5,104 @@
  *  用到费率的规则必须有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: ''
-    }
-];
+let defaultBillTemplate = {
+    ID: 15,
+    name: "清单缺省",
+    calcItems: [
+        {
+            ID: 1,
+            code: "1",
+            name: "定额直接费",
+            dispExpr: "F2+F3+F4",
+            expression: "@('2')+@('3')+@('4')",
+            statement: "人工费+材料费+机械费",
+            feeRate: null,
+            memo: ''
+        },
+        {
+            ID: 2,
+            code: "1.1",
+            name: "人工费",
+            dispExpr: "HJ",
+            expression: "HJ",
+            statement: "合计",
+            feeRate: 50,
+            fieldName: 'labour',
+            memo: ''
+        },
+        {
+            ID: 3,
+            code: "1.2",
+            name: "材料费",
+            dispExpr: "HJ",
+            expression: "HJ",
+            statement: "合计",
+            feeRate: 30,
+            fieldName: 'material',
+            memo: ''
+        },
+        {
+            ID: 4,
+            code: "1.3",
+            name: "机械费",
+            dispExpr: "HJ",
+            expression: "HJ",
+            statement: "合计",
+            feeRate: 20,
+            fieldName: 'machine',
+            memo: ''
+        },
+        {
+            ID: 5,
+            code: "2",
+            name: "企业管理费",
+            dispExpr: "F1",
+            expression: "@('1')",
+            statement: "定额直接费",
+            feeRate: null,
+            fieldName: 'manage',
+            memo: ''
+        },
+        {
+            ID: 6,
+            code: "3",
+            name: "利润",
+            dispExpr: "F1",
+            expression: "@('1')",
+            statement: "定额直接费",
+            feeRate: null,
+            fieldName: 'profit',
+            memo: ''
+        },
+        {
+            ID: 7,
+            code: "4",
+            name: "风险费用",
+            dispExpr: "F1",
+            expression: "@('1')",
+            statement: "定额直接费",
+            feeRate: null,
+            fieldName: 'risk',
+            memo: ''
+        },
+        {
+            ID: 8,
+            code: "5",
+            name: "综合单价",
+            dispExpr: "F1+F5+F6+F7",
+            expression: "@('1')+@('5')+@('6')+@('7')",
+            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);
     };
@@ -124,6 +126,7 @@ class CalcProgram {
         let calcFeeRates = this.project.FeeRate.datas.rates;
         let calcLabourCoes = this.project.labourCoe.datas.coes;
         let calcTemplates = this.project.calcProgram.datas.templates;
+        calcTemplates.push(defaultBillTemplate);
 
         this.calc.compilePublics(calcFeeRates, calcLabourCoes, feeType, rationCalcBase);
         for (let ct of calcTemplates){
@@ -143,22 +146,18 @@ 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);
-        }
-        else if (treeNode.sourceType === this.project.Bills.getSourceType()) {
-             let rations = this.project.Ration.getBillsSortRation(treeNode.source.getID());
-            treeNode.data.gljList = this.project.ration_glj.getGatherGljArrByRations(rations);
-        };
 
-        this.calc.calculate(treeNode);
+        me.calc.calculate(treeNode);
+
+        // 还原,防止出现混乱影响下次计算
+        delete treeNode.data.baseTotalPrice;
+        delete treeNode.data.gatherType;
 
         // 存储、刷新本结点、所有父结点
         if (treeNode.changed) {
             me.saveAndCalcParents(treeNode);
             delete treeNode.changed;
         };
-
     };
 
     saveAndCalcParents(treeNode) {
@@ -174,125 +173,6 @@ class CalcProgram {
         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 = [];
@@ -301,9 +181,8 @@ class CalcProgram {
         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;
+        if (isRation) {
+            //
         }
         else if (isLeafBill) {
             let ct = '';
@@ -320,26 +199,28 @@ class CalcProgram {
             };
 
             if (ct == childrenType.ration){
-                if (isBillPriceCalc){   // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算
-                    me.calculate(treeNode);
-                    rst = treeNode.data.calcTemplate.calcItems;
-                }else{                  // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
-                    rst = me.gatherFeeTypes(treeNode, CP_GatherType.rations);
+                if (isBillPriceCalc){                   // 清单单价计算模式下的叶子清单:取自己的计算程序ID,找到自己的计算程序计算
+
+                }
+                else{                                   // 前三种计算模式下的叶子清单:汇总定额的计算程序的费用类别
+                    treeNode.data.gatherType = CP_GatherType.rations;
                 };
             }
             else if (ct == childrenType.volumePrice){
                 let totalPrice = 10000;
-                rst = me.calcDefaultBillTemp(treeNode, totalPrice);
+                treeNode.data.baseTotalPrice = totalPrice;
             }
             else if (ct == childrenType.formula){
                 let totalPrice = 20000;
-                rst = me.calcDefaultBillTemp(treeNode, totalPrice);
+                treeNode.data.baseTotalPrice = totalPrice;
             };
         }
-        else if (isBill){    // 父清单:汇总子清单的费用类别
-            rst = me.gatherFeeTypes(treeNode, CP_GatherType.bills);
+        else if (isBill){                                   // 父清单:汇总子清单的费用类别
+            treeNode.data.gatherType = CP_GatherType.bills;
         };
 
+        me.calculate(treeNode);
+        rst = treeNode.data.calcTemplate.calcItems;
         return rst;
     }
 }

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

@@ -52,7 +52,6 @@ let rationPM = {
         };
         me.mainSpread = sheetCommonObj.buildSheet($('#mainSpread')[0], me.mainSetting, me.datas.length);
         me.detailSpread = sheetCommonObj.buildSheet($('#detailSpread')[0], me.detailSetting, me.datas[0].calcItems.length);
-
         var fieldName = new GC.Spread.Sheets.CellTypes.ComboBox();
         fieldName.items(projectObj.project.calcProgram.calc.compiledFeeTypeNames);
         me.detailSpread.getSheet(0).getRange(-1, 4, -1, 1).cellType(fieldName);
@@ -75,6 +74,7 @@ let rationPM = {
         me.detailSpread.suspendPaint();
         var dSheet = me.detailSpread.getSheet(0);
         var dData = me.datas[row].calcItems;
+        dSheet.setRowCount(dData.length, GC.Spread.Sheets.SheetArea.viewport);
         sheetCommonObj.showData(dSheet, me.detailSetting, dData);
         me.detailSpread.resumePaint();
     },

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

@@ -229,22 +229,6 @@ let calcProgramObj = {
         var me = this;
         me.treeNode = treeNode;
         me.datas = projectObj.project.calcProgram.getCalcDatas(treeNode);
-
-        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);
-                };
-            }
-            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);
 

+ 16 - 18
web/building_saas/main/js/views/character_content_view.js

@@ -733,24 +733,22 @@ let pageCCOprObj = {
         else{
             updateCol = null;
         }
-        $.ajax({
-            type: 'post',
-            url: '/bills/updateCharacterContent',
-            dataType: 'json',
-            data: {findSet: JSON.stringify(findSet), updateObj: JSON.stringify(updateObj), txtObj: JSON.stringify(txtObj)},
-            success: function (result) {
-                if(!result.error){
-                    //更新节点数据
-                    let selectedNode = projectObj.mainController.tree.selected;
-                    selectedNode.data[updateObj.field] = updateObj.updateArr;
-                    selectedNode.data[txtObj.field] = txtObj.text;
-                    me.showData(oprObj.workBook.getSheet(0), oprObj.setting, oprObj.currentCache);//刷新特征及内容Spread
-                    if(updateCol){
-                        let activeCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];
-                        projectObj.mainSpread.getActiveSheet().setValue(activeCell.row, updateCol, txtObj.text + ''); //刷新输出显示
-                        projectObj.mainSpread.getActiveSheet().autoFitRow(activeCell.row);
-                    }
-                }
+        let url = '/bills/updateCharacterContent';
+        let postData = {
+            findSet: findSet,
+            updateObj: updateObj,
+            txtObj: txtObj
+        };
+        CommonAjax.post(url, postData, function (rstData) {
+            //更新节点数据
+            let selectedNode = projectObj.mainController.tree.selected;
+            selectedNode.data[updateObj.field] = updateObj.updateArr;
+            selectedNode.data[txtObj.field] = txtObj.text;
+            me.showData(oprObj.workBook.getSheet(0), oprObj.setting, oprObj.currentCache);//刷新特征及内容Spread
+            if(updateCol){
+                let activeCell = projectObj.mainSpread.getActiveSheet().getSelections()[0];
+                projectObj.mainSpread.getActiveSheet().setValue(activeCell.row, updateCol, txtObj.text + ''); //刷新输出显示
+                projectObj.mainSpread.getActiveSheet().autoFitRow(activeCell.row);
             }
         });
     }

+ 9 - 24
web/building_saas/main/js/views/options_view.js

@@ -8,36 +8,21 @@ let optionsOprObj = {
     rationQuanACToRationUnit: $('#generalOpts2'),
     getOptions: function () {
         let me = this;
-        $.ajax({
-            type: 'post',
-            url: '/options/getOptions',
-            dataType: 'json',
-            success: function (result) {
-                if(!result.error){
-                   me.options = result.data;
-                    for(let i = 0, len = me.options.length; i < len; i++){
-                        let optsType = me.options[i].type,
-                            opts = me.options[i].opts;
-                        if(optsType === me.optionsTypes.GENERALOPTS){
-                            for(let attr in opts){
-                                me[attr][0].checked = opts[attr];
-                            }
-                        }
+        CommonAjax.post('/options/getOptions', [], function (rstData) {
+            me.options = rstData;
+            for(let i = 0, len = me.options.length; i < len; i++){
+                let optsType = me.options[i].type,
+                    opts = me.options[i].opts;
+                if(optsType === me.optionsTypes.GENERALOPTS){
+                    for(let attr in opts){
+                        me[attr][0].checked = opts[attr];
                     }
                 }
             }
         });
     },
     saveOptions: function (optsType, opts) {
-        $.ajax({
-            type: 'post',
-            url: '/options/saveOptions',
-            data: {optsType: optsType, opts: JSON.stringify(opts)},
-            dataType: 'json',
-            success: function (result) {
-
-            }
-        })
+        CommonAjax.post('/options/saveOptions', {optsType: optsType, opts: opts});
     },
     //更新optionsOprObj对象options数据
     updateOptions: function (options, updateObj) {

+ 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);
+        }
+    });
+});

+ 1 - 2
web/building_saas/main/js/views/project_property_decimal_view.js

@@ -5,7 +5,7 @@
 let defaultDecimal = {
     min: 0,
     max: 6,
-    _def: {//定义往这加, editable: 开放给用户编辑的(入库),定义editable为true的字段时,要在后端project_model.js defaultDecimal中添加定义,html添加input
+    _def: {//定义往这加, editable: 开放给用户编辑的(入库),定义editable为false的字段时,只需在此定义便可,定义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}},
@@ -194,7 +194,6 @@ function a_updateDigits(updateDecimal){
         setDecimal(decimalObj, updateDecimal);
         let v_data = m_getInitData(decimalObj);
         v_initPanel(v_data);
-        console.log(decimalObj);
     };
     let errCaller = function () {
         alert('更新小数位数失败!');

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

@@ -40,53 +40,11 @@
         </div>
     </div>
     <%include ../../../common/html/header.html %>
-    <nav class="navbar navbar-expand-lg justify-content-between navbar-light p-0">
-        <ul class="nav navbar-nav px-1">
-            <li class="nav-item dropdown">
-                <a class="nav-link dropdown-toggle" href="http://example.com" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">文件</a>
-                <div class="dropdown-menu" aria-labelledby="supportedContentDropdown">
-                    <a class="dropdown-item" href="#">Action</a>
-                    <a class="dropdown-item" href="#">Another action</a>
-                    <a class="dropdown-item" href="#">Something else here</a>
-                </div>
-            </li>
-            <li class="nav-item dropdown">
-                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">编辑</a>
-                <div class="dropdown-menu">
-                    <a class="dropdown-item" href="#">Action</a>
-                    <a class="dropdown-item" href="#">Another action</a>
-                    <a class="dropdown-item" href="#">Something else here</a>
-                </div>
-            </li>
-            <li class="nav-item dropdown">
-                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">工具</a>
-                <div class="dropdown-menu">
-                    <a class="dropdown-item" href="#">定额库编辑器</a>
-                    <a class="dropdown-item" href="/complementaryGlj">工料机库编辑器</a>
-                </div>
-            </li>
-            <li class="nav-item dropdown">
-                <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o"></i> 帮助</a>
-                <div class="dropdown-menu">
-                    <a class="dropdown-item" href="#">帮助</a>
-                    <a class="dropdown-item" href="#">升级说明</a>
-                    <a class="dropdown-item" href="#">重庆市2008定额说明</a>
-                    <a class="dropdown-item" href="#">纵横官网</a>
-                    <a class="dropdown-item" href="#">动画教程</a>
-                    <a class="dropdown-item" href="#">联系客服</a>
-                    <a class="dropdown-item" href="#">关于</a>
-                </div>
-            </li>
-        </ul>
-        <form class="form-inline">
-            <input class="form-control form-control-sm mr-1" type="text" placeholder="告诉我你想做什么">
-        </form>
-    </nav>
 </div>
     <div class="main">
         <div class="poj-manage container-fluid">
             <div class="row">
-                <div class="col-lg-2">
+                <div class="col-auto">
                     <div class="poj-cate">
                         <input type="text" class="my-2 form-control form-control-sm" placeholder="搜索所有工程">
                         <ul class="nav nav-pills flex-column">
@@ -111,7 +69,7 @@
                         </ul>
                     </div>
                 </div>
-                <div class="col-lg-10">
+                <div class="col">
                     <div class="tab-content">
                         <div class="tab-pane active" id="pm_all" role="tabpanel">
                         <div class="toolsbar">

+ 29 - 2
web/common/html/header.html

@@ -1,11 +1,38 @@
-<nav class="navbar navbar-expand-lg navbar-light bg-light p-0 justify-content-between">
+<nav class="navbar navbar-expand-lg p-0 d-flex">
     <span class="header-logo px-2">Smartcost</span>
     <div class="navbar-text navbar-crumb p-0" id="fullpath">
         <% if (action !== 'index' || controller !== 'pm') {%>
         <span class="text-truncate"><a href="/pm">项目管理</a></span>
         <% } %>
     </div>
-    <div class="float-lg-right navbar-text p-0">
+    <ul class="nav navbar-nav px-1">
+        <li class="nav-item">
+            <a class="nav-link" href="#" aria-expanded="false" data-toggle="modal" data-target="#poj-set"><i class="fa fa-cube"></i> 项目属性</a>
+        </li>
+        <li class="nav-item">
+            <a class="nav-link" href="#" aria-expanded="false"><i class="fa fa-sliders"></i> 选项</a>
+        </li>
+        <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-wrench"></i> 工具</a>
+            <div class="dropdown-menu">
+                <a class="dropdown-item" href="#">定额库编辑器</a>
+                <a class="dropdown-item" href="/complementaryGlj">工料机库编辑器</a>
+            </div>
+        </li>
+        <li class="nav-item dropdown">
+            <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-question-circle-o"></i> 帮助</a>
+            <div class="dropdown-menu">
+                <a class="dropdown-item" href="#">帮助</a>
+                <a class="dropdown-item" href="#">升级说明</a>
+                <a class="dropdown-item" href="#">重庆市2008定额说明</a>
+                <a class="dropdown-item" href="#">纵横官网</a>
+                <a class="dropdown-item" href="#">动画教程</a>
+                <a class="dropdown-item" href="#">联系客服</a>
+                <a class="dropdown-item" href="#">关于</a>
+            </div>
+        </li>
+    </ul>
+    <div class="ml-auto navbar-text p-0">
         <div class="dropdown d-inline-block navbar-nav">
             <button class="btn btn-link btn-sm dropdown-toggle" type="button" data-toggle="dropdown"><%= sessionUser.email %></button>
             <div class="dropdown-menu dropdown-menu-right">