浏览代码

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

MaiXinRong 1 年之前
父节点
当前提交
d536142eca

+ 30 - 1
app/controller/budget_controller.js

@@ -13,6 +13,7 @@ const stdDataAddType = {
     next: 3,
 };
 const auditConst = require('../const/audit');
+const changeConst = require('../const/change');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
 module.exports = app => {
@@ -46,6 +47,34 @@ module.exports = app => {
             }
         }
 
+        async budgetInfo(ctx) {
+            try {
+                // 获取变更费用前10的变更令
+                const changeList = await ctx.service.change.getListByBudgetInfo(ctx.budget.rela_tender);
+                // 获取变更后总金额
+                const total_change_tp = await ctx.service.change.getTotalTpByBudgetInfo(ctx.budget.rela_tender);
+                const renderData = {
+                    changeList,
+                    changeConst,
+                    total_change_tp,
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.budget.info),
+                    auditConst,
+                };
+                // const relaTenderId = ctx.helper._.map(ctx.budget.rela_tender.split(','), ctx.helper._.toInteger);
+                // const tenderList = await ctx.service.tender.getList4Select('stage');
+                // renderData.tenderList = relaTenderId.length > 0 ? tenderList.filter(x => {
+                //     return relaTenderId.indexOf(x.id) >= 0;
+                // }) : tenderList;
+                // renderData.tenderList = renderData.tenderList.map(y => {
+                //     return { id: y.id, name: y.name, lastStageOrder: y.lastStage.order, lastStageStatus: auditConst.stage.statusString[y.lastStage.status], category: y.category };
+                // });
+                // renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                await this.layout('budget/info.ejs', renderData);
+            } catch (err) {
+                ctx.log(err);
+            }
+        }
+
         async compare(ctx) {
             try {
                 const renderData = {
@@ -316,4 +345,4 @@ module.exports = app => {
     }
 
     return BudgetController;
-};
+};

二进制
app/public/css/bg_participate_blue2.png


二进制
app/public/css/bg_participate_orange2.png


+ 57 - 2
app/public/css/main.css

@@ -1940,17 +1940,72 @@ overflow-y: auto;
 .canyu-width{
   height: 98px;
 }
+.canyu-pill{
+  height: 72px;
+  border-radius: 40px;
+}
+.canyu-pill .icon{
+  width: 40px;
+  height:40px;
+  line-height: 40px;
+  text-align: center;
+  border-radius: 40px;
+  background: #fff;
+  display: inline-block;
+  float: left;
+  font-size:24px;
+}
+.canyu-pill h5{
+  margin-left:50px;
+  margin-bottom: 4px
+}
+.canyu-pill h6{
+  margin-left:50px;
+  font-size: 12px
+}
 .canyu-bg-blue{
-  background: url(bg_participate_blue.png) no-repeat;
+  background: url(bg_participate_blue.png) no-repeat ;
   background-size: 100% 100%;
 }
 .canyu-bg-yellow{
-  background: url(bg_participate_orange.png) no-repeat;
+  background: url(bg_participate_orange.png) no-repeat ;
+  background-size: 100% 100%;
+}
+.canyu-pill.canyu-bg-blue{
+  background: url(bg_participate_blue2.png) no-repeat ;
+  background-size: 100% 100%;
+}
+.canyu-pill.canyu-bg-yellow{
+   background: url(bg_participate_orange2.png) no-repeat ;
   background-size: 100% 100%;
 }
+.canyu-bg-yellow .icon{
+  color:#ff8033;
+}
+.canyu-bg-blue .icon{
+  color:#009DFF;
+}
 .canyu-text{
   font-size: 36px;
 }
+.canyu-band{
+  height: 100%;
+  padding-top: 5%
+}
+.canyu-band h1{
+  text-align: center;
+  font-size:72px;
+}
+.canyu-band h3{
+  text-align: center;
+  font-size:18px;
+}
+.canyu-band.text-success{
+  background:linear-gradient(#fff 30%, #28a745 350%);
+}
+.canyu-band.text-danger{
+  background:linear-gradient(#fff 30%, #dc3545 350%);
+}
 .list-text-vertical{
   overflow:hidden;
   text-overflow:ellipsis;

+ 2 - 1
app/public/js/budget_compare.js

@@ -187,6 +187,7 @@ $(document).ready(() => {
             const expandTag = getLocalCache('revise-compare-level');
             if (expandTag) compareObj.expand(compareTree, expandTag);
             this.calcStackedBar(compareTree);
+            console.log(compareTree);
             SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
         },
         loadFinalData(result, msg) {
@@ -443,4 +444,4 @@ $(document).ready(() => {
             }
         });
     });
-});
+});

