Browse Source

TASK#2999

TonyKang 5 năm trước cách đây
mục cha
commit
6fbf445424

+ 24 - 0
app/controller/report_controller.js

@@ -40,6 +40,7 @@ module.exports = app => {
                 let stage_times = -1;
                 let stage_times = -1;
                 let stage_status = -1;
                 let stage_status = -1;
                 const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);
                 const treeNodes = await ctx.service.rptTreeNode.getNodesByProjectId([-1, tender.data.project_id]);
+                const custTreeNodes = await ctx.service.rptTreeNodeCust.getCustFoldersByUserId(this.ctx.session.sessionUser.accountId);
                 const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');
                 const custCfg = await ctx.service.rptCustomizeCfg.getCustomizeCfgByUserId('Administrator');
                 const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
                 const stageList = await ctx.service.stage.getValidStagesShort(tender.id);
                 const prjAccList = await ctx.service.projectAccount.getAllAccountByProjectId(tender.data.project_id);
                 const prjAccList = await ctx.service.projectAccount.getAllAccountByProjectId(tender.data.project_id);
@@ -130,10 +131,15 @@ module.exports = app => {
                 for (const prjAcc of prjAccList) {
                 for (const prjAcc of prjAccList) {
                     prjAcc.account_group = accountGroup[prjAcc.account_group];
                     prjAcc.account_group = accountGroup[prjAcc.account_group];
                 }
                 }
+                let rpt_tpl_items = '{ customize: { "isDftAll": true }, common: [] }';
+                if (custTreeNodes.length > 0) {
+                    rpt_tpl_items = custTreeNodes[0].rpt_tpl_items;
+                }
                 const renderData = {
                 const renderData = {
                     tender: tender.data,
                     tender: tender.data,
                     tenderInfo: tender.info,
                     tenderInfo: tender.info,
                     rpt_tpl_data: JSON.stringify(treeNodes),
                     rpt_tpl_data: JSON.stringify(treeNodes),
+                    cust_tpl_data: rpt_tpl_items,
                     cust_cfg: JSON.stringify(custCfg),
                     cust_cfg: JSON.stringify(custCfg),
                     project_id: tender.data.project_id,
                     project_id: tender.data.project_id,
                     tender_id: tender.id,
                     tender_id: tender.id,
@@ -454,6 +460,24 @@ module.exports = app => {
             }
             }
         }
         }
 
 
