Browse Source

项目版相关

MaiXinRong 3 months ago
parent
commit
0cab91bed2
82 changed files with 1868 additions and 1212 deletions
  1. 44 28
      app/base/base_controller.js
  2. 3 3
      app/controller/budget_controller.js
  3. 1 1
      app/controller/construction_controller.js
  4. 2 2
      app/controller/contract_controller.js
  5. 1 1
      app/controller/datacollect_controller.js
  6. 2 2
      app/controller/file_controller.js
  7. 1 1
      app/controller/financial_controller.js
  8. 1 1
      app/controller/ledger_controller.js
  9. 1 0
      app/controller/payment_controller.js
  10. 4 4
      app/controller/report_controller.js
  11. 3 3
      app/controller/revise_controller.js
  12. 3 152
      app/controller/setting_controller.js
  13. 1 1
      app/controller/stage_controller.js
  14. 2 5
      app/controller/sub_proj_controller.js
  15. 433 0
      app/controller/sub_proj_setting_controller.js
  16. 8 8
      app/controller/tender_controller.js
  17. 1 1
      app/controller/wap_controller.js
  18. 4 7
      app/middleware/budget_check.js
  19. 0 2
      app/middleware/session_auth.js
  20. 1 4
      app/middleware/sub_project_check.js
  21. 8 2
      app/middleware/url_parse.js
  22. 1 1
      app/public/js/shares/batch_import.js
  23. 1 1
      app/public/js/shares/tender_select.js
  24. 1 1
      app/public/js/sr_info.js
  25. 17 9
      app/public/js/sub_project.js
  26. 2 2
      app/public/js/tender_list_base.js
  27. 670 639
      app/router.js
  28. 19 18
      app/service/category.js
  29. 3 3
      app/service/project_log.js
  30. 3 0
      app/service/report_memory.js
  31. 48 0
      app/service/sub_project.js
  32. 12 6
      app/service/tender.js
  33. 7 8
      app/view/budget/sub_menu_list.ejs
  34. 1 0
      app/view/layout/layout.ejs
  35. 11 8
      app/view/layout/menu.ejs
  36. 38 0
      app/view/layout/project_menu.ejs
  37. 0 0
      app/view/sp_setting/category.ejs
  38. 0 0
      app/view/sp_setting/category_modal.ejs
  39. 4 4
      app/view/setting/datacollect.ejs
  40. 0 0
      app/view/sp_setting/datacollect_modal.ejs
  41. 0 0
      app/view/sp_setting/fun.ejs
  42. 0 0
      app/view/sp_setting/fun_modal.ejs
  43. 1 1
      app/view/setting/logs.ejs
  44. 27 0
      app/view/sp_setting/sub_menu.ejs
  45. 0 1
      app/view/stage_rela/index.ejs
  46. 3 3
      app/view/sub_proj/index.ejs
  47. 0 1
      app/view/sub_proj/sp_info_menu_list.ejs
  48. 2 16
      app/view/tender/manage_modal.ejs
  49. 1 9
      app/view/tender/modal.ejs
  50. 2 2
      app/view/tender/sub_memu_finish.ejs
  51. 7 7
      app/view/tender/sub_menu.ejs
  52. 1 1
      config/config.default.js
  53. 103 44
      config/menu.js
  54. 0 0
      db_script/bak/audit_order.js
  55. 0 0
      db_script/bak/belong_spid.js
  56. 0 0
      db_script/bak/budget_zb.js
  57. 0 0
      db_script/bak/change.js
  58. 0 0
      db_script/bak/change_audit_order.js
  59. 0 0
      db_script/bak/change_bills.js
  60. 0 0
      db_script/bak/change_valuation.js
  61. 0 0
      db_script/bak/checkStageFinal.js
  62. 0 0
      db_script/bak/depart-database-table.js
  63. 0 0
      db_script/bak/depart_table.js
  64. 0 0
      db_script/bak/ledger_his.js
  65. 0 0
      db_script/bak/ledger_his_count.js
  66. 0 0
      db_script/bak/material_audit_order.js
  67. 0 0
      db_script/bak/material_rate_tp.js
  68. 0 0
      db_script/bak/minus_no_value.js
  69. 0 0
      db_script/bak/pay_att.js
  70. 0 0
      db_script/bak/pay_order.js
  71. 0 0
      db_script/bak/project_spread.js
  72. 0 0
      db_script/bak/recover_ledger_his.js
  73. 0 0
      db_script/bak/repair1027.js
  74. 0 0
      db_script/bak/stage-change-final.js
  75. 0 0
      db_script/bak/stage_pay_recover.js
  76. 0 0
      db_script/bak/stage_stash.js
  77. 75 0
      db_script/bak/sub_project.js
  78. 0 0
      db_script/bak/tender_cache.js
  79. 0 0
      db_script/bak/update_revise.js
  80. 98 46
      db_script/sub_project.js
  81. 14 153
      sql/update.sql
  82. 172 0
      sql/update20250120.sql

+ 44 - 28
app/base/base_controller.js

@@ -10,10 +10,52 @@
 const moment = require('moment');
 const messageType = require('../const/message_type');
 const Controller = require('egg').Controller;
-const menuList = require('../../config/menu').menu;
 const maintainConst = require('../const/maintain');