+ 397 - 0
app/public/js/budget_info.js

@@ -0,0 +1,397 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+$(document).ready(() => {
+    autoFlashHeight();
+    const compareObj = {
+        curFinalId() {
+            return this.finalInfo ? this.finalInfo.id : undefined;
+        },
+        expand(tree, tag) {
+            switch (tag) {
+                case "1":
+                case "2":
+                case "3":
+                case "4":
+                case "5":
+                    tree.expandByLevel(parseInt(tag));
+                    break;
+                case "last":
+                    tree.expandByCustom(() => { return true; });
+                    break;
+            }
+        },
+        calcStackedBar(tree) {
+            const calcField = this.stackedBarField;
+            const calcFieldColor = { 'gu_tp': '#657798', 'gai_tp': '#EE6666', 'yu_tp': '#74CBED', 'total_price': '#FAC858', 'final_tp': '#62DAAB' };
+            const calcFieldCaption = { 'gu_tp': '估算', 'gai_tp': '概算', 'yu_tp': '预算', 'total_price': '台账', 'final_tp': '决算' };
+            const calc = function(node, base){
+                // const parent = tree.getParent(node);
+                // if (!parent) {
+                //     base = 0;
+                //     for (const cf of calcField) {
+                //         base = Math.max(node[cf], base);
+                //     }
+                // }
+                node.stackedBar = [];
+                node.stackedBarTips = [];
+                for (const cf of calcField) {
+                    node.stackedBar.push({color: calcFieldColor[cf], percent: ZhCalc.div(node[cf], base), field: cf});
+                    node.stackedBarTips.push(`${calcFieldCaption[cf]}: ${node[cf] || 0}`);
+                }
+                if (node.children) {
+                    for (const child of node.children) {
+                        calc(child, base);
+                    }
+                }
+            };
+            let commonBase = 0;
+            tree.children.forEach(x => {
+                for (const cf of calcField) {
+                    commonBase = Math.max(x[cf] || 0, commonBase);
+                }
+            });
+            for (const child of tree.children) {
+                calc(child, commonBase);
+            }
+        },
+        loadBudgetData(result) {
+            const compareTree = createNewPathTree('final', {
+                id: 'id',
+                pid: 'pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
+            const guTree = createNewPathTree('ledger', setting);
+            guTree.loadDatas(result.gu);
+            treeCalc.calculateAll(guTree);
+            compareTree.loadTree(guTree, function (cur, source) {
+                cur.base = true;
+                cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
+                cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
+                cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
+            });
+            const gaiTree = createNewPathTree('ledger', setting);
+            gaiTree.loadDatas(result.gai);
+            treeCalc.calculateAll(gaiTree);
+            compareTree.loadTree(gaiTree, function (cur, source) {
+                cur.base = true;
+                cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
+                cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
+                cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
+            });
+            const yuTree = createNewPathTree('ledger', setting);
+            yuTree.loadDatas(result.yu);
+            treeCalc.calculateAll(yuTree);
+            compareTree.loadTree(yuTree, function (cur, source) {
+                cur.base = true;
+                cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
+                cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
+                cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
+            });
+            compareTree.afterLoad(node => {
+                node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
+                node.gu_dgn_qty = node.gu_dgn_qty1
+                    ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
+                    : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
+                node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
+                node.gai_dgn_qty = node.gai_dgn_qty1
+                    ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
+                    : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
+                node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.yu_dgn_qty1, 2);
+                node.yu_dgn_qty = node.yu_dgn_qty1
+                    ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
+                    : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
+            });
+            compareTree.resortChildrenByCustom(function (x, y) {
+                const iCode = compareCode(x.code, y.code);
+                if (iCode) return iCode;
+                if (!x.name) return -1;
+                if (!y.name) return 1;
+                return x.name.localeCompare(y.name);
+            });
+            const expandTag = getLocalCache('revise-compare-level');
+            if (expandTag) compareObj.expand(compareTree, expandTag);
+            this.calcStackedBar(compareTree);
+            // console.log(compareTree);
+            setPageData(compareTree);
+        },
+        loadFinalData(result, msg) {
+            if (msg) toastr.warning(msg);
+            this.finalInfo = result.finalInfo;
+            const finalTree = createNewPathTree('ledger', {
+                id: 'tree_id',
+                pid: 'tree_pid',
+                order: 'order',
+                level: 'level',
+                rootId: -1,
+            });
+            finalTree.loadDatas(result.final);
+            const expandTag = getLocalCache('revise-compare-level');
+            if (expandTag) compareObj.expand(finalTree, expandTag);
+            this.calcStackedBar(finalTree);
+            // console.log(finalTree);
+            setPageData(finalTree);
+
+        },
+        loadCacheData(){
+            const stackedBarCache = 'gai_tp,total_price,final_tp';
+            this.setStackedBarField(stackedBarCache.split(','));
+        },
+        setStackedBarField(field){
+            this.stackedBarField = field;
+        },
+    };
+    compareObj.loadCacheData();
+
+    function compareCode(str1, str2, symbol = '-') {
+        if (!str1) {
+            return 1;
+        } else if (!str2) {
+            return -1;
+        }
+
+        function compareSubCode(code1, code2) {
+            if (numReg.test(code1)) {
+                if (numReg.test(code2)) {
+                    return parseInt(code1) - parseInt(code2);
+                } else {
+                    return -1
+                }
+            } else {
+                if (numReg.test(code2)) {
+                    return 1;
+                } else {
+                    return code1 === code2 ? 0 : (code1 < code2 ? -1 : 1); //code1.localeCompare(code2);
+                }
+            }
+        }
+        const numReg = /^[0-9]+$/;
+        const aCodes = str1.split(symbol), bCodes = str2.split(symbol);
+        for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+            const iCompare = compareSubCode(aCodes[i], bCodes[i]);
+            if (iCompare !== 0) {
+                return iCompare;
+            }
+        }
+        return aCodes.length - bCodes.length;
+    }
+
+    postData(window.location.pathname + '/compare/load', {}, function (result, msg) {
+        if (result.final) {
+            compareObj.loadFinalData(result, msg);
+        } else {
+            compareObj.loadBudgetData(result);
+        }
+    });
+
+    //金额对比
+    var chartDom = document.getElementById('jlchart2');
+    var myChart = echarts.init(chartDom);
+    var option = {
+        tooltip: {
+            trigger: 'axis',
+        },
+        grid: {
+            left: '3%',
+            right: '3%',
+            bottom: '4%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            data: ['投资估算', '设计概算', '施工图预算', '台账', '变更后台账', '决算']
+        },
+        yAxis: {
+            type: 'value'
+        },
+        series: [
+            {
+                data: [0, 0, 0, 0, 0, 0],
+                type: 'bar',
+                showBackground: true,
+                backgroundStyle: {
+                    color: 'rgba(180, 180, 180, 0.2)'
+                },
+                itemStyle:{
+                    borderRadius: [30, 30, 0, 0]
+                },
+                barWidth : 30
+            }
+        ],
+
+        color:{
+            type: 'linear',
+            x: 0,
+            y: 0,
+            x2: 0,
+            y2: 1,
+            colorStops: [{
+                offset: 0, color: '#3A6FB5' // 0% 处的颜色
+            }, {
+                offset: 1, color: '#44BEE3' // 100% 处的颜色
+            }],
+            global: false // 缺省为 false
+        }
+    };
+
+    //全过程造价趋势
+    var chartDom2 = document.getElementById('jlchart3');
+    var myChart2 = echarts.init(chartDom2);
+    var option2 = {
+        title: {
+            text: ''
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        legend: {
+        },
+        grid: {
+            left: '3%',
+            right: '5%',
+            bottom: '3%',
+            containLabel: true
+        },
+        xAxis: {
+            type: 'category',
+            boundaryGap: false,
+            data: ['投资估算', '设计概算', '施工图预算', '台账金额', '决算金额']
+        },
+        yAxis: {
+            type: 'value'
+        },
+        series: []
+    };
+
+    function setPageData(tree) {
+        console.log(tree);
+        if (tree.children.length > 0) {
+            const level2List = tree.children[0].children;
+            if (level2List.length > 0) {
+                let jianAnHtml = '';
+                for (const level2 of level2List) {
+                    jianAnHtml += `<tr>
+                                            <td class="text-left pl-3">${level2.name}</td>
+                                            <td>${level2.gai_tp ? level2.gai_tp/10000 : 0}</td>
+                                            <td>${level2.final_tp ? level2.final_tp/10000 : 0}</td>
+                                            <td class="${ZhCalc.sub(level2.final_tp, level2.gai_tp) > 0 ? 'text-danger' : ZhCalc.sub(level2.final_tp, level2.gai_tp) === 0 ? '' : 'text-success'}">${ZhCalc.sub(level2.final_tp, level2.gai_tp)/10000}</td>
+                                        </tr>`;
+                }
+                $('#jianan-table').html(jianAnHtml);
+            }
+            // 分析第一层结构,获取各个部分金额非全0的及除了回收金额层
+            const level1List = _.filter(tree.children, function (item) {
+                return item.name.indexOf('回收金额') === -1 &&
+                    (item.gu_tp || item.gai_tp || (item.final_tp !== undefined && item.final_tp) || item.yu_tp || (item.total_price !== undefined && item.total_price))
+            });
+            // 回收金额
+            const huishouInfo = _.find(tree.children, function (item) {
+                return item.name.indexOf('回收金额') !== -1 && (item.gu_tp || item.gai_tp || (item.final_tp !== undefined && item.final_tp) || item.yu_tp || (item.total_price !== undefined && item.total_price))
+            });
+            let total_rate = 0, total_gai_tp = 0, total_gu_tp = 0, total_yu_tp = 0, total_price = 0, total_final_tp = 0;
+            if (level1List.length > 0) {
+                if (huishouInfo) {
+                    total_gai_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gai_tp')), huishouInfo.gai_tp);
+                    total_final_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'final_tp')), huishouInfo.final_tp);
+                    total_gu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'gu_tp')), huishouInfo.gu_tp);
+                    total_yu_tp = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'yu_tp')), huishouInfo.yu_tp);
+                    total_price = ZhCalc.sub(ZhCalc.sum(_.map(level1List, 'total_price')), huishouInfo.total_price);
+                    total_rate = ZhCalc.round(ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp), 2);
+                    $('#total_gai_tp').text(total_gai_tp ? total_gai_tp/10000 : 0);
+                    $('#total_final_tp').text(total_final_tp ? total_final_tp/10000 : 0);
+                    // level1List.push(huishouInfo);
+                } else {
+                    total_gai_tp = ZhCalc.sum(_.map(level1List, 'gai_tp'));
+                    total_final_tp = ZhCalc.sum(_.map(level1List, 'final_tp'));
+                    total_gu_tp = ZhCalc.sum(_.map(level1List, 'gu_tp'));
+                    total_yu_tp = ZhCalc.sum(_.map(level1List, 'yu_tp'));
+                    total_price = ZhCalc.sum(_.map(level1List, 'total_price'));
+                    total_rate = ZhCalc.round(ZhCalc.div(ZhCalc.sub(total_final_tp, total_gai_tp), total_gai_tp), 2);
+                    $('#total_gai_tp').text(total_gai_tp ? total_gai_tp/10000 : 0);
+                    $('#total_final_tp').text(total_final_tp ? total_final_tp/10000 : 0);
+                }
+            }
+            $('#total_rate').text((total_rate ? total_rate : 0) + '%');
+            if (total_rate > 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-success').addClass('text-danger');
+            } else if (total_rate < 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-danger').addClass('text-success');
+            } else if (total_rate === 0) {
+                $('#total_rate').parents('.canyu-band').removeClass('text-success').removeClass('text-danger');
+            }
+            console.log(level1List);
+            option.series[0].data = [
+                total_gu_tp/10000,
+                total_gai_tp/10000,
+                total_yu_tp/10000,
+                total_price/10000,
+                ZhCalc.add(total_change_tp, total_price)/10000,
+                total_final_tp/10000,
+            ];
+            if (huishouInfo) level1List.push(huishouInfo);
+            option2.legend.data = _.map(level1List, 'name');
+            for (const level1 of level1List) {
+                option2.series.push({
+                    name: level1.name,
+                    type: 'line',
+                    stack: 'Total',
+                    data: [level1.gu_tp/10000, level1.gai_tp/10000, level1.yu_tp/10000, level1.total_price/10000, level1.final_tp/10000]
+                });
+            }
+        }
+        myChart.setOption(option);
+        myChart2.setOption(option2);
+    }
+
+    let resizeTimer = null;
+    $(window).bind('resize', function () {
+        if (resizeTimer) clearTimeout(resizeTimer);
+        resizeTimer = setTimeout(function () {
+            echartsReset();
+        }, 500);
+    });
+    function echartsReset() {
+        myChart.resize();
+        myChart2.resize();
+    }
+
+    function setDashboardHeight() {
+        function getObjHeight(select) {
+            return select.length > 0 ? select.outerHeight(true) : 0;
+        }
+        $('.dashboard-height').height($(window).height() - 34 - 16);
+        $('.agency-partheight').height($('.dashboard-height').height()/2);
+        $('.contant-height-one').height($('.agency-partheight').height() - 52 - 20);
+        $('.contant-height-two').height($('.agency-partheight').height() - 52 - getObjHeight($(".echart-height")) - 20);
+        // $('.echart-height').width(parseInt($(".echart-height").width()));
+    }
+    setDashboardHeight();
+    $(window).resize(setDashboardHeight);
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
+});