+        /**
+         * 更新用户通用报表显示配置
+         *
+         * @param {Object} ctx - egg全局context
+         * @return {void}
+         */
+        async updateCustNode(ctx) {
+            const params = JSON.parse(ctx.request.body.params);
+            // console.log(params);
+            const custId = this.ctx.session.sessionUser.accountId;
+            const newNodeItems = JSON.stringify(params.nodeItems);
+            const rst = await ctx.service.rptTreeNodeCust.updateCustNode(custId, newNodeItems);
+            // console.log(rst);
+            ctx.body = { data: rst };
+            // ctx.body = { data: { msg: 'test the network' } };
+            ctx.status = 201;
+        }
+
         async setCustomDefine(ctx) {
         async setCustomDefine(ctx) {
             try {
             try {
                 const data = JSON.parse(ctx.request.body.data);
                 const data = JSON.parse(ctx.request.body.data);

+ 1 - 0
app/router.js

@@ -241,6 +241,7 @@ module.exports = app => {
     app.post('/tender/report_api/updateSignatureUsed', sessionAuth, datetimeFill, 'signatureController.updateSignatureUsed');
     app.post('/tender/report_api/updateSignatureUsed', sessionAuth, datetimeFill, 'signatureController.updateSignatureUsed');
     app.post('/tender/report_api/updateRoleRelationship', sessionAuth, 'signatureController.updateRoleRel');
     app.post('/tender/report_api/updateRoleRelationship', sessionAuth, 'signatureController.updateRoleRel');
     app.post('/tender/report_api/createRoleRelationship', sessionAuth, 'signatureController.createRoleRel');
     app.post('/tender/report_api/createRoleRelationship', sessionAuth, 'signatureController.createRoleRel');
+    app.post('/tender/report_api/updateCustNode', sessionAuth, 'reportController.updateCustNode');
     app.post('/report/cDefine', sessionAuth, 'reportController.setCustomDefine');
     app.post('/report/cDefine', sessionAuth, 'reportController.setCustomDefine');
 
 
     // 变更管理
     // 变更管理

+ 88 - 0
app/service/rpt_tree_node_cust.js

@@ -0,0 +1,88 @@
+'use strict';
+
+/**
+ * Created by Tony on 2020/6/6.
+
+ * 用户自定义显示报表模板组织
+ * @author TonyKang
+ * @date 2019/05/05
+ * @version
+ */
+
+const BaseService = require('../base/base_service');
+module.exports = app => {
+
+    class RptTreeNodeCust extends BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'rpt_tree_node_cust';
+            this.dataId = 'id';
+        }
+
+        async getCustFoldersByUserId(userId) {
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('cust_acc_id', {
+                value: userId,
+                operate: '=',
+            });
+            this.sqlBuilder.columns = ['id', 'cust_acc_id', 'rpt_tpl_items'];
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+
+            const list = await this.db.query(sql, sqlParam);
+            return list;
+        }
+
+        async createCustNode(cust_acc_id, newRptItems) {
+            let rst = null;
+            this.transaction = await this.db.beginTransaction();
+            try {
+                const data = {
+                    cust_acc_id: cust_acc_id,
+                    rpt_tpl_items: newRptItems,
+                };
+                // console.log('createNode:');
+                // console.log(data);
+                rst = await this.transaction.insert(this.tableName, data);
+                await this.transaction.commit();
+            } catch (ex) {
+                console.log(ex);
+                // 回滚
+                await this.transaction.rollback();
+            } finally {
+                return rst;
+            }
+        }
+
+        async updateCustNode(cust_acc_id, newNodeItems) {
+            let rst = null;
+            try {
+                const custNode = await this.getCustFoldersByUserId(cust_acc_id);
+                // console.log('query Node:');
+                // console.log(custNode);
+                if (custNode.length > 0) {
+                    this.transaction = await this.db.beginTransaction();
+                    custNode[0].rpt_tpl_items = newNodeItems;
+                    rst = await this.transaction.update(this.tableName, custNode[0]);
+                    this.transaction.commit();
+                } else {
+                    rst = await this.createCustNode(cust_acc_id, newNodeItems);
+                }
+            } catch (ex) {
+                console.log(ex);
+                this.transaction.rollback();
+                rst = null;
+            } finally {
+                return rst;
+            }
+        }
+    }
+
+    return RptTreeNodeCust;
+};

+ 14 - 5
app/view/report/index.ejs

@@ -17,7 +17,9 @@
                 <div class="d-inline-block">
                 <div class="d-inline-block">
                     <ul class="nav nav-pills m-0">
                     <ul class="nav nav-pills m-0">
                         <li class="nav-item mr-1"><a href="#man-c" data-toggle="modal" data-target="#man-c" class=" btn btn-outline-primary btn-sm"><i class="fa fa-cog"></i> 通用报表</a></li>
                         <li class="nav-item mr-1"><a href="#man-c" data-toggle="modal" data-target="#man-c" class=" btn btn-outline-primary btn-sm"><i class="fa fa-cog"></i> 通用报表</a></li>
+                        <!--
                         <li class="nav-item"><a href="#add-c" data-toggle="modal" data-target="#add-c" class=" btn btn-outline-primary btn-sm"><i class="fa fa-plus"></i> 定制报表</a></li>
                         <li class="nav-item"><a href="#add-c" data-toggle="modal" data-target="#add-c" class=" btn btn-outline-primary btn-sm"><i class="fa fa-plus"></i> 定制报表</a></li>
+                        -->
                     </ul>
                     </ul>
                 </div>
                 </div>
             </div>
             </div>
@@ -246,7 +248,9 @@
 
 
 
 
 <script type="text/javascript">
 <script type="text/javascript">
-    const TOP_TREE_NODES = <%- rpt_tpl_data %>;
+    let TOP_TREE_NODES = <%- rpt_tpl_data %>;
+    const CUST_TREE_NODES = <%- cust_tpl_data %>;
+    const ORG_CUST_TREE_NODES = JSON.parse(JSON.stringify(CUST_TREE_NODES));
     let CUST_CFG = <%- cust_cfg %>;
     let CUST_CFG = <%- cust_cfg %>;
     CUST_CFG = JSON.parse(CUST_CFG[0].cfg_content);
     CUST_CFG = JSON.parse(CUST_CFG[0].cfg_content);
     const PROJECT_ID = <%- project_id %>;
     const PROJECT_ID = <%- project_id %>;
@@ -338,7 +342,7 @@
 
 
     function buildTplTree() {
     function buildTplTree() {
         if (TOP_TREE_NODES.length > 0) {
         if (TOP_TREE_NODES.length > 0) {
-            //1. 整理模板树
+            //1. 整理模板树 (原始状态的TOP_TREE_NODES包含了通用报表与定制表,需要分割)
             const individualNode = {id: 99999, name: '定制报表', pid: -1, rpt_type: 0, items: [], isParent: true};
             const individualNode = {id: 99999, name: '定制报表', pid: -1, rpt_type: 0, items: [], isParent: true};
             for (let tnIdx = TOP_TREE_NODES.length - 1; tnIdx >= 0; tnIdx--) {
             for (let tnIdx = TOP_TREE_NODES.length - 1; tnIdx >= 0; tnIdx--) {
                 if (TOP_TREE_NODES[tnIdx].pid !== -1) {
                 if (TOP_TREE_NODES[tnIdx].pid !== -1) {
@@ -348,8 +352,13 @@
                     TOP_TREE_NODES.splice(tnIdx, 1);
                     TOP_TREE_NODES.splice(tnIdx, 1);
                 }
                 }
             }
             }
-            //TOP_TREE_NODES.push(individualNode);
-            TOP_TREE_NODES.unshift(individualNode);
+            // 1.1 移除未被选择的模板
+            for (let rIdx = TOP_TREE_NODES[0].items.length - 1; rIdx >= 0; rIdx--) {
+                if (CUST_TREE_NODES.common.indexOf(TOP_TREE_NODES[0].items[rIdx].name) < 0) {
+                    TOP_TREE_NODES[0].items.splice(rIdx, 1);
+                }
+            }
+            TOP_TREE_NODES.unshift(individualNode); //定制在前
             //2. 原始的模板树(恢复用)
             //2. 原始的模板树(恢复用)
             const individualNodeOrg = {id: 99999, name: '定制报表', pid: -1, rpt_type: 0, items: [], isParent: true};
             const individualNodeOrg = {id: 99999, name: '定制报表', pid: -1, rpt_type: 0, items: [], isParent: true};
             for (let tnIdx = ORG_TOP_TREE_NODES.length - 1; tnIdx >= 0; tnIdx--) {
             for (let tnIdx = ORG_TOP_TREE_NODES.length - 1; tnIdx >= 0; tnIdx--) {
@@ -361,7 +370,7 @@
                 }
                 }
             }
             }
             //ORG_TOP_TREE_NODES.push(individualNode);
             //ORG_TOP_TREE_NODES.push(individualNode);
-            ORG_TOP_TREE_NODES.unshift(individualNode);
+            ORG_TOP_TREE_NODES.unshift(individualNodeOrg);
         }
         }
     }
     }
 
 

+ 121 - 0
app/view/report/rpt_all_popup.ejs

@@ -317,14 +317,135 @@
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
+<!--管理通用报表-->
+<div class="modal fade" id="man-c" 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">
+                <div class="modal-height-500" style="overflow: auto;">
+                    <table class="table table-sm table-bordered" id="report_cust_group_common">
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal" onclick="revertCustRptCfg();">关闭</button>
+                <!--有结果出现添加-->
+                <button type="button" class="btn btn-primary" data-dismiss="modal" onclick="updateCustRptCfg()">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
 
 
 <script>
 <script>
     zTreeOprObj.getCustomerCfg();
     zTreeOprObj.getCustomerCfg();
     zTreeOprObj.iniFontCfgDom(CUST_CFG);
     zTreeOprObj.iniFontCfgDom(CUST_CFG);
+    buildCommonCustRpt();
 
 
     function searchAccount() {
     function searchAccount() {
         if (event.keyCode == 13) {
         if (event.keyCode == 13) {
             rptSignatureHelper.buildSelectableAccount(this);
             rptSignatureHelper.buildSelectableAccount(this);
         }
         }
     }
     }
+
+    function buildCommonCustRpt() {
+        //CUST_TREE_NODES
+        let tbDom = $("#report_cust_group_common");
+        tbDom.empty();
+        tbDom.append('<tr><th>类别</th><th>包含报表</th><th>显示</th></tr>');
+        let _countAvailableTpls = function (tItem) {
+            let rst = 0;
+            if (tItem.nodeType === 1 && tItem.items && tItem.items.length > 0) {
+                for (const dItem of tItem.items) {
+                    if (dItem.nodeType === 2) {
+                        if (dItem.released) rst++;
+                    } else {
+                        rst += _countAvailableTpls(dItem);
+                    }
+                }
+            }
+            return rst;
+        };
+        let _pushRptLine = function (rptItem, level) {
+            if (rptItem.nodeType === 1) {
+                let amt = _countAvailableTpls(rptItem);
+                let classStr = '';
+                if (level > 0) {
+                    classStr = 'pl-' + (level + 3);
+                }
+                tbDom.append('<tr><td class="' + classStr + '">' + rptItem.name + '</td><td>' + amt + '</td><td></td></tr>');
+                if (rptItem.items && rptItem.items.length > 0) {
+                    for (const subItem of rptItem.items) {
+                        _pushRptLine(subItem, level + 1);
+                    }
+                }
+            }
+        }
+        let TplAmts = [];
+        for (const topItem of ORG_TOP_TREE_NODES[1].items) {
+            TplAmts.push(_countAvailableTpls(topItem));
+            let checkedStr = (CUST_TREE_NODES.common.indexOf(topItem.name) >= 0) ? ' checked' : '';
+            //let checkedStr = (CUST_TREE_NODES.length > 0) ? ' checked' : '';
+            tbDom.append('<tr><td>' + topItem.name + '</td><td>' + TplAmts[TplAmts.length - 1] + '</td><td><input onchange="changeFolder(this, true)" hiddenval="' + topItem.name + '" type="checkbox"' + checkedStr + '></td></tr>');
+            if (topItem.items && topItem.items.length > 0) {
+                for (const subItem of topItem.items) {
+                    _pushRptLine(subItem, 1);
+                }
+            }
+        }
+    }
+
+    function updateCustRptCfg(){
+        let params = {};
+        params.nodeItems = CUST_TREE_NODES;
+        CommonAjax.postXsrfEx("/tender/report_api/updateCustNode", params, 60000, true, getCookie('csrfToken'),
+            function(result){
+                try {
+                    // console.log(result);
+                    ORG_CUST_TREE_NODES.common = JSON.parse(JSON.stringify(CUST_TREE_NODES.common));
+                    //刷新报表模板树
+                    TOP_TREE_NODES = JSON.parse(JSON.stringify(ORG_TOP_TREE_NODES));
+                    // 移除未被选择的模板
+                    for (let rIdx = TOP_TREE_NODES[1].items.length - 1; rIdx >= 0; rIdx--) {
+                        if (CUST_TREE_NODES.common.indexOf(TOP_TREE_NODES[1].items[rIdx].name) < 0) {
+                            TOP_TREE_NODES[1].items.splice(rIdx, 1);
+                        }
+                    }
+                    zTreeOprObj.getReportTemplateTree();
+                } catch(err) {
+                }
+            }, function(err){
+                // hintBox.unWaitBox();
+            }, function(ex){
+                // hintBox.unWaitBox();
+            }
+        );
+    }
+
+    function revertCustRptCfg(){
+        CUST_TREE_NODES.common = JSON.parse(JSON.stringify(ORG_CUST_TREE_NODES.common));
+    }
+
+    function changeFolder(dom, isCommon) {
+        let idx = CUST_TREE_NODES.common.indexOf(dom.attributes['hiddenval'].value);
+        if (dom.checked) {
+            if (isCommon) {
+                if (idx < 0) {
+                    CUST_TREE_NODES.common.push(dom.attributes['hiddenval'].value);
+                }
+            } else {
+                //保留定制报表分支
+            }
+        } else {
+            if (isCommon) {
+                if (idx >= 0) {
+                    CUST_TREE_NODES.common.splice(idx, 1);
+                }
+            } else {
+                //保留定制报表分支
+            }
+        }
+    }
 </script>
 </script>

+ 8 - 0
sql/update.sql

@@ -49,3 +49,11 @@ ALTER TABLE `zh_stage_pos`
 ADD COLUMN `contract_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '合同计量-公式' AFTER `postil`;
 ADD COLUMN `contract_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '合同计量-公式' AFTER `postil`;
 
 
 ALTER TABLE `zh_project_account` ADD `backdoor_password` VARCHAR(255) NULL DEFAULT NULL COMMENT '副密码' AFTER `password`;
 ALTER TABLE `zh_project_account` ADD `backdoor_password` VARCHAR(255) NULL DEFAULT NULL COMMENT '副密码' AFTER `password`;
+
+CREATE TABLE `calculation`.`zh_rpt_tree_node_cust` (
+  `id` INT NOT NULL AUTO_INCREMENT,
+  `cust_acc_id` VARCHAR(50) NULL COMMENT '用户的SSO account id',
+  `rpt_tpl_items` JSON NULL COMMENT '用户自定义显示模板,会根据实际情况调整',
+  PRIMARY KEY (`id`),
+  INDEX `cust_acc` (`cust_acc_id` ASC))
+COMMENT = '报表模板节点的用户定制化显示';