Browse Source

项目列表,金额概况

MaiXinRong 1 tháng trước cách đây
mục cha
commit
d58b055547

+ 36 - 1
app/const/project_setting.js

@@ -41,7 +41,41 @@ const colSet = {
         { field: 'sf_tp', name: '本期实付', fixed: [] },
         { field: 'end_sf_tp', name: '截止本期实付', fixed: [] },
         { field: 'wf_tp', name: '未付金额', fixed: [], hint:'截止本期应付-截止本期实付' },
-    ]
+    ],
+    projectInfo: [
+        { field: 'name', name: '项目名称', fixed: ['show', 'alias'] },
+        { field: 'contract_price', name: '签约合同价', fixed: ['show', 'alias'] },
+        { field: 'total_price', name: '0号台账', fixed: [] },
+        { field: 'gather_tp', name: '本期完成', fixed: [] },
+        { field: 'end_contract_tp', name: '截止本期合同', fixed: [] },
+        { field: 'end_qc_tp', name: '截止本期变更', fixed: [] },
+        { field: 'end_gather_tp', name: '截止本期完成', fixed: [] },
+        { field: 'pre_gather_tp', name: '截止上期完成', fixed: [] },
+        { field: 'advance_tp', name: '预付款', fixed: [] },
+        { field: 'yf_tp', name: '本期应付', fixed: [] },
+        { field: 'end_yf_tp', name: '截止本期应付', fixed: [] },
+        { field: 'sf_tp', name: '本期实付', fixed: [] },
+        { field: 'end_sf_tp', name: '截止本期实付', fixed: [] },
+        { field: 'wf_tp', name: '未付金额', fixed: [], hint:'截止本期应付-截止本期实付' },
+    ],
+};
+const defaultProjectColSet = {
+    info: [
+        { field: 'name', show: 1, alias: '' },
+        { field: 'contract_price', show: 1, alias: '' },
+        { field: 'total_price', show: 1, alias: '' },
+        { field: 'gather_tp', show: 1, alias: '' },
+        { field: 'end_contract_tp', show: 1, alias: '' },
+        { field: 'end_qc_tp', show: 1, alias: '' },
+        { field: 'end_gather_tp', show: 1, alias: '' },
+        { field: 'pre_gather_tp', show: 1, alias: '' },
+        { field: 'advance_tp', show: 1, alias: '' },
+        { field: 'yf_tp', show: 1, alias: '' },
+        { field: 'end_yf_tp', show: 1, alias: '' },
+        { field: 'sf_tp', show: 0, alias: '' },
+        { field: 'end_sf_tp', show: 0, alias: '' },
+        { field: 'wf_tp', show: 0, alias: '' },
+    ],
 };
 const defaultColSet = {
     info: [
@@ -75,6 +109,7 @@ const daPing06Set = {
 module.exports = {
     noticeSetting: notice_setting,
     colSet,
+    defaultProjectColSet,
     defaultColSet,
     daPing06Set,
 };

+ 36 - 1
app/controller/sub_proj_controller.js

@@ -11,6 +11,7 @@ const auditConst = require('../const/audit');
 const accountGroup = require('../const/account_group').group;
 const sendToWormhole = require('stream-wormhole');
 const path = require('path');
+const projectSetting = require('../const/project_setting');
 
 module.exports = app => {
     class SubProjController extends app.BaseController {
@@ -500,13 +501,47 @@ module.exports = app => {
             try {
                 const colType = ctx.request.body.col_type;
                 const colSet = JSON.parse(ctx.request.body.col_set);
-                await ctx.service.projectColSet.setProjectColSet(ctx.subProject.project_id, ctx.subProject.id, colType, colSet);
+                await ctx.service.projectColSet.setProjectColSet(ctx.session.sessionProject.id, ctx.subProject ? ctx.subProject.id : '', colType, colSet);
                 ctx.redirect(ctx.request.header.referer);
             } catch (err) {
                 ctx.log(err);
                 ctx.redirect(ctx.request.header.referer);
             }
         }
+
+        async info(ctx) {
+            try {
+                const renderData = {
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.subProject.listInfo),
+                };
+                const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.session.sessionProject.id);
+                renderData.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.projectInfo, projectColSet.info, projectSetting.defaultProjectColSet.info);
+                renderData.projectList = await ctx.service.subProject.getSubProject(ctx.session.sessionProject.id, ctx.session.sessionUser.accountId, ctx.session.sessionUser.is_admin);
+                await this._loadCount(renderData.projectList);
+                await this.layout('sub_proj/list_info.ejs', renderData, 'sub_proj/list_info_modal.ejs');
+            } catch (err) {
+                ctx.log(err);
+                ctx.postError(err, '加载金额概况数据错误');
+                ctx.redirect(this.menu.menu.dashboard.url);
+            }
+        }
+
+        async refreshInfoCache(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.spid) throw '参数错误';
+                const subProjectId = data.spid.split(';');
+                const result = [];
+                for (const spid of subProjectId ) {
+                    const tp_cache = await ctx.service.subProject.refreshTpCache(spid, ctx.session.sessionUser.accountId, ctx.session.sessionUser.is_admin);
+                    result.push({ id: spid, tp_cache });
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '刷新金额概况数据失败');
+            }
+        }
     }
 
     return SubProjController;