+ 2 - 2
app/public/js/budget_list.js

@@ -38,7 +38,7 @@ $(document).ready(() => {
                     }
                 } else {
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push(`<a href="/budget/${node.budget_id}/compare" name="name" id="${node.id}">`, node.name, '</a>');
+                    html.push(`<a href="/budget/${node.budget_id}" name="name" id="${node.id}">`, node.name, '</a>');
                 }
                 html.push('</td>');
                 // 概预算标准
@@ -188,4 +188,4 @@ $(document).ready(() => {
         if (!srSelect) srSelect = new srObject();
         srSelect.init();
     });
-});
+});

+ 14 - 11
app/public/js/material.js

@@ -1337,23 +1337,26 @@ $(document).ready(() => {
     function setCurBillSourceList(mb_id, ms_id = null) {
         const showSourceList = [];
         if (mb_id) {
-            const list = _.filter(materialListData2, { mb_id, ms_id });
+            const list = _.filter(materialListData2, { mb_id, ms_id, is_join: 1 });
             console.log(list);
             if (list.length > 0) {
                 for (const l of list) {
                     const gcl = _.find(gclGatherData, function (item) {
                         return item.leafXmjs && item.leafXmjs.length > 0 && _.findIndex(item.leafXmjs, { gcl_id: l.gcl_id }) !== -1;
                     });
-                    const index = _.findIndex(showSourceList, { code: gcl.b_code, name: gcl.name, unit: gcl.unit });
-                    if (gcl && index === -1) {
-                        showSourceList.push({
-                            code: gcl.b_code,
-                            name: gcl.name,
-                            unit: gcl.unit,
-                            quantity: ZhCalc.mul(l.gather_qty, l.quantity),
-                        });
-                    } else if (gcl && index !== -1) {
-                        showSourceList[index].quantity = ZhCalc.add(showSourceList[index].quantity, ZhCalc.mul(l.gather_qty, l.quantity));
+                    console.log(gcl);
+                    if (gcl) {
+                        const index = _.findIndex(showSourceList, { code: gcl.b_code, name: gcl.name, unit: gcl.unit });
+                        if (index === -1) {
+                            showSourceList.push({
+                                code: gcl.b_code,
+                                name: gcl.name,
+                                unit: gcl.unit,
+                                quantity: ZhCalc.mul(l.gather_qty, l.quantity),
+                            });
+                        } else {
+                            showSourceList[index].quantity = ZhCalc.add(showSourceList[index].quantity, ZhCalc.mul(l.gather_qty, l.quantity));
+                        }
                     }
                 }
             }

+ 1 - 0
app/router.js

@@ -716,6 +716,7 @@ module.exports = app => {
     app.post('/subproj/memberSave', sessionAuth, projectManagerCheck, 'subProjController.memberSave');
     // 概算投资
     app.get('/budget', sessionAuth, 'budgetController.list');
+    app.get('/budget/:id', sessionAuth, budgetCheck, 'budgetController.budgetInfo');
     app.get('/budget/:id/compare', sessionAuth, budgetCheck, 'budgetController.compare');
     app.post('/budget/:id/compare/load', sessionAuth, budgetCheck, 'budgetController.compareLoad');
     app.post('/budget/:id/compare/final', sessionAuth, budgetCheck, 'budgetController.compareFinal');

+ 19 - 0
app/service/change.js

@@ -1695,6 +1695,25 @@ module.exports = app => {
             const sql = `SELECT * FROM ${this.tableName} WHERE tid = ? AND negative_tp < 0 ORDER BY code asc`;
             return this.db.query(sql, [tenderId]);
         }
+
+        async getListByBudgetInfo(tenders) {
+            if (tenders) {
+                const sql = 'SELECT * FROM ?? WHERE `status` = ? AND `tid` in (' + this.ctx.helper.getInArrStrSqlFilter(tenders.split(',')) + ') ORDER BY `total_price` DESC LIMIT 0, 10';
+                const params = [this.tableName, audit.flow.status.checked];
+                return await this.db.query(sql, params);
+            }
+            return [];
+        }
+
+        async getTotalTpByBudgetInfo(tenders) {
+            if (tenders) {
+                const sql = 'SELECT SUM(`total_price`) AS tp FROM ?? WHERE `status` = ? AND `tid` in (' + this.ctx.helper.getInArrStrSqlFilter(tenders.split(',')) + ')';
+                const sqlParam = [this.tableName, audit.flow.status.checked];
+                const result = await this.db.queryOne(sql, sqlParam);
+                return result && result.tp ? result.tp : 0;
+            }
+            return 0;
+        }
     }
 
     return Change;

+ 1 - 1
app/service/material_list.js

@@ -377,7 +377,7 @@ module.exports = app => {
          * @return {void}
          */
         async getMaterialData(tid, mid) {
-            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`gather_qty`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mb.m_spread, ml.ms_id' +
+            const sql = 'SELECT ml.`id`, mb.`code`, mb.`name`, mb.`unit`, ml.`order`, ml.`gather_qty`, ml.`quantity`, ml.`expr`, ml.`mb_id`, ml.`gcl_id`, ml.`xmj_id`, ml.`mx_id`, ml.`ms_id`, ml.`tid`, ml.`mid`, mb.m_spread, ml.ms_id, ml.is_join' +
                 ' FROM ' + this.tableName + ' as ml' +
                 ' LEFT JOIN ' + this.ctx.service.materialBills.tableName + ' as mb' +
                 ' ON ml.`mb_id` = mb.`id`' +

+ 157 - 0
app/view/budget/info.ejs

@@ -0,0 +1,157 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div>
+                <div class="d-inline-block">
+                    投资概况
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap my-3">
+        <div class="dashboard-height mx-3">
+            <div class="row agency-partheight mt-3">
+                <div class="col-4 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>建安费增减情况</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <table class="table table-middle">
+                                    <thead class="text-center thead-light">
+                                    <tr>
+                                        <th class="pl-3">费用名称</th>
+                                        <th width="20%">设计概算</th>
+                                        <th width="20%">工程决算</th>
+                                        <th width="20%">增减金额</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody class="text-right" id="jianan-table">
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-4 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>增减幅度</div>
+                            <div class="mt-1">万元</div></div>
+                        <div class="card-body p-0">
+                            <style>
+                                @media (max-width: 1400px) {
+                                    .echart-height h5 {
+                                        font-size: 1rem;
+                                    }
+                                    #total_rate {
+                                        font-size: 3.5rem;
+                                    }
+                                }
+                            </style>
+                            <div class="row mx-0 echart-height" style="width: 99.91%;padding:13px 0">
+                                <div class="col-6 px-0 text-right">
+                                    <div class="canyu-pill canyu-bg-yellow ml-2 mr-1 my-2 p-3 text-white">
+                                        <div class="icon"><i class="fa fa-pie-chart"></i></div>
+                                        <div class="pr-2">
+                                            <h5><b id="total_gai_tp">0</b></h5> <h6>设计概算</h6>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="col-6 px-0 text-right">
+                                    <div class="canyu-pill canyu-bg-blue my-2 ml-1 mr-2 p-3 text-white">
+                                        <div class="icon"><i class="fa fa-pie-chart"></i></div>
+                                        <div class="pr-2">
+                                            <h5><b id="total_final_tp">0</b></h5> <h6>工程决算</h6>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="contant-height-two">
+                                <div class="canyu-band">
+                                    <h1 id="total_rate" class="">0%</h1>
+                                    <h3 class="text-muted">增减幅度</h3>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-4 pl-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>金额对比</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <div id="jlchart2" style="height: 100%; width: 100%;">
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="row agency-partheight">
+                <div class="col-6 px-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>全过程造价趋势</div>
+                            <div class="mt-1">万元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <div id="jlchart3" style="height: 90%; width: 100%;">
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="col-6 pl-0">
+                    <div class="card ml-3">
+                        <div class="card-header card-white d-flex justify-content-between">
+                            <div class="card-big-htext"><span class="card-icon mr-2"></span>工程变更费用TOP10</div>
+                            <div class="mt-1">元</div>
+                        </div>
+                        <div class="card-body p-0">
+                            <div class="contant-height-one">
+                                <table class="table table-middle">
+                                    <thead  class="text-center thead-light">
+                                    <tr>
+                                        <th width="8%">序号</th>
+                                        <th width="13%">变更令号</th>
+                                        <th width="">变更名称</th>
+                                        <th width="17%">变更性质</th>
+                                        <th width="12%">变更金额</th>
+                                    </tr>
+                                    </thead>
+                                    <tbody class="text-left">
+                                    <% if (changeList.length > 0) { %>
+                                        <% for (const [index, c] of changeList.entries()) { %>
+                                            <tr>
+                                                <td class="text-center"><%- index+1 %></td>
+                                                <td><%- c.p_code %></td>
+                                                <td><%- c.name %></td>
+                                                <td class="text-center"><%- changeConst.qualityName[c.quality] %></td>
+                                                <td class="text-right" ><%- c.total_price %></td>
+                                            </tr>
+                                        <% } %>
+                                    <% } %>
+                                    </tbody>
+                                </table>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const total_change_tp = <%- total_change_tp %>;
+</script>

+ 3 - 2
app/view/budget/sub_menu_list.ejs

@@ -1,8 +1,9 @@
 <nav-menu title="返回" url="/budget" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
+<nav-menu title="投资概况" url="/budget/<%= ctx.budget.id %>" ml="3" active="<%= ctx.url === '/budget/' + ctx.budget.id ? 1 : -1 %>"></nav-menu>
 <nav-menu title="造价对比" url="/budget/<%= ctx.budget.id %>/compare" ml="3" active="<%= ctx.url.indexOf('/compare') %>"></nav-menu>
 <nav-menu title="投资估算" url="/budget/<%= ctx.budget.id %>/gu" ml="3" active="<%= ctx.url.indexOf('/gu') %>"></nav-menu>
 <nav-menu title="初步概算" url="/budget/<%= ctx.budget.id %>/gai%>" ml="3" active="<%= ctx.url.indexOf('/gai') %>"></nav-menu>
 <nav-menu title="施工图预算" url="/budget/<%= ctx.budget.id %>/yu" ml="3" active="<%= ctx.url.indexOf('/yu') %>"></nav-menu>
-<% if (!ctx.budget.readOnly && ctx.url.indexOf('/compare') === -1 ) { %>
+<% if (!ctx.budget.readOnly && ctx.url.indexOf('/compare') === -1 && ctx.url !== '/budget/' + ctx.budget.id) { %>
 <div class="contarl-box"><button class="btn btn-primary btn-sm btn-block" data-toggle="modal" data-target="#budget-set">设置</button></div>
-<% } %>
+<% } %>

+ 1 - 0
app/view/report/rpt_individual.ejs

@@ -6,6 +6,7 @@
     <script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
     <script type="text/javascript" src="/public/jspdf/jspdf.min.js"></script>
     <script type="text/javascript" src="/public/js/common_ajax.js"></script>
+    <script type="text/javascript" src="/public/report/js/rpt_public.js"></script>
     <script type="text/javascript" src="/public/report/js/jpc_output_value_define.js"></script>
     <script type="text/javascript" src="/public/report/js/jpc_output.js"></script>
     <script type="text/javascript" src="/public/js/string_util_light.js"></script>

+ 16 - 0
config/web.js

@@ -1091,6 +1091,22 @@ const JsFiles = {
                 ],
                 mergeFile: 'budget_list',
             },
+            info: {
+                files: [
+                    '/public/js/echarts/echarts.min.js',
+                    '/public/js/moment/moment.min.js',
+                    '/public/js/decimal.min.js',
+                    '/public/js/component/menu.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/tenders2tree.js',
+                    '/public/js/budget_info.js',
+                ],
+                mergeFile: 'budget_info.js',
+            },
             compare: {
                 files: [
                     '/public/js/moment/moment.min.js',