+const otherProjectController = ['setting', 'file'];
+
 class BaseController extends Controller {
 
+    loadMenu(ctx){
+        if (ctx.isProjectController !== false) ctx.isProjectController = otherProjectController.indexOf(ctx.controllerName) >= 0 || !!this.app.menu.projectMenu[ctx.controllerName];
+        const menuList = ctx.isProjectController
+            ? JSON.parse(JSON.stringify(this.app.menu.projectMenu))
+            : JSON.parse(JSON.stringify(this.app.menu.menu));
+        ctx.menu = menuList[ctx.controllerName] || {};
+        ctx.title = ctx.menu.name || '';
+        if (ctx.menu && ctx.menu.children) {
+            if (ctx.menu.children[ctx.actionName]) ctx.title = ctx.menu.children[ctx.actionName].name;
+        }
+        if (!ctx.isProjectController && ctx.subProject) {
+            if (ctx.controllerName === 'sp') {
+                if (ctx.url.indexOf('file') > 0 || ctx.url.indexOf('fm')) {
+                    ctx.menu = menuList.file;
+                } else {
+                    ctx.menu = menuList.budget;
+                }
+            }
+            menuList.datacollect.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showDataCollect : false;
+            menuList.file.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openFile : false;
+            menuList.construction.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openConstruction : false;
+            menuList.contract.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openContract : false;
+            menuList.financial.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openFinancial : false;
+            menuList.budget.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showBudget : false;
+            for (const index in menuList) {
+                const im = menuList[index];
+                if (!im.url) {
+                    if (index === 'tender') {
+                        im.url = `/sp/${ctx.subProject.id}${ctx.curListUrl}`;
+                    } else {
+                        im.url = `/sp/${ctx.subProject.id}/${im.controller}`;
+                    }
+                }
+            }
+        } else {
+            menuList.payment.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showPayment : false;
+            menuList.management.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openManagement : false;
+        }
+        ctx.menuList = menuList;
+    }
+
     /**
      * 构造函数
      *
@@ -25,32 +67,7 @@ class BaseController extends Controller {
         this.messageType = messageType;
         this.jsValidator = this.app.jsValidator;
         this.menu = this.app.menu;
-        // 当前菜单
-        ctx.menu = menuList[ctx.controllerName] === undefined ? {} : menuList[ctx.controllerName];
-        ctx.title = ctx.menu === {} ? '' : ctx.menu.name;
-        if (ctx.menu !== {} && ctx.menu.children !== null) {
-            for (const index in ctx.menu.children) {
-                if (index === ctx.actionName) {
-                    ctx.title = ctx.menu.children[index].name;
-                }
-            }
-        }
-        if (ctx.controllerName === 'sp') {
-            if (ctx.url.indexOf('file') > 0 || ctx.url.indexOf('fm')) {
-                ctx.menu = menuList.file;
-            } else {
-                ctx.menu = menuList.budget;
-            }
-        }
-        menuList.datacollect.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showDataCollect : false;
-        menuList.payment.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.showPayment : false;
-        menuList.management.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openManagement : false;
-        menuList.file.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openFile : false;
-        menuList.construction.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openConstruction : false;
-        menuList.contract.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openContract : false;
-        menuList.financial.display = ctx.session && ctx.session.sessionProject ? ctx.session.sessionProject.page_show.openFinancial : false;
         // 菜单列表
-        ctx.menuList = menuList;
         ctx.showProject = false;
         ctx.showTender = false;
         ctx.showTitle = false;
@@ -81,10 +98,9 @@ class BaseController extends Controller {
                 message: postError,
             };
         }
-        this.ctx.menuList.subproj.display = this.ctx.session.sessionProject.showSubProj;
-        this.ctx.menuList.budget.display = this.ctx.session.sessionProject.showBudget;
 
         try {
+            this.loadMenu(this.ctx);
             data.min = this.app.config.min;
             const viewString = await this.ctx.renderView(view, data);
             const modalString = modal === '' ? '' : await this.ctx.renderView(modal, data);

+ 3 - 3
app/controller/budget_controller.js

@@ -44,7 +44,7 @@ module.exports = app => {
                     bl.zb_tp = await ctx.service.budgetZb.getSumTp(bl.budget_id);
                 }
                 renderData.tenderList = await ctx.service.tender.getList4Select('stage');
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 await this.layout('budget/list.ejs', renderData, 'budget/list_modal.ejs');
             } catch (err) {
                 ctx.log(err);
@@ -74,7 +74,7 @@ module.exports = app => {
                 // 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);
+                // renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 await this.layout('budget/info.ejs', renderData);
             } catch (err) {
                 ctx.log(err);
@@ -95,7 +95,7 @@ module.exports = app => {
                 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);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 await this.layout('budget/compare.ejs', renderData, 'budget/compare_modal.ejs');
             } catch (err) {
                 ctx.log(err);

+ 1 - 1
app/controller/construction_controller.js

@@ -34,7 +34,7 @@ module.exports = app => {
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
                     ? JSON.parse(accountInfo.permission) : null;
                 const tenderList = await ctx.service.tender.getConstructionList('', userPermission, ctx.session.sessionUser.is_admin);
-                const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+                const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.construction.index),
                     tenderList,

+ 2 - 2
app/controller/contract_controller.js

@@ -60,7 +60,7 @@ module.exports = app => {
                     return { groupName: item.name, groupList };
                 });
                 // renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
                 // renderData.templates = await this.ctx.service.filingTemplateList.getAllTemplate(ctx.session.sessionProject.id);
                 await this.layout('contract/index.ejs', renderData, 'contract/modal.ejs');
@@ -113,7 +113,7 @@ module.exports = app => {
                 if (removeTidList.length > 0) {
                     tenderList = tenderList.filter(item => !removeTidList.includes(item.id));
                 }
-                const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+                const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 const renderData = {
                     // jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.contract.tender),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.contract.index),

+ 1 - 1
app/controller/datacollect_controller.js

@@ -54,7 +54,7 @@ module.exports = app => {
                 const tenderidList = [];
                 const noticeList = await ctx.service.noticePush.getNoticeByDataCollect(ctx.session.sessionProject.id, tenderidList);
                 // 获取分类
-                const categoryData = await this.ctx.service.category.getListByCategoryLevel(ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getListByCategoryLevel(ctx.subProject.id);
                 // 默认坐标,否则则取办事处坐标
                 const projectData = await ctx.service.project.getDataById(ctx.session.sessionProject.id);
                 projectData.data_collect_pages = projectData.data_collect_pages ? projectData.data_collect_pages.split(',') : [];

+ 2 - 2
app/controller/file_controller.js

@@ -43,7 +43,7 @@ module.exports = app => {
                     if (!p.is_folder) p.file_count = await this.service.filing.sumFileCount(p.id);
                 }
                 renderData.tenderList = await ctx.service.tender.getList4Select('stage');
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 await this.layout('file/index.ejs', renderData, 'file/modal.ejs');
             } catch (err) {
                 ctx.log(err);
@@ -58,7 +58,7 @@ module.exports = app => {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.file.file),
                 };
                 renderData.filing = await ctx.service.filing.getValidFiling(ctx.params.id, ctx.subProject.permission.filing_type);
-                renderData.categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+                renderData.categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 renderData.canFiling = ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.filing.value) >= 0;
                 renderData.canUpload = ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.upload.value) >= 0;
                 renderData.canDelete = ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.delete.value) >= 0;

+ 1 - 1
app/controller/financial_controller.js

@@ -57,7 +57,7 @@ module.exports = app => {
                     return { groupName: item.name, groupList };
                 });
                 // renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
                 // renderData.templates = await this.ctx.service.filingTemplateList.getAllTemplate(ctx.session.sessionProject.id);
                 await this.layout('financial/index.ejs', renderData, 'financial/modal.ejs');

+ 1 - 1
app/controller/ledger_controller.js

@@ -116,7 +116,7 @@ module.exports = app => {
                 }
 
                 const whiteList = this.ctx.app.config.multipart.whitelist;
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 const syncLedger = await this.ctx.service.specPull.syncLedger(this.ctx.session.sessionProject.id);
                 // 是否已验证手机短信
                 const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);

+ 1 - 0
app/controller/payment_controller.js

@@ -817,6 +817,7 @@ module.exports = app => {
                     }
                     trDetailList[0].emptySign = await ctx.service.paymentRptAudit.haveEmptySign(trDetailList[0].id);
                 }
+                // todo 支付审批 目前没有所属子项目,自定义分类暂无法按所属子项目取值
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 const tenderInfo = await this.ctx.service.paymentTenderInfo.getTenderInfo(ctx.paymentTender.id);
                 const renderData = {

+ 4 - 4
app/controller/report_controller.js

@@ -254,7 +254,7 @@ module.exports = app => {
                 }
 
                 // 分类列表
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
@@ -442,7 +442,7 @@ module.exports = app => {
                     }
                     prjAccList = newAccList;
                 }
-                // 分类列表
+                // 分类列表 todo 支付审批 目前没有所属子项目,自定义分类暂无法按所属子项目取值
                 const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
@@ -593,7 +593,7 @@ module.exports = app => {
                     prjAccList = newAccList;
                 }
                 // 分类列表
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 // const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
@@ -808,7 +808,7 @@ module.exports = app => {
                     prjAccList = newAccList;
                 }
                 // 分类列表
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 // const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;

+ 3 - 3
app/controller/revise_controller.js

@@ -308,7 +308,7 @@ module.exports = app => {
             renderData.readOnly = true;
             renderData.history = true;
             renderData.historyRevise = await ctx.service.ledgerRevise.getAllReviseList(ctx.tender.id);
-            renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+            renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
             renderData.preUrl = ctx.url.replace('/info', '');
             await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs');
         }
@@ -327,7 +327,7 @@ module.exports = app => {
             renderData.posSpread.readOnly = true;
             renderData.history = false;
             renderData.historyRevise = [];
-            renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+            renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
             renderData.preUrl = ctx.url.replace('/info', '');
             await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs');
         }
@@ -350,7 +350,7 @@ module.exports = app => {
                 const groupList = renderData.accountList.filter(item1 => item1.company === item.name);
                 return { groupName: item.name, groupList };
             });
-            renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+            renderData.categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
             renderData.preUrl = ctx.url.replace('/info', '');
             await this.layout('revise/info.ejs', renderData, 'revise/info_modal.ejs');
         }

+ 3 - 152
app/controller/setting_controller.js

@@ -46,8 +46,6 @@ module.exports = app => {
             const ctx = this.ctx;
             await this.ctx.service.s2bProj.refreshSessionS2b(pid);
             ctx.subMenu.s2b.display = !!ctx.session.sessionProject.gxby || !!ctx.session.sessionProject.dagl;
-            ctx.subMenu.datacollect.display = ctx.session.sessionProject.page_show.openDataCollect;
-            // this.ctx.subMenu.s2b.display = false;
         }
 
         /**
@@ -632,6 +630,7 @@ module.exports = app => {
             }
         }
 
+        // todo 暂时保留,如果支付审批不需要,则取消
         /**
          * 项目设置 -- 自定义标段分类(Get)
          *
@@ -1128,155 +1127,6 @@ module.exports = app => {
             }
         }
 
-        async dataCollect(ctx) {
-            try {
-                if (!ctx.session.sessionProject.page_show.openDataCollect) {
-                    throw '该功能已关闭或无法查看';
-                }
-                const projectId = ctx.session.sessionProject.id;
-                await this._checkMenu(projectId);
-                const projectData = await ctx.service.project.getDataById(projectId);
-                if (projectData === null) throw '没有对应的项目数据';
-                projectData.data_collect_pages = projectData.data_collect_pages ? projectData.data_collect_pages.split(',') : [];
-                ctx.session.sessionProject.dataCollect = projectData.data_collect;
-                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
-                const dataCollectAudits = await ctx.service.datacollectAudit.getList(projectId);
-                // 获取所有项目参与者
-                const accountList = await ctx.service.projectAccount.getAllDataByCondition({
-                    where: {project_id: ctx.session.sessionProject.id, enable: 1},
-                    columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
-                });
-                // const accountGroupList = accountGroup.map((item, idx) => {
-                //     const groupList = accountList.filter(item => item.account_group === idx);
-                //     return { groupName: item, groupList };
-                // });
-                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
-                const accountGroupList = unitList.map(item => {
-                    const groupList = accountList.filter(item1 => item1.company === item.name);
-                    return {groupName: item.name, companyId: item.id, groupList};
-                });
-                const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
-                const tenders = await ctx.service.tender.getList('', null, 1);
-                const dcTenders = await ctx.service.datacollectTender.getList(projectId);
-                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103'].indexOf(ctx.session.sessionProject.code) !== -1 ? 6 : false;
-                const renderData = {
-                    projectData,
-                    dataCollectAudits,
-                    accountList,
-                    accountGroup: accountGroupList,
-                    categoryData,
-                    tenders,
-                    dcTenders,
-                    is_dz1: ['P0505', 'MI22U'].indexOf(ctx.session.sessionProject.code) !== -1 ? 5 : false,
-                    is_dz2,
-                };
-                if (is_dz2) {
-                    // 获取分类及对应值
-                    const commonJson = projectData.common_json ? JSON.parse(projectData.common_json) : null;
-                    renderData.daPing06Set = commonJson && commonJson.daPing06_set ? commonJson.daPing06_set : ctx.helper._.cloneDeep(projectSettingConst.daPing06Set);
-                }
-                await this.layout('setting/datacollect.ejs', renderData, 'setting/datacollect_modal.ejs');
-            } catch (error) {
-                ctx.helper.log(error);
-                ctx.session.postError = error.toString();
-                ctx.redirect('/dashboard');
-            }
-        }
-
-        async dataCollectSave(ctx) {
-            try {
-                if (ctx.session.sessionUser.is_admin === 0) throw '没有设置权限';
-                const projectId = ctx.session.sessionProject.id;
-                const responseData = {
-                    err: 0, msg: '', data: null,
-                };
-
-                const data = JSON.parse(ctx.request.body.data);
-                if (!data.type) {
-                    throw '提交数据错误';
-                }
-                switch (data.type) {
-                    case 'show':
-                        responseData.data = await ctx.service.project.update({
-                            data_collect: data.data_collect,
-                            data_collect_pages: data.data_collect_pages.join(','),
-                        }, {id: projectId});
-                        ctx.session.sessionProject.dataCollect = data.data_collect;
-                        ctx.session.sessionProject.showDataCollect = data.data_collect ? 1 : 0;
-                        break;
-                    case 'add-audit':
-                        // 判断该用户是否已加入到表中,已加入则提示无需添加
-                        const auditInfo = await ctx.service.datacollectAudit.getDataByCondition({
-                            pid: projectId,
-                            uid: data.id
-                        });
-                        if (auditInfo) {
-                            throw '该用户已存在权限中,无需重复添加';
-                        }
-                        // const accountInfo = await ctx.service.projectAccount.getDataById(data.id);
-                        // const companyInfo = await ctx.service.datacollectAudit.getCompanyInfo(projectId, accountInfo.company_id);
-                        // if (companyInfo) {
-                        //     throw '该用户所在单位已存在权限中,无需单独添加';
-                        // }
-                        await ctx.service.datacollectAudit.saveAudit(projectId, data.id);
-                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
-                        break;
-                    case 'add-company':
-                        const companyInfo = await ctx.service.datacollectAudit.getCompanyInfo(projectId, data.id);
-                        if (companyInfo) {
-                            throw '该单位已存在权限中,无需重复添加';
-                        }
-                        await ctx.service.datacollectAudit.saveCompany(projectId, data.id);
-                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
-                        break;
-                    case 'del-audit':
-                        const auditInfo2 = await ctx.service.datacollectAudit.getDataById(data.id);
-                        if (!auditInfo2) {
-                            throw '该用户已不存在权限中,移除失败';
-                        }
-                        await ctx.service.datacollectAudit.delAudit(data.id);
-                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
-                        break;
-                    case 'del-group':
-                        const groupInfo3 = await ctx.service.datacollectAudit.getDataById(data.id);
-                        if (!groupInfo3) {
-                            throw '该单位已不存在权限中,移除失败';
-                        }
-                        await ctx.service.datacollectAudit.delAudit(data.id);
-                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
-                        break;
-                    case 'del-company':
-                        const companyInfo2 = await ctx.service.datacollectAudit.getDataById(data.id);
-                        if (!companyInfo2) {
-                            throw '该单位已不存在权限中,移除失败';
-                        }
-                        await ctx.service.datacollectAudit.delAudit(data.id);
-                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
-                        break;
-                    case 'tender':
-                        if (ctx.session.sessionProject.page_show.addDataCollect !== parseInt(data.add_datacollect)) {
-                            ctx.session.sessionProject.page_show.addDataCollect = parseInt(data.add_datacollect);
-                            await ctx.service.project.updatePageshow(projectId);
-                        }
-                        await ctx.service.datacollectTender.updateList(projectId, data.tids);
-                        responseData.data = {
-                            dcTenders: await ctx.service.datacollectTender.getList(projectId),
-                            addDatacollect: ctx.session.sessionProject.page_show.addDataCollect,
-                        };
-                        break;
-                    case 'save-dp06':
-                        responseData.data = await ctx.service.project.saveCommonJson(projectId, 'daPing06_set', data.daPing06Set);
-                        break;
-                    default:
-                        throw '参数有误';
-                }
-                ctx.body = responseData;
-            } catch (err) {
-                this.log(err);
-                ctx.body = {err: 1, msg: err.toString(), data: null};
-            }
-        }
-
         async manage(ctx) {
             try {
                 const projectId = ctx.session.sessionProject.id;
@@ -1296,7 +1146,8 @@ module.exports = app => {
                     t.visitor = (await this.ctx.service.tenderTourist.getTourists(t.id)).map(x => { return x.user_name; });
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                 }
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                // todo 自定义分类移动到子项目内部后,在整个项目上没有分类了
+                const categoryData = []; // await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
 
                 // 获取所有项目参与者
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({

+ 1 - 1
app/controller/stage_controller.js

@@ -187,7 +187,7 @@ module.exports = app => {
                 renderData.sfAttDelPower = ctx.session.sessionUser.accountId === ctx.stage.user_id || ctx.helper._.findIndex(ctx.stage.auditors2, { aid: ctx.session.sessionUser.accountId }) !== -1;
                 renderData.hintOver = projectFunInfo.hintOver && ctx.tender.info.fun_rela.hintOver;
                 renderData.hintMinusCb = projectFunInfo.banMinusChangeBills && ctx.tender.info.ledger_check.banMinusChangeBills;
-                renderData.categoryData = await ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
                 renderData.settleStatus = ctx.service.settle.settleStatus;
                 renderData.deleteFilePermission = PermissionCheck.delFile(this.ctx.session.sessionUser.permission);
                 await this.layout('stage/index.ejs', renderData, 'stage/modal.ejs');

+ 2 - 5
app/controller/sub_proj_controller.js

@@ -23,16 +23,13 @@ module.exports = app => {
          */
         async index(ctx) {
             try {
-                if (!ctx.session.sessionProject.showSubProj) {
-                    throw '该功能已关闭或无法查看';
-                }
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.subProject.list),
                     auditConst,
                 };
                 renderData.budgetStd = await ctx.service.budgetStd.getDataByProjectId(ctx.session.sessionProject.id);
                 renderData.projectList = await ctx.service.subProject.getSubProject(ctx.session.sessionProject.id, ctx.session.sessionUser.accountId, ctx.session.sessionUser.is_admin);
-                renderData.tenderList = await ctx.service.tender.getList4Select('stage');
+                renderData.tenderList = await ctx.service.tender.getManageTenderList(ctx.session.sessionProject.id);
                 const accountList = await ctx.service.projectAccount.getAllDataByCondition({
                     where: { project_id: ctx.session.sessionProject.id, enable: 1 },
                     columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
@@ -44,11 +41,11 @@ module.exports = app => {
                     return { groupName: item.name, groupList };
                 });
                 renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
                 renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 renderData.templates = await this.ctx.service.filingTemplateList.getAllTemplate(ctx.session.sessionProject.id);
                 await this.layout('sub_proj/index.ejs', renderData, 'sub_proj/modal.ejs');
             } catch (err) {
+                console.log(err);
                 ctx.log(err);
                 ctx.session.postError = err.toString();
                 ctx.redirect(this.menu.menu.dashboard.url);

+ 433 - 0
app/controller/sub_proj_setting_controller.js

@@ -0,0 +1,433 @@
+ 'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const tenderConst = require('../const/tender');
+const auditConst = require('../const/audit');
+const officeList = require('../const/cld_office').list;
+const settingConst = require('../const/setting.js');
+const scheduleConst = require('../const/schedule');
+const settingMenu = require('../../config/menu').projectSettingMenu;
+const projectLog = require('../const/project_log');
+const imType = require('../const/tender').imType;
+const path = require('path');
+const funSet = require('../const/fun_set');
+const projectSettingConst = require('../const/project_setting');
+const shenpiConst = require('../const/shenpi');
+
+module.exports = app => {
+
+    class SettingController extends app.BaseController {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局context
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            ctx.subMenu = JSON.parse(JSON.stringify(settingMenu));
+            for (const index in ctx.subMenu) {
+                const menu = ctx.subMenu[index];
+                menu.url = `/sp/${ctx.subProject.id}` + menu.url;
+            }
+        }
+
+        async defaultCheck(ctx) {
+            if (!ctx.subProject) throw '没有对应的项目数据';
+            if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+        }
+
+        /**
+         * 项目设置 -- 自定义标段分类(Get)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async category(ctx) {
+            try {
+                this.defaultCheck(ctx);
+                // 获取项目数据
+
+                const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
+                const tenderData = await ctx.service.tender.getList('', null, 1);
+                const renderData = {
+                    categoryType: settingConst.cType,
+                    categoryData,
+                    tenderData,
+                };
+                await this.layout('sp_setting/category.ejs', renderData, 'sp_setting/category_modal.ejs');
+            } catch (error) {
+                console.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        /**
+         * 新增分类(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async addCategory(ctx) {
+            try {
+                const responseData = {
+                    err: 0, msg: '', data: null,
+                };
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.name) {
+                    throw '提交数据错误';
+                }
+
+                responseData.data = await ctx.service.category.addCategory(ctx.subProject, data.name, settingConst.cType.key.dropDown);
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        /**
+         * 编辑分类(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async updateCategory(ctx) {
+            try {
+                const responseData = {err: 0, msg: '', data: null};
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id) {
+                    throw '提交数据错误';
+                }
+                if (data.name) {
+                    const count = await ctx.service.category.count({spid: ctx.subProject.id, name: data.name});
+                    if (count >= 1) {
+                        throw '存在同名类别';
+                    }
+                }
+
+                const result = await ctx.service.category.update(data, {id: data.id});
+                if (!result) {
+                    throw '提交数据失败';
+                }
+
+                responseData.data = await ctx.service.category.getCategory(data.id);
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        async setCategoryValue(ctx) {
+            try {
+                const responseData = {err: 0, msg: '', data: {}};
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id) {
+                    throw '提交数据错误';
+                }
+                await ctx.service.categoryValue.setCategoryValue(data.id, data.updateValue);
+
+                responseData.data.category = await ctx.service.category.getCategory(data.id);
+                // todo 查询标段
+                responseData.data.tenders = await ctx.service.tender.getList('', null, 1);
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err instanceof String ? err : '提交数据失败', data: null};
+            }
+        }
+
+        /**
+         * 删除分类(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async deleteCategory(ctx) {
+            try {
+                const responseData = {
+                    err: 0, msg: '', data: null,
+                };
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id) {
+                    throw '提交数据错误';
+                }
+
+                await ctx.service.category.deleteCategory(ctx.subProject, data.id);
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        /**
+         * 调整分类层次排序(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async resetCategoryLevel(ctx) {
+            try {
+                const responseData = {
+                    err: 0, msg: '', data: null,
+                };
+                const data = JSON.parse(ctx.request.body.data);
+
+                await ctx.service.category.resetCategoryLevel(data);
+                responseData.data = await ctx.service.category.getAllCategory(ctx.subProject);
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+
+        async selfCategoryLevel(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+
+                await ctx.service.subProjPermission.defaultUpdate({
+                    self_category_level: data.self_category_level || ''
+                }, { where: { spid: ctx.subProject.id, uid: ctx.session.sessionUser.accountId}});
+                ctx.body = {
+                    err: 0, msg: '', data: null,
+                };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存自定义分类失败');
+            }
+        }
+
+        async logs(ctx) {
+            try {
+                this.defaultCheck(ctx);
+                // 获取项目数据
+                const settingType = ctx.params.type ? parseInt(ctx.params.type) : 0;
+                const logs = await ctx.service.projectLog.getLogs(ctx.subProject.id, settingType);
+                const renderData = {
+                    officeList,
+                    projectLog,
+                    settingType,
+                    logs,
+                };
+                await this.layout('sp_setting/logs.ejs', renderData);
+            } catch (error) {
+                console.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        async fun(ctx) {
+            try {
+                this.defaultCheck(ctx);
+                const funRela = await ctx.service.subProject.getFunRela(ctx.subProject);
+                const fun_set = await ctx.service.subProject.getFunSet(ctx.subProject.fun_set);
+                await this.layout('sp_setting/fun.ejs', {
+                    funRela,
+                    imType,
+                    endMonth: funSet.endMonth,
+                    funSet: fun_set,
+                }, 'sp_setting/fun_modal.ejs');
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        /**
+         * 保存功能设置相关
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async updateFun(ctx) {
+            try {
+                this.defaultCheck(ctx);
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (data) ctx.request.body = data;
+                const rule = ctx.service.subProject.rule('fun');
+                ctx.validate(rule);
+
+                const result = await ctx.service.subProject.updateFunRela(ctx.subProject, ctx.request.body);
+                if (!result) throw '保存数据失败';
+                this.ctx.session.sessionProject.page_show.openChangeProject = data.openChangeProject ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openChangeApply = data.openChangeApply ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openChangePlan = data.openChangePlan ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openChangeWhiteList = data.openChangeWhiteList ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openChangeState = data.openChangeState ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialTax = data.openMaterialTax ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialChecklist = data.openMaterialChecklist ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialSelf = data.openMaterialSelf ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openMaterialEditForAudit = data.openMaterialEditForAudit ? 1 : 0;
+                this.ctx.session.sessionProject.page_show.openStageStart = data.openStageStart ? 1 : 0;
+                const result2 = await ctx.service.subProject.updatePageshow(ctx.subProject);
+                if (!result2) throw '保存数据失败';
+                if (data.addFunSet) {
+                    const funSet = ctx.subProject.fun_set ? JSON.parse(ctx.subProject.fun_set) : {};
+                    ctx.helper._.defaultsDeep(data.addFunSet, funSet);
+                    const result3 = await ctx.service.subProject.updateFunSet(ctx.subProject, data.addFunSet);
+                    if (!result3) throw '保存数据失败';
+                }
+
+                ctx.body = {err: 0, msg: '', data: null};
+            } catch (error) {
+                ctx.helper.log(error);
+                this.ajaxErrorBody(error, '保存数据失败');
+            }
+        }
+
+        async dataCollect(ctx) {
+            try {
+                this.defaultCheck(ctx);
+                if (!ctx.session.sessionProject.page_show.openDataCollect) {
+                    throw '该功能已关闭或无法查看';
+                }
+                ctx.subProject.data_collect_pages = ctx.subProject.data_collect_pages ? ctx.subProject.data_collect_pages.split(',') : [];
+                ctx.session.sessionProject.dataCollect = ctx.subProject.data_collect;
+                if (ctx.session.sessionUser.is_admin === 0) throw '没有访问权限';
+                const dataCollectAudits = await ctx.service.datacollectAudit.getList(ctx.subProject.project_id);
+                // 获取所有项目参与者
+                const accountList = await ctx.service.projectAccount.getAllDataByCondition({
+                    where: {project_id: ctx.session.sessionProject.id, enable: 1},
+                    columns: ['id', 'name', 'company', 'role', 'enable', 'is_admin', 'account_group', 'mobile'],
+                });
+                const unitList = await ctx.service.constructionUnit.getAllDataByCondition({where: {pid: ctx.session.sessionProject.id}});
+                const accountGroupList = unitList.map(item => {
+                    const groupList = accountList.filter(item1 => item1.company === item.name);
+                    return {groupName: item.name, companyId: item.id, groupList};
+                });
+                const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
+                const tenders = await ctx.service.tender.getList('', null, 1);
+                const dcTenders = await ctx.service.datacollectTender.getList(ctx.subProject.project_id);
+                const is_dz2 = ['P0505', 'P0506', 'P1201', 'P1202', 'GY18Y', 'GYJJ1', 'P1103'].indexOf(ctx.session.sessionProject.code) !== -1 ? 6 : false;
+                const renderData = {
+                    dataCollectAudits,
+                    accountList,
+                    accountGroup: accountGroupList,
+                    categoryData,
+                    tenders,
+                    dcTenders,
+                    is_dz1: ['P0505', 'MI22U'].indexOf(ctx.session.sessionProject.code) !== -1 ? 5 : false,
+                    is_dz2,
+                };
+                if (is_dz2) {
+                    const projectData = await this.ctx.service.project.getDataById(ctx.subProject.project_id);
+                    // 获取分类及对应值
+                    const commonJson = projectData.common_json ? JSON.parse(projectData.common_json) : null;
+                    renderData.daPing06Set = commonJson && commonJson.daPing06_set ? commonJson.daPing06_set : ctx.helper._.cloneDeep(projectSettingConst.daPing06Set);
+                }
+                await this.layout('sp_setting/datacollect.ejs', renderData, 'sp_setting/datacollect_modal.ejs');
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.session.postError = error.toString();
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        async dataCollectSave(ctx) {
+            try {
+                this.defaultCheck(ctx);
+                const responseData = {
+                    err: 0, msg: '', data: null,
+                };
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type) {
+                    throw '提交数据错误';
+                }
+                switch (data.type) {
+                    case 'show':
+                        responseData.data = await ctx.service.subProject.update({
+                            data_collect: data.data_collect,
+                            data_collect_pages: data.data_collect_pages.join(','),
+                        }, {id: ctx.subProject.id});
+                        ctx.subProject.dataCollect = data.data_collect;
+                        ctx.subProject.showDataCollect = data.data_collect ? 1 : 0;
+                        break;
+                    case 'add-audit':
+                        // 判断该用户是否已加入到表中,已加入则提示无需添加
+                        const auditInfo = await ctx.service.datacollectAudit.getDataByCondition({
+                            pid: ctx.subProject.project_id,
+                            uid: data.id
+                        });
+                        if (auditInfo) {
+                            throw '该用户已存在权限中,无需重复添加';
+                        }
+                        await ctx.service.datacollectAudit.saveAudit(ctx.subProject.project_id, data.id);
+                        responseData.data = await ctx.service.datacollectAudit.getList(ctx.subProject.project_id);
+                        break;
+                    case 'add-company':
+                        const companyInfo = await ctx.service.datacollectAudit.getCompanyInfo(ctx.subProject.project_id, data.id);
+                        if (companyInfo) {
+                            throw '该单位已存在权限中,无需重复添加';
+                        }
+                        await ctx.service.datacollectAudit.saveCompany(ctx.subProject.project_id, data.id);
+                        responseData.data = await ctx.service.datacollectAudit.getList(ctx.subProject.project_id);
+                        break;
+                    case 'del-audit':
+                        const auditInfo2 = await ctx.service.datacollectAudit.getDataById(data.id);
+                        if (!auditInfo2) {
+                            throw '该用户已不存在权限中,移除失败';
+                        }
+                        await ctx.service.datacollectAudit.delAudit(data.id);
+                        responseData.data = await ctx.service.datacollectAudit.getList(ctx.subProject.project_id);
+                        break;
+                    case 'del-group':
+                        const groupInfo3 = await ctx.service.datacollectAudit.getDataById(data.id);
+                        if (!groupInfo3) {
+                            throw '该单位已不存在权限中,移除失败';
+                        }
+                        await ctx.service.datacollectAudit.delAudit(data.id);
+                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
+                        break;
+                    case 'del-company':
+                        const companyInfo2 = await ctx.service.datacollectAudit.getDataById(data.id);
+                        if (!companyInfo2) {
+                            throw '该单位已不存在权限中,移除失败';
+                        }
+                        await ctx.service.datacollectAudit.delAudit(data.id);
+                        responseData.data = await ctx.service.datacollectAudit.getList(projectId);
+                        break;
+                    case 'tender':
+                        if (ctx.session.sessionProject.page_show.addDataCollect !== parseInt(data.add_datacollect)) {
+                            ctx.session.sessionProject.page_show.addDataCollect = parseInt(data.add_datacollect);
+                            await ctx.service.project.updatePageshow(projectId);
+                        }
+                        await ctx.service.datacollectTender.updateList(projectId, data.tids);
+                        responseData.data = {
+                            dcTenders: await ctx.service.datacollectTender.getList(projectId),
+                            addDatacollect: ctx.session.sessionProject.page_show.addDataCollect,
+                        };
+                        break;
+                    case 'save-dp06':
+                        responseData.data = await ctx.service.project.saveCommonJson(projectId, 'daPing06_set', data.daPing06Set);
+                        break;
+                    default:
+                        throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
+    }
+
+    return SettingController;
+};

+ 8 - 8
app/controller/tender_controller.js

@@ -57,7 +57,7 @@ module.exports = app => {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                     t.canFinish = await this.ctx.service.tender.checkTenderCanFinish(t);
                 }
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.subProject);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 const subProject = await this.ctx.service.subProject.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin, true);
                 const renderData = {
@@ -96,7 +96,7 @@ module.exports = app => {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                     t.canFinish = await this.ctx.service.tender.checkTenderCanFinish(t);
                 }
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.subProject);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 const subProject = await this.ctx.service.subProject.getSubProject(this.ctx.session.sessionProject.id, this.ctx.session.sessionUser.accountId, this.ctx.session.sessionUser.is_admin, true);
                 const renderData = {
@@ -133,7 +133,7 @@ module.exports = app => {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                     t.canFinish = await this.ctx.service.tender.checkTenderCanFinish(t);
                 }
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.subProject);
                 renderData.valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 renderData.tenderConst = tenderConst;
                 renderData.settingConst = settingConst;
@@ -163,7 +163,7 @@ module.exports = app => {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                     t.canFinish = await this.ctx.service.tender.checkTenderCanFinish(t);
                 }
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.subProject);
                 renderData.valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 renderData.tenderConst = tenderConst;
                 renderData.settingConst = settingConst;
@@ -493,7 +493,7 @@ module.exports = app => {
                 const revise = await ctx.service.ledgerRevise.getLastestRevise(tender.id);
                 // const tenders = await ctx.service.tender.getList('', null, 1);
                 const tenders = await ctx.service.tender.getList('manage');
-                const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+                const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
 
                 // 变更图表数据
                 const change_done_total = await ctx.service.change.getCountByStatus2(tender.id, auditConst.filter.status.checked);
@@ -1001,7 +1001,7 @@ module.exports = app => {
                     return removeTenders.indexOf(n.id) !== -1;
                 });
             }
-            const categoryData = await ctx.service.category.getAllCategory(ctx.session.sessionProject.id);
+            const categoryData = await ctx.service.category.getAllCategory(ctx.subProject);
             // 是否修订中
             const lastRevise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
             const revising = (lastRevise && lastRevise.status !== auditConst.revise.status.checked) || false;
@@ -1350,7 +1350,7 @@ module.exports = app => {
                     msg: '',
                     data: { ledgerAuditConst: auditConst.ledger, stageAuditConst: auditConst.stage },
                 };
-                responseData.data.category = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                responseData.data.category = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
@@ -1417,7 +1417,7 @@ module.exports = app => {
                     msg: '',
                     data: { ledgerAuditConst: auditConst.ledger, stageAuditConst: auditConst.stage },
                 };
-                responseData.data.category = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                responseData.data.category = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 // 获取用户权限
                 const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
                 const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;

+ 1 - 1
app/controller/wap_controller.js

@@ -189,7 +189,7 @@ module.exports = app => {
                 for (const t of tenderList) {
                     await this.ctx.service.tenderCache.loadTenderCache(t, this.ctx.session.sessionUser.accountId);
                 }
-                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
                 const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
                 const renderData = {
                     tenderList,

+ 4 - 7
app/middleware/budget_check.js

@@ -23,19 +23,16 @@ module.exports = options => {
                 throw '该功能已关闭或无法查看';
             }
             // 读取标段数据
-            const id = parseInt(this.params.id);
-            if (!id) throw '参数错误';
-            this.budget = yield this.service.budget.getCurBudget(id);
+            this.budget = yield this.service.budget.getCurBudget(this.subProject.budget_id);
+            this.budget.name = this.subProject.name || '';
             if (!this.budget) throw '项目不存在';
             if (this.budget.pid !== this.session.sessionProject.id) throw '您无权查看该项目';
 
-            const subProj = yield this.service.subProject.getDataByCondition({ budget_id: this.budget.id });
-            if (subProj) this.budget.name = subProj.name || '';
             if (this.session.sessionUser.is_admin) {
                 this.budget.readOnly = false;
             } else {
                 const bp = yield this.service.subProjPermission.getBudgetUserPermission(id);
-                if (!bp) throw '您无权查看该项目';
+                if (!bp) throw '您无权查看该项目动态投资数据';
                 this.budget.readOnly = bp.budget_permission.indexOf(this.service.subProjPermission.PermissionConst.budget.edit.value) < 0;
             }
             yield next;
@@ -45,7 +42,7 @@ module.exports = options => {
                 this.ajaxErrorBody(err, '概算投资项目未知错误');
             } else {
                 this.postError(err, '概算投资项目未知错误');
-                err === '该功能已关闭或无法查看' ? this.redirect('/dashboard') : this.redirect(this.request.headers.referer);
+                err === '该功能已关闭或无法查看' ? this.redirect(`/sp/${this.subProject.id}/dashboard`) : this.redirect(this.request.headers.referer);
             }
         }
     };

+ 0 - 2
app/middleware/session_auth.js

@@ -82,11 +82,9 @@ module.exports = options => {
             // 判断是否有权限查看支付审批
             let showPayment = 0;
             if (sessionUser.is_admin) {
-                this.session.sessionProject.showSubProj = true;
                 this.session.sessionProject.showBudget = this.session.sessionProject.page_show.openBudget;
                 showPayment = this.session.sessionProject.page_show.openPayment ? 1 : 0;
             } else {
-                this.session.sessionProject.showSubProj = false;
                 this.session.sessionProject.showBudget = this.session.sessionProject.page_show.openBudget ? yield this.service.subProjPermission.showBudget(sessionUser.accountId) : false;
                 if (this.session.sessionProject.page_show.openPayment) {
                     const auditInfo = yield this.service.paymentPermissionAudit.getDataByCondition({ pid: projectData.id, uid: accountInfo.id });

+ 1 - 4
app/middleware/sub_project_check.js

@@ -19,11 +19,8 @@ module.exports = options => {
      */
     return function* subProjectCheck(next) {
         try {
-            if (!this.session.sessionProject.page_show.openFile && !this.session.sessionProject.showSubProj) {
-                throw '该功能已关闭或无法查看';
-            }
             // 读取标段数据
-            const id = this.params.id || this.query.id;
+            const id = this.tender ? this.tender.data.spid : this.params.id || this.query.id;
             if (!id) throw '参数错误';
 
             this.subProject = yield this.service.subProject.getDataById(id);

+ 8 - 2
app/middleware/url_parse.js

@@ -16,8 +16,14 @@ module.exports = options => {
         const url = urlInfo.pathname.substr(1);
         const actionInfo = url.split('/');
 
-        this.controllerName = typeof actionInfo === 'object' && actionInfo[0] !== undefined ? actionInfo[0] : '';
-        this.actionName = typeof actionInfo === 'object' && actionInfo[1] !== undefined ? actionInfo[1] : '';
+        if (actionInfo[0] === 'sp') {
+            this.isProjectController = false;
+            this.controllerName = typeof actionInfo === 'object' && actionInfo[2] !== undefined ? actionInfo[2] : '';
+            this.actionName = typeof actionInfo === 'object' && actionInfo[3] !== undefined ? actionInfo[3] : '';
+        } else {
+            this.controllerName = typeof actionInfo === 'object' && actionInfo[0] !== undefined ? actionInfo[0] : '';
+            this.actionName = typeof actionInfo === 'object' && actionInfo[1] !== undefined ? actionInfo[1] : '';
+        }
 
         this.urlInfo = urlInfo;
         // 防止为空

+ 1 - 1
app/public/js/shares/batch_import.js

@@ -68,7 +68,7 @@ const BatchImportStageGcl = function (setting) {
             if (biObj.batching) return;
 
             biObj.tender_id = biObj.setting.stageTree.nodes[0].tender_id;
-            postData('/list/load', {type: 'stageBatch', tid: biObj.tender_id}, data => {
+            postData(`/sp/${spid}/list/load`, {type: 'stageBatch', tid: biObj.tender_id}, data => {
                 biObj.history = data.history || [];
                 // 屏蔽自己
                 const curIndex = data.tenders.findIndex(x => { return x.id === biObj.tender_id });

+ 1 - 1
app/public/js/shares/tender_select.js

@@ -104,7 +104,7 @@ const TenderSelect = function (setting) {
             data[col.field] = info.sheet.getValue(info.row, info.col);
         },
         loadHistory: function () {
-            postData('/list/load', {type: tsObj.setting.type, tid: tsObj.select.tender_id, lid: tsObj.select.id}, data => {
+            postData(`/sp/${spid}/list/load`, {type: tsObj.setting.type, tid: tsObj.select.tender_id, lid: tsObj.select.id}, data => {
                 tsObj.orgHistory = data.history || {};
                 // 屏蔽自己
                 const curIndex = data.tenders.findIndex(x => { return x.id === tsObj.select.tender_id });

+ 1 - 1
app/public/js/sr_info.js

@@ -131,7 +131,7 @@ const AddRela = function (setting) {
             this.tenderSpread.bind(spreadNS.Events.ButtonClicked, tsObj.asButtonClicked);
             this.resultSpread.bind(spreadNS.Events.EditEnded, tsObj.arEditEnded);
 
-            postData('/list/load2', { type: 'stage' }, function (data) {
+            postData(`/sp/${spid}/list/load2`, { type: 'stage' }, function (data) {
                 tsObj.tenderSourceTree = Tender2Tree.convert(data.category, data.tenders, data.ledgerAuditConst, data.stageAuditConst);
                 SpreadJsObj.loadSheetData(tsObj.tenderSheet, SpreadJsObj.DataType.Tree, tsObj.tenderSourceTree);
                 SpreadJsObj.loadSheetData(tsObj.resultSheet, SpreadJsObj.DataType.Data, tsObj.trArray);

+ 17 - 9
app/public/js/sub_project.js

@@ -27,7 +27,7 @@ $(document).ready(function() {
                     }
                 } else {
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push('<a href="javascript: void(0)" name="name" id="' + node.id + '">', node.name, '</a>');
+                    html.push(`<a href="sp/${node.id}/dashboard" name="name" id="${node.id}">`, node.name, '</a>');
                 }
                 html.push('</td>');
                 // 概预算标准
@@ -36,8 +36,10 @@ $(document).ready(function() {
                 } else {
                     if (node.std_name) {
                         html.push(`<td class="text-center">${node.std_name}</td>`);
-                    } else {
+                    } else if (canEdit) {
                         html.push(`<td class="text-center"><button class="btn btn-outline-primary btn-sm ml-1" name="set-std">选择</button></td>`);
+                    } else {
+                        html.push(`<td class="text-center"></td>`);
                     }
                 }
                 // 创建时间
@@ -46,16 +48,22 @@ $(document).ready(function() {
                     html.push(`<td class="text-center"></td>`);
                 } else {
                     html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
-                    html.push(`<td class="text-center">${node.management || ''}<a class="ml-2" href="javascript: void(0)" name="set-management"><i class="fa fa-pencil-square-o "></i></a>${node.management ? '<a class="ml-2" href="javascript: void(0)" name="refresh-management"><i class="fa fa-refresh "></i></a>': ''}</td>`);
+                    if (canEdit) {
+                        html.push(`<td class="text-center">${node.management || ''}<a class="ml-2" href="javascript: void(0)" name="set-management"><i class="fa fa-pencil-square-o "></i></a>${node.management ? '<a class="ml-2" href="javascript: void(0)" name="refresh-management"><i class="fa fa-refresh "></i></a>': ''}</td>`);
+                    } else {
+                        html.push(`<td class="text-center">${node.management || ''}</td>`);
+                    }
                 }
                 // 操作
                 html.push(`<td>`);
-                html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="edit">编辑</button>');
-                html.push('<button class="btn btn-outline-danger btn-sm ml-1" name="del">删除</button>');
-                html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="up"><i class="fa fa-arrow-up"></i></button>');
-                html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="down"><i class="fa fa-arrow-down"></i></button>');
-                html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="top">顶层</button>');
-                if (!node.is_folder) html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="member">成员管理</button>');
+                if (canEdit) {
+                    html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="edit">编辑</button>');
+                    html.push('<button class="btn btn-outline-danger btn-sm ml-1" name="del">删除</button>');
+                    html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="up"><i class="fa fa-arrow-up"></i></button>');
+                    html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="down"><i class="fa fa-arrow-down"></i></button>');
+                    html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="top">顶层</button>');
+                    if (!node.is_folder) html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="member">成员管理</button>');
+                }
                 html.push('</td>');
                 return html.join('');
             },

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

@@ -407,7 +407,7 @@ $(document).ready(() => {
         createTree('treeLevel-self');
     });
     $('#set-cate-self-reset').click(function () {
-        postData('/setting/category/self-level', {self_category_level: ''}, function (rst) {
+        postData(`/sp/${spid}/setting/category/self-level`, {self_category_level: ''}, function (rst) {
             loadSelfCategoryLevel('');
             sortCategory();
             initCategoryLevelNode();
@@ -432,7 +432,7 @@ $(document).ready(() => {
         const defaultLevelStr = defaultLevel.sort((x, y) => { return x.level - y.level; }).map(x => {return x.id; }).join(',');
         const selfLevelStr = selfLevel.sort((x, y) => { return x.level - y.level; }).map(x => {return x.id; }).join(',');
         const self_category_level = selfLevelStr === defaultLevelStr ? '' : selfLevelStr;
-        postData('/setting/category/self-level', {self_category_level}, function (rst) {
+        postData(`/sp/${spid}/setting/category/self-level`, {self_category_level}, function (rst) {
             loadSelfCategoryLevel(self_category_level);
             sortCategory();
             initCategoryLevelNode();

File diff suppressed because it is too large
+ 670 - 639
app/router.js


+ 19 - 18
app/service/category.js

@@ -26,19 +26,20 @@ module.exports = app => {
         /**
          * 新增分类
          *
-         * @param {Number} pid - 项目id
+         * @param {Number} subProject - 子项目
          * @param {String} name - 分类名称
          * @param {Number} type - 分类类型
          * @returns {Promise<{pid: *, name: *, type: *}>}
          */
-        async addCategory(pid, name, type) {
-            const count = await this.count({pid: pid, name: name});
+        async addCategory(subProject, name, type) {
+            const count = await this.count({spid: subProject.id, name: name});
             if (count > 0) {
                 throw '存在同名类别';
             }
 
             const category = {
-                pid: pid,
+                pid: subProject.project_id,
+                spid: subProject.id,
                 name: name,
                 type: type,
             };
@@ -61,10 +62,10 @@ module.exports = app => {
          * @returns {Promise<*>}
          * @private
          */
-        async _upLevelCategory(pid, level) {
+        async _upLevelCategory(spid, level) {
             this.initSqlBuilder();
-            this.sqlBuilder.setAndWhere('pid', {
-                value: pid,
+            this.sqlBuilder.setAndWhere('spid', {
+                value: this.db.escape(spid),
                 operate: '='
             });
             this.sqlBuilder.setAndWhere('level', {
@@ -82,20 +83,20 @@ module.exports = app => {
         }
         /**
          * 删除分类
-         * @param {Number} pid - 项目id
+         * @param {Number} subProject - 子项目
          * @param {Number} cid - 分类id
          * @returns {Promise<void>}
          */
-        async deleteCategory(pid, cid) {
+        async deleteCategory(subProject, cid) {
             const category = await this.getDataById(cid);
-            if (category.pid !== pid) {
+            if (category.spid !== subProject.id) {
                 throw '提交数据错误';
             }
             this.transaction = await this.db.beginTransaction();
             try {
                 // 调整已用分类排序
                 if (category.level >= 1) {
-                    await this._upLevelCategory(pid, category.level);
+                    await this._upLevelCategory(subProject.spid, category.level);
                 }
                 // 删除标段分类数据
                 const tenders = await this.ctx.service.tender.getList('', null, 1);
@@ -135,13 +136,13 @@ module.exports = app => {
         /**
          * 获取项目下全部分类数据
          *
-         * @param {Number} pid - 标段id
+         * @param {Number} subProject - 子项目
          * @returns {Promise<*>}
          */
-        async getAllCategory(pid) {
-            const data = await this.getAllDataByCondition({ where: { pid } });
+        async getAllCategory(subProject) {
+            const data = await this.getAllDataByCondition({ where: { spid: subProject.id } });
             const values = await this.ctx.service.categoryValue.getAllDataByCondition({
-                where: { pid },
+                where: { spid: subProject.id },
                 orders: [['sort', 'asc'], ['id', 'asc']],
             });
             // values 按名称排序
@@ -179,13 +180,13 @@ module.exports = app => {
          * @param {Number} pid - 标段id
          * @returns {Promise<*>}
          */
-        async getListByCategoryLevel(pid) {
-            let data = await this.getAllDataByCondition({ where: { pid }, orders: [['level', 'asc']] });
+        async getListByCategoryLevel(spid) {
+            let data = await this.getAllDataByCondition({ where: { spid }, orders: [['level', 'asc']] });
             data = this._.filter(data, function(item) {
                 return item.level !== 0;
             });
             const values = await this.ctx.service.categoryValue.getAllDataByCondition({
-                where: { pid },
+                where: { spid },
                 orders: [['sort', 'asc'], ['id', 'asc']],
             });
             // values 按名称排序

+ 3 - 3
app/service/project_log.js

@@ -42,10 +42,10 @@ module.exports = app => {
          * @param {Number} type - 类型
          * @return {Promise<Array>} 日志数组
          */
-        async getLogs(pid, type = 0) {
+        async getLogs(spid, type = 0) {
             const typeSql = parseInt(type) !== 0 ? ' AND A.`type` = ' + type : '';
-            const sql = 'SELECT A.*, B.`name` as `username`, B.`mobile` FROM ?? as A LEFT JOIN ?? as B ON A.`uid` = B.`id` WHERE A.`pid` = ?' + typeSql + ' AND TO_DAYS(NOW()) - TO_DAYS(A.`create_time`) <= 30  ORDER BY A.`id` DESC';
-            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, pid];
+            const sql = 'SELECT A.*, B.`name` as `username`, B.`mobile` FROM ?? as A LEFT JOIN ?? as B ON A.`uid` = B.`id` WHERE A.`spid` = ?' + typeSql + ' AND TO_DAYS(NOW()) - TO_DAYS(A.`create_time`) <= 30  ORDER BY A.`id` DESC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, spid];
             return await this.db.query(sql, sqlParam);
         }
     }

+ 3 - 0
app/service/report_memory.js

@@ -1548,6 +1548,9 @@ module.exports = app => {
                 const cbu = await this.ctx.service.stageChange.getUsedData(tid, c.cid);
                 const curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, c.cid);
                 for (const b of cb) {
+                    b.o_qty = this.ctx.helper._.toNumber(b.oamount);
+                    b.o_tp = this.ctx.helper.round(this.ctx.helper.mul(b.o_qty, b.unit_price), this.ctx.tender.info.decimal.tp);
+
                     b.qty = this.ctx.helper._.toNumber(b.samount);
                     b.tp = this.ctx.helper.round(this.ctx.helper.mul(b.qty, b.unit_price), this.ctx.tender.info.decimal.tp);
 

+ 48 - 0
app/service/sub_project.js

@@ -8,6 +8,19 @@
  * @version
  */
 const rootId = '-1';
+const imType = require('../const/tender').imType;
+const defaultFunRela = {
+    banOver: true,
+    hintOver: true,
+    banMinusChangeBills: true,
+    minusNoValue: true,
+    lockPayExpr: false,
+    showMinusCol: true,
+    imType: imType.zl.value,
+    needGcl: false,
+};
+const funSet = require('../const/fun_set');
+const defaultFunSet = funSet.defaultInfo;
 
 module.exports = app => {
     class SubProject extends app.BaseService {
@@ -509,6 +522,41 @@ module.exports = app => {
         async getFileReference(subProject) {
             return await this.db.query(`SELECT id, name FROM zh_file_reference_list`);
         };
+
+        /**
+         * 功能设置
+         * @param id
+         * @returns {Promise<null>}
+         */
+        async getFunRela(subProject) {
+            const result = subProject.fun_rela ? JSON.parse(subProject.fun_rela) : {};
+            this.ctx.helper._.defaults(result, defaultFunRela);
+            return result;
+        }
+
+        async updateFunRela(id, data) {
+            const result = await this.db.update(this.tableName, {
+                id: id, fun_rela: JSON.stringify({
+                    banOver: data.banOver, hintOver: data.hintOver, banMinusChangeBills: data.banMinusChangeBills,
+                    imType: data.imType, needGcl: data.needGcl, minusNoValue: data.minusNoValue,
+                    lockPayExpr: data.lockPayExpr, showMinusCol: data.showMinusCol,
+                }),
+            });
+            return result.affectedRows === 1;
+        }
+
+        async getFunSet(fun_set = null) {
+            const result = fun_set ? JSON.parse(fun_set) : {};
+            this.ctx.helper._.defaults(result, defaultFunSet);
+            return result;
+        }
+
+        async updateFunSet(id, funSet) {
+            const result = await this.db.update(this.tableName, {
+                id, fun_set: JSON.stringify(funSet),
+            });
+            return result.affectedRows === 1;
+        }
     }
 
     return SubProject;

+ 12 - 6
app/service/tender.js

@@ -86,6 +86,8 @@ module.exports = app => {
          * @return {Array} - 返回标段数据
          */
         async getList(listStatus = '', permission = null, getAll = 0, buildStatusFilter = '') {
+            if (!this.ctx.subProject) return [];
+
             // 获取当前项目信息
             const session = this.ctx.session;
             let sql = '';
@@ -98,8 +100,8 @@ module.exports = app => {
                     '  FROM ?? As t ' +
                     '  Left Join ?? As pa ' +
                     '  ON t.`user_id` = pa.`id` ' +
-                    '  WHERE t.`project_id` = ? ' + buildStatusFilter + userFilter + ' ORDER BY CONVERT(t.`name` USING GBK) ASC';
-                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, session.sessionProject.id];
+                    '  WHERE t.`spid` = ? ' + buildStatusFilter + userFilter + ' ORDER BY CONVERT(t.`name` USING GBK) ASC';
+                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.subProject.id];
             } else if (getAll === 1 || (permission !== null && permission.tender !== undefined && permission.tender.indexOf('2') !== -1)) {
                 // 具有查看所有标段权限的用户查阅标段
                 sql = 'SELECT t.`id`, t.`project_id`, t.`name`, t.`status`, t.`category`, t.`ledger_times`, t.`ledger_status`, t.`measure_type`, t.`user_id`, t.`create_time`, t.`total_price`, t.`deal_tp`, t.`spid`,' +
@@ -107,8 +109,8 @@ module.exports = app => {
                     '  FROM ?? As t ' +
                     '  Left Join ?? As pa ' +
                     '  ON t.`user_id` = pa.`id` ' +
-                    '  WHERE t.`project_id` = ?' + buildStatusFilter + ' ORDER BY CONVERT(t.`name` USING GBK) ASC';
-                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, session.sessionProject.id];
+                    '  WHERE t.`spid` = ?' + buildStatusFilter + ' ORDER BY CONVERT(t.`name` USING GBK) ASC';
+                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.subProject.id];
             } else {
                 // 根据用户权限查阅标段
                 // tender 163条数据,project_account 68条数据测试
@@ -128,7 +130,7 @@ module.exports = app => {
                     '  FROM ?? As t ' +
                     '  Left Join ?? As pa ' +
                     '  ON t.`user_id` = pa.`id` ' +
-                    '  WHERE t.`project_id` = ? ' + buildStatusFilter + ' AND (' +
+                    '  WHERE t.`spid` = ? ' + buildStatusFilter + ' AND (' +
                     // 创建的标段
                     '    t.`user_id` = ?' +
                     // 参与审批 台账 的标段
@@ -160,7 +162,7 @@ module.exports = app => {
                     '    OR (t.id IN ( SELECT tt.`tid` FROM ?? AS tt WHERE tt.`user_id` = ?))' +
                     // 未参与,但可见的标段
                     ') ORDER BY CONVERT(t.`name` USING GBK) ASC';
-                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, session.sessionProject.id, session.sessionUser.accountId,
+                sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, this.ctx.subProject.id, session.sessionUser.accountId,
                     this.ctx.service.ledgerAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.stageAudit.tableName, session.sessionUser.accountId,
                     this.ctx.service.auditAss.tableName, session.sessionUser.accountId,
@@ -249,6 +251,10 @@ module.exports = app => {
             return tender;
         }
 
+        async getManageTenderList(projectId) {
+            return await this.ctx.service.tender.getAllDataByCondition({ where: { project_id: projectId }});
+        }
+
         /**
          * 新增标段
          *

+ 7 - 8
app/view/budget/sub_menu_list.ejs

@@ -1,11 +1,10 @@
-<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>
-<nav-menu title="招标预算" url="/budget/<%= ctx.budget.id %>/zb" ml="3" active="<%= ctx.url.indexOf('/zb') %>"></nav-menu>
-<!--<nav-menu title="输出报表" url="/budget/<%= ctx.budget.id %>/report" ml="3" active="<%= ctx.url.indexOf('/report') %>"></nav-menu>-->
+<nav-menu title="投资概况" url="/sp/<%= ctx.subProject.id %>/budget" ml="3" active="<%= ctx.url === `/sp/${ctx.subProject.id}/budget` ? 1 : -1 %>"></nav-menu>
+<nav-menu title="造价对比" url="/sp/<%= ctx.subProject.id %>/budget/compare" ml="3" active="<%= ctx.url.indexOf('/compare') %>"></nav-menu>
+<nav-menu title="投资估算" url="/sp/<%= ctx.subProject.id %>/budget/gu" ml="3" active="<%= ctx.url.indexOf('/gu') %>"></nav-menu>
+<nav-menu title="设计概算" url="/sp/<%= ctx.subProject.id %>/budget/gai%>" ml="3" active="<%= ctx.url.indexOf('/gai') %>"></nav-menu>
+<nav-menu title="施工图预算" url="/sp/<%= ctx.subProject.id %>/budget/yu" ml="3" active="<%= ctx.url.indexOf('/yu') %>"></nav-menu>
+<nav-menu title="招标预算" url="/sp/<%= ctx.subProject.id %>/budget/zb" ml="3" active="<%= ctx.url.indexOf('/zb') %>"></nav-menu>
+<!--<nav-menu title="输出报表" url="/sp/<%= ctx.subProject.id %>/budget/report" ml="3" active="<%= ctx.url.indexOf('/report') %>"></nav-menu>-->
 <% 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/layout/layout.ejs

@@ -80,6 +80,7 @@
     const userID = <%- ctx.session.sessionUser.accountId %>;
     let user = '<%= ctx.session.sessionUser.name %>';
     const is_debug = <%- ctx.session.sessionUser.loginStatus ? true : false %>;
+    const spid = '<%- (ctx.subProject ? ctx.subProject.id : '') %>';
 </script>
 <style type="text/css">
 

+ 11 - 8
app/view/layout/menu.ejs

@@ -10,13 +10,12 @@
             <!--系统维护信息 end-->
             <% } %>
             <% for (const index in ctx.menuList) { %>
-            <% if (ctx.menuList[index].display === undefined || !ctx.menuList[index].display) { continue } %>
-            <li <% if(ctx.controllerName === index || (ctx.controllerName === 'list' && index === 'tender') || (ctx.menuList[index].url && ctx.menuList[index].url === ctx.menu.url)) { %>class="active"<% } %>>
-                <a href="<%- (index === 'tender' ? ctx.curListUrl : ctx.menuList[index].url) %>" id="<%- 'nav_' + index%>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- ctx.menuList[index].name %>">
-                    <i class="fa <%- ctx.menuList[index].icon %>"></i>
-                    <% if (ctx.menuList[index].caption) { %>
-                    <span><%- ctx.menuList[index].caption %></span>
-                    <% } %>
+            <% const menu = ctx.menuList[index]; %>
+            <% if (!menu.display) { continue } %>
+            <li <% if(ctx.controllerName === menu.controller || (menu.controllers && menu.controllers.indexOf(ctx.controllerName) >= 0) || (menu.url && menu.url === ctx.menu.url)) { %>class="active"<% } %>>
+                <a href="<%- menu.url %>" id="<%- 'nav_' + index%>" data-toggle="tooltip" data-placement="right" title="" data-original-title="<%- menu.name %>">
+                    <i class="fa <%- menu.icon %>"></i>
+                    <% if (menu.caption) { %><span><%- menu.caption %></span><% } %>
                 </a>
             </li>
             <% } %>
@@ -27,7 +26,11 @@
             <li><a href="https://doc.zhzdwd.com/docs/yunjiliangAPI/yunjiliangAPI-1ccjk7h426enp" target="_blank" data-toggle="tooltip" data-placement="right" title="" data-original-title="数据接口">API</a></li>
         </ul>
         <ul class="nav nav-pills nav-stacked bg-nav">
-            <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="项目设置"><i class="fa fa-cogs"></i><span>项目设置</span></a></li>
+            <% if (ctx.isProjectController) { %>
+            <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="平台设置"><i class="fa fa-cogs"></i><span>平台设置</span></a></li>
+            <% } else { %>
+            <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/sp/<%- ctx.subProject.id %>/setting/category" data-toggle="tooltip" data-placement="right" title="" data-original-title="项目设置"><i class="fa fa-cogs"></i><span>项目设置</span></a></li>
+            <% } %>
         </ul>
         <div class="dropup mb-1 ml-1 mr-1">
             <a href="" class="btn btn-sm btn-light p-1 w-100" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">

+ 38 - 0
app/view/layout/project_menu.ejs

@@ -0,0 +1,38 @@
+<div class="main-nav d-flex align-items-start flex-column">
+    <div class="logo"><img class="w-100" style="padding: 5px" src="/public/css/logo.png"></div>
+    <div class="nav-top">
+        <ul class="nav nav-pills nav-stacked bg-nav">
+            <% if (maintainData.status !== maintainConst.status.notset && new Date().getTime() + (60*60*1000) > parseFloat(maintainData.maintain_time)) { %>
+            <!--系统维护信息-->
+            <li class="bg-danger">
+                <a class="text-white maintain-icon"><i class="fa fa-wrench "></i>
+                    <span class="bg-danger maintain-info">系统将于 <%- ctx.helper.dateTran(parseFloat(maintainData.maintain_time)) %> 开始停机维护<%- (maintainData.duration !== maintainConst.duration.forever ? ',持续'+ maintainConst.durationString[maintainData.duration] +'。' : '') %></span></a></li>
+            <!--系统维护信息 end-->
+            <% } %>
+            <li <% if (ctx.controllerName === 'dashboard') { %>class="active"<% } %>><a href="/dashboard" data-toggle="tooltip" data-placement="right" data-original-title="工作台"><i class="fa fa-check-square-o"></i><span>工作台</span></a></li>
+            <li <% if (ctx.controllerName === 'spshow') { %>class="active"<% } %>><a href="/spshow" data-toggle="tooltip" data-placement="right" data-original-title="数据大屏"><i class="fa fa-th-large"></i><span>数据大屏</span></a></li>
+            <li <% if (ctx.controllerName === 'subproj') { %>class="active"<% } %>><a href="/subproj" data-toggle="tooltip" data-placement="right" data-original-title="项目列表"><i class="fa fa-cubes"></i><span>项目列表</span></a></li>
+            <li <% if (ctx.controllerName === 'spgather') { %>class="active"<% } %>><a href="/spgather" data-toggle="tooltip" data-placement="right" data-original-title="项目汇总"><i class="fa fa-sitemap"></i><span>项目汇总</span></a></li>
+        </ul>
+    </div>
+    <div class="nav-bottom mt-auto">
+        <ul class="nav nav-pills nav-stacked bg-nav">
+            <li><a href="https://doc.zhzdwd.com/docs/yunjiliangAPI/yunjiliangAPI-1ccjk7h426enp" target="_blank" data-toggle="tooltip" data-placement="right" title="" data-original-title="数据接口">API</a></li>
+        </ul>
+        <ul class="nav nav-pills nav-stacked bg-nav">
+            <li <% if (ctx.controllerName === 'setting') { %>class="active"<% } %>><a href="/setting/info" data-toggle="tooltip" data-placement="right" title="" data-original-title="平台设置"><i class="fa fa-cogs"></i><span>平台设置</span></a></li>
+        </ul>
+        <div class="dropup mb-1 ml-1 mr-1">
+            <a href="" class="btn btn-sm btn-light p-1 w-100" data-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
+                <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 3 ? ctx.session.sessionUser.name.length - 3 : 0) %>
+            </a>
+            <div class="dropdown-menu">
+                <a href="/profile/info" class="dropdown-item">账号资料</a>
+                <a href="/profile/safe" class="dropdown-item">账号安全</a>
+                <div class="dropdown-divider"></div>
+                <a href="https://doc.zhzdwd.com/docs/measure" target="_blank" class="dropdown-item">用户手册</a>
+                <a href="/logout" class="dropdown-item">退出登录</a>
+            </div>
+        </div>
+    </div>
+</div>

app/view/setting/category.ejs → app/view/sp_setting/category.ejs


app/view/setting/category_modal.ejs → app/view/sp_setting/category_modal.ejs


+ 4 - 4
app/view/setting/datacollect.ejs

@@ -14,15 +14,15 @@
                 <nav class="nav nav-tabs m-3" id="tablist" role="tablist">
                     <% for (let i = 1; i <= 2; i++) { %>
                         <a class="nav-item nav-link<% if ((ctx.session.sessionProject.dataCollect === 0 && i === 1) || ctx.session.sessionProject.dataCollect === i) { %> active<% } %>" data-datacollect="<%- i %>" data-toggle="tab" href="#shujudaping-<%- i %>" role="tab">
-                            决策大屏<%- ctx.helper.transFormToChinese(i) %><% if (ctx.helper._.indexOf(projectData.data_collect_pages, i.toString()) !== -1) { %>(已开启<% if (i === ctx.session.sessionProject.dataCollect) { %>、默认<% } %>)<% } %></a>
+                            决策大屏<%- ctx.helper.transFormToChinese(i) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, i.toString()) !== -1) { %>(已开启<% if (i === ctx.session.sessionProject.dataCollect) { %>、默认<% } %>)<% } %></a>
                     <% } %>
                     <% if (is_dz1) { %>
                         <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === is_dz1) { %> active<% } %>" data-datacollect="<%- is_dz1 %>" data-toggle="tab" href="#shujudaping-<%- is_dz1 %>" role="tab">
-                            决策大屏<%- ctx.helper.transFormToChinese(is_dz1) %><% if (ctx.helper._.indexOf(projectData.data_collect_pages, is_dz1.toString()) !== -1) { %>(已开启<% if (ctx.session.sessionProject.dataCollect === is_dz1) { %>、默认<% } %>)<% } %></a>
+                            决策大屏<%- ctx.helper.transFormToChinese(is_dz1) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, is_dz1.toString()) !== -1) { %>(已开启<% if (ctx.session.sessionProject.dataCollect === is_dz1) { %>、默认<% } %>)<% } %></a>
                     <% } %>
                     <% if (is_dz2) { %>
                         <a class="nav-item nav-link<% if (ctx.session.sessionProject.dataCollect === is_dz2) { %> active<% } %>" data-datacollect="<%- is_dz2 %>" data-toggle="tab" href="#shujudaping-<%- is_dz2 %>" role="tab">
-                            决策大屏<%- ctx.helper.transFormToChinese(is_dz2) %><% if (ctx.helper._.indexOf(projectData.data_collect_pages, is_dz2.toString()) !== -1) { %>(已开启<% if (ctx.session.sessionProject.dataCollect === is_dz2) { %>、默认<% } %>)<% } %></a>
+                            决策大屏<%- ctx.helper.transFormToChinese(is_dz2) %><% if (ctx.helper._.indexOf(ctx.subProject.data_collect_pages, is_dz2.toString()) !== -1) { %>(已开启<% if (ctx.session.sessionProject.dataCollect === is_dz2) { %>、默认<% } %>)<% } %></a>
                     <% } %>
                     <div class="ml-auto">
                         <!--<div class="form-check form-check-inline">-->
@@ -67,7 +67,7 @@
 <script src="/public/js/tender_showhide.js"></script>
 <script src="/public/js/shares/tender_list_order.js"></script>
 <script>
-    let dataCollectPages = JSON.parse(unescape('<%- escape(JSON.stringify(projectData.data_collect_pages)) %>'));
+    let dataCollectPages = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.subProject.data_collect_pages)) %>'));
     let dataCollect = parseInt('<%- ctx.session.sessionProject.dataCollect %>');
     const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
     const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenders)) %>'));

app/view/setting/datacollect_modal.ejs → app/view/sp_setting/datacollect_modal.ejs


app/view/setting/fun.ejs → app/view/sp_setting/fun.ejs


app/view/setting/fun_modal.ejs → app/view/sp_setting/fun_modal.ejs


+ 1 - 1
app/view/setting/logs.ejs

@@ -11,7 +11,7 @@
                         <div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start" style="position: absolute; transform: translate3d(0px, 26px, 0px); top: 0px; left: 0px; will-change: transform;">
                             <% for (const type in projectLog.type_list) { %>
                             <% if (parseInt(type) !== settingType) { %>
-                            <a class="dropdown-item" href="/setting/logs<% if (parseInt(type) !== 0) { %>/type/<%- type %><% } %>"><%- projectLog.type_list[type].name %></a>
+                            <a class="dropdown-item" href="/sp/<%- ctx.subProject.id %>/setting/logs<% if (parseInt(type) !== 0) { %>/type/<%- type %><% } %>"><%- projectLog.type_list[type].name %></a>
                             <% } %>
                             <% } %>
                         </div>

+ 27 - 0
app/view/sp_setting/sub_menu.ejs

@@ -0,0 +1,27 @@
+<div class="panel-sidebar">
+    <div class="sidebar-title">
+        项目设置
+    </div>
+    <div class="scrollbar-auto">
+        <div class="nav-box">
+            <ul class="nav-list list-unstyled">
+                <% if (ctx.session.sessionUser.is_admin) { %>
+                <% for (const index in ctx.subMenu) { %>
+                <% if (!ctx.subMenu[index].display) continue; %>
+                <li <% if (ctx.url.indexOf(ctx.subMenu[index].url) !== -1) { %>class="active"<% } %>>
+                    <a href="<%- ctx.subMenu[index].url %>">
+                        <span><%- ctx.subMenu[index].caption %></span>
+                    </a>
+                </li>
+                <% } %>
+                <% } else { %>
+                <li <% if (ctx.url === ctx.subMenu.info.url) { %>class="active"<% } %>>
+                    <a href="<%- ctx.subMenu.info.url %>">
+                        <span><%- ctx.subMenu.info.caption %></span>
+                    </a>
+                </li>
+                <% } %>
+            </ul>
+        </div>
+    </div>
+</div>

+ 0 - 1
app/view/stage_rela/index.ejs

@@ -95,5 +95,4 @@
     </div>
 </div>
 <script>
-
 </script>

+ 3 - 3
app/view/sub_proj/index.ejs

@@ -3,13 +3,13 @@
         <div class="title-main  d-flex justify-content-between">
             <div>项目列表</div>
             <div class="d-inline-block ml-1" id="show-level"></div>
-            <% if (ctx.session.sessionUser.is_admin) { %>
             <div class="ml-auto">
+                <% if (ctx.session.sessionUser.is_admin) { %>
                 <a href="#add-folder" name="add" data-toggle="modal" data-target="#add-folder" class="btn btn-sm btn-primary">新建文件夹</a>
                 <a href="#add-project" name="add" data-toggle="modal" data-target="#add-project" class="btn btn-sm btn-primary ml-2">新建项目</a>
                 <a href="/file/template" class="btn btn-sm btn-primary ml-2">资料模板库</a>
+                <% } %>
             </div>
-            <% } %>
         </div>
     </div>
     <div class="content-wrap">
@@ -33,5 +33,5 @@
 <script>
     autoFlashHeight();
     const projectList = JSON.parse(unescape('<%- escape(JSON.stringify(projectList)) %>'));
-    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const canEdit = <%- !!ctx.session.sessionUser.is_admin %>;
 </script>

+ 0 - 1
app/view/sub_proj/sp_info_menu_list.ejs

@@ -1,3 +1,2 @@
-<nav-menu title="返回" url="/budget" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
 <nav-menu title="项目信息" url="/sp/<%- ctx.subProject.id %>/info" ml="3" active="<%= (ctx.url.indexOf('info') > 0 ? 1 : -1) %>"></nav-menu>
 <nav-menu title="数据指标" url="/sp/<%- ctx.subProject.id %>/data" ml="3" active="<%= (ctx.url.indexOf('data') > 0 ? 1 : -1) %>"></nav-menu>

+ 2 - 16
app/view/tender/manage_modal.ejs

@@ -10,15 +10,7 @@
                     <label>标段名称<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm"  placeholder="输入标段名称" type="text" name="name">
                 </div>
-                <div class="form-group">
-                    <label>所属项目</label>
-                    <select class="form-control form-control-sm" name="spid">
-                        <option value="">无</option>
-                        <% for (const sp of subProject) { %>
-                        <option value="<%- sp.id %>"><%- sp.name %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <input type="hidden" name="spid" value="<%- ctx.subProject.id %>">
                 <div class="form-group">
                     <style>
                         .tooltip-inner {
@@ -55,13 +47,7 @@
                     <label>标段名称<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm"  placeholder="输入标段名称" type="text" name="name">
                 </div>
-                <label>所属项目</label>
-                <select class="form-control form-control-sm" name="spid">
-                    <option value="">无</option>
-                    <% for (const sp of subProject) { %>
-                    <option value="<%- sp.id %>"><%- sp.name %></option>
-                    <% } %>
-                </select>
+                <input type="hidden" name="spid" value="<%- ctx.subProject.id %>">
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>

+ 1 - 9
app/view/tender/modal.ejs

@@ -10,15 +10,7 @@
                     <label>标段名称<b class="text-danger">*</b></label>
                     <input class="form-control form-control-sm"  placeholder="输入标段名称" type="text" name="name">
                 </div>
-                <div class="form-group">
-                    <label>所属项目</label>
-                    <select class="form-control form-control-sm" name="spid">
-                        <option value="">无</option>
-                        <% for (const sp of subProject) { %>
-                        <option value="<%- sp.id %>"><%- sp.name %></option>
-                        <% } %>
-                    </select>
-                </div>
+                <input type="hidden" name="spid" value="<%- ctx.subProject.id %>">
                 <div class="form-group">
                     <style>
                         .tooltip-inner {

+ 2 - 2
app/view/tender/sub_memu_finish.ejs

@@ -4,11 +4,11 @@
             <div class="d-inline-block" id="show-level"></div>
             <div class="d-inline-block">
                 <div class="btn-group btn-group-toggle group-tab" data-toggle="buttons">
-                    <label class="btn btn-sm btn-light <% if (ctx.url === '/list/info/finish') { %>active<% } %>" onclick="window.location.href='/list/info/finish'">
+                    <label class="btn btn-sm btn-light <% if (ctx.url === `/sp/${ctx.subProject.id}/list/info/finish`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list/info/finish'">
                         <input type="radio" name="options" id="option1" autocomplete="off"> 金额概况
                     </label>
                     <% if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) { %>
-                    <label class="btn btn-sm btn-light  <% if (ctx.url === '/list/manage/finish') { %>active<% } %>" onclick="window.location.href='/list/manage/finish'">
+                    <label class="btn btn-sm btn-light  <% if (ctx.url === `/sp/${ctx.subProject.id}/list/manage/finish`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list/manage/finish'">
                         <input type="radio" name="options" id="option2" autocomplete="off"> 管理标段
                     </label>
                     <% } %>

+ 7 - 7
app/view/tender/sub_menu.ejs

@@ -11,17 +11,17 @@
             <div class="d-inline-block" id="show-level"></div>
             <div class="d-inline-block">
                 <div class="btn-group btn-group-toggle group-tab" data-toggle="buttons">
-                    <label class="btn btn-sm btn-light <% if (ctx.url === '/list') { %>active<% } %>" onclick="window.location.href='/list'">
+                    <label class="btn btn-sm btn-light <% if (ctx.url === `/sp/${ctx.subProject.id}/list`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list'">
                         <input type="radio" name="options" id="option1" autocomplete="off"> 标段列表
                     </label>
-                    <label class="btn btn-sm btn-light <% if (ctx.url === '/list/info') { %>active<% } %>" onclick="window.location.href='/list/info'">
+                    <label class="btn btn-sm btn-light <% if (ctx.url === `/sp/${ctx.subProject.id}/list/info`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list/info'">
                         <input type="radio" name="options" id="option1" autocomplete="off"> 金额概况
                     </label>
-                    <label class="btn btn-sm btn-light  <% if (ctx.url === '/list/progress') { %>active<% } %>" onclick="window.location.href='/list/progress'">
+                    <label class="btn btn-sm btn-light  <% if (ctx.url === `/sp/${ctx.subProject.id}/list/progress`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list/progress'">
                         <input type="radio" name="options" id="option2" autocomplete="off"> 计量进度
                     </label>
                     <% if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) { %>
-                    <label class="btn btn-sm btn-light  <% if (ctx.url === '/list/manage') { %>active<% } %>" onclick="window.location.href='/list/manage'">
+                    <label class="btn btn-sm btn-light  <% if (ctx.url === `/sp/${ctx.subProject.id}/list/manage`) { %>active<% } %>" onclick="window.location.href='/sp/<%= ctx.subProject.id %>/list/manage'">
                         <input type="radio" name="options" id="option2" autocomplete="off"> 管理标段
                     </label>
                     <% } %>
@@ -30,7 +30,7 @@
             <!--<div class="d-inline-block ml-3">-->
                 <!--<div class="form-check-inline">-->
                     <!--<label class="form-check-label" onclick="window.location.href='/list/manage'">-->
-                        <!--<input class="form-check-input" type="checkbox" <% if (ctx.url === '/list/manage') { %>checked="checked"<% } %>>-->
+                        <!--<input class="form-check-input" type="checkbox" <% if (ctx.url === `/sp/${ctx.subProject.id}/list/manage`) { %>checked="checked"<% } %>>-->
                         <!--管理标段-->
                     <!--</label>-->
                 <!--</div>-->
@@ -40,12 +40,12 @@
             <% 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 === '/list/info') { %>
+            <% 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>
-            <a href="/list/info/finish" class="btn btn-sm btn-primary pull-right ml-1" target="_blank">完工标段</a>
+            <a href="/sp/<%= ctx.subProject.id %>/list/info/finish" class="btn btn-sm btn-primary pull-right ml-1" target="_blank">完工标段</a>
             <% if (userPermission !== null && userPermission.tender !== undefined && userPermission.tender.indexOf('1') !== -1) { %>
             <a href="#add-bd" name="add" data-toggle="modal" data-target="#add-bd" class="btn btn-sm btn-primary pull-right">新建标段</a>
             <% } %>

+ 1 - 1
config/config.default.js

@@ -43,7 +43,7 @@ module.exports = appInfo => {
     };
 
     // should change to your own
-    config.keys = appInfo.name + '_1503910434503_882';
+    config.keys = 'calculation_1503910434503_882';
 
     // view相关
     config.view = {

+ 103 - 44
config/menu.js

@@ -8,38 +8,104 @@
  * @version
  */
 
+
+const projectMenu = {
+    dashboard: {
+        name: '工作台',
+        icon: 'fa-check-square-o',
+        display: true,
+        url: '/dashboard',
+        children: null,
+        caption: '工作台',
+        controller: 'dashboard',
+    },
+    dataview: {
+        name: '数据大屏',
+        icon: 'fa-th-large',
+        display: true,
+        url: '/dataview',
+        children: null,
+        caption: '数据大屏',
+        controller: 'dataview',
+    },
+    subproj: {
+        name: '项目列表',
+        icon: 'fa-cubes',
+        display: true,
+        url: '/subproj',
+        children: null,
+        caption: '项目列表',
+        controller: 'subproj',
+    },
+    spgather: {
+        name: '项目汇总',
+        icon: 'fa-sitemap',
+        display: true,
+        url: '/spgather',
+        children: null,
+        caption: '项目汇总',
+        controller: 'spgather',
+    },
+    payment: {
+        name: '支付审批',
+        icon: 'fa-handshake-o',
+        display: true,
+        url: '/payment',
+        caption: '支付审批',
+        children: null,
+        controller: 'payment',
+    },
+    management: {
+        name: '项目管理系统',
+        icon: 'fa-cubes',
+        display: false,
+        caption: '项目管理系统',
+        children: null,
+    },
+};
+
 const menu = {
+    back: {
+        name: '项目列表',
+        icon: 'fa-cubes',
+        display: true,
+        url: '/subproj',
+        children: null,
+        caption: '项目列表',
+        controller: 'dashboard',
+    },
     dashboard: {
         name: '待办事项',
         icon: 'fa-check-square-o',
         display: true,
-        url: '/dashboard',
         children: null,
         caption: '待办',
+        controller: 'dashboard',
     },
     datacollect: {
         name: '决策大屏',
         icon: 'fa-th-large',
         display: true,
-        url: '/datacollect',
         children: null,
         caption: '决策大屏',
+        controller: 'datacollect',
+    },
+    info: {
+        name: '项目概况',
+        icon: 'fa-tags',
+        display: true,
+        children: null,
+        caption: '项目概况',
+        controller: 'info',
     },
     tender: {
         name: '标段管理',
         icon: 'fa-list-ul',
         display: true,
-        url: '/list',
         children: null,
         caption: '标段管理',
-    },
-    subproj: {
-        name: '项目管理',
-        icon: 'fa-tags',
-        display: true,
-        url: '/subproj',
-        children: null,
-        caption: '项目管理',
+        controller: 'list',
+        controllers: ['list', 'tender'],
     },
     contract: {
         name: '合同管理',
@@ -48,30 +114,23 @@ const menu = {
         url: '/contract',
         children: null,
         caption: '合同管理',
+        controller: 'contract',
     },
     file: {
         name: '资料归集',
         icon: 'fa-file-zip-o',
         display: true,
-        url: '/file',
         children: null,
         caption: '资料归集',
+        controller: 'file',
     },
     budget: {
         name: '动态投资',
         icon: 'fa-pie-chart',
         display: true,
-        url: '/budget',
         children: null,
         caption: '动态投资',
-    },
-    payment: {
-        name: '支付审批',
-        icon: 'fa-handshake-o',
-        display: true,
-        url: '/payment',
-        caption: '支付审批',
-        children: null,
+        controller: 'budget',
     },
     construction: {
         name: '施工日志',
@@ -80,6 +139,7 @@ const menu = {
         url: '/construction',
         caption: '施工日志',
         children: null,
+        controller: 'construction',
     },
     financial: {
         name: '资金监管',
@@ -88,13 +148,7 @@ const menu = {
         url: '/financial',
         caption: '资金监管',
         children: null,
-    },
-    management: {
-        name: '项目管理系统',
-        icon: 'fa-cubes',
-        display: false,
-        caption: '项目管理系统',
-        children: null,
+        controller: 'financial',
     },
     // sum: {
     //     name: '总分包',
@@ -328,11 +382,11 @@ const settingMenu = {
         url: '/setting/user',
         caption: '账号设置',
     },
-    fun: {
-        name: '功能设置',
-        display: true,
-        url: '/setting/fun',
-        caption: '功能设置',
+    s2b: {
+        name: '接口设置',
+        display: false,
+        url: '/setting/api',
+        caption: '接口设置',
     },
     show: {
         name: '显示设置',
@@ -340,17 +394,26 @@ const settingMenu = {
         url: '/setting/show',
         caption: '显示设置',
     },
+    manage: {
+        name: '标段管理',
+        display: false,
+        url: '/setting/manage',
+        caption: '标段管理',
+    },
+};
+
+const projectSettingMenu = {
     category: {
         name: '标段自定义类别',
         display: true,
         url: '/setting/category',
         caption: '标段自定义类别',
     },
-    s2b: {
-        name: '接口设置',
-        display: false,
-        url: '/setting/api',
-        caption: '接口设置',
+    fun: {
+        name: '功能设置',
+        display: true,
+        url: '/setting/fun',
+        caption: '功能设置',
     },
     log: {
         name: '操作日志',
@@ -364,12 +427,6 @@ const settingMenu = {
         url: '/setting/datacollect',
         caption: '决策大屏',
     },
-    manage: {
-        name: '标段管理',
-        display: false,
-        url: '/setting/manage',
-        caption: '标段管理',
-    },
 };
 
 const profileMenu = {
@@ -406,10 +463,12 @@ const profileMenu = {
 };
 
 module.exports = {
+    projectMenu,
     menu,
     tenderMenu,
     stageMenu,
     sumMenu,
     settingMenu,
+    projectSettingMenu,
     profileMenu,
 };

db_script/audit_order.js → db_script/bak/audit_order.js


db_script/belong_spid.js → db_script/bak/belong_spid.js


db_script/budget_zb.js → db_script/bak/budget_zb.js


db_script/change.js → db_script/bak/change.js


db_script/change_audit_order.js → db_script/bak/change_audit_order.js


db_script/change_bills.js → db_script/bak/change_bills.js


db_script/change_valuation.js → db_script/bak/change_valuation.js


db_script/checkStageFinal.js → db_script/bak/checkStageFinal.js


db_script/depart-database-table.js → db_script/bak/depart-database-table.js


db_script/depart_table.js → db_script/bak/depart_table.js


db_script/ledger_his.js → db_script/bak/ledger_his.js


db_script/ledger_his_count.js → db_script/bak/ledger_his_count.js


db_script/material_audit_order.js → db_script/bak/material_audit_order.js


db_script/material_rate_tp.js → db_script/bak/material_rate_tp.js


db_script/minus_no_value.js → db_script/bak/minus_no_value.js


db_script/pay_att.js → db_script/bak/pay_att.js


db_script/pay_order.js → db_script/bak/pay_order.js


db_script/project_spread.js → db_script/bak/project_spread.js


db_script/recover_ledger_his.js → db_script/bak/recover_ledger_his.js


db_script/repair1027.js → db_script/bak/repair1027.js


db_script/stage-change-final.js → db_script/bak/stage-change-final.js


db_script/stage_pay_recover.js → db_script/bak/stage_pay_recover.js


db_script/stage_stash.js → db_script/bak/stage_stash.js


+ 75 - 0
db_script/bak/sub_project.js

@@ -0,0 +1,75 @@
+const BaseUtil = require('./baseUtils');
+const querySql = BaseUtil.querySql;
+const uuid = require('node-uuid');
+
+const getInsertSql = function (tableName, data) {
+    const column = [], query = [], value = [];
+    for (const prop in data) {
+        column.push(prop);
+        query.push('?');
+        value.push(data[prop]);
+    }
+    return [`INSERT INTO ${tableName} (${column.join(',')}) VALUES (${query.join(',')})`, value]
+};
+
+const syncBudget = async function(budget, order) {
+    try {
+        const subProject = {
+            id: uuid.v4(), project_id: budget.pid, tree_pid: '-1', tree_level: 1, tree_order: order,
+            name: budget.name, user_id: budget.user_id, rela_tender: budget.rela_tender,
+            is_folder: 0, budget_id: budget.id,
+        };
+        const [subProjSql, subProjSqlParam] = getInsertSql('zh_sub_project', subProject);
+        await querySql(subProjSql, subProjSqlParam);
+        const permission = await querySql('SELECT * FROM zh_budget_permission WHERE bid = ?', [budget.id]);
+        for (const p of permission) {
+            const np = {
+                id: uuid.v4(), spid: subProject.id, pid: budget.pid, uid: p.uid,
+                budget_permission: p.permission, manage_permission: '1'
+            };
+            const [pSql, pSqlParam] = getInsertSql('zh_sub_project_permission', np);
+            await querySql(pSql, pSqlParam);
+        }
+    } catch (err) {
+        console.log(err);
+    }
+};
+
+const doComplete = async function() {
+    try {
+        const project = await querySql('Select * From zh_project');
+        for (const p of project) {
+            console.log(`Update Project ${p.code}(${p.id}):`);
+            const budget = await querySql('SELECT * FROM zh_budget where pid = ?', [p.id]);
+            for (const [i, b] of budget.entries()) {
+                console.log(`Sync Budget ${b.name}(${b.id})`);
+                await syncBudget(b, i + 1);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+const doCompleteTest = async function(code) {
+    try {
+        const project = await querySql('Select * From zh_project where code = ?', [code]);
+        for (const p of project) {
+            console.log(`Update Project ${p.code}(${p.id}):`);
+            const budget = await querySql('SELECT * FROM zh_budget where pid = ?', [p.id]);
+            for (const [i, b] of budget.entries()) {
+                console.log(`Sync Budget ${b.name}(${b.id})`);
+                await syncBudget(b, i + 1);
+            }
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+const projectCode = process.argv[3];
+if (projectCode) {
+    doCompleteTest(projectCode);
+} else {
+    doComplete()
+}

db_script/tender_cache.js → db_script/bak/tender_cache.js


db_script/update_revise.js → db_script/bak/update_revise.js


+ 98 - 46
db_script/sub_project.js

@@ -1,3 +1,12 @@
+/*
+ 修改脚本,请现在指定项目测试
+ 例如: node db_script/sub_project local T201711273363
+ 没有意外再全部执行
+
+ 所有修改均应考虑脚本二次执行时的兼容,应检查是否已执行过,避免在生产环境运行时出现问题后需要二次执行,参见自定义分类脚本
+  */
+
+
 const BaseUtil = require('./baseUtils');
 const querySql = BaseUtil.querySql;
 const uuid = require('node-uuid');
@@ -12,55 +21,102 @@ const getInsertSql = function (tableName, data) {
     return [`INSERT INTO ${tableName} (${column.join(',')}) VALUES (${query.join(',')})`, value]
 };
 
-const syncBudget = async function(budget, order) {
-    try {
-        const subProject = {
-            id: uuid.v4(), project_id: budget.pid, tree_pid: '-1', tree_level: 1, tree_order: order,
-            name: budget.name, user_id: budget.user_id, rela_tender: budget.rela_tender,
-            is_folder: 0, budget_id: budget.id,
-        };
-        const [subProjSql, subProjSqlParam] = getInsertSql('zh_sub_project', subProject);
-        await querySql(subProjSql, subProjSqlParam);
-        const permission = await querySql('SELECT * FROM zh_budget_permission WHERE bid = ?', [budget.id]);
-        for (const p of permission) {
-            const np = {
-                id: uuid.v4(), spid: subProject.id, pid: budget.pid, uid: p.uid,
-                budget_permission: p.permission, manage_permission: '1'
-            };
-            const [pSql, pSqlParam] = getInsertSql('zh_sub_project_permission', np);
-            await querySql(pSql, pSqlParam);
-        }
-    } catch (err) {
-        console.log(err);
+const createDefaultSubProject = async function (project) {
+    console.log('Insert deafult sub_project');
+    const users = await querySql('SELECT * FROM zh_project_account where project_id = ?', [project.id]);
+    const manager = users.find(x => { return x.is_admin; });
+    const subProject = { id: uuid.v4(), project_id: project.id, tree_pid: '-1', tree_order: 1, tree_level: 1, name: project.name, user_id: manager ? manager.id : 0};
+    const [sql, sqlParam] = getInsertSql('zh_sub_project', subProject);
+    await querySql(sql, sqlParam);
+    // 所有标段归属到改项目下
+    await querySql('UPDATE zh_tender SET spid = ? WHERE project_id = ?', [subProject.id, project.id]);
+    // 项目下所有用户都在子项目下新增用户权限
+    console.log('Insert default sub_project user permission');
+    for (const u of users) {
+        if (manager && u.id === manager.id) continue;
+        const sp_permission = { id: uuid.v4(), spid: subProject.id, pid: project.id, uid: u.id, self_category_level: u.self_category_level};
+        const [spSql, spSqlParam] = getInsertSql('zh_sub_project_permission', sp_permission);
+        await querySql(spSql, spSqlParam);
     }
 };
 
-const doComplete = async function() {
+const doComplete = async function(code) {
     try {
-        const project = await querySql('Select * From zh_project');
+        const filter = code ? ` where code = '${code}'` : '';
+        const project = await querySql('Select * From zh_project' + filter);
         for (const p of project) {
             console.log(`Update Project ${p.code}(${p.id}):`);
-            const budget = await querySql('SELECT * FROM zh_budget where pid = ?', [p.id]);
-            for (const [i, b] of budget.entries()) {
-                console.log(`Sync Budget ${b.name}(${b.id})`);
-                await syncBudget(b, i + 1);
+            const existSubProj = await querySql('SELECT * FROM zh_sub_project where project_id = ? and is_folder = 0 and is_delete = 0;', [p.id]);
+            if (existSubProj.length === 0) {
+                console.log(`DELETE empty sub_project data`);
+                // 删除旧数据(即使有数据,数据应该都是文件夹,应清理掉)
+                await querySql('Update zh_sub_project SET is_delete = 1 where project_id = ?', [p.id]);
+                // 创建默认数据
+                await createDefaultSubProject(p);
+            } else {
+                console.log('Exist sub_project');
             }
-        }
-    } catch (err) {
-        console.log(err);
-    }
-    BaseUtil.closePool();
-};
-const doCompleteTest = async function(code) {
-    try {
-        const project = await querySql('Select * From zh_project where code = ?', [code]);
-        for (const p of project) {
-            console.log(`Update Project ${p.code}(${p.id}):`);
-            const budget = await querySql('SELECT * FROM zh_budget where pid = ?', [p.id]);
-            for (const [i, b] of budget.entries()) {
-                console.log(`Sync Budget ${b.name}(${b.id})`);
-                await syncBudget(b, i + 1);
+
+            // 初始化所有subProject的项目配置 todo
+            // 拷贝显示设置page_show, 功能设置fun_rela,决策大屏设置data_collect data_collect_pages
+            // page_show可以后台设置,如果迁移至子项目下,后台已有的功能设置将作废,需要做针对子项目的项目设置
+            // todo rpt_authority rpt_items rpt_level rpt_nature 是否拷贝?
+            // 拷贝sql必须放在这里,不能放在update.sql,必须先执行上一步创建默认子项目
+            console.log('Copy sub_project properties: page_show, fun_rela, data_collect, data_collect_pages');
+            await querySql('Update zh_sub_project sp LEFT JOIN zh_project p ON sp.project_id = p.id SET sp.page_show = p.page_show, sp.fun_rela = p.fun_rela, sp.data_collect = p.data_collect, sp.data_collect_pages = p.data_collect_pages WHERE sp.is_folder = 0 and is_delete = 0; ');
+            // 拷贝标段自定义类别设置
+            const subProj = await querySql('SELECT * FROM zh_sub_project where project_id = ? and is_folder = 0 and is_delete = 0;', [p.id]);
+            // 不使用Insert Into SELECT, 避免死锁
+            const category = await querySql('SELECT * FROM zh_category where pid = ? and spid = ?', [p.id, '']);
+            const categoryValue = await querySql('SELECT * FROM zh_category_value where pid = ? and spid = ?', [p.id, '']);
+            for (const c of category) {
+                c.value = categoryValue.filter(x => { return x.cid === c.id; });
+            }
+            for (const sp of subProj) {
+                console.log(`Copy sub_project ${sp.name}(${sp.id}) category`);
+                // 存在分类信息跳过 --- 防止脚本执行问题,需要重复执行
+                const existCate = await querySql('SELECT * FROM zh_category where pid = ? and spid = ?', [p.id, sp.id]);
+                if (existCate.length > 0) continue;
+
+                const copyCategory = [];
+                for (const cate of category) {
+                    const copyCate = { pid: cate.pid, spid: sp.id, name: cate.name, type: cate.type, level: cate.level };
+                    const [cateSql, cateSqlParam] = getInsertSql('zh_category', copyCate);
+                    const insertResult = await querySql(cateSql, cateSqlParam);
+                    copyCate.id = insertResult.insertId;
+                    copyCate.orgId = cate.id;
+                    copyCate.value = [];
+                    for (const value of cate.value) {
+                        const copyValue = { pid: value.pid, spid: sp.id, cid: insertResult.insertId, value: value.value, sort: value.sort};
+                        const [cateSql, cateSqlParam] = getInsertSql('zh_category_value', copyValue);
+                        const insertResultValue = await querySql(cateSql, cateSqlParam);
+                        copyValue.id = insertResultValue.insertId;
+                        copyValue.orgId = value.id;
+                        copyCate.value.push(copyValue);
+                    }
+                    copyCategory.push(copyCate);
+                }
+
+                const tenders = await querySql('SELECT * FROM zh_tender WHERE spid = ?', [sp.id]);
+                for (const t of tenders) {
+                    const tCategory = t.category ? JSON.parse(t.category) : null;
+                    const newTCateGory = [];
+                    if (tCategory) {
+                        for (const tCate of tCategory) {
+                            const cate = copyCategory.find(x => { return x.orgId === tCate.cid; });
+                            if (!cate) continue;
+                            const value = cate.value.find(x => { return x.orgId === tCate.value; });
+                            if (!value) continue;
+                            newTCateGory.push({ cid: cate.id, value: value.id});
+                        }
+                        await querySql('UPDATE zh_tender SET category = ? WHERE id = ?', [JSON.stringify(newTCateGory), t.id]);
+                    }
+                }
+
+                // await querySql('INSERT INTO zh_category (pid, spid, name, type, level) SELECT pid, ?, name, type, level FROM zh_category WHERE pid = ? and spid = ?', [sp.id, p.id, '']);
+                // await querySql('INSERT INTO zh_category_value (pid, spid, cid, value, sort) SELECT pid, ?, cid, value, sort FROM zh_category_value WHERE pid = ? and spid = ?', [sp.id, p.id, '']);
             }
+            console.log('END Update;');
         }
     } catch (err) {
         console.log(err);
@@ -68,8 +124,4 @@ const doCompleteTest = async function(code) {
     BaseUtil.closePool();
 };
 const projectCode = process.argv[3];
-if (projectCode) {
-    doCompleteTest(projectCode);
-} else {
-    doComplete()
-}
+doComplete(projectCode);

+ 14 - 153
sql/update.sql

@@ -1,172 +1,32 @@
 -- 请按如下分类提交sql!!!
 -- Version V3.5.47.0141
+-- uat 2025-1-16/20
 -- prod todo
 
 ------------------------------------
 -- 表结构
 ------------------------------------
 
-ALTER TABLE `zh_tender`
-ADD COLUMN `c_mode` tinyint(1) NULL DEFAULT 0 COMMENT '变更清单模式' AFTER `c_rule_first`;
-
-ALTER TABLE `zh_change_ledger`
-ADD COLUMN `features` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT '' COMMENT '项目特征' AFTER `memo`;
-
 ALTER TABLE `zh_sub_project`
-ADD COLUMN `lock_file` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '资料归集,是否锁定' AFTER `filing_template_name`;
-
-ALTER TABLE `zh_filing_template_list`
-ADD COLUMN `is_share` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否分享' AFTER `memo`;
-
-CREATE TABLE `zh_phase_pay`  (
-  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `phase_order` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '期序号',
-  `phase_date` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付年月',
-  `create_user_id` int(11) NOT NULL COMMENT '创建人id',
-  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `update_user_id` int(10) UNSIGNED NOT NULL COMMENT '最后修改人id',
-  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
-  `rela_stage` json NOT NULL COMMENT '关联期信息(json)--[{sid: int, sorder: int}]',
-  `calc_base` json NOT NULL COMMENT '计算基数',
-  `calc_base_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '计算基数缓存时间',
-  `memo` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
-  `audit_times` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批轮次',
-  `audit_status` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批状态',
-  `audit_max_sort` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大审批排序',
-  `audit_begin_time` timestamp NULL DEFAULT NULL COMMENT '审批开始时间',
-  `audit_end_time` timestamp NULL DEFAULT NULL COMMENT '审批结束时间',
-  `final_auditor_str` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '终审缓存信息',
-  `calc_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期计量=工程计量款',
-  `pay_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期付款=工程计量款+其他付款项',
-  `cut_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期扣款=其他扣款项',
-  `yf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期应付',
-  `sf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期实付',
-  `pre_calc_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期计量',
-  `pre_pay_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期付款',
-  `pre_cut_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期扣款',
-  `pre_yf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期应付',
-  `pre_sf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期实付',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-
-CREATE TABLE `zh_phase_pay_audit`  (
-  `id` bigint(20) NOT NULL AUTO_INCREMENT,
-  `tid` int(11) UNSIGNED NOT NULL COMMENT '标段id',
-  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '合同支付期id',
-  `audit_id` int(11) UNSIGNED NOT NULL COMMENT '流程参与人id(含原报)',
-  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-姓名',
-  `company` varchar(60) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-单位',
-  `role` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-角色',
-  `mobile` varchar(15) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-电话',
-  `audit_times` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批次数',
-  `audit_order` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '预定流程顺序',
-  `audit_type` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '流程类型',
-  `active_order` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '实际流程顺序',
-  `audit_status` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '流程状态',
-  `audit_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '流程结束时间',
-  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
-  `opinion` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '意见',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+ADD COLUMN `page_show` varchar(5000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT '{\"bwtz\":\"1\"}' COMMENT '前台页面或功能展示与隐藏' AFTER `lock_file`,
+ADD COLUMN `fun_rela` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '功能设置(json.stringify)' AFTER `page_show`,
+ADD COLUMN `data_collect` tinyint(1) NOT NULL DEFAULT 0 COMMENT '决策大屏是否显示及对应大屏编号' AFTER `fun_rela`,
+ADD COLUMN `data_collect_pages` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '需要展示的大屏字符串,以,分隔' AFTER `data_collect`;
 
-CREATE TABLE `zh_phase_pay_detail`  (
-  `id` int(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
-  `uuid` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
-  `tid` int(11) NOT NULL COMMENT '标段id',
-  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '合同支付期id',
-  `create_phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '创建期id',
-  `create_phase_order` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '创建期序号',
-  `master_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '拼接phase_id-audit_times-audit_sort',
-  `tree_id` int(11) UNSIGNED NOT NULL COMMENT '树结构-id',
-  `tree_pid` int(11) NOT NULL COMMENT '树结构-父id',
-  `tree_order` int(11) UNSIGNED NOT NULL COMMENT '树结构-排序',
-  `tree_level` int(4) UNSIGNED NOT NULL COMMENT '树结构-层级',
-  `tree_is_leaf` tinyint(4) UNSIGNED NOT NULL COMMENT '树结构-是否子项',
-  `tree_full_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '树结构-完整路径',
-  `audit_times` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批轮次',
-  `audit_sort` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批流程',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
-  `create_user_id` int(11) UNSIGNED NOT NULL COMMENT '创建人id',
-  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `update_user_id` int(11) UNSIGNED NOT NULL COMMENT '最后修改人id',
-  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
-  `is_minus` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否扣款',
-  `is_fixed` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否固定项',
-  `is_deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除',
-  `is_pause` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否暂停计量',
-  `is_gather` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否汇总计算',
-  `pay_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '支付类型',
-  `start_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '起扣金额',
-  `start_expr` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '起扣金额-计算式',
-  `range_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '扣款限额',
-  `range_expr` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '扣款限额-计算式',
-  `expr` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '本期金额-计算式',
-  `tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期金额',
-  `pre_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期金额',
-  `pre_used` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '上期是否已计量',
-  `pre_finished` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '上期是否已计完',
-  `end_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止本期金额',
-  `postil` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '本期批注',
-  `dl_value` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '计提期限-限制',
-  `dl_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '计提期限-类型',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+ALTER TABLE `zh_category`
+ADD COLUMN `spid` varchar(36) NOT NULL DEFAULT '' COMMENT '子项目id(uuid)' AFTER `pid`;
 
-CREATE TABLE `zh_phase_pay_file`  (
-  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
-  `tid` int(11) UNSIGNED NOT NULL COMMENT '标段id',
-  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '合同支付期phase_id(zh_phase_pay.id)',
-  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '类型(pay/...)',
-  `rela_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid(zh_phase_pay_detail.uuid/...)',
-  `filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名',
-  `fileext` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
-  `filesize` int(11) NOT NULL COMMENT '文件大小',
-  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
-  `user_id` int(11) UNSIGNED NOT NULL COMMENT '用户id(zh_project_account.id)',
-  `user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '用户名(缓存)',
-  `user_company` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '公司(缓存)',
-  `user_role` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '角色(缓存)',
-  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
-  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
-  `is_deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+ALTER TABLE `zh_category_value`
+ADD COLUMN `spid` varchar(36) NOT NULL DEFAULT '' COMMENT '子项目id(uuid)' AFTER `pid`;
 
-CREATE TABLE `zh_file_reference_list`  (
-  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '自增id',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '名称',
-  `create_time` int(10) NOT NULL COMMENT '创建时间',
-  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '标准清单列表' ROW_FORMAT = Dynamic;
+ALTER TABLE `zh_sub_project_permission`
+ADD COLUMN `self_category_level` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' AFTER `update_time`;
 
-CREATE TABLE `zh_file_reference`  (
-  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
-  `list_id` int(8) NOT NULL COMMENT '列表id',
-  `template_id` int(8) NOT NULL,
-  `pid` int(10) NOT NULL COMMENT '父id',
-  `order` tinyint(4) NOT NULL COMMENT '同级排序',
-  `level` tinyint(2) NOT NULL COMMENT '层级',
-  `full_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '层级定位辅助字段parent.full_path.ledger_id',
-  `is_leaf` tinyint(1) NOT NULL COMMENT '是否叶子节点,界面显示辅助字段',
-  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
-  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注',
-  `node_type` int(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '节点类别',
-  PRIMARY KEY (`id`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '标段项目节点模板数据表' ROW_FORMAT = Dynamic;
+ALTER TABLE `calculation`.`zh_project_log`
+ADD COLUMN `spid` varchar(36) NOT NULL DEFAULT '' COMMENT '操作子项目' AFTER `pid`;
 
 ------------------------------------
 -- 表数据
 ------------------------------------
 
-INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (74, '关联项目', 'enterprise', 'conn', 48, 'add', NULL, 1);
-INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (75, '停用提示设置', 'project', 'stopmsg', 38, 'stopmsg', NULL, 1);
-INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (76, '参考文件', 'fileReference', 'all', 44, '', 6, 1);
-
-UPDATE `zh_permission` SET `create_time` = 5 WHERE `id` = 57;
-
-
-
+UPDATE zh_project_log pl LEFT JOIN zh_tender t ON pl.tid = t.id SET pl.spid = IF(ISNULL(t.spid),'',t.spid);

+ 172 - 0
sql/update20250120.sql

@@ -0,0 +1,172 @@
+-- 请按如下分类提交sql!!!
+-- Version V3.5.47.0141
+-- uat 2025-1-16/20 
+-- prod todo
+
+------------------------------------
+-- 表结构
+------------------------------------
+
+ALTER TABLE `zh_tender`
+ADD COLUMN `c_mode` tinyint(1) NULL DEFAULT 0 COMMENT '变更清单模式' AFTER `c_rule_first`;
+
+ALTER TABLE `zh_change_ledger`
+ADD COLUMN `features` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT '' COMMENT '项目特征' AFTER `memo`;
+
+ALTER TABLE `zh_sub_project`
+ADD COLUMN `lock_file` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '资料归集,是否锁定' AFTER `filing_template_name`;
+
+ALTER TABLE `zh_filing_template_list`
+ADD COLUMN `is_share` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否分享' AFTER `memo`;
+
+CREATE TABLE `zh_phase_pay`  (
+  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `phase_order` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '期序号',
+  `phase_date` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '支付年月',
+  `create_user_id` int(11) NOT NULL COMMENT '创建人id',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_user_id` int(10) UNSIGNED NOT NULL COMMENT '最后修改人id',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
+  `rela_stage` json NOT NULL COMMENT '关联期信息(json)--[{sid: int, sorder: int}]',
+  `calc_base` json NOT NULL COMMENT '计算基数',
+  `calc_base_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '计算基数缓存时间',
+  `memo` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '备注',
+  `audit_times` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批轮次',
+  `audit_status` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批状态',
+  `audit_max_sort` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最大审批排序',
+  `audit_begin_time` timestamp NULL DEFAULT NULL COMMENT '审批开始时间',
+  `audit_end_time` timestamp NULL DEFAULT NULL COMMENT '审批结束时间',
+  `final_auditor_str` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '终审缓存信息',
+  `calc_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期计量=工程计量款',
+  `pay_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期付款=工程计量款+其他付款项',
+  `cut_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期扣款=其他扣款项',
+  `yf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期应付',
+  `sf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期实付',
+  `pre_calc_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期计量',
+  `pre_pay_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期付款',
+  `pre_cut_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期扣款',
+  `pre_yf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期应付',
+  `pre_sf_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期实付',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+
+CREATE TABLE `zh_phase_pay_audit`  (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) UNSIGNED NOT NULL COMMENT '标段id',
+  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '合同支付期id',
+  `audit_id` int(11) UNSIGNED NOT NULL COMMENT '流程参与人id(含原报)',
+  `name` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-姓名',
+  `company` varchar(60) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-单位',
+  `role` varchar(30) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-角色',
+  `mobile` varchar(15) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '参与人-电话',
+  `audit_times` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批次数',
+  `audit_order` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '预定流程顺序',
+  `audit_type` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '流程类型',
+  `active_order` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '实际流程顺序',
+  `audit_status` int(11) UNSIGNED NOT NULL DEFAULT 0 COMMENT '流程状态',
+  `audit_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '流程结束时间',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
+  `opinion` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '意见',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+
+CREATE TABLE `zh_phase_pay_detail`  (
+  `id` int(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'id',
+  `uuid` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '合同支付期id',
+  `create_phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '创建期id',
+  `create_phase_order` int(11) UNSIGNED NOT NULL DEFAULT 1 COMMENT '创建期序号',
+  `master_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '拼接phase_id-audit_times-audit_sort',
+  `tree_id` int(11) UNSIGNED NOT NULL COMMENT '树结构-id',
+  `tree_pid` int(11) NOT NULL COMMENT '树结构-父id',
+  `tree_order` int(11) UNSIGNED NOT NULL COMMENT '树结构-排序',
+  `tree_level` int(4) UNSIGNED NOT NULL COMMENT '树结构-层级',
+  `tree_is_leaf` tinyint(4) UNSIGNED NOT NULL COMMENT '树结构-是否子项',
+  `tree_full_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '树结构-完整路径',
+  `audit_times` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '审批轮次',
+  `audit_sort` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '审批流程',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '名称',
+  `create_user_id` int(11) UNSIGNED NOT NULL COMMENT '创建人id',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_user_id` int(11) UNSIGNED NOT NULL COMMENT '最后修改人id',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
+  `is_minus` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否扣款',
+  `is_fixed` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否固定项',
+  `is_deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除',
+  `is_pause` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否暂停计量',
+  `is_gather` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '是否汇总计算',
+  `pay_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '支付类型',
+  `start_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '起扣金额',
+  `start_expr` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '起扣金额-计算式',
+  `range_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '扣款限额',
+  `range_expr` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '扣款限额-计算式',
+  `expr` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '本期金额-计算式',
+  `tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '本期金额',
+  `pre_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止上期金额',
+  `pre_used` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '上期是否已计量',
+  `pre_finished` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '上期是否已计完',
+  `end_tp` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '截止本期金额',
+  `postil` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '本期批注',
+  `dl_value` decimal(24, 8) NOT NULL DEFAULT 0.00000000 COMMENT '计提期限-限制',
+  `dl_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '计提期限-类型',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+
+CREATE TABLE `zh_phase_pay_file`  (
+  `id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid',
+  `tid` int(11) UNSIGNED NOT NULL COMMENT '标段id',
+  `phase_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '合同支付期phase_id(zh_phase_pay.id)',
+  `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '类型(pay/...)',
+  `rela_id` varchar(36) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'uuid(zh_phase_pay_detail.uuid/...)',
+  `filename` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件名',
+  `fileext` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件后缀',
+  `filesize` int(11) NOT NULL COMMENT '文件大小',
+  `filepath` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '文件存储路径',
+  `user_id` int(11) UNSIGNED NOT NULL COMMENT '用户id(zh_project_account.id)',
+  `user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '用户名(缓存)',
+  `user_company` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '公司(缓存)',
+  `user_role` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '角色(缓存)',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
+  `is_deleted` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否删除',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
+
+CREATE TABLE `zh_file_reference_list`  (
+  `id` int(8) NOT NULL AUTO_INCREMENT COMMENT '自增id',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '名称',
+  `create_time` int(10) NOT NULL COMMENT '创建时间',
+  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '备注',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '标准清单列表' ROW_FORMAT = Dynamic;
+
+CREATE TABLE `zh_file_reference`  (
+  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增id',
+  `list_id` int(8) NOT NULL COMMENT '列表id',
+  `template_id` int(8) NOT NULL,
+  `pid` int(10) NOT NULL COMMENT '父id',
+  `order` tinyint(4) NOT NULL COMMENT '同级排序',
+  `level` tinyint(2) NOT NULL COMMENT '层级',
+  `full_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '层级定位辅助字段parent.full_path.ledger_id',
+  `is_leaf` tinyint(1) NOT NULL COMMENT '是否叶子节点,界面显示辅助字段',
+  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
+  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注',
+  `node_type` int(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '节点类别',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '标段项目节点模板数据表' ROW_FORMAT = Dynamic;
+
+------------------------------------
+-- 表数据
+------------------------------------
+
+INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (74, '关联项目', 'enterprise', 'conn', 48, 'add', NULL, 1);
+INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (75, '停用提示设置', 'project', 'stopmsg', 38, 'stopmsg', NULL, 1);
+INSERT INTO `zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES (76, '参考文件', 'fileReference', 'all', 44, '', 6, 1);
+
+UPDATE `zh_permission` SET `create_time` = 5 WHERE `id` = 57;
+
+
+