+ 6 - 4
app/controller/tender_controller.js

@@ -204,8 +204,9 @@ module.exports = app => {
          */
         async listInfo(ctx) {
             this.jsFiles = this.app.jsFiles.tender.info;
-            const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id, ctx.subProject.id);
-            this.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.info, projectColSet.info);
+            // const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id, ctx.subProject.id);
+            const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id);
+            this.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.info, projectColSet.info, projectSetting.defaultColSet.info);
             await this._listDetailCache('tender/info.ejs', 'tender/modal.ejs');
         }
 
@@ -256,8 +257,9 @@ module.exports = app => {
 
         async listInfoFinish(ctx) {
             this.jsFiles = this.app.jsFiles.tender.info;
-            const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id, ctx.subProject.id);
-            this.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.info, projectColSet.info);
+            // const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id, ctx.subProject.id);
+            const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.subProject.project_id);
+            this.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.info, projectColSet.info, projectSetting.defaultColSet.info);
             await this._listDetailFinish('tender/info.ejs', 'tender/modal.ejs');
         }
 

+ 318 - 0
app/public/js/sub_project_info.js

@@ -0,0 +1,318 @@
+$(document).ready(function() {
+    const projectTreeObj = (function(setting){
+        const ProjectTree = createDragTree(setting.treeSetting);
+        ProjectTree.loadDatas(setting.source);
+        const TableObj = $(setting.table);
+        const TableHeaderObj = $(setting.tableHeader);
+        let tenderTreeShowLevel;
+        let colSetCache;
+
+        const Utils = {
+            calculateFolder: function(node) {
+                node.tp_cache = {};
+                for (const c of node.children) {
+                    for (const prop in c.tp_cache) {
+                        node.tp_cache[prop] = ZhCalc.add(node.tp_cache[prop], c.tp_cache[prop]);
+                    }
+                }
+                console.log(node.name, node.tp_cache);
+            },
+            calculateSubProject: function(node) {
+                node.tp_cache.gather_tp = ZhCalc.sum([node.tp_cache.contract_tp, node.tp_cache.qc_tp, node.tp_cache.pc_tp]);
+                node.tp_cache.end_contract_tp = ZhCalc.sum([node.tp_cache.pre_contract_tp, node.tp_cache.contract_tp, node.tp_cache.contract_pc_tp]);
+                node.tp_cache.end_qc_tp = ZhCalc.sum([node.tp_cache.pre_qc_tp, node.tp_cache.qc_tp, node.tp_cache.qc_pc_tp]);
+                node.tp_cache.end_gather_tp = ZhCalc.add(node.tp_cache.end_contract_tp, node.tp_cache.end_qc_tp);
+                node.tp_cache.pre_gather_tp = ZhCalc.add(node.tp_cache.pre_contract_tp, node.tp_cache.pre_qc_tp);
+                node.tp_cache.end_yf_tp = ZhCalc.add(node.tp_cache.pre_yf_tp, node.tp_cache.yf_tp);
+                node.tp_cache.end_sf_tp = ZhCalc.add(node.tp_cache.pre_sf_tp, node.tp_cache.sf_tp);
+                node.tp_cache.wf_tp = ZhCalc.sub(node.tp_cache.end_yf_tp, node.tp_cache.end_sf_tp);
+            },
+            calculateNode: function(node) {
+                if (node.children && node.children.length > 0) {
+                    for (const c of node.children) {
+                        this.calculateNode(c);
+                    }
+                    this.calculateFolder(node);
+                } else {
+                    this.calculateSubProject(node);
+                }
+            },
+            calculateAll: function() {
+                for (const p of ProjectTree.children) {
+                    this.calculateNode(p);
+                }
+            },
+            generateColSetCache: function() {
+                const result = {};
+                colSet.forEach(x => {
+                    result[x.field] = { show: x.show, alias: x.alias || x.name };
+                });
+                return result;
+            },
+            getHeaderHtml: function() {
+                colSetCache = this.generateColSetCache();
+                const html = [];
+                html.push('<tr>');
+                if (colSetCache.name.show) html.push('<th class="text-center" style="min-width: 300px;">', colSetCache.name.alias, '</th>');
+                if (colSetCache.contract_price.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.contract_price.alias, '</th>');
+                if (colSetCache.total_price.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.total_price.alias, '</th>');
+                if (colSetCache.gather_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.gather_tp.alias, '</th>');
+                if (colSetCache.end_contract_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.end_contract_tp.alias, '</th>');
+                if (colSetCache.end_qc_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.end_qc_tp.alias, '</th>');
+                if (colSetCache.end_gather_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.end_gather_tp.alias, '</th>');
+                if (colSetCache.pre_gather_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.pre_gather_tp.alias, '</th>');
+                if (colSetCache.advance_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.advance_tp.alias, '<i class="fa fa-question-circle text-primary" data-placement="bottom" data-toggle="tooltip" data-original-title="预付款流程中截止本期金额"></i>', '</th>');
+                if (colSetCache.yf_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.yf_tp.alias, '</th>');
+                if (colSetCache.end_yf_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.end_yf_tp.alias, '</th>');
+                if (colSetCache.sf_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.sf_tp.alias, '</th>');
+                if (colSetCache.end_sf_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.end_sf_tp.alias, '</th>');
+                if (colSetCache.wf_tp.show) html.push('<th class="text-center" style="width: 100px">', colSetCache.wf_tp.alias, '</th>');
+                html.push('</tr>');
+                return html.join('');
+            },
+            getRowTdHtml: function (node, tree) {
+                const html = [];
+                // 名称
+                if (colSetCache.name.show) {
+                    html.push('<td width="20%" class="in-' + node.tree_level + '">');
+                    if (node.is_folder) {
+                        if (node.children.length > 0) {
+                            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" id="'+ node.id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
+                        } else {
+                            html.push('<i class="fa fa-folder-o"></i> ', node.name);
+                        }
+                    } else {
+                        html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
+                        html.push(`<a href="/sp/${node.id}/dashboard" name="name" id="${node.id}">`, node.name, '</a>');
+                    }
+                    html.push('</td>');
+                }
+                if (colSetCache.contract_price.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.contract_price || '');
+                    html.push('</td>');
+                }
+                // 0号台账合同
+                if (colSetCache.total_price.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.total_price || '');
+                    html.push('</td>');
+                }
+                // 本期完成
+                if (colSetCache.gather_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.gather_tp || '');
+                    html.push('</td>');
+                }
+                // 截止本期合同
+                if (colSetCache.end_contract_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.end_contract_tp || '');
+                    html.push('</td>');
+                }
+                // 截止本期变更
+                if (colSetCache.end_qc_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.end_qc_tp || '');
+                    html.push('</td>');
+                }
+                // 截止本期完成
+                if (colSetCache.end_gather_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.end_gather_tp || '');
+                    html.push('</td>');
+                }
+                // 截止上期完成
+                if (colSetCache.pre_gather_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.pre_gather_tp || '');
+                    html.push('</td>');
+                }
+                // 预付款
+                if (colSetCache.advance_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.advance_tp || '');
+                    html.push('</td>');
+                }
+                // 本期应付
+                if (colSetCache.yf_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.yf_tp || '');
+                    html.push('</td>');
+                }
+                // 截止本期应付
+                if (colSetCache.end_yf_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.end_yf_tp || '');
+                    html.push('</td>');
+                }
+                // 本期实付
+                if (colSetCache.sf_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.sf_tp || '');
+                    html.push('</td>');
+                }
+                // 截止本期实付
+                if (colSetCache.end_sf_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.end_sf_tp || '');
+                    html.push('</td>');
+                }
+                // 本期未付
+                if (colSetCache.wf_tp.show) {
+                    html.push('<td style="width: 100px" class="text-right">');
+                    html.push(node.tp_cache.wf_tp || '');
+                    html.push('</td>');
+                }
+                return html.join('');
+            },
+            getNodeTrHtml: function (node, tree) {
+                const html = [];
+                html.push(`<tr tree_id="${node.id}" draggable="true">`);
+                html.push(Utils.getRowTdHtml(node, tree));
+                html.push(`</tr>`);
+                return html.join('');
+            },
+            reloadTable: function () {
+                this.calculateAll();
+                TableHeaderObj.html(Utils.getHeaderHtml());
+                const html = [];
+                for (const node of ProjectTree.nodes) {
+                    html.push(Utils.getNodeTrHtml(node, ProjectTree));
+                }
+                TableObj.html(html.join(''));
+            },
+            getSelectNode: function() {
+                const selectId = $('tr.table-active').attr('tree_id');
+                return selectId ? ProjectTree.getItems(selectId) : null;
+            },
+            getSelectNodeId: function() {
+                const selectId = $('tr.table-active').attr('tree_id');
+                return selectId || setting.treeSetting.rootId;
+            },
+            refreshTreeTable: function(result) {
+                ProjectTree.loadDatas(result);
+                if (ProjectTree.nodes.length > 0 && $('#no-project').length > 0) window.location.reload();
+                Utils.reloadTable();
+            },
+            refreshRow: function(result) {
+                const refreshData = ProjectTree.loadPostData(result);
+                if (!refreshData.update) return;
+                for (const u of refreshData.update) {
+                    $(`tr[tree_id=${u.id}]`).html(Utils.getRowTdHtml(u, ProjectTree));
+                }
+            },
+            expandByLevel: function(level){
+                ProjectTree.expandByLevel(level);
+                for (const node of ProjectTree.nodes) {
+                    const tr = $(`tr[tree_id=${node.id}]`);
+                    if (node.expanded) {
+                        $('.fold-switch', tr).html(`<i class="fa fa-minus-square-o"></i>`);
+                    } else {
+                        $('.fold-switch', tr).html(`<i class="fa fa-plus-square-o"></i>`);
+                    }
+                    if (node.visible) {
+                        tr.show();
+                    } else {
+                        tr.hide();
+                    }
+                }
+            }
+        };
+
+        Utils.reloadTable();
+        $('body').on('click', 'tr[tree_id]', function() {
+            if ($(this).hasClass('table-active')) {
+                $(this).removeClass('table-active');
+            } else {
+                $('tr[tree_id].table-active').removeClass('table-active');
+                $(this).addClass('table-active');
+            }
+            Utils.refreshAddButton();
+        });
+
+        $('body').on('click', '.fold-switch', function() {
+            const id = this.getAttribute('id');
+            const node = ProjectTree.getItems(id);
+            ProjectTree.setExpanded(node, !node.expanded);
+            const posterity = ProjectTree.getPosterity(node);
+            if (node.expanded) {
+                $(this).html(`<i class="fa fa-minus-square-o"></i>`);
+            } else {
+                $(this).html(`<i class="fa fa-plus-square-o"></i>`);
+            }
+            for (const p of posterity) {
+                if (p.visible) {
+                    $(`tr[tree_id=${p.id}]`).show();
+                } else {
+                    $(`tr[tree_id=${p.id}]`).hide();
+                }
+            }
+        });
+
+        const getChildrenLevel = function (node) {
+            let iLevel = node.tree_level || 1;
+            if (node.children && node.children.length > 0) {
+                for (const c of node.children) {
+                    iLevel = Math.max(iLevel, getChildrenLevel(c));
+                }
+            }
+            return iLevel;
+        };
+        tenderTreeShowLevel = $.cs_showLevel({
+            selector: '#show-level',
+            levels: [
+                {
+                    type: 'sort', count: 5, visible_count: function () {
+                        return ProjectTree.children.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                    }
+                },
+                {
+                    type: 'last', title: '最底层', visible: function () {
+                        const count = ProjectTree.children.map(getChildrenLevel).reduce((x, y) => { return Math.max(x, y); }, 0) - 1;
+                        return count > 0;
+                    }
+                },
+            ],
+            showLevel: function (tag) {
+                switch (tag) {
+                    case "1":
+                    case "2":
+                    case "3":
+                    case "4":
+                    case "5":
+                        Utils.expandByLevel(parseInt(tag));
+                        break;
+                    case "last":
+                        Utils.expandByLevel(20);
+                        break;
+                    default: return;
+                }
+            }
+        });
+        tenderTreeShowLevel.initShowLevel();
+        tenderTreeShowLevel.refreshMenuVisible();
+        return { ProjectTree, TableObj, ...Utils };
+    })({
+        treeSetting: { id: 'id', pid: 'tree_pid', level: 'tree_level', order: 'tree_order', rootId: '-1' },
+        source: projectList,
+        table: '#projectList',
+        tableHeader: '#projectListHeader',
+    });
+
+    $('#refresh-cache').click(() => {
+        const spid = [];
+        for (const p of projectTreeObj.ProjectTree.nodes) {
+            if (p.is_folder) continue;
+            spid.push(p.id);
+        }
+        postData('/subproj/info/refreshCache', { spid: spid.join(';') }, function(result) {
+            for (const r of result) {
+                const project = projectTreeObj.ProjectTree.nodes.find(x => { return x.id === r.id; });
+                if (!project) continue;
+                project.tp_cache = r.tp_cache;
+            }
+            projectTreeObj.reloadTable();
+        });
+    })
+});

+ 4 - 0
app/router.js

@@ -154,6 +154,10 @@ module.exports = app => {
     app.post('/subproj/rela', sessionAuth, 'subProjController.rela');
     app.post('/subproj/member', sessionAuth, projectManagerCheck, 'subProjController.member');
     app.post('/subproj/memberSave', sessionAuth, projectManagerCheck, 'subProjController.memberSave');
+
+    app.get('/subproj/info', sessionAuth, 'subProjController.info');
+    app.post('/subproj/col-set', sessionAuth, projectManagerCheck, 'subProjController.colSet');
+    app.post('/subproj/info/refreshCache', sessionAuth, 'subProjController.refreshInfoCache');
     // **项目汇总 todo spgather
 
     // **平台设置

+ 7 - 6
app/service/project_col_set.js

@@ -31,14 +31,14 @@ module.exports = app => {
             JsonFields.forEach(jf => { if(data[jf]) data[jf] = JSON.parse(data[jf]); });
         }
 
-        async loadProjectColSet(pid, spid) {
+        async loadProjectColSet(pid, spid = '') {
             const result = await this.getDataByCondition({ pid, spid });
             this._analysisData(result);
             return result;
         }
 
-        async initProjectColSet(pid, spid) {
-            const data = JSON.parse(JSON.stringify(ProjectSetting.defaultColSet));
+        async initProjectColSet(pid, spid = '') {
+            const data = JSON.parse(JSON.stringify(spid ? ProjectSetting.defaultColSet : ProjectSetting.defaultProjectColSet));
             JsonFields.forEach(jf => { if(data[jf]) data[jf] = JSON.stringify(data[jf]); });
             data.pid = pid;
             data.spid = spid;
@@ -55,17 +55,18 @@ module.exports = app => {
         async setProjectColSet(pid, spid, colSetType, colSet) {
             const data = {};
             data[colSetType] = JSON.stringify(colSet);
-            await this.defaultUpdate(data, { where: { pid, spid } });
+            await this.defaultUpdate(data, { where: { pid, spid: spid || '' } });
         }
 
-        analysisColSetWithDefine(colSetDefine, colSet) {
+        analysisColSetWithDefine(colSetDefine, colSet, defaultColSet) {
             const result = [];
             for (const csd of colSetDefine) {
                 const cs = colSet.find(x => { return x.field === csd.field });
                 if (cs) {
                     result.push({...csd, ...cs});
                 } else {
-                    result.push({...csd});
+                    const dcs = defaultColSet.find(x => { return x.field === csd.field; });
+                    result.push({...csd, ...dcs});
                 }
             }
             return result;

+ 51 - 1
app/service/sub_project.js

@@ -82,10 +82,14 @@ module.exports = app => {
             const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             result = result.filter(x => {
                 if (x.is_folder) return !filterFolder;
-                if (admin) return true;
                 const pb = permission.find(y => { return x.id === y.spid});
+                if (admin) {
+                    x.tp_cache = pb && pb.tp_cache ? JSON.parse(pb.tp_cache) : {};
+                    return true;
+                }
                 if (!pb) return false;
                 x.user_permission = pb;
+                x.tp_cache = pb.tp_cache ? JSON.parse(pb.tp_cache) : {};
                 // 只要项目下添加了账号,就允许看到项目
                 return true;
                 // return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.user_permission.manage_permission.length > 0;
@@ -673,6 +677,52 @@ module.exports = app => {
                 return JSON.stringify({ daPing06_set: subProjDaping06Set });
             }
         }
+
+        async refreshTpCache(spid, user_id, is_admin) {
+            const tp_cache = {};
+            let permission = await this.ctx.service.subProjPermission.getDataByCondition({ spid: spid, uid: user_id });
+            if (!permission && is_admin) {
+                await this.db.insert(this.ctx.service.subProjPermission.tableName, {
+                    id: this.uuid.v4(), spid, pid: this.ctx.session.sessionProject.id, uid: user_id
+                });
+                permission = await this.ctx.service.subProjPermission.getDataByCondition({ spid: spid, uid: user_id });
+            }
+            if (!permission) return tp_cache;
+
+
+            const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
+            const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
+                ? JSON.parse(accountInfo.permission) : null;
+            const subProject = await this.getDataById(spid);
+            subProject.page_show = this.getPageShow(subProject.page_show);
+            const tenders = await this.ctx.service.tender.getList('', userPermission, this.ctx.session.sessionUser.is_admin, '', subProject);
+            for (const t of tenders) {
+                await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
+                tp_cache.contract_price = this.ctx.helper.add(tp_cache.contract_price, t.contract_price);
+                tp_cache.ledger_tp = this.ctx.helper.add(tp_cache.ledger_tp, t.ledger_tp);
+                tp_cache.advance_tp = this.ctx.helper.add(tp_cache.advance_tp, t.advance_tp);
+                tp_cache.change_tp = this.ctx.helper.add(tp_cache.change_tp, t.change_tp);
+
+
+                tp_cache.contract_tp = this.ctx.helper.add(tp_cache.contract_tp, t.stage_tp.contract_tp);
+                tp_cache.qc_tp = this.ctx.helper.add(tp_cache.qc_tp, t.stage_tp.qc_tp);
+                tp_cache.positive_qc_tp = this.ctx.helper.add(tp_cache.positive_qc_tp, t.stage_tp.positive_qc_tp);
+                tp_cache.negative_qc_tp = this.ctx.helper.add(tp_cache.negative_qc_tp, t.stage_tp.negative_qc_tp);
+                tp_cache.yf_tp = this.ctx.helper.add(tp_cache.yf_tp, t.stage_tp.yf_tp);
+                tp_cache.sf_tp = this.ctx.helper.add(tp_cache.sf_tp, t.stage_tp.sf_tp);
+                tp_cache.pre_contract_tp = this.ctx.helper.add(tp_cache.pre_contract_tp, t.stage_tp.pre_contract_tp);
+                tp_cache.pre_qc_tp = this.ctx.helper.add(tp_cache.pre_qc_tp, t.stage_tp.pre_qc_tp);
+                tp_cache.pre_positive_qc_tp = this.ctx.helper.add(tp_cache.pre_positive_qc_tp, t.stage_tp.pre_positive_qc_tp);
+                tp_cache.pre_positive_qc_tp = this.ctx.helper.add(tp_cache.pre_positive_qc_tp, t.stage_tp.pre_positive_qc_tp);
+                tp_cache.pre_yf_tp = this.ctx.helper.add(tp_cache.pre_yf_tp, t.stage_tp.pre_yf_tp);
+                tp_cache.pre_sf_tp = this.ctx.helper.add(tp_cache.pre_sf_tp, t.stage_tp.pre_sf_tp);
+                tp_cache.contract_pc_tp = this.ctx.helper.add(tp_cache.contract_pc_tp, t.stage_tp.contract_pc_tp);
+                tp_cache.qc_pc_tp = this.ctx.helper.add(tp_cache.qc_pc_tp, t.stage_tp.qc_pc_tp);
+                tp_cache.pc_tp = this.ctx.helper.add(tp_cache.pc_tp, t.stage_tp.pc_tp);
+            }
+            await this.db.update(this.ctx.service.subProjPermission.tableName, { id: permission.id, tp_cache: JSON.stringify(tp_cache)});
+            return tp_cache;
+        }
     }
 
     return SubProject;

+ 6 - 1
app/view/sub_proj/index.ejs

@@ -1,7 +1,12 @@
 <div class="panel-content">
     <div class="panel-title fluid">
         <div class="title-main  d-flex justify-content-between">
-            <div>项目列表</div>
+            <div class="d-inline-block">
+                <div class="btn-group btn-group-toggle group-tab">
+                    <a class="btn btn-sm btn-light active" href="/subproj">项目列表</a>
+                    <a class="btn btn-sm btn-light" href="/subproj/info">金额概况</a>
+                </div>
+            </div>
             <div class="d-inline-block ml-1" id="show-level"></div>
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.is_admin) { %>

+ 42 - 0
app/view/sub_proj/list_info.ejs

@@ -0,0 +1,42 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main  d-flex justify-content-between">
+            <div class="d-inline-block">
+                <div class="btn-group btn-group-toggle group-tab">
+                    <a class="btn btn-sm btn-light" href="/subproj">项目列表</a>
+                    <a class="btn btn-sm btn-light active" href="/subproj/info">金额概况</a>
+                </div>
+            </div>
+            <div class="d-inline-block ml-1" id="show-level"></div>
+            <div class="ml-auto">
+                <% if (ctx.session.sessionUser.is_admin) { %>
+                <a href="#col-set" class="btn btn-sm btn-primary ml-1" data-toggle="modal" data-target="#col-set">列设置</a>
+                <% } %>
+                <a href="javascript: void(0)" class="btn btn-sm btn-primary ml-1" id="refresh-cache">刷新数据</a>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="sjs-height-0" style="background-color: #fff">
+            <div class="c-body">
+                <% if (!projectList || projectList.length === 0) { %>
+                <div class="jumbotron" id="no-project">
+                    <h3 class="display-6">还没有项目数据</h3>
+                </div>
+                <% } else { %>
+                <table class="table table-bordered">
+                    <thead id="projectListHeader">
+                    </thead>
+                    <tbody id="projectList">
+                    </tbody>
+                </table>
+                <% } %>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    autoFlashHeight();
+    const projectList = JSON.parse(unescape('<%- escape(JSON.stringify(projectList)) %>'));
+    const colSet = JSON.parse('<%- JSON.stringify(colSet) %>');
+</script>

+ 61 - 0
app/view/sub_proj/list_info_modal.ejs

@@ -0,0 +1,61 @@
+
+<!--弹出列设置-->
+<div class="modal fade" id="col-set" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">列设置</h5>
+            </div>
+            <div class="modal-body">
+                <table class="table table-hover table-bordered">
+                    <thead><tr><th width="150px">列名</th><th width="150px">别名</th><th width="50px">显示</th></tr></thead>
+                    <tbody class="text-center">
+                    <% for (const cs of colSet) { %>
+                    <tr code="<%- cs.field %>">
+                        <td><%- cs.name %><% if (cs.hint) { %><i class="fa fa-question-circle text-primary ml-1" data-placement="bottom" data-toggle="tooltip" data-original-title="<%- cs.hint %>"></i><% } %></td>
+                        <% if (cs.fixed.indexOf('alias') >= 0) { %>
+                        <td class="disabled">-</td>
+                        <% } else {%>
+                        <td><input type="text" class="form-control form-control-sm" value="<%- cs.alias %>"></td>
+                        <% } %>
+                        <% if (cs.fixed.indexOf('show') >= 0 ) { %>
+                        <td>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" checked="" disabled="">
+                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
+                            </div>
+                        </td>
+                        <% } else { %>
+                        <td>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" <% if (cs.show) { %> checked <% } %> >
+                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
+                            </div>
+                        </td>
+                        <% } %>
+                    </tr>
+                    <% } %>
+                    </tbody>
+                </table>
+                <div class="text-danger">请勿全选(页面显示不下);谨慎修改别名(修改后,各页面概念将不一致)</div>
+            </div>
+            <form class="modal-footer" action="/subproj/col-set" method="post" onsubmit="return onSetCol();">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
+                <input type="hidden" name="col_set" value="">
+                <input type="hidden" name="col_type" value="info">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary btn-sm" id="add-bd-ok">确定修改</button>
+            </form>
+        </div>
+    </div>
+</div>
+<script>
+    const onSetCol = function() {
+        const trs = $('tr[code]', '#col-set');
+        const colSet = [];
+        for (const tr of trs) {
+            colSet.push({ field: $(tr).attr('code'), show: $('input[type=checkbox]', tr)[0].checked, alias: $('input[type=text]', tr).val() || ''});
+        }
+        $('input[name=col_set]').val(JSON.stringify(colSet));
+    };
+</script>

+ 0 - 58
app/view/tender/modal.ejs

@@ -73,56 +73,6 @@
         </div>
     </div>
 </div>
-<!--弹出列设置-->
-<div class="modal fade" id="col-set" data-backdrop="static">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h5 class="modal-title">列设置</h5>
-            </div>
-            <div class="modal-body">
-                <table class="table table-hover table-bordered">
-                    <thead><tr><th width="150px">列名</th><th width="150px">别名</th><th width="50px">显示</th></tr></thead>
-                    <tbody class="text-center">
-                    <% for (const cs of colSet) { %>
-                    <tr code="<%- cs.field %>">
-                        <td><%- cs.name %><% if (cs.hint) { %><i class="fa fa-question-circle text-primary ml-1" data-placement="bottom" data-toggle="tooltip" data-original-title="<%- cs.hint %>"></i><% } %></td>
-                        <% if (cs.fixed.indexOf('alias') >= 0) { %>
-                        <td class="disabled">-</td>
-                        <% } else {%>
-                        <td><input type="text" class="form-control form-control-sm" value="<%- cs.alias %>"></td>
-                        <% } %>
-                        <% if (cs.fixed.indexOf('show') >= 0 ) { %>
-                        <td>
-                            <div class="form-check form-check-inline">
-                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" checked="" disabled="">
-                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
-                            </div>
-                        </td>
-                        <% } else { %>
-                        <td>
-                            <div class="form-check form-check-inline">
-                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" <% if (cs.show) { %> checked <% } %> >
-                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
-                            </div>
-                        </td>
-                        <% } %>
-                    </tr>
-                    <% } %>
-                    </tbody>
-                </table>
-                <div class="text-danger">请勿全选(页面显示不下);谨慎修改别名(修改后,各页面概念将不一致)</div>
-            </div>
-            <form class="modal-footer" action="/sp/<%- ctx.subProject.id %>/list/info/col-set" method="post" onsubmit="return onSetCol();">
-                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
-                <input type="hidden" name="col_set" value="">
-                <input type="hidden" name="col_type" value="info">
-                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
-                <button type="submit" class="btn btn-primary btn-sm" id="add-bd-ok">确定修改</button>
-            </form>
-        </div>
-    </div>
-</div>
 <!--弹出计量模式选择-->
 <div class="modal fade" id="jlms" data-backdrop="static">
     <div class="modal-dialog modal-lg" role="document">
@@ -170,14 +120,6 @@
 <script>
     const auditType = JSON.parse('<%- JSON.stringify(auditConst.auditType) %>');
     const colSet = JSON.parse('<%- JSON.stringify(colSet) %>');
-    const onSetCol = function() {
-        const trs = $('tr[code]', '#col-set');
-        const colSet = [];
-        for (const tr of trs) {
-            colSet.push({ field: $(tr).attr('code'), show: $('input[type=checkbox]', tr)[0].checked, alias: $('input[type=text]', tr).val() || ''});
-        }
-        $('input[name=col_set]').val(JSON.stringify(colSet));
-    };
     $(document).ready(function () {
         const getAuditTypeText = function (type) {
             if (type === auditType.key.common) return '';

+ 0 - 3
app/view/tender/sub_menu.ejs

@@ -41,9 +41,6 @@
             <% if (ctx.app.config.is_debug) { %>
             <a href="/compare/tz" class="btn btn-sm btn-primary" target="_blank">统计分析</a>
             <% } %>
-            <% if (ctx.session.sessionUser.is_admin && ctx.url === `/sp/${ctx.subProject.id}/list/info`) { %>
-            <a href="#col-set" class="btn btn-sm btn-primary ml-1" data-toggle="modal" data-target="#col-set">列设置</a>
-            <% } %>
         </div>
         <div class="ml-auto">
             <a href="/sp/<%= ctx.subProject.id %>/list/info/finish" class="btn btn-sm btn-primary pull-right ml-1" target="_blank">完工标段</a>

+ 13 - 0
config/web.js

@@ -1235,6 +1235,19 @@ const JsFiles = {
                 ],
                 mergeFile: 'sub_project',
             },
+            listInfo: {
+                files: [
+                    '/public/js/moment/moment.min.js',
+                    '/public/js/decimal.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/zh_calc.js',
+                    '/public/js/shares/drag_tree.js',
+                    '/public/js/shares/show_level.js',
+                    '/public/js/sub_project_info.js',
+                ],
+                mergeFile: 'sub_project_info',
+            },
             wap: {
                 files: [
                     '/public/js/moment/moment.min.js',

+ 3 - 1
sql/update.sql

@@ -7,7 +7,9 @@
 -- 表结构
 ------------------------------------
 
-
+ALTER TABLE `zh_sub_project_permission`
+ADD COLUMN `tp_cache` json NULL COMMENT '金额概况缓存' AFTER `payment_permission`,
+ADD COLUMN `tp_cache_time` timestamp NULL COMMENT '缓存时间' AFTER `tp_cache`;
 
 ------------------------------------
 -- 表数据