Browse Source

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

Tony Kang 2 years ago
parent
commit
8564185b6b
55 changed files with 3121 additions and 424 deletions
  1. 1 1
      app/base/base_controller.js
  2. 270 0
      app/controller/file_controller.js
  3. 6 2
      app/controller/login_controller.js
  4. 87 20
      app/controller/payment_controller.js
  5. 18 0
      app/controller/sub_proj_controller.js
  6. 21 0
      app/extend/helper.js
  7. 2 0
      app/lib/ledger.js
  8. 1 1
      app/middleware/budget_check.js
  9. 67 0
      app/middleware/payment_tender_check.js
  10. 48 0
      app/middleware/sub_project_check.js
  11. 1 1
      app/public/css/main.css
  12. 3 4
      app/public/js/budget_list.js
  13. 37 0
      app/public/js/change_information_set.js
  14. 953 0
      app/public/js/file_detail.js
  15. 3 3
      app/public/js/file.js
  16. 1 0
      app/public/js/path_tree.js
  17. 1 1
      app/public/js/payment_index.js
  18. 339 0
      app/public/js/payment_process.js
  19. 58 0
      app/public/js/shares/ali_oss.js
  20. 10 0
      app/public/js/shares/drag_tree.js
  21. 12 2
      app/public/js/shenpi.js
  22. 3 2
      app/public/js/spreadjs_rela/spreadjs_zh.js
  23. 47 11
      app/public/js/stage.js
  24. 37 9
      app/public/js/sub_project.js
  25. 2 2
      app/public/js/tender_list_base.js
  26. 23 3
      app/router.js
  27. 134 0
      app/service/file.js
  28. 203 0
      app/service/filing.js
  29. 5 0
      app/service/payment_detail.js
  30. 87 0
      app/service/payment_shenpi_audit.js
  31. 1 1
      app/service/payment_tender.js
  32. 130 0
      app/service/payment_tender_rpt.js
  33. 5 4
      app/service/rpt_gather_memory.js
  34. 4 3
      app/service/stage_audit.js
  35. 1 1
      app/service/stage_detail_att.js
  36. 49 5
      app/service/sub_proj_permission.js
  37. 63 18
      app/service/sub_project.js
  38. 2 1
      app/service/tender_cache.js
  39. 1 1
      app/view/advance/detail.ejs
  40. 5 5
      app/view/advance/modal_audit.ejs
  41. 1 1
      app/view/budget/list_modal.ejs
  42. 1 0
      app/view/change/information_modal.ejs
  43. 67 0
      app/view/file/file.ejs
  44. 174 0
      app/view/file/file_modal.ejs
  45. 1 1
      app/view/file/modal.ejs
  46. 3 3
      app/view/material/audit_modal.ejs
  47. 53 270
      app/view/payment/process.ejs
  48. 8 13
      app/view/payment/process_modal.ejs
  49. 2 12
      app/view/stage/audit_modal.ejs
  50. 7 2
      app/view/stage/modal.ejs
  51. 2 2
      app/view/sub_proj/index.ejs
  52. 34 9
      app/view/sub_proj/modal.ejs
  53. 5 5
      config/menu.js
  54. 19 4
      config/web.js
  55. 3 1
      sql/update20230329.sql

+ 1 - 1
app/base/base_controller.js

@@ -74,7 +74,7 @@ class BaseController extends Controller {
                 message: postError,
                 message: postError,
             };
             };
         }
         }
-        this.ctx.menuList.sub_project.dispaly = this.ctx.session.sessionProject.showSubProj;
+        this.ctx.menuList.subproj.display = this.ctx.session.sessionProject.showSubProj;
         this.ctx.menuList.budget.display = this.ctx.session.sessionProject.showBudget;
         this.ctx.menuList.budget.display = this.ctx.session.sessionProject.showBudget;
 
 
         try {
         try {

+ 270 - 0
app/controller/file_controller.js

@@ -8,6 +8,10 @@
  * @version
  * @version
  */
  */
 const auditConst = require('../const/audit');
 const auditConst = require('../const/audit');
+const sendToWormhole = require('stream-wormhole');
+const path = require('path');
+const advanceConst = require('../const/advance');
+
 module.exports = app => {
 module.exports = app => {
     class BudgetController extends app.BaseController {
     class BudgetController extends app.BaseController {
 
 
@@ -31,6 +35,272 @@ module.exports = app => {
                 ctx.log(err);
                 ctx.log(err);
             }
             }
         }
         }
+
+        async file(ctx) {
+            try {
+                const renderData = {
+                    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.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.filingTypes = ctx.service.filing.filingType;
+                await this.layout('file/file.ejs', renderData, 'file/file_modal.ejs');
+            } catch (err) {
+                ctx.log(err);
+            }
+        }
+
+        async getFilingTypePermission(ctx) {
+            try {
+                if (ctx.subProject.project_id !== this.ctx.session.sessionProject.id) throw '您无权操作该数据';
+
+                const filingType = await ctx.service.subProjPermission.getFilingType(ctx.subProject.id);
+                ctx.body = { err: 0, msg: '', data: filingType };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '获取授权用户数据错误');
+            }
+        }
+
+        async saveFilingTypePermission(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                await ctx.service.subProjPermission.saveFilingType(data);
+                ctx.body = { err: 0, msg: '', data: '' };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存授权用户信息错误');
+            }
+        }
+
+        async addFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.add(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '新增分类失败');
+            }
+        }
+        async delFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.del(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '删除分类失败');
+            }
+        }
+        async saveFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.filing.save(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存分类数据失败');
+            }
+        }
+
+        async moveFiling(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id || !(data.tree_order >= 0)) throw '数据错误';
+                const result = await ctx.service.filing.move(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '移动分类失败');
+            }
+        }
+
+        async loadFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.file.getFiles({
+                    where: { filing_id: data.filing_id, is_deleted: 0 },
+                    limit: data.count,
+                    offset: (data.page-1)*data.count
+                });
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '加载文件失败');
+            }
+        }
+
+        async checkCanUpload(ctx) {
+            if (ctx.subProject.permission.file_permission.indexOf(ctx.service.subProjPermission.PermissionConst.file.upload.value) < 0) {
+                throw '您无权上传、导入、删除文件';
+            }
+        }
+
+        async checkFiling(filing) {
+            const child = await this.ctx.service.filing.getDataByCondition({ tree_pid: filing.id, is_deleted: 0 });
+            if (child) throw '该分类下存在子分类,请在子分类下上传、导入文件';
+        }
+
+        async uploadFile(ctx){
+            let stream;
+            try {
+                await this.checkCanUpload(ctx);
+
+                const parts = ctx.multipart({ autoFields: true });
+
+                let index = 0;
+                const create_time = Date.parse(new Date()) / 1000;
+                let stream = await parts();
+                const user = await ctx. service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const filing = await ctx.service.filing.getDataById(parts.field.filing_id);
+                if (!filing || filing.is_deleted) throw '分类不存在,请刷新页面后重试';
+                await this.checkFiling(filing);
+
+                const uploadfiles = [];
+                while (stream !== undefined) {
+                    if (!stream.filename) throw '未发现上传文件!';
+
+                    const fileInfo = path.parse(stream.filename);
+                    const filepath = `sp/file/${filing.spid}/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + index + fileInfo.ext}`;
+
+                    // 保存文件
+                    await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream);
+                    await sendToWormhole(stream);
+
+                    // 插入到stage_pay对应的附件列表中
+                    uploadfiles.push({
+                        filename: fileInfo.name,
+                        fileext: fileInfo.ext,
+                        filesize: Array.isArray(parts.field.size) ? parts.field.size[index] : parts.field.size,
+                        filepath,
+                    });
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+
+                const result = await ctx.service.file.addFiles(filing, uploadfiles, user);
+                ctx.body = {err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.helper.log(error);
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.body = this.ajaxErrorBody(error, '上传附件失败,请重试');
+            }
+        }
+        async delFile(ctx) {
+            try{
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.del) throw '缺少参数';
+                const result = await ctx.service.file.delFiles(data.del);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch(error) {
+                this.log(error);
+                ctx.ajaxErrorBody(error, '删除附件失败');
+            }
+        }
+        async saveFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id) throw '缺少参数';
+                const result = await ctx.service.file.saveFile(data.id, data.filename);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (error) {
+                this.log(error);
+                ctx.ajaxErrorBody(error, '编辑附件失败');
+            }
+        }
+
+        async loadValidRelaTender(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (data.type) throw '参数错误';
+
+                const accountInfo = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const userPermission = accountInfo !== undefined && accountInfo.permission !== ''
+                    ? JSON.parse(accountInfo.permission) : null;
+                const tenderList = await ctx.service.tender.getList('', userPermission, ctx.session.sessionUser.is_admin);
+
+                const rela_tender = await ctx.subProject.rela_tender.split(',');
+                const result = tenderList.filter(x => { return rela_tender.indexOf(x.id + '') >= 0});
+                for (const r of result) {
+                    r.advance = await ctx.service.advance.getAllDataByCondition({ columns: ['id', 'order', 'type'], where: { tid: r.id }});
+                    r.advance.forEach(a => {
+                        const type = advanceConst.typeCol.find(x => { return x.type === a.type });
+                        if (type) a.type_str = type.name;
+                    });
+                    r.stage = await ctx.service.stage.getAllDataByCondition({ columns: ['id', 'order'], where: { tid: r.id, status: auditConst.stage.status.checked } });
+                }
+                ctx.body = {err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '加载标段信息失败');
+            }
+        }
+        async _loadLedgerAtt(data) {
+            if (!data.tender_id) throw '参数错误';
+            return await this.ctx.service.ledgerAtt.getAllDataByCondition({ where: { tid: data.tender_id }, order: [['id', 'desc']]});
+        }
+        async _loadStageAtt(data) {
+            if (!data.tender_id || !data.stage) throw '参数错误';
+            switch (data.sub_type) {
+                case 'att':
+                    const stage = await this.ctx.service.stage.getDataById(data.stage);
+                    return await this.ctx.service.stageAtt.getAllDataByCondition({ where: { tid: data.tender_id, sid: stage.order }, order: [['id', 'desc']]});
+            }
+        }
+        async _loadAdvanceAtt(data) {
+            if (!data.stage) throw '参数错误';
+            return await this.ctx.service.advanceFile.getAllDataByCondition({ where: { vid: data.stage }, order: [['id', 'desc']]});
+        }
+        async loadRelaFiles(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                console.log(data);
+                if (!data.type) throw '参数错误';
+
+                let files;
+                switch(data.type) {
+                    case 'ledger':
+                        files = await this._loadLedgerAtt(data);
+                        break;
+                    case 'stage':
+                        files = await this._loadStageAtt(data);
+                        break;
+                    case 'advance':
+                        files = await this._loadAdvanceAtt(data);
+                        break;
+                    default: throw '未知文件类型';
+                }
+                ctx.body = {err: 0, msg: '', data: files };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '加载附件失败,请重试');
+            }
+        }
+        async relaFile(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.filing_id || !data.files) throw '缺少参数';
+
+                const user = await ctx. service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
+                const filing = await ctx.service.filing.getDataById(data.filing_id);
+                if (!filing || filing.is_deleted) throw '分类不存在,请刷新页面后重试';
+                await this.checkFiling(filing);
+
+                const result = await ctx.service.file.relaFiles(filing, data.files, user);
+                ctx.body = {err: 0, msg: '', data: result };
+            } catch (error) {
+                ctx.helper.log(error);
+                ctx.body = this.ajaxErrorBody(error, '导入附件失败,请重试');
+            }
+        }
     }
     }
 
 
     return BudgetController;
     return BudgetController;

+ 6 - 2
app/controller/login_controller.js

@@ -326,7 +326,8 @@ module.exports = app => {
                     if (!result) {
                     if (!result) {
                         throw '登录出错';
                         throw '登录出错';
                     }
                     }
-                    ctx.redirect('/dashboard');
+                    const returnUrl = ctx.projectData.after_login_url ? ctx.projectData.after_login_url : '/dashboard';
+                    ctx.redirect(returnUrl);
                 }
                 }
             } catch (error) {
             } catch (error) {
                 // this.log(error);
                 // this.log(error);
@@ -381,7 +382,10 @@ module.exports = app => {
                     if (!result) {
                     if (!result) {
                         throw '绑定登录出错,请使用账号密码登录';
                         throw '绑定登录出错,请使用账号密码登录';
                     }
                     }
-                    ctx.redirect('/dashboard');
+                    const projectData = await this.ctx.service.project.getProjectByCode(ctx.request.body.code.toString().trim());
+                    const returnUrl = projectData.after_login_url ? projectData.after_login_url : '/dashboard';
+                    ctx.redirect(returnUrl);
+                    // ctx.redirect('/dashboard');
                 }
                 }
             } catch (error) {
             } catch (error) {
                 this.log(error);
                 this.log(error);

+ 87 - 20
app/controller/payment_controller.js

@@ -1,6 +1,7 @@
 'use strict';
 'use strict';
 const accountGroup = require('../const/account_group').group;
 const accountGroup = require('../const/account_group').group;
 const JV = require('../reports/rpt_component/jpc_value_define');
 const JV = require('../reports/rpt_component/jpc_value_define');
+const shenpiConst = require('../const/shenpi');
 
 
 module.exports = app => {
 module.exports = app => {
 
 
@@ -204,10 +205,10 @@ module.exports = app => {
          */
          */
         async detail(ctx) {
         async detail(ctx) {
             try {
             try {
-                const id = parseInt(ctx.params.id);
+                const id = parseInt(ctx.params.did);
                 if (!id) throw '参数错误';
                 if (!id) throw '参数错误';
                 const info = await ctx.service.paymentDetail.getDataById(id);
                 const info = await ctx.service.paymentDetail.getDataById(id);
-                const rptTpl = await ctx.service.rptTpl.getDataById(3030);
+                const rptTpl = await ctx.service.rptTpl.getDataById(3029);
                 const pageRst = ctx.service.jpcReport.getAllPreviewPagesCommon(rptTpl, 'A4');
                 const pageRst = ctx.service.jpcReport.getAllPreviewPagesCommon(rptTpl, 'A4');
                 const renderData = {
                 const renderData = {
                     info,
                     info,
@@ -223,36 +224,102 @@ module.exports = app => {
 
 
         async process(ctx) {
         async process(ctx) {
             try {
             try {
-                const id = parseInt(ctx.params.pid);
-                if (!id) throw '参数错误';
-                const info = await ctx.service.paymentTender.getDataById(id);
-                if (!info) throw '标段不存在';
+                const auditPermission = await this.ctx.service.paymentPermissionAudit.getOnePermission(ctx.session.sessionUser.is_admin, ctx.session.sessionUser.accountId);
+                if (!auditPermission || !auditPermission.process) {
+                    throw '权限不足';
+                }
                 // 获取报表表单列表
                 // 获取报表表单列表
                 const rptProject = await ctx.service.rptTreeNode.getDataByCondition({ name: '01.支付审批报表' });
                 const rptProject = await ctx.service.rptTreeNode.getDataByCondition({ name: '01.支付审批报表' });
-                const rptProjectList = rptProject.items ? JSON.parse(rptProject.items) : [];
-                const rptTplList = [];
-                if (rptProjectList.length > 0) {
-                    const params = { tender_id: id };
-                    for (const rpt of rptProjectList) {
-                        const rptTpl = await ctx.service.rptTpl.getDataById(rpt.ID);
-                        // 根据模板ID获取报表JSON
-                        const pageRst = ctx.service.jpcReport.getAllPreviewPagesCommon(rptTpl, 'A4');
-                        rptTplList.push(pageRst.items[0]);
-                    }
-                }
+                let rptProjectList = rptProject.items ? JSON.parse(rptProject.items) : [];
+                // const rptTplList = [];
+                // if (rptProjectList.length > 0) {
+                //     const params = { tender_id: id };
+                //     for (const rpt of rptProjectList) {
+                //         const rptTpl = await ctx.service.rptTpl.getDataById(rpt.ID);
+                //         // 根据模板ID获取报表JSON
+                //         const pageRst = ctx.service.jpcReport.getAllPreviewPagesCommon(rptTpl, 'A4');
+                //         console.log(pageRst.items[0]);
+                //         // return;
+                //         rptTplList.push(pageRst.items[0]);
+                //     }
+                // }
+                let tenderRptList = await ctx.service.paymentTenderRpt.getProcessList(ctx.tender.id, rptProjectList);
+                [tenderRptList, rptProjectList] = await ctx.service.paymentTenderRpt.checkAndUpdateList(tenderRptList, rptProjectList);
+                // for (const tr of tenderRptList) {
+                //     if (tr.status === shenpiConst.sp_status.gdspl) {
+                //         tr.auditList = await ctx.service.paymentShenpiAudit.getAuditList(ctx.tender.id, tr.id, tr.sp_status);
+                //     } else if (tr.status === shenpiConst.sp_status.gdzs) {
+                //         tr.audit = await ctx.service.paymentShenpiAudit.getAudit(ctx.tender.id, tr.id, tr.sp_status);
+                //     }
+                // }
+                // 获取所有项目参与者
+                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 renderData = {
                 const renderData = {
-                    info,
+                    tender: ctx.tender,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.payment.process),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.payment.process),
                     rptProjectList,
                     rptProjectList,
-                    rptTplList,
+                    tenderRptList,
+                    shenpi: shenpiConst,
+                    accountList,
+                    accountGroup: accountGroupList,
                 };
                 };
                 await this.layout('payment/process.ejs', renderData, 'payment/process_modal.ejs');
                 await this.layout('payment/process.ejs', renderData, 'payment/process_modal.ejs');
             } catch (err) {
             } catch (err) {
                 console.log(err);
                 console.log(err);
                 this.log(err);
                 this.log(err);
-                ctx.redirect(this.menu.menu.dashboard.url);
+                ctx.redirect(this.request.headers.referer ? this.request.headers.referer : this.menu.menu.dashboard.url);
             }
             }
         }
         }
+
+        async processSave(ctx) {
+            try {
+                const auditPermission = await this.ctx.service.paymentPermissionAudit.getOnePermission(ctx.session.sessionUser.is_admin, ctx.session.sessionUser.accountId);
+                if (!auditPermission || !auditPermission.process) {
+                    throw '权限不足';
+                }
+                const responseData = {
+                    err: 0, msg: '', data: {},
+                };
+
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type) {
+                    throw '提交数据错误';
+                }
+                switch (data.type) {
+                    case 'add-rpt':
+                        responseData.data = await ctx.service.paymentTenderRpt.setRpt(ctx.tender.id, data.rpt_list);
+                        break;
+                    case 'change-status':
+                        responseData.data = await ctx.service.paymentTenderRpt.setStatus(data.tr_id, data.status);
+                        break;
+                    case 'get-audits':
+                        responseData.data = await ctx.service.paymentShenpiAudit.getShenpiAudit(data.tr_id, data.status);
+                        break;
+                    case 'add-audit':
+                        responseData.data = await ctx.service.paymentShenpiAudit.addAudit(data);
+                        break;
+                    case 'del-audit':
+                        responseData.data = await ctx.service.paymentShenpiAudit.removeAudit(data);
+                        break;
+                    default: throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
+        async rptList(ctx) {
+
+        }
     }
     }
 
 
     return PaymentController;
     return PaymentController;

+ 18 - 0
app/controller/sub_proj_controller.js

@@ -38,6 +38,7 @@ module.exports = app => {
                 });
                 });
                 renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
                 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(this.ctx.session.sessionProject.id);
+                renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 await this.layout('sub_proj/index.ejs', renderData, 'sub_proj/modal.ejs');
                 await this.layout('sub_proj/index.ejs', renderData, 'sub_proj/modal.ejs');
             } catch (err) {
             } catch (err) {
                 ctx.log(err);
                 ctx.log(err);
@@ -104,6 +105,8 @@ module.exports = app => {
                     result = await ctx.service.subProject.setRelaTender({ id: data.id, rela_tender: data.rela_tender });
                     result = await ctx.service.subProject.setRelaTender({ id: data.id, rela_tender: data.rela_tender });
                 } else if (data.std_id !== undefined) {
                 } else if (data.std_id !== undefined) {
                     result = await ctx.service.subProject.setBudgetStd({ id: data.id, std_id: data.std_id });
                     result = await ctx.service.subProject.setBudgetStd({ id: data.id, std_id: data.std_id });
+                } else if (data.management !== undefined) {
+                    result = await ctx.service.subProject.setManagement({ id: data.id, management: data.management });
                 }
                 }
                 ctx.body = { err: 0, msg: '', data: { update: [result] } };
                 ctx.body = { err: 0, msg: '', data: { update: [result] } };
             } catch(err) {
             } catch(err) {
@@ -132,6 +135,20 @@ module.exports = app => {
             }
             }
         }
         }
 
 
+        async saveRela(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.id || !data.rela_tender) throw '参数有误';
+                const permission = await ctx.service.subProjPermission.getSubProjectUserPermission(data.id, ctx.session.sessionUser.accountId);
+                if (!permission || permission.manage_permission.indexOf(ctx.service.subProjPermission.PermissionConst.manage.rela.value) < 0) throw '您无权进行该操作';
+                const result = await ctx.service.subProject.setRelaTender({ id: data.id, rela_tender: data.rela_tender });
+                ctx.body = { err: 0, msg: '', data: { update: [result] } };
+            } catch(err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存数据失败');
+            }
+        }
+
         async member(ctx) {
         async member(ctx) {
             try {
             try {
                 const data = JSON.parse(ctx.request.body.data);
                 const data = JSON.parse(ctx.request.body.data);
@@ -150,6 +167,7 @@ module.exports = app => {
                 await ctx.service.subProjPermission.savePermission(data.id, data.member);
                 await ctx.service.subProjPermission.savePermission(data.id, data.member);
                 ctx.body = { err: 0, msg: '', data: '' };
                 ctx.body = { err: 0, msg: '', data: '' };
             } catch (err) {
             } catch (err) {
+                console.log(err);
                 ctx.log(err);
                 ctx.log(err);
                 ctx.ajaxErrorBody(err, '保存数据失败');
                 ctx.ajaxErrorBody(err, '保存数据失败');
             }
             }

+ 21 - 0
app/extend/helper.js

@@ -1272,6 +1272,19 @@ module.exports = {
         const reg = /(.png)|(.gif)|(.txt)|(.jpg)|(.jpeg)|(.pdf)/i;
         const reg = /(.png)|(.gif)|(.txt)|(.jpg)|(.jpeg)|(.pdf)/i;
         return reg.test(ext);
         return reg.test(ext);
     },
     },
+    fileExtStr(ext) {
+        const ExtStr = [
+            { text: '图片', ext: ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.cad', '.dwg',] },
+            { text: '压缩包', ext: ['.zip', '.rar', '.7z',] },
+            { text: '音频', ext: ['.mp3'] },
+            { text: '视频', ext: ['.mp4'] },
+            { text: '文档', ext: ['.json', '.txt', '.xls', '.xlsx', '.doc', '.docx', '.pdf', '.ppt', '.pptx',]},
+        ];
+        for (const es of ExtStr) {
+            if (es.ext.indexOf(ext) >= 0) return es.text;
+        }
+        return '';
+    },
 
 
     /**
     /**
      * 查找数组中某个字符的个数
      * 查找数组中某个字符的个数
@@ -1565,4 +1578,12 @@ module.exports = {
         }
         }
         return message;
         return message;
     },
     },
+
+    mapAllSubField(obj, field) {
+        const result = [];
+        for (const prop in obj) {
+            result.push(obj[prop][field]);
+        }
+        return result;
+    }
 };
 };

+ 2 - 0
app/lib/ledger.js

@@ -1129,6 +1129,7 @@ class reviseTree extends billsTree {
                     sgfh_qty: 0, sgfh_tp: 0, sgfh_expr: '',
                     sgfh_qty: 0, sgfh_tp: 0, sgfh_expr: '',
                     sjcl_qty: 0, sjcl_tp: 0, sjcl_expr: '',
                     sjcl_qty: 0, sjcl_tp: 0, sjcl_expr: '',
                     qtcl_qty: 0, qtcl_tp: 0, qtcl_expr: '',
                     qtcl_qty: 0, qtcl_tp: 0, qtcl_expr: '',
+                    deal_qty: 0, deal_tp: 0,
                 };
                 };
             } else {
             } else {
                 return {
                 return {
@@ -1142,6 +1143,7 @@ class reviseTree extends billsTree {
                     sgfh_qty: x.sgfh_qty, sgfh_tp: x.sgfh_tp, sgfh_expr: x.sgfh_expr,
                     sgfh_qty: x.sgfh_qty, sgfh_tp: x.sgfh_tp, sgfh_expr: x.sgfh_expr,
                     sjcl_qty: x.sjcl_qty, sjcl_tp: x.sjcl_tp, sjcl_expr: x.sjcl_expr,
                     sjcl_qty: x.sjcl_qty, sjcl_tp: x.sjcl_tp, sjcl_expr: x.sjcl_expr,
                     qtcl_qty: x.qtcl_qty, qtcl_tp: x.qtcl_tp, qtcl_expr: x.qtcl_expr,
                     qtcl_qty: x.qtcl_qty, qtcl_tp: x.qtcl_tp, qtcl_expr: x.qtcl_expr,
+                    deal_qty: x.deal_qty, deal_tp: x.deal_tp,
                 };
                 };
             }
             }
         });
         });

+ 1 - 1
app/middleware/budget_check.js

@@ -29,7 +29,7 @@ module.exports = options => {
             } else {
             } else {
                 const bp = yield this.service.subProjPermission.getBudgetUserPermission(id);
                 const bp = yield this.service.subProjPermission.getBudgetUserPermission(id);
                 if (!bp) throw '您无权查看该项目';
                 if (!bp) throw '您无权查看该项目';
-                this.budget.readOnly = bp.permission.indexOf(this.service.subProjPermission.PermissionConst.budget.edit.value) < 0;
+                this.budget.readOnly = bp.budget_permission.indexOf(this.service.subProjPermission.PermissionConst.budget.edit.value) < 0;
             }
             }
             yield next;
             yield next;
         } catch (err) {
         } catch (err) {

+ 67 - 0
app/middleware/payment_tender_check.js

@@ -0,0 +1,67 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const auditConst = require('../const/audit').ledger;
+const messageType = require('../const/message_type');
+const scPermission = require('../const/schedule').permission;
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* paymentTenderCheck(next) {
+        try {
+            if (!this.params.id) {
+                throw '当前未打开标段';
+            }
+            const tender = yield this.service.paymentTender.getDataById(this.params.id);
+            if (!tender) {
+                throw '标段不存在';
+            }
+            this.tender = tender;
+            yield next;
+        } catch (err) {
+            // 输出错误到日志
+            if (err.stack) {
+                this.logger.error(err);
+            } else {
+                this.session.message = {
+                    type: messageType.ERROR,
+                    icon: 'exclamation-circle',
+                    message: err,
+                };
+                this.getLogger('fail').info(JSON.stringify({
+                    error: err,
+                    project: this.session.sessionProject,
+                    user: this.session.sessionUser,
+                    body: this.session.body,
+                }));
+            }
+            if (this.helper.isAjax(this.request)) {
+                if (err.stack) {
+                    this.body = {err: 4, msg: '标段数据未知错误', data: null};
+                } else {
+                    this.body = {err: 3, msg: err.toString(), data: null};
+                }
+            } else {
+                if (this.helper.isWap(this.request)) {
+                    this.redirect('/wap/list');
+                } else {
+                    err === '您无权查看该内容' ? this.redirect(this.request.headers.referer) : this.redirect('/payment');
+                }
+            }
+        }
+    };
+};

+ 48 - 0
app/middleware/sub_project_check.js

@@ -0,0 +1,48 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = options => {
+    /**
+     * 标段校验 中间件
+     * 1. 读取标段数据(包括属性)
+     * 2. 检验用户是否可见标段(不校验具体权限)
+     *
+     * @param {function} next - 中间件继续执行的方法
+     * @return {void}
+     */
+    return function* subProjectCheck(next) {
+        try {
+            // 读取标段数据
+            const id = this.params.id || this.query.id;
+            if (!id) throw '参数错误';
+
+            this.subProject = yield this.service.subProject.getDataById(id);
+            if (!this.subProject) throw '项目不存在';
+
+            if (this.session.sessionUser.is_admin) {
+                this.subProject.readOnly = false;
+                this.subProject.permission = this.service.subProjPermission.adminPermission;
+            } else {
+                const bp = yield this.service.subProjPermission.getSubProjectUserPermission(id, this.session.sessionUser.accountId);
+                if (!bp) throw '您无权查看该项目';
+                this.subProject.permission = bp;
+            }
+            yield next;
+        } catch (err) {
+            this.log(err);
+            if (this.helper.isAjax(this.request)) {
+                this.ajaxErrorBody(err, '未知错误');
+            } else {
+                this.postError(err, '未知错误');
+                this.redirect(this.request.headers.referer);
+            }
+        }
+    };
+};

+ 1 - 1
app/public/css/main.css

@@ -688,7 +688,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 }
 }
 
 
 .bg-nav a span{
 .bg-nav a span{
-  display: none;
+  display: block;
 }
 }
 .bg-nav > li{
 .bg-nav > li{
   width:120px
   width:120px

+ 3 - 4
app/public/js/budget_list.js

@@ -62,7 +62,8 @@ $(document).ready(() => {
                     html.push(`<td></td>`);
                     html.push(`<td></td>`);
                 } else {
                 } else {
                     html.push(`<td>`);
                     html.push(`<td>`);
-                    html.push('<button class="btn btn-outline-primary btn-sm" data-target="#select-rela" name="del" onclick="showModal(this);">关联标段</button>');
+                    if (node.manage_permission.indexOf(1) >= 0)
+                        html.push('<button class="btn btn-outline-primary btn-sm" data-target="#select-rela" name="del" onclick="showModal(this);">关联标段</button>');
                     html.push('</td>');
                     html.push('</td>');
                 }
                 }
                 return html.join('');
                 return html.join('');
@@ -146,7 +147,7 @@ $(document).ready(() => {
             });
             });
             $('#select-rela-ok').click(() => {
             $('#select-rela-ok').click(() => {
                 const rela = self.getSelects();
                 const rela = self.getSelects();
-                postData('/subproj/save', { id: curBudget.id, rela_tender: rela.join(',') }, function () {
+                postData('/subproj/rela/save', { id: curBudget.id, rela_tender: rela.join(',') }, function () {
                     $(`[bid=${curBudget.bid}]`)[0].setAttribute('rela-tender', rela.join(','));
                     $(`[bid=${curBudget.bid}]`)[0].setAttribute('rela-tender', rela.join(','));
                     $('#select-rela').modal('hide');
                     $('#select-rela').modal('hide');
                 });
                 });
@@ -171,13 +172,11 @@ $(document).ready(() => {
             const self = this;
             const self = this;
             postData(`/subproj/rela?id=${curBudget.id}`, {}, tenders => {
             postData(`/subproj/rela?id=${curBudget.id}`, {}, tenders => {
                 const rela = curBudget.rela_tender ? curBudget.rela_tender.split(',') : [];
                 const rela = curBudget.rela_tender ? curBudget.rela_tender.split(',') : [];
-                console.log(rela);
                 self.selectTree = Tender2Tree.convert(category, tenders, null, null, function (node, source) {
                 self.selectTree = Tender2Tree.convert(category, tenders, null, null, function (node, source) {
                     node.lastStageOrder = `第${source.lastStageOrder}期`;
                     node.lastStageOrder = `第${source.lastStageOrder}期`;
                     node.lastStageStatus = source.lastStageStatus;
                     node.lastStageStatus = source.lastStageStatus;
                 });
                 });
                 for (const node of self.selectTree.nodes) {
                 for (const node of self.selectTree.nodes) {
-                    console.log(node);
                     node.selected = rela.indexOf(node.tid + '') >= 0;
                     node.selected = rela.indexOf(node.tid + '') >= 0;
                 }
                 }
                 SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.selectTree);
                 SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.selectTree);

+ 37 - 0
app/public/js/change_information_set.js

@@ -1435,6 +1435,43 @@ $(document).ready(() => {
             }
             }
         }
         }
     });
     });
+    // 选中所有新增部位/清单
+    $('#select-all-revise').click(function() {
+        const qtySpr = '*;*', infoSpr = '!_!', recSpr = '$#$';
+        const gclTr = $('#table-list-select tr');
+        for (const tr of gclTr) {
+            const lid = $(tr).data('lid');
+            let gcl = _.find(gclGatherData, function (item) {
+                return item.leafXmjs && item.leafXmjs[0].gcl_id === lid;
+            });
+            if (!gcl) gcl = gclGatherData[$(this).data('gcl')];
+            if (!gcl || !gcl.cid) continue;
+
+            let data_bwmx = $(tr).attr('data-bwmx').split(recSpr);
+            data_bwmx = data_bwmx.filter(x => {
+                const rec = x.split(qtySpr);
+                const info = rec[0].split(infoSpr);
+                if (info.length < 8) return true;
+
+                const leaf = gcl.leafXmjs.find(lx => { return lx.mx_id === info[6]});
+                return !leaf || !leaf.cid;
+            });
+            for (const [index, leaf] of gcl.leafXmjs.entries()) {
+                if (!leaf.cid) continue;
+
+                const bwmx = [leaf.code, leaf.jldy || '', leaf.dwgc || '', leaf.fbgc || '', leaf.fxgc || '', leaf.gcl_id, leaf.mx_id, leaf.bwmx || ''];
+                const de_qu = bwmx.join(infoSpr) + qtySpr + (leaf.quantity || 0);
+                data_bwmx.push(de_qu);
+            }
+            $(tr).attr('data-bwmx', data_bwmx.join(recSpr)).addClass('table-success');
+        }
+
+        // 触发点击当前清单,重载当前全部部位
+        const dataIndex = $('#code-list').attr('data-index');
+        if (dataIndex) {
+            $(`tr[data-index=${dataIndex}]`).trigger('click');
+        }
+    });
 
 
     // 记录变更信息操作
     // 记录变更信息操作
     $('body').on('valuechange', '#change_form input[type="text"]', function (e, previous) {
     $('body').on('valuechange', '#change_form input[type="text"]', function (e, previous) {

+ 953 - 0
app/public/js/file_detail.js

@@ -0,0 +1,953 @@
+$(document).ready(function() {
+    class FilingObj {
+        constructor(setting) {
+            // 原始数据整理后的树结构,用来整理zTree显示
+            this.dragTree = createDragTree({
+                id: 'id',
+                pid: 'tree_pid',
+                level: 'tree_level',
+                order: 'tree_order',
+                rootId: '-1'
+            });
+            // 界面显示的zTree
+            this.setting = setting;
+            this.filingTree = null;
+            this.pageCount = 15;
+            this.expandKey = 'filing-' + window.location.pathname.split('/')[2];
+            const cache = getLocalCache(this.expandKey);
+            this.expandCache = cache ? cache.split(',') : [];
+        }
+        analysisFiling(data) {
+            const self = this;
+            this.dragTree.loadDatas(data);
+            this.dragTree.recursiveFun(this.dragTree.children, x => {
+                if (x.children && x.children.length > 0) {
+                    x.total_file_count = x.children.map(y => {
+                        return y.total_file_count;
+                    }).reduce((pre, value) => {
+                        return pre + value
+                    }, 0);
+                } else {
+                    x.total_file_count = x.file_count;
+                }
+            });
+            const sortNodes = this.dragTree.nodes.map(x => {
+                const result = {
+                    id: x.id,
+                    tree_pid: x.tree_pid,
+                    name: x.name + (x.total_file_count > 0 ? `(${x.total_file_count})` : ''),
+                    spid: x.spid,
+                    source_node: x,
+                };
+                if (result.source_node.is_fixed) result.isParent = true;
+                if (result.source_node.is_folder || result.source_node.is_fixed) result.open = self.expandCache.indexOf(result.id) >= 0;
+                return result;
+            });
+            this.filingTree = $.fn.zTree.init($('#filing'), this.setting, sortNodes);
+        }
+        _getFileHtml(file) {
+            const html = [];
+            html.push(`<tr fid="${file.id}">`);
+            html.push(`<td><input type="checkbox" name="bd-check" fid="${file.id}"></td>`);
+            const editHtml = file.canEdit ? `<a href="javascript: void(0);" class="mr-1" name="edit-file" fid="${file.id}"><i class="fa fa-pencil fa-fw"></i></a>` : '';
+            const viewHtml = file.viewpath ? `<a href="${file.viewpath}" class="mr-1" target="_blank"><i class="fa fa-eye fa-fw"></i></a>` : '';
+            const downHtml = `<a href="javascript: void(0);" onclick="AliOss.downloadFile('${file.filepath}', '${file.filename + file.fileext}')" class="mr-1"><i class="fa fa-download fa-fw"></i></a>`;
+            const delHtml = file.canEdit ? `<a href="javascript: void(0);" class="mr-1 text-danger" name="del-file" fid="${file.id}"><i class="fa fa-trash-o fa-fw"></i></a>` : '';
+            html.push(`<td><div class="d-flex justify-content-between align-items-center table-file"><div name="filename" fid="${file.id}">${file.filename}${file.fileext}</div><div class="btn-group-table" style="display: none;">${editHtml}${viewHtml}${downHtml}${delHtml}</div></div></td>`);
+            html.push(`<td>${file.user_name}</td>`);
+            html.push(`<td>${moment(file.create_time).format('YYYY-MM-DD HH:mm:ss')}</td>`);
+            html.push(`<td>${file.fileext_str}</td>`);
+            html.push('</tr>');
+            return html.join('');
+        }
+        refreshFilesTable() {
+            const html = [];
+            const files = this.curFiling.source_node.files;
+            if (!files || files.length === 0) {
+                $('#file-list').html('');
+                return;
+            }
+
+            const startIndex = (this.curPage - 1)*this.pageCount;
+            const endIndex = this.curPage*this.pageCount;
+            for (const [i, f] of files.entries()) {
+                if (i < startIndex || i >= endIndex) continue;
+                html.push(this._getFileHtml(f));
+            }
+            $('#file-list').html(html.join(''));
+        }
+        refreshPages() {
+            if (!filingObj.curFiling) return;
+
+            filingObj.curTotalPage = Math.ceil(filingObj.curFiling.source_node.file_count / this.pageCount);
+            $('#curPage').html(filingObj.curPage);
+            $('#curTotalPage').html(filingObj.curTotalPage);
+            if (filingObj.curTotalPage > 1) {
+                $('#showPage').show();
+            } else {
+                $('#showPage').hide();
+            }
+        }
+        async loadFiles(node, page) {
+            if (node.source_node.children && node.source_node.children.length > 0) return;
+            if (!node.source_node.files) node.source_node.files = [];
+
+            if (!node.source_node.file_count) return;
+            if (node.source_node.files && node.source_node.files.length === node.source_node.file_count) return;
+
+            const needFiles = Math.min(page*this.pageCount, node.source_node.file_count);
+            if (node.source_node.files && needFiles <= node.source_node.files.length) return;
+
+            const files = await postDataAsync('file/load', { filing_id: node.id, page, count: this.pageCount });
+            files.forEach(x => {
+                const file = node.source_node.files.find(f => {return x.id === f.id; });
+                if (file) {
+                    Object.assign(file, x);
+                } else {
+                    node.source_node.files.push(x);
+                }
+            });
+            node.source_node.files.sort((x, y) => {
+                return x.create_time - y.create_time;
+            });
+        }
+        addSiblingFiling(node) {
+            const self = this;
+            postData('filing/add', { tree_pid: node.tree_pid, tree_pre_id: node.id }, function(result) {
+                const refreshData = self.dragTree.loadPostData(result);
+                const newNode = refreshData.create[0];
+                self.filingTree.addNodes(node.getParentNode(), node.getIndex() + 1, [{ id: newNode.id, tree_pid: newNode.tree_pid, name: newNode.name, spid: newNode.spid, source_node: newNode}]);
+            });
+        }
+        addChildFiling(node) {
+            const self = this;
+            postData('filing/add', { tree_pid: node.id }, function(result) {
+                const refreshData = self.dragTree.loadPostData(result);
+                const newNode = refreshData.create[0];
+                self.filingTree.addNodes(node, -1, [{ id: newNode.id, tree_pid: newNode.tree_pid, name: newNode.name, spid: newNode.spid, source_node: newNode}]);
+            });
+        }
+        delFiling(node, callback) {
+            const self = this;
+            postData('filing/del', { id: node.id }, function(result) {
+                self.dragTree.loadPostData(result);
+                self.filingTree.removeNode(node);
+                if (callback) callback();
+            });
+        }
+        async renameFiling(node, newName) {
+            const result = await postDataAsync('filing/save', { id: node.id, name: newName });
+            node.source_node.name = newName;
+            node.name = node.source_node.name + (node.source_node.total_file_count > 0 ? `(${node.source_node.total_file_count})` : '');
+            return result;
+        }
+        updateFilingFileCount(filing, count) {
+            let differ = count - filing.source_node.file_count;
+            filing.source_node.file_count = count;
+            filing.source_node.total_file_count = count;
+            filing.name = filing.source_node.name + (filing.source_node.total_file_count > 0 ? `(${filing.source_node.total_file_count})` : '');
+            filingObj.filingTree.updateNode(filing);
+            let parent = filing.getParentNode();
+            while (!!parent) {
+                parent.source_node.total_file_count = parent.source_node.total_file_count + differ;
+                parent.name = parent.source_node.name + (parent.source_node.total_file_count > 0 ? `(${parent.source_node.total_file_count})` : '');
+                filingObj.filingTree.updateNode(parent);
+                parent = parent.getParentNode();
+            }
+        }
+        uploadFiles(files, callback) {
+            const formData = new FormData();
+            formData.append('filing_id', filingObj.curFiling.id);
+            for (const file of files) {
+                if (file === undefined) {
+                    toastr.error('未选择上传文件。');
+                    return false;
+                }
+                if (file.size > 30 * 1024 * 1024) {
+                    toastr.error('上传文件大小超过30MB。');
+                    return false;
+                }
+                const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
+                if (whiteList.indexOf(fileext) === -1) {
+                    toastr.error('仅支持office文档、图片、压缩包格式,请勿上传' + fileext + '格式文件。');
+                    return false;
+                }
+                formData.append('size', file.size);
+                formData.append('file[]', file);
+            }
+            postDataWithFile('file/upload', formData, function (data) {
+                filingObj.curFiling.source_node.files.unshift(...data.files);
+                filingObj.updateFilingFileCount(filingObj.curFiling, data.filing.file_count);
+                filingObj.refreshFilesTable();
+                filingObj.refreshPages();
+                if (callback) callback();
+            });
+        }
+        delFiles(files, callback) {
+            postData('file/del', { del: files }, async function(data) {
+                for (const id of data.del) {
+                    const fIndex = filingObj.curFiling.source_node.files.findIndex(x => { return x.id === id });
+                    if (fIndex >= 0) filingObj.curFiling.source_node.files.splice(fIndex, 1);
+                }
+                filingObj.updateFilingFileCount(filingObj.curFiling, data.filing.file_count);
+                await filingObj.loadFiles(filingObj.curFiling, filingObj.curPage);
+                filingObj.refreshFilesTable();
+                filingObj.refreshPages();
+                if (callback) callback();
+            });
+        }
+        renameFile(fileId, filename) {
+            const file = filingObj.curFiling.source_node.files.find(x => { return x.id === fileId });
+            if (!file) return;
+
+            const filenameDiv = $(`[name=filename][fid=${fileId}]`);
+            if (filename === file.filename + file.fileext) {
+                filenameDiv.html(file.filename + file.fileext);
+                return;
+            }
+
+            postData('file/save', { id: fileId, filename }, function(data) {
+                file.filename = data.filename;
+                file.fileext = data.fileext;
+                filenameDiv.html(file.filename + file.fileext);
+            }, function() {
+                filenameDiv.html(file.filename + file.fileext);
+            });
+        }
+        relaFiles(files, callback) {
+            postData('file/rela', { filing_id: this.curFiling.id, files: files }, async function(data) {
+                filingObj.curFiling.source_node.files.unshift(...data.files);
+                filingObj.updateFilingFileCount(filingObj.curFiling, data.filing.file_count);
+                filingObj.refreshFilesTable();
+                filingObj.refreshPages();
+                if (callback) callback();
+            });
+        }
+        async setCurFiling(node) {
+            filingObj.curFiling = node;
+            filingObj.curPage = 1;
+            filingObj.refreshPages();
+
+            if (filingObj.curFiling.source_node.children && filingObj.curFiling.source_node.children.length > 0) {
+                $('#file-view').hide();
+            } else {
+                $('#file-view').show();
+                await filingObj.loadFiles(node, 1);
+                filingObj.refreshFilesTable();
+            }
+            if (filingObj.curFiling.source_node.filing_type === 5) {
+                $('#rela-file-btn').show();
+            } else {
+                $('#rela-file-btn').hide();
+            }
+        }
+        prePage() {
+            if (this.curPage === 1) return;
+            this.curPage = this.curPage - 1;
+            this.refreshPages();
+            this.refreshFilesTable();
+        }
+        async nextPage() {
+            if (this.curPage === this.curTotalPage) return;
+            await filingObj.loadFiles(this.curFiling, this.curPage + 1);
+            this.curPage = this.curPage + 1;
+            this.refreshPages();
+            this.refreshFilesTable();
+        }
+        getCurFilingFullPath(){
+            let cur = filingObj.curFiling;
+            const result = [];
+            while (cur) {
+                result.unshift(cur.source_node.name);
+                cur = cur.getParentNode();
+            }
+            return result.join('/');
+        }
+        expandFiling(node, expand) {
+            if (expand) {
+                this.expandCache.push(node.id);
+            } else{
+                const index = this.expandCache.indexOf(node.id);
+                if (index >= 0) this.expandCache.splice(index, 1);
+            }
+            setLocalCache(this.expandKey, this.expandCache.join(','));
+        }
+        moveFiling(node, tree_order) {
+            if (tree_order === node.source_node.tree_order) return;
+
+            const self = this;
+            postData('filing/move', { id: node.id, tree_order }, function(result) {
+                self.dragTree.loadPostData(result);
+            });
+        }
+    }
+    const levelTreeSetting = {
+        view: {
+            selectedMulti: false
+        },
+        data: {
+            simpleData: {
+                idKey: 'id',
+                pIdKey: 'tree_pid',
+                rootPId: '-1',
+                enable: true,
+            }
+        },
+        edit: {
+            enable: true,
+            showRemoveBtn: function(treeId, treeNode) {
+                if (!canFiling) return false;
+                return !treeNode.source_node.is_fixed;
+            },
+            showRenameBtn: function(treeId, treeNode) {
+                if (!canFiling) return false;
+                return !treeNode.source_node.is_fixed;
+            },
+            renameTitle: '编辑',
+            drag: {
+                isCopy: false,
+                isMove: true,
+                pre: true,
+                next: true,
+                inner: false,
+            },
+            editNameSelectAll: true,
+        },
+        callback: {
+            onClick: async function (e, key, node) {
+                if (filingObj.curFiling && filingObj.curFiling.id === node.id) return;
+
+                filingObj.setCurFiling(node);
+            },
+            beforeEditName: function(key, node) {
+                node.name = node.source_node.name;
+            },
+            beforeRename: async function(key, node, newName, isCancel) {
+                if (!isCancel) await filingObj.renameFiling(node, newName);
+                return true;
+            },
+            onRename: function(e, key, node, isCancel) {
+                node.name = node.name + (node.source_node.total_file_count > 0 ? `(${node.source_node.total_file_count})` : '');
+                filingObj.filingTree.updateNode(node);
+            },
+            beforeRemove: function(e, key, node, isCancel) {
+                $('#del-filing').modal('show');
+                return false;
+            },
+            onExpand(e, key, node) {
+                filingObj.expandFiling(node, true);
+            },
+            onCollapse: function(e, key, node) {
+                filingObj.expandFiling(node, false);
+            },
+            beforeDrop: function(key, nodes, target, moveType, isCopy) {
+                if (!target) return false;
+                if (nodes[0].tree_pid !== target.tree_pid) return false;
+
+                const order = target.getIndex() + 1;
+                const max = target.getParentNode().children.length;
+                if (moveType === 'prev') {
+                    filingObj.moveFiling(nodes[0], order === 1 ? 1 : order - 1)
+                } else if (moveType === 'next') {
+                    filingObj.moveFiling(nodes[0], order === max ? max : order + 1);
+                }
+            }
+        }
+    };
+    const filingObj = new FilingObj(levelTreeSetting);
+    filingObj.analysisFiling(filing);
+    $('#add-slibing').click(() => {
+        if (!filingObj.curFiling) return;
+        if (filingObj.curFiling.source_node.is_fixed) {
+            toastr.error('固定分类不可添加同级');
+            return;
+        }
+
+        filingObj.addSiblingFiling(filingObj.curFiling);
+    });
+    $('#add-child').click(() => {
+        if (!filingObj.curFiling) return;
+        if (filingObj.curFiling.source_node.file_count > 0) {
+            toastr.error('该分类下已导入文件,不可添加子级');
+            return;
+        }
+
+        filingObj.addChildFiling(filingObj.curFiling);
+    });
+    // $('#del-filing-btn').click(() => {
+    //     if (!filingObj.curFiling) return;
+    //     if (filingObj.curFiling.source_node.is_fixed) {
+    //         toastr.error('固定分类不可删除');
+    //         return;
+    //     }
+    //
+    //     $('#del-filing').modal('show');
+    // });
+    $('#del-filing-ok').click(() => {
+        filingObj.delFiling(filingObj.curFiling, function() {
+            $('#del-filing').modal('hide');
+        });
+    });
+    $('#add-file-ok').click(() => {
+        const input = $('#upload-file');
+        filingObj.uploadFiles(input[0].files, function() {
+            $(input).val('');
+            $('#add-file').modal('hide');
+        });
+    });
+    $('body').on('mouseenter', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","block");
+    });
+    $('body').on('mouseleave', ".table-file", function(){
+        $(this).children(".btn-group-table").css("display","none");
+    });
+    $('body').on('click', "a[name=del-file]", function() {
+        const del = [this.getAttribute('fid')];
+        filingObj.delFiles(del);
+    });
+    $('body').on('click', "a[name=edit-file]", function() {
+        const check = $('[name=filename] input[fid]');
+        if (check.length > 0 && check[0].getAttribute('fid') === this.getAttribute('fid')) return;
+
+        const id = this.getAttribute('fid');
+        const filename = $(`[name=filename][fid=${id}]`);
+        const file = filingObj.curFiling.source_node.files.find(x => { return x.id === id });
+        filename.html(`<input type="text" class="form-control form-control-sm" maxlength="100" value="${file.filename + file.fileext}" fid="${id}">`);
+    });
+    $('body').on('blur', "[name=filename] input[fid]", function() {
+        filingObj.renameFile(this.getAttribute('fid'), this.value);
+    });
+    $('body').on('keypress', "[name=filename] input[fid]", function(e) {
+        if (e.keyCode == 13) {
+            filingObj.renameFile(this.getAttribute('fid'), this.value);
+        }
+    });
+    $('.page-select').click(function() {
+        const content = this.getAttribute('content');
+        switch(content) {
+            case 'pre': filingObj.prePage(); break;
+            case 'next': filingObj.nextPage(); break;
+            default: return;
+        }
+    });
+    $('#batch-download').click(function () {
+        const self = this;
+        const files = [];
+        const checkes = $('[name=bd-check]:checked');
+        checkes.each(function() {
+            const fid = this.getAttribute('fid');
+            const file = filingObj.curFiling.source_node.files.find(x => { return x.id === fid; });
+            file && files.push(file);
+        });
+
+        if (files.length === 0) return;
+
+        $(self).attr('disabled', 'true');
+        AliOss.zipFiles(files, filingObj.curFiling.source_node.name + '.zip', (fails) => {
+            $(self).removeAttr('disabled');
+            if (fails.length === 0) {
+                toastr.success('下载成功');
+            } else {
+                toastr.warning(`下载成功(${fails.length}个文件下载失败)`);
+            }
+        }, () => {
+            $(self).removeAttr('disabled');
+            toastr.error('批量下载失败');
+        });
+    });
+    $('#batch-del-file').on('show.bs.modal', function(e) {
+        const checkes = $('[name=bd-check]:checked');
+        if (checkes.length === 0) {
+            e.preventDefault();
+        } else {
+            for (const c of checkes) {
+                const fid = c.getAttribute('fid');
+                const file = filingObj.curFiling.source_node.files.find(x => { return x.id === fid });
+                if (!file) continue;
+
+                if (file.user_id !== userID) {
+                    toastr.error(`文件【${file.filename + file.fileext}】不是您上传的文件,请勿删除`);
+                    e.preventDefault();
+                }
+            }
+        }
+    });
+    $('#batch-del-file-ok').click(function() {
+        const del = [];
+        const checkes = $('[name=bd-check]:checked');
+        checkes.each(function() {
+            del.push(this.getAttribute('fid'));
+        });
+        filingObj.delFiles(del, function() {
+            $('#batch-del-file').modal('hide');
+        });
+    });
+
+    class RelaFileLoader {
+        constructor() {
+            const self = this;
+            // 可导入的标段
+            this.treeSetting = {
+                view: {
+                    selectedMulti: false
+                },
+                data: {
+                    simpleData: {
+                        idKey: 'id',
+                        pIdKey: 'tree_pid',
+                        rootPId: '-1',
+                        enable: true,
+                    }
+                },
+                edit: {
+                    enable: false,
+                },
+                callback: {
+                    onClick: async function (e, key, node) {
+                        if (this.curTender && this.curTender.id === node.id) return;
+
+                        self.setCurTender(node);
+                    },
+                }
+            };
+            $('body').on('click', '[name=rf-check]', function () {
+                self.selectFile(this.getAttribute('rfid'), this.checked);
+            });
+            $('#tf-type').change(function() {
+                self.selectTfType(this.value);
+            });
+            $('#tf-sub-type').change(function() {
+                self.selectTfSubType(this.value);
+            });
+            $('#tf-stage').change(function() {
+                self.selectTfStage(this.value);
+            });
+            $('#rela-file-ok').click(function() {
+                const selectFiles = self.getSelectRelaFile();
+                filingObj.relaFiles(selectFiles, function() {
+                    $('#rela-file').modal('hide');
+                });
+            });
+        }
+        clearFileSelect() {
+            if (!this.tenderTree) return;
+
+            const nodes = this.tenderTree.getNodes();
+            nodes.forEach(node => {
+                const x = node.source_node;
+                x.selectFiles = [];
+                if (x.att) x.att.forEach(la => { la.checked = false });
+                if (x.advance) {
+                    x.advance.forEach(a => {
+                        if (a.att) a.att.forEach(aa => { aa.checked = false });
+                    });
+                }
+                if (x.stage) {
+                    x.stage.forEach(s => {
+                        if (s.att) s.att.forEach(sa => { sa.checked = false });
+                    })
+                }
+            });
+        }
+        refreshSelectHint(){
+            if (this.curTender) {
+                $('#cur-tender-hint').html(`当前标段,已选${this.curTender.source_node.selectFiles.length}文件`);
+            } else {
+                $('#cur-tender-hint').html('');
+            }
+            const nodes = this.tenderTree.getNodes();
+            const selectTenders = nodes.filter(x => { return x.source_node.selectFiles.length > 0; });
+            if (selectTenders.length > 0) {
+                const count = selectTenders.reduce((rst, x) => { return rst + x.source_node.selectFiles.length; }, 0);
+                $('#rela-file-hint').html(`已选择${selectTenders.length}个标段,共${count}个文件`);
+            } else {
+                $('#rela-file-hint').html('未选择标段、文件');
+            }
+        }
+        selectFile(fileId, isSelect) {
+            const file = this.curFiles.find(x => { return x.rf_id == fileId });
+            if (file) {
+                file.checked = isSelect;
+                if (isSelect) {
+                    this.curTender.source_node.selectFiles.push(file);
+                } else {
+                    const index = this.curTender.source_node.selectFiles.findIndex(x => { return x.rf_id === file.rf_id });
+                    this.curTender.source_node.selectFiles.splice(index, 1);
+                }
+                this.refreshSelectHint();
+            }
+        }
+        async showRelaFile(){
+            $('#rela-filing-hint').html(`当前目录:${filingObj.getCurFilingFullPath()}`);
+            if (!this.tenderTree) {
+                const tenders = await postDataAsync('file/rela/tender', {});
+                const sortNodes = tenders.map(x => {
+                    return { id: x.id, tree_pid: -1, name: x.name, source_node: x };
+                });
+                this.tenderTree = this.filingTree = $.fn.zTree.init($('#rela-tender'), this.treeSetting, sortNodes);
+            }
+            this.clearFileSelect();
+            this.refreshSelectHint();
+            const firstNode = this.filingTree.getNodes()[0];
+            if (firstNode) {
+                this.filingTree.selectNode(firstNode);
+                await this.setCurTender(firstNode);
+            }
+        }
+        refreshTenderFileStage() {
+            if (this.rfType.sub_type) {
+                const type = this.tenderFileType.find(x => { return x.value === this.rfType.type});
+                const subType = type.subType ? type.subType.find(x => { return x.value === this.rfType.sub_type; }) : null;
+                if (subType) {
+                    this.rfType.stage = subType.stage[0].value;
+                    const html= [];
+                    for (const stage of subType.stage) {
+                        html.push(`<option value="${stage.value}">${stage.text}</option>`);
+                    }
+                    $('#tf-stage').html(html.join('')).show();
+                } else {
+                    $('#tf-stage').html('').hide();
+                }
+            } else {
+                $('#tf-stage').html('').hide();
+            }
+        }
+        refreshTenderFileSubType() {
+            const type = this.tenderFileType.find(x => { return x.value === this.rfType.type});
+            if (type.subType && type.subType.length > 0) {
+                this.rfType.sub_type = type.subType[0].value;
+                const html= [];
+                for (const tfst of type.subType) {
+                    html.push(`<option value="${tfst.value}">${tfst.text}</option>`);
+                }
+                $('#tf-sub-type').html(html.join('')).show();
+            } else {
+                $('#tf-sub-type').html('').hide();
+            }
+        }
+        refreshTenderFileType() {
+            const html= [];
+            for (const tft of this.tenderFileType) {
+                html.push(`<option value="${tft.value}">${tft.text}</option>`);
+            }
+            $('#tf-type').html(html.join(''));
+        }
+        refreshSelects(tender) {
+            this.tenderFileType = [];
+            this.tenderFileType.push({ value: 'ledger', text: '台账附件' });
+            if (tender.stage && tender.stage.length > 0) {
+                const stages = tender.stage.map(x => { return {value: x.id, text: `第${x.order}期`}; });
+                this.tenderFileType.push({
+                    value: 'stage', text: '计量期',
+                    subType: [
+                        { value: 'att', text: '计量附件', stage: JSON.parse(JSON.stringify(stages)) },
+                    ],
+                });
+            }
+            if (tender.advance && tender.advance.length > 0) {
+                const advanceType = [];
+                tender.advance.forEach(x => {
+                    let at = advanceType.find(y => { return y.value === x.type });
+                    if (!at) {
+                        at = { value: x.type, text: x.type_str, stage: [] };
+                        advanceType.push(at);
+                    }
+                    at.stage.push({ value: x.id, text: `第${x.order}期`});
+                });
+                this.tenderFileType.push({
+                    value: 'advance', text: '预付款', subType: advanceType
+                });
+            }
+            this.rfType = { type: this.tenderFileType[0].value };
+            this.refreshTenderFileType();
+            this.refreshTenderFileSubType();
+            this.refreshTenderFileStage();
+        }
+        async selectTfStage(stage){
+            this.rfType.stage = stage;
+            await this.loadFiles();
+            this.refreshFileTable();
+        }
+        async selectTfSubType(sub_type){
+            this.rfType.sub_type = sub_type;
+            this.refreshTenderFileStage();
+            await this.loadFiles();
+            this.refreshFileTable();
+        }
+        async selectTfType(type){
+            this.rfType.type = type;
+            this.refreshTenderFileSubType();
+            this.refreshTenderFileStage();
+            await this.loadFiles();
+            this.refreshFileTable();
+        }
+        refreshFileTable() {
+            const html = [];
+            const typeStr = [];
+            const selectOption = $('option:selected');
+            selectOption.each((i, x) => {
+               typeStr.push(x.innerText);
+            });
+            for (const f of this.curFiles) {
+                html.push('<tr>');
+                const checked = f.checked ? "checked" : '';
+                html.push(`<td><input type="checkbox" name="rf-check" rfid="${f.rf_id}" ${checked}></td>`);
+                html.push(`<td>${f.filename}${f.fileext}</td>`);
+                html.push(`<td>${typeStr.join(' - ')}</td>`);
+                html.push('</tr>');
+            }
+            $('#rf-files').html(html.join(''));
+        };
+        initFilesId(files){
+            const tender_id = this.curTender.id;
+            const rfType = this.rfType;
+            files.forEach((f, i) => {
+                f.rf_id = `${tender_id}-${rfType.type}-${rfType.sub_type}-${rfType.stage}-${i}`;
+                f.rf_key = {
+                    tender_id, ...rfType, id: f.id,
+                };
+            });
+        }
+        async _loadRelaFiles(rfType) {
+            return await postDataAsync('file/rela/files', { tender_id: this.curTender.id, ...rfType });
+        }
+        async _loadLedgerFile() {
+            if (!this.curTender.source_node.att) this.curTender.source_node.att = await this._loadRelaFiles(this.rfType);
+            this.curFiles = this.curTender.source_node.att;
+        }
+        async _loadStageFile() {
+            const rfType = this.rfType;
+            const stage = this.curTender.source_node.stage.find(x => {
+                return x.id === rfType.stage;
+            });
+            if (!stage) {
+                this.curFiles = [];
+                return;
+            }
+            if (!stage[this.rfType.sub_type]) stage[this.rfType.sub_type] = await this._loadRelaFiles(rfType);
+            this.curFiles = stage[this.rfType.sub_type];
+        }
+        async _loadAdvanceFile() {
+            const rfType = this.rfType;
+            const advance = this.curTender.source_node.advance.find(x => {
+                return x.id === rfType.stage;
+            });
+            if (!advance) {
+                this.curFiles = [];
+                return;
+            }
+            if (!advance.files) advance.files = await this._loadRelaFiles(rfType);
+            this.curFiles = advance.files;
+        }
+        async loadFiles() {
+            switch (this.rfType.type) {
+                case 'ledger': await this._loadLedgerFile(); break;
+                case 'stage': await this._loadStageFile(); break;
+                case 'advance': await this._loadAdvanceFile(); break;
+            }
+            this.initFilesId(this.curFiles);
+        }
+        async setCurTender(node) {
+            this.curTender = node;
+            this.refreshSelects(node.source_node);
+            await this.loadFiles();
+            this.refreshSelectHint();
+            this.refreshFileTable();
+        }
+        getSelectRelaFile() {
+            const data = [];
+            const nodes = this.tenderTree.getNodes();
+            nodes.forEach(node => {
+                if (node.source_node.selectFiles.length === 0) return;
+
+                node.source_node.selectFiles.forEach(x => {
+                    data.push({
+                        filename: x.filename, fileext: x.fileext, filepath: x.filepath, filesize: x.filesize,
+                        rela_info: x.rf_key,
+                    })
+                });
+            });
+            return data;
+        }
+    }
+    const relaFileLoader = new RelaFileLoader();
+    $('#rela-file').on('show.bs.modal', function() {
+        relaFileLoader.showRelaFile(this.getAttribute('content'));
+    });
+
+    // 授权相关
+    class FilingPermission {
+        constructor (setting) {
+            this.setting = setting;
+            const self = this;
+            $(setting.modal).on('show.bs.modal', () => {
+                self.loadPermission();
+            });
+            $(`${setting.modal}-ok`).click(() => {
+                self.savePermission();
+            });
+            $('[name=ftName]').click(function () {
+                const filingId = this.getAttribute('ftid');
+                self.setCurFiling(filingId);
+            });
+
+            $('.book-list').on('click', 'dt', function () {
+                const idx = $(this).find('.acc-btn').attr('data-groupid');
+                const type = $(this).find('.acc-btn').attr('data-type');
+                if (type === 'hide') {
+                    $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                        $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o')
+                        $(this).find('.acc-btn').attr('data-type', 'show')
+
+                    })
+                } else {
+                    $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                        $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square')
+                        $(this).find('.acc-btn').attr('data-type', 'hide')
+                    })
+                }
+                return false;
+            });
+            $('dl').on('click', 'dd', function () {
+                const type = $(this).data('type');
+                if (type === 'all') {
+                    const cid = parseInt($(this).data('id'));
+                    const company = self.company.find(x => { return x.id === cid });
+                    for (const u of company.users) {
+                        if (u.filing_type.indexOf(self.curFiling) < 0) u.filing_type.push(self.curFiling);
+                    }
+                } else {
+                    const uid = $(this).data('id');
+                    const pu = self.permissionUser.find(x => { return x.id === uid });
+                    if (pu.filing_type.indexOf(self.curFiling) < 0) pu.filing_type.push(self.curFiling);
+                }
+                self.loadCurFiling();
+            });
+            $('#sync-filing').click(function() {
+                const selectFiling = $('[name=cbft]:checked');
+                if (selectFiling.length === 0) {
+                    toastr.warning('请先选择文档类别');
+                    return;
+                }
+
+                const selectFilingId = [];
+                selectFiling.each((i, x) => { selectFilingId.push(x.value); });
+                self.syncFiling(self.curFiling, selectFilingId);
+                toastr.success('同步成功');
+                $('[name=cbft]').each((i, x) => { x.checked = false; });
+            });
+            $('#batch-del-filing').click(() => {
+                const selectUser = $('[name=ftu-check]:checked');
+                if (selectUser.length === 0) {
+                    toastr.warning('请先选择用户');
+                    return;
+                }
+                const userId = [];
+                selectUser.each((i, x) => { userId.push(x.getAttribute('uid')); });
+                self.delFiling(self.curFiling, userId);
+                self.loadCurFiling();
+            });
+            $('body').on('click', '[name=del-filing]', function() {
+                const id = this.getAttribute('uid');
+                self.delFiling(self.curFiling, id);
+                self.loadCurFiling();
+            })
+        }
+        analysisFiling(data) {
+            this.permissionUser = data;
+            this.permissionUser.forEach(x => { x.filing_type = x.filing_type ? x.filing_type.split(',') : []; });
+
+            this.company = [];
+            for (const pu of this.permissionUser) {
+                let c = this.company.find(x => { return x.company === pu.company });
+                if (!c) {
+                    c = { id: this.company.length + 1, company: pu.company, users: [] };
+                    this.company.push(c);
+                }
+                c.users.push(pu);
+            }
+        }
+        loadCurFiling() {
+            const html = [];
+            for (const f of this.permissionUser) {
+                if (f.filing_type.indexOf(this.curFiling) < 0) continue;
+                html.push('<tr>');
+                html.push(`<td><input uid="${f.id}" type="checkbox" name="ftu-check"></td>`);
+                html.push(`<td>${f.name}</td>`);
+                html.push(`<td>${moment(f.create_time).format('YYYY-MM-DD HH:mm:ss')}</td>`);
+                html.push(`<td>${f.file_permission}</td>`);
+                html.push(`<td><button class="btn btn-sm btn-outline-danger" uid="${f.id}" name="del-filing">移除</button></td>`);
+                html.push('</tr>');
+            }
+            $(this.setting.list).html(html.join(''));
+        }
+        setCurFiling(filingType) {
+            this.curFiling = filingType;
+            $('[name=ftName]').removeClass('bg-warning-50');
+            $(`[ftid=${filingType}]`).addClass('bg-warning-50');
+            this.loadCurFiling();
+        }
+        loadPermissionUser() {
+            const html = [];
+            for (const c of this.company) {
+                html.push(`<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${c.id}" data-type="hide"><i class="fa fa-plus-square"></i></a> ${c.company}</dt>`);
+                html.push(`<div class="dd-content" data-toggleid="${c.id}">`);
+                html.push(`<dd class="border-bottom p-2 mb-0 " data-id="${c.id}" data-type="all"><p class="mb-0 d-flex"><span class="text-primary">添加单位下全部用户</span></p></dd>`);
+                for (const u of c.users) {
+                    html.push(`<dd class="border-bottom p-2 mb-0 " data-id="${u.id}" >`);
+                    html.push(`<p class="mb-0 d-flex"><span class="text-primary">${u.name}</span><span class="ml-auto">${u.mobile}</span></p>`);
+                    html.push(`<span class="text-muted">${u.role}</span>`);
+                    html.push(`</dd>`);
+                }
+                html.push('</div>');
+            }
+            $('#puList').html(html.join(''));
+        }
+        loadPermission() {
+            const self = this;
+            postData('permission', {}, function(result) {
+                self.analysisFiling(result);
+                if (!self.curFiling) {
+                    self.setCurFiling($('[name=ftName]').attr('ftid'));
+                } else {
+                    self.loadCurFiling();
+                }
+                self.loadPermissionUser();
+            });
+        }
+        syncFiling(sourceId, targetIds) {
+            for (const pu of this.permissionUser) {
+                if (pu.filing_type.indexOf(sourceId) >= 0) {
+                    targetIds.forEach(id => {
+                        if (pu.filing_type.indexOf(id) < 0) pu.filing_type.push(id);
+                    });
+                } else {
+                    targetIds.forEach(id => {
+                        if (pu.filing_type.indexOf(id) >= 0) pu.filing_type.splice(pu.filing_type.indexOf(id), 1);
+                    })
+                }
+            }
+        }
+        delFiling(filingId, userId) {
+            const userIds = userId instanceof Array ? userId : [userId];
+            for (const id of userIds) {
+                const pu = this.permissionUser.find(x => { return x.id === id });
+                if (!pu) continue;
+                if (pu.filing_type.indexOf(filingId) >= 0) pu.filing_type.splice(pu.filing_type.indexOf(filingId), 1);
+            }
+        }
+        savePermission() {
+            const self = this;
+            const data = this.permissionUser.map(x => {
+                return { id: x.id, filing_type: x.filing_type.join(',') };
+            });
+            postData('permission/save', data, function(result) {
+                $(self.setting.modal).modal('hide');
+            });
+        }
+    }
+    const filingPermission = new FilingPermission({
+        modal: '#filing-permission',
+        list: '#filing-valid',
+    });
+});

+ 3 - 3
app/public/js/file.js

@@ -37,7 +37,7 @@ $(document).ready(() => {
                     }
                     }
                 } else {
                 } else {
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
                     html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push(`<a href="/file/${node.id}/compare" name="name" id="${node.id}">`, node.name, '</a>');
+                    html.push(`<a href="/sp/${node.id}/file" name="name" id="${node.id}">`, node.name, '</a>');
                 }
                 }
                 html.push('</td>');
                 html.push('</td>');
                 // 管理单位
                 // 管理单位
@@ -53,7 +53,7 @@ $(document).ready(() => {
                     html.push(`<td></td>`);
                     html.push(`<td></td>`);
                 } else {
                 } else {
                     html.push(`<td>`);
                     html.push(`<td>`);
-                    html.push('<button class="btn btn-outline-primary btn-sm" data-target="#select-rela" name="del" onclick="showModal(this);">关联标段</button>');
+                    if (node.manage_permission.indexOf(1) >= 0) html.push('<button class="btn btn-outline-primary btn-sm" data-target="#select-rela" name="del" onclick="showModal(this);">关联标段</button>');
                     html.push('</td>');
                     html.push('</td>');
                 }
                 }
                 return html.join('');
                 return html.join('');
@@ -137,7 +137,7 @@ $(document).ready(() => {
             });
             });
             $('#select-rela-ok').click(() => {
             $('#select-rela-ok').click(() => {
                 const rela = self.getSelects();
                 const rela = self.getSelects();
-                postData('/subproj/save', { id: curProject.id, rela_tender: rela.join(',') }, function () {
+                postData('/subproj/rela/save', { id: curProject.id, rela_tender: rela.join(',') }, function () {
                     $(`[tree_id=${curProject.id}]`)[0].setAttribute('rela-tender', rela.join(','));
                     $(`[tree_id=${curProject.id}]`)[0].setAttribute('rela-tender', rela.join(','));
                     $('#select-rela').modal('hide');
                     $('#select-rela').modal('hide');
                 });
                 });

+ 1 - 0
app/public/js/path_tree.js

@@ -406,6 +406,7 @@ const createNewPathTree = function (type, setting) {
             }
             }
             parents.sort((x, y) => { return y.level - x.level});
             parents.sort((x, y) => { return y.level - x.level});
             for (const parent of parents) {
             for (const parent of parents) {
+                if (!parent.children || parent.children.length === 0) continue;
                 parent.filter = !parent.children.find(x => { return !x.filter });
                 parent.filter = !parent.children.find(x => { return !x.filter });
 
 
             }
             }

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

@@ -129,7 +129,7 @@ $(function () {
     }
     }
 
 
     // 首次加载展示目录
     // 首次加载展示目录
-    postData('/payment/load', {}, function (result) {
+    postData('/payment/list/load', {}, function (result) {
         allFolders = result.folderList;
         allFolders = result.folderList;
         allTenders = result.tenderList;
         allTenders = result.tenderList;
         makeList(result.folderList, result.tenderList);
         makeList(result.folderList, result.tenderList);

+ 339 - 0
app/public/js/payment_process.js

@@ -1,3 +1,342 @@
 $(function () {
 $(function () {
     autoFlashHeight();
     autoFlashHeight();
+
+    // 全选报表
+    $('#select_all_rpt_checkbox').click(function () {
+        $('#rpt_table input[name="rptId[]"]').prop('checked', true);
+        $(this).prop('checked', false);
+    });
+
+    $('#add_rpt_btn').click(function () {
+        const checkArray = [];
+        $('#rpt_table input[name="rptId[]"]:checked').each(function() {
+            checkArray.push({
+                id: parseInt($(this).val()),
+                name: $(this).attr('data-name'),
+            }); //向数组中添加元素
+        });
+        postData('/payment/' + tenderId + '/process/save', { type: 'add-rpt', rpt_list: checkArray }, function (result) {
+            const html = [];
+            for (const data of result) {
+                html.push(`<tr data-id="${data.id}">\n`);
+                html.push(`<td>${data.rpt_name}</td>\n`);
+                html.push(`<td>${data.user_name}</td>\n`);
+                html.push('</tr>\n');
+            }
+            $('#tender_rpt_table').html(html.join(''));
+            $('#add-rpt').modal('hide');
+            tenderRptList = result;
+        })
+    });
+
+    let timer = null;
+    let oldSearchVal = null;
+    $('body').on('input propertychange', '.gr-search', function(e) {
+        oldSearchVal = e.target.value;
+        timer && clearTimeout(timer);
+        timer = setTimeout(() => {
+            const newVal = $(this).val();
+            const code = $(this).attr('data-trid');
+            let html = '';
+            if (newVal && newVal === oldSearchVal) {
+                accountList.filter(item => item && item.id !== cur_uid && (item.name.indexOf(newVal) !== -1 || (item.mobile && item.mobile.indexOf(newVal) !== -1))).forEach(item => {
+                    html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                        <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                class="ml-auto">${item.mobile || ''}</span></p>
+                        <span class="text-muted">${item.role || ''}</span>
+                    </dd>`
+                });
+                $('#' + code + '_dropdownMenu .book-list').empty();
+                $('#' + code + '_dropdownMenu .book-list').append(html);
+            } else {
+                if (!$('#' + code + '_dropdownMenu .acc-btn').length) {
+                    accountGroup.forEach((group, idx) => {
+                        if (!group) return;
+                        html += `<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="${idx}" data-type="hide"><i class="fa fa-plus-square"></i>
+                        </a> ${group.groupName}</dt>
+                        <div class="dd-content" data-toggleid="${idx}">`;
+                        group.groupList.forEach(item => {
+                            if (item.id !== cur_uid) {
+                                html += `<dd class="border-bottom p-2 mb-0 " data-id="${item.id}" >
+                                    <p class="mb-0 d-flex"><span class="text-primary">${item.name}</span><span
+                                            class="ml-auto">${item.mobile || ''}</span></p>
+                                    <span class="text-muted">${item.role || ''}</span>
+                                </dd>`;
+                            }
+                        });
+                        html += '</div>';
+                    });
+                    $('#' + code + '_dropdownMenu .book-list').empty();
+                    $('#' + code + '_dropdownMenu .book-list').append(html);
+                }
+            }
+        }, 400);
+    });
+
+    function setLcShowHtml(this_status, this_tr_id, data) {
+        if (this_status === sp_status.sqspr) {
+            $('#process_set').find('.lc-show').html('');
+        } else if (this_status === sp_status.gdspl) {
+            let addhtml = '<ul class="list-unstyled">\n';
+            if (data.length !== 0) {
+                for(const [i, audit] of data.entries()) {
+                    addhtml += makeAudit(audit, transFormToChinese(i+1));
+                }
+                addhtml += '<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>';
+            } else {
+                addhtml += makeSelectAudit(this_tr_id, '一');
+            }
+            addhtml += '</ul>\n';
+            $('#process_set').find('.lc-show').html(addhtml);
+
+        } else if (this_status === sp_status.gdzs) {
+            let addhtml = '<ul class="list-unstyled">\n' +
+                '                                        <li class="d-flex justify-content-start mb-3">\n' +
+                '                                            <span class="col-auto">授权审批人</span>\n' +
+                '                                            <span class="col-7">\n' +
+                '                                                <span class="d-inline-block"></span>\n' +
+                '                                            </span>\n' +
+                '                                        </li>\n';
+            addhtml += data ? makeAudit(data) : makeSelectAudit(this_tr_id);
+            addhtml += '</ul>\n';
+            $('#process_set').find('.lc-show').html(addhtml);
+        }
+    }
+
+    // 初始化选中table
+    if ($('#tender_rpt_table tr').length > 0) {
+        $('#tender_rpt_table tr').eq(0).addClass('table-warning');
+        const tr_id = parseInt($('#tender_rpt_table tr').eq(0).attr('data-id'));
+        $('#process_set').show();
+        makeProcess(tr_id);
+    }
+
+    // 选中切换table
+    $('#tender_rpt_table tr').click(function () {
+        if (!$(this).hasClass('table-warning')) {
+            $('#tender_rpt_table tr').removeClass('table-warning');
+            $(this).addClass('table-warning');
+            const tr_id = parseInt($(this).attr('data-id'));
+            makeProcess(tr_id);
+        }
+    });
+
+    // 初始化审批流程
+    function makeProcess(tr_id) {
+        const trInfo = _.find(tenderRptList, { id: tr_id });
+        $('#process_set').find('.card-title').text(trInfo.rpt_name);
+        $('#process_set input[name="tender_process"][value="'+ trInfo.sp_status +'"]').prop('checked', true);
+        const spt = sp_status_list[trInfo.sp_status];
+        $('#process_set').find('.alert-warning').text(spt.name + ':' + spt.msg);
+        const prop = {
+            type: 'get-audits',
+            tr_id: trInfo.id,
+            status: trInfo.sp_status,
+        };
+        postData('/payment/' + tenderId + '/process/save', prop, function (data) {
+            setLcShowHtml(trInfo.sp_status, trInfo.id, data);
+        });
+    }
+
+    // 添加审批流程按钮逻辑
+    $('body').on('click', '.book-list dt', function () {
+        const idx = $(this).find('.acc-btn').attr('data-groupid');
+        const type = $(this).find('.acc-btn').attr('data-type');
+        if (type === 'hide') {
+            $(this).parent().find(`div[data-toggleid="${idx}"]`).show(() => {
+                $(this).children().find('i').removeClass('fa-plus-square').addClass('fa-minus-square-o');
+                $(this).find('.acc-btn').attr('data-type', 'show');
+
+            })
+        } else {
+            $(this).parent().find(`div[data-toggleid="${idx}"]`).hide(() => {
+                $(this).children().find('i').removeClass('fa-minus-square-o').addClass('fa-plus-square');
+                $(this).find('.acc-btn').attr('data-type', 'hide');
+            })
+        }
+        return false;
+    });
+
+
+    // 更改审批流程状态
+    $('#process_set .form-check input').on('change', function () {
+        // 获取所有审批的checked值并更新
+        const this_status = parseInt($(this).val());
+        const this_tr_id = parseInt($('#tender_rpt_table').find('.table-warning').attr('data-id'));
+        const spt = sp_status_list[this_status];
+        $(this).parents('.form-group').siblings('.alert-warning').text(spt.name + ':' + spt.msg);
+        // 拼接post json
+        const prop = {
+            type: 'change-status',
+            tr_id: this_tr_id,
+            status: this_status
+        };
+        postData('/payment/' + tenderId + '/process/save', prop, function (data) {
+            setLcShowHtml(this_status, this_tr_id, data);
+        });
+    });
+
+    // 选中审批人
+    $('body').on('click', 'dl dd', function () {
+        const id = parseInt($(this).data('id'));
+        if (!id) return;
+
+        let this_tr_id = parseInt($('#tender_rpt_table').find('.table-warning').attr('data-id'));
+        if (!this_tr_id) this_tr_id = $(this).parents('.dropdown').attr('data-trid');
+        const user = _.find(accountList, function (item) {
+            return item.id === id;
+        });
+
+        const this_status = parseInt($(this).parents('.lc-show').siblings('.form-group').find('input:checked').val());
+        if (this_status === sp_status.gdspl) {
+            // 判断是否已存在审批人
+            const aid_num = $(this).parents('ul').find('.remove-audit').length;
+            for (let i = 0; i < aid_num; i++) {
+                const aid = parseInt($(this).parents('ul').find('.remove-audit').eq(i).data('id'));
+                if (aid === id) {
+                    toastr.warning('该审核人已存在,请勿重复添加');
+                    return;
+                }
+            }
+
+        }
+        const prop = {
+            status: this_status,
+            tr_id: this_tr_id,
+            audit_id: id,
+            type: 'add-audit',
+        };
+        const _self = $(this);
+        postData('/payment/' + tenderId + '/process/save', prop, function (data) {
+            if (this_status === sp_status.gdspl) {
+                _self.parents('ul').append('<li class="pl-3"><a href="javascript:void(0);" class="add-audit"><i class="fa fa-plus"></i> 添加流程</a></li>');
+            }
+            _self.parents('.spr-span').html('<span class="d-inline-block"></span>\n' +
+                '<span class="d-inline-block"><span class="badge badge-light">'+ user.name +' <span class="dropdown">\n' +
+                '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
+                '                                                            <div class="dropdown-menu">\n' +
+                '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
+                '                                                                <div class="dropdown-divider"></div>\n' +
+                '                                                                <div class="px-2 py-1 text-center">\n' +
+                '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + user.id + '">移除</button>\n' +
+                '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
+                '                                                                </div>\n' +
+                '                                                            </div>\n' +
+                '                                                        </span> ' +
+                '                                            </span></span></span>\n');
+            // <a href="javascript:void(0);" class="remove-audit btn-sm text-danger px-1" title="移除" data-id="'+ user.id +'"><i class="fa fa-remove"></i></a></span> </span>');
+        });
+    });
+
+    // 移除审批人
+    $('body').on('click', '.remove-audit', function () {
+        const id = parseInt($(this).data('id'));
+        const this_status = parseInt($(this).parents('.lc-show').siblings('.form-group').find('input:checked').val());
+        const this_tr_id = parseInt($('#tender_rpt_table').find('.table-warning').attr('data-id'));
+        const prop = {
+            status: this_status,
+            tr_id: this_tr_id,
+            audit_id: id,
+            type: 'del-audit',
+        };
+        const _self = $(this);
+        postData('/payment/' + tenderId + '/process/save', prop, function (data) {
+            if (this_status === sp_status.gdspl) {
+                const _selflc = _self.parents('.lc-show');
+                _self.parents('li').remove();
+                const aid_num = parseInt(_selflc.children('ul').find('li.d-flex').length);
+                if (aid_num === 0) {
+                    let addhtml = '<ul class="list-unstyled">\n';
+                    addhtml += makeSelectAudit(this_tr_id, '一');
+                    addhtml += '</ul>\n';
+                    _selflc.html(addhtml);
+                } else {
+                    for (let i = 0; i < aid_num; i++) {
+                        _selflc.find('li.d-flex').eq(i).find('.col-auto').text(transFormToChinese(i+1) + '审');
+                    }
+                }
+            } else if (this_status === sp_status.gdzs) {
+                let addhtml = '<ul class="list-unstyled">\n' +
+                    '                                        <li class="d-flex justify-content-start mb-3">\n' +
+                    '                                            <span class="col-auto">授权审批人</span>\n' +
+                    '                                            <span class="col-7">\n' +
+                    '                                                <span class="d-inline-block"></span>\n' +
+                    '                                            </span>\n' +
+                    '                                        </li>\n';
+                addhtml += makeSelectAudit(this_tr_id);
+                addhtml += '</ul>\n';
+                _self.parents('.lc-show').html(addhtml);
+            }
+        })
+    });
+
+    // 固定审批流-添加流程
+    $('body').on('click', '.add-audit', function () {
+        const num = $(this).parents('ul').children('li').length;
+        const this_tr_id = parseInt($('#tender_rpt_table').find('.table-warning').attr('data-id'));
+        const addhtml = makeSelectAudit(this_tr_id, transFormToChinese(num));
+        $(this).parents('ul').append(addhtml);
+        $(this).parents('li').remove();
+    });
+
+    // 审批流程-审批人html 生成
+    function makeAudit(audit, i = '终') {
+        return '<li class="d-flex justify-content-start mb-3">\n' +
+            '                                            <span class="col-auto">'+ i +'审</span>\n' +
+            '                                            <span class="col-7 spr-span">\n' +
+            '                                            <span class="d-inline-block"></span>\n' +
+            '                                            <span class="d-inline-block"><span class="badge badge-light">'+ audit.name +' <span class="dropdown">\n' +
+            '                                                            <a href="javascript:void(0);" class="btn-sm text-danger px-1" title="移除" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa fa-remove"></i></a>\n' +
+            '                                                            <div class="dropdown-menu">\n' +
+            '                                                                <a class="dropdown-item" href="javascript:void(0);">确认移除审批人?</a>\n' +
+            '                                                                <div class="dropdown-divider"></div>\n' +
+            '                                                                <div class="px-2 py-1 text-center">\n' +
+            '                                                                    <button class="remove-audit btn btn-sm btn-danger" data-id="' + audit.audit_id + '">移除</button>\n' +
+            '                                                                    <button class="btn btn-sm btn-secondary">取消</button>\n' +
+            '                                                                </div>\n' +
+            '                                                            </div>\n' +
+            '                                                        </span> ' +
+            // '<a href="javascript:void(0);" class="remove-audit btn-sm text-danger px-1" title="移除" data-id="'+ audit.audit_id +'"><i class="fa fa-remove"></i></a></span> </span>\n' +
+            '                                            </span></span></span>\n' +
+            '                                        </li>';
+    }
+
+    // 审批流程-选择审批人html 生成
+    function makeSelectAudit(tr_id, i = '终') {
+        let divhtml = '';
+        accountGroup.forEach((group, idx) => {
+            let didivhtml = '';
+            if(group) {
+                group.groupList.forEach(item => {
+                    didivhtml += item.id !== cur_uid ? '<dd class="border-bottom p-2 mb-0 " data-id="' + item.id + '" >\n' +
+                        '<p class="mb-0 d-flex"><span class="text-primary">' + item.name + '</span><span\n' +
+                        '                                                                                class="ml-auto">' + item.mobile + '</span></p>\n' +
+                        '                                                                    <span class="text-muted">' + item.role + '</span>\n' +
+                        '                                                                    </dd>\n' : '';
+                });
+                divhtml += '<dt><a href="javascript: void(0);" class="acc-btn" data-groupid="' + idx + '" data-type="hide"><i class="fa fa-plus-square"></i></a> ' + group.groupName + '</dt>\n' +
+                    '                                                                <div class="dd-content" data-toggleid="' + idx + '">\n' + didivhtml +
+                    '                                                                </div>\n';
+            }
+        });
+        let html = '<li class="d-flex justify-content-start mb-3">\n' +
+            '                                            <span class="col-auto">' + i + '审</span>\n' +
+            '                                            <span class="col-7 spr-span">\n' +
+            '                                            <span class="d-inline-block">\n' +
+            '                                                <div class="dropdown text-right">\n' +
+            '                                                    <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="' + tr_id + '_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+            '                                                        选择审批人\n' +
+            '                                                    </button>\n' +
+            '                                                    <div class="dropdown-menu dropdown-menu-right" id="' + tr_id + '_dropdownMenu" aria-labelledby="' + tr_id + '_dropdownMenuButton" style="width:220px">\n' +
+            '                                                        <div class="mb-2 p-2"><input class="form-control form-control-sm gr-search"\n' +
+            '                                                                                     placeholder="姓名/手机 检索" autocomplete="off" data-trid="' + tr_id + '"></div>\n' +
+            '                                                        <dl class="list-unstyled book-list">\n' + divhtml +
+            '                                                        </dl>\n' +
+            '                                                    </div>\n' +
+            '                                                </div>\n' +
+            '                                            </span>\n' +
+            '                                        </span>\n' +
+            '                                        </li>';
+        return html;
+    }
 });
 });

+ 58 - 0
app/public/js/shares/ali_oss.js

@@ -0,0 +1,58 @@
+const AliOss = (function (){
+    const setting = {
+        hint: true
+    };
+
+    const downloadFile = function (url, filename) {
+        axios.get(url, {responseType: 'blob' }).then(res => {
+            saveAs(res.data, filename);
+        });
+    };
+
+    const downloadFileSync = function(url) {
+        return new Promise((resolve, reject) => {
+            axios.get(url, {responseType: 'blob'}).then(res => {
+                resolve(res.data);
+            }).catch(err => {
+                reject(err);
+            });
+        })
+    };
+
+    const zipFiles = function (files, filename = '打包.zip', successCallback, errorCallback) {
+        const zip = new JSZip();
+        const download = [], fails = [];
+        files.forEach(f => {
+            download.push(downloadFileSync(f.filepath).then(data => {
+                if (setting.hint) {
+                    toastr.success(`文件 “${f.filename + f.fileext}” 下载成功...`);
+                }
+                zip.file(f.filename + f.fileext, data, {binary: true});
+            }).catch(err => {
+                fails.push(f);
+            }));
+        });
+        Promise.all(download).then(() => {
+            toastr.clear();
+            if (fails.length < files.length) {
+                if (setting.hint) {
+                    toastr.success('所有文件下载成功,压缩中...');
+                }
+                zip.generateAsync({ type: "blob" }).then(content => {
+                    saveAs(content, filename);
+                    successCallback && successCallback(fails);
+                });
+            } else {
+                errorCallback && errorCallback(fails);
+            }
+        })
+    };
+
+    const setSetting = function(data) {
+        if (!data) return;
+
+        setting.hint = data.hint || setting.hint;
+    };
+
+    return { downloadFile, downloadFileSync, zipFiles, setSetting }
+})();

+ 10 - 0
app/public/js/shares/drag_tree.js

@@ -439,6 +439,16 @@ const createDragTree = function (setting) {
             }
             }
             return result;
             return result;
         }
         }
+
+        recursiveFun(children, fun) {
+            if (!fun) return;
+            if (!children || children.length === 0) return;
+
+            for (const c of children) {
+                this.recursiveFun(c.children, fun);
+                fun(c);
+            }
+        }
     }
     }
     return new DragTree(setting);
     return new DragTree(setting);
 };
 };

+ 12 - 2
app/public/js/shenpi.js

@@ -59,7 +59,7 @@ function initTenderTree () {
                 if (index === 0 && tender.category) {
                 if (index === 0 && tender.category) {
                     for (const [i,c] of tender.category.entries()) {
                     for (const [i,c] of tender.category.entries()) {
                         const cate = findNode('id', c.cid, category);
                         const cate = findNode('id', c.cid, category);
-                        tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
+                        if (cate) tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
                     }
                     }
                 }
                 }
                 return tenderCategory;
                 return tenderCategory;
@@ -684,6 +684,9 @@ $(document).ready(function () {
             $('#del-audit-ass').click(function () {
             $('#del-audit-ass').click(function () {
                 self.setAuditAssist();
                 self.setAuditAssist();
             });
             });
+            $('body').on('click', 'button[asid]', function () {
+                self.removeAuditAss(parseInt(this.getAttribute('asid')));
+            });
         }
         }
         set uid(id) {
         set uid(id) {
             this._uid = parseInt(id);
             this._uid = parseInt(id);
@@ -697,7 +700,7 @@ $(document).ready(function () {
             const html = [];
             const html = [];
             for (const sa of this.showAssList) {
             for (const sa of this.showAssList) {
                 const lid = sa.ass_ledger_id ? sa.ass_ledger_id.split(',') : [];
                 const lid = sa.ass_ledger_id ? sa.ass_ledger_id.split(',') : [];
-                html.push(`<tr><td>${sa.name}</td><td>${sa.company}</td><td>${lid.length}</td></tr>`);
+                html.push(`<tr><td>${sa.name}</td><td>${sa.company}</td><td>${lid.length}<button class="ml-2 btn btn-sm btn-outline-danger" asid="${sa.id}">移除</button></td></tr>`);
             }
             }
             $('#coo_table').html(html.join(''));
             $('#coo_table').html(html.join(''));
         }
         }
@@ -784,6 +787,13 @@ $(document).ready(function () {
                 self.refreshOperate();
                 self.refreshOperate();
             });
             });
         }
         }
+        removeAuditAss(assistId) {
+            const self = this;
+            postData('/tender/' + cur_tenderid + '/shenpi/audit/save', { type: 'audit-ass', del: { id: assistId } }, function (result) {
+                self.loadPostData(result);
+                self.refreshOperate();
+            });
+        }
         refreshOperate() {
         refreshOperate() {
             const node = SpreadJsObj.getSelectObject(this.sheet);
             const node = SpreadJsObj.getSelectObject(this.sheet);
             if (node.ass_audit_id) {
             if (node.ass_audit_id) {

+ 3 - 2
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -2954,7 +2954,8 @@ const SpreadJsObj = {
              * @param pattern
              * @param pattern
              */
              */
             proto.formatNum = function (num, pattern) {
             proto.formatNum = function (num, pattern) {
-                var strarr = num?num.toString().split('.'):['0'];
+                var minusStr = num < 0 ? '-' : '';
+                var strarr = num ? Math.abs(num).toString().split('.'):['0'];
                 var fmtarr = pattern?pattern.split('.'):[''];
                 var fmtarr = pattern?pattern.split('.'):[''];
                 var retstr='';
                 var retstr='';
 
 
@@ -3005,7 +3006,7 @@ const SpreadJsObj = {
                             break;
                             break;
                     }
                     }
                 }
                 }
-                return retstr.replace(/^,+/,'').replace(/\.$/,'');
+                return minusStr + retstr.replace(/^,+/,'').replace(/\.$/,'');
             };
             };
             proto.format = function (obj, formattedData) {
             proto.format = function (obj, formattedData) {
                 if (this.pattern) {
                 if (this.pattern) {

+ 47 - 11
app/public/js/stage.js

@@ -358,13 +358,7 @@ $(document).ready(() => {
             // 初始化 清单编号窗口 参数
             // 初始化 清单编号窗口 参数
             this.spreadSetting = {
             this.spreadSetting = {
                 cols: [
                 cols: [
-                    {title: '已用', field: '', width: 45, formatter: '@', cellType: 'image', readOnly: true, hAlign: 1, indent: 14, img: function (data) {
-                        if (data.uamount && !checkZero(data.uamount)) {
-                            return $('#icon-ok')[0];
-                        } else {
-                            return null;
-                        }
-                    }},
+                    {title: '已用', field: 'select', width: 45, formatter: '@', cellType: 'checkbox', hAlign: 1, indent: 14, getValue: function (data) {return data.uamount && !checkZero(data.uamount);}},
                     {title: '变更令号', field: 'p_code', width: 100, formatter: '@', readOnly: true, hAlign: 0, },
                     {title: '变更令号', field: 'p_code', width: 100, formatter: '@', readOnly: true, hAlign: 0, },
                     {title: '名称', field: 'name', width: 120, formatter: '@', readOnly: true, hAlign: 0,},
                     {title: '名称', field: 'name', width: 120, formatter: '@', readOnly: true, hAlign: 0,},
                     {title: '变更部位', field: 'b_bwmx', width: 100, formatter: '@', readOnly: true, hAlign: 0,},
                     {title: '变更部位', field: 'b_bwmx', width: 100, formatter: '@', readOnly: true, hAlign: 0,},
@@ -413,6 +407,8 @@ $(document).ready(() => {
             this.spread.bind(spreadNS.Events.EditEnded, function (e, info) {
             this.spread.bind(spreadNS.Events.EditEnded, function (e, info) {
                 if (info.sheet.zh_setting) {
                 if (info.sheet.zh_setting) {
                     const col = info.sheet.zh_setting.cols[info.col];
                     const col = info.sheet.zh_setting.cols[info.col];
+                    if (col.cellType === 'checkbox') return;
+
                     const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
                     const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
                     const node = sortData[info.row];
                     const node = sortData[info.row];
                     node[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
                     node[col.field] = col.type === 'Number' ? parseFloat(info.editingText) : info.editingText;
@@ -432,6 +428,19 @@ $(document).ready(() => {
                     SpreadJsObj.reLoadRowData(sheet, info.cellRange.row, sel.cellRange.rowCount);
                     SpreadJsObj.reLoadRowData(sheet, info.cellRange.row, sel.cellRange.rowCount);
                 }
                 }
             });
             });
+            this.spread.bind(spreadNS.Events.ButtonClicked, function(e, info) {
+                if (!info.sheet.zh_setting) return;
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                if (col.cellType === 'checkbox') {
+                    if (info.sheet.isEditing()) {
+                        info.sheet.endEdit(true);
+                    }
+                    const select = info.sheet.zh_data[info.row];
+                    select.uamount = select.uamount ? 0 : select.vamount;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+                }
+            });
             SpreadJsObj.addDeleteBind(this.spread, function (sheet) {
             SpreadJsObj.addDeleteBind(this.spread, function (sheet) {
                 if (sheet.zh_setting) {
                 if (sheet.zh_setting) {
                     const sel = sheet.getSelections()[0];
                     const sel = sheet.getSelections()[0];
@@ -2846,7 +2855,7 @@ $(document).ready(() => {
                         : '';
                         : '';
                     const viewHtml = att.viewpath ? `<a class="ml-1" href="${att.viewpath}" target="_blank" title="预览"><i class="fa fa-eye"></i></a>` : '';
                     const viewHtml = att.viewpath ? `<a class="ml-1" href="${att.viewpath}" target="_blank" title="预览"><i class="fa fa-eye"></i></a>` : '';
                     const downloadHtml = '<a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a>';
                     const downloadHtml = '<a href="'+ window.location.pathname + '/im-file/download?im_id='+ data.att_uuid +'&file_id='+ att.file_id +'" title="下载"><i class="fa fa-download "></i></a>';
-                    html.push('<tr><td style="width: 200px">' + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>',
+                    html.push(`<tr><td><input type="checkbox" name="sia-check" fid="${att.file_id}"></td><td style="width: 200px">` + att.filename + att.fileext + '</td><td>' + att.username + '</td><td>' + att.in_time + '</td>',
                         '<td>', downloadHtml, viewHtml, delHtml, '</td></tr>');
                         '<td>', downloadHtml, viewHtml, delHtml, '</td></tr>');
                 }
                 }
             }
             }
@@ -2932,7 +2941,34 @@ $(document).ready(() => {
                     $('#load-file').attr('href', filepath);
                     $('#load-file').attr('href', filepath);
                     $('#load-file')[0].click();
                     $('#load-file')[0].click();
                 });
                 });
-            })
+            });
+            $('#batch-download-im-file').click(function() {
+                const select = $('[name=sia-check]:checked');
+                if (select.length === 0) {
+                    toastr.warning('请选择需要批量下载的文件');
+                    return;
+                }
+
+                const files = [];
+                const curIm = SpreadJsObj.getSelectObject(self.sheet);
+                for (const s of select) {
+                    const att = curIm.attachment.find(x => { return x.file_id === s.getAttribute('fid')});
+                    if (att) files.push(att);
+                }
+                if (files.length === 0) return;
+
+                AliOss.zipFiles(files, `中间计量[${curIm.code}].zip`, (fails) => {
+                    $(self).removeAttr('disabled');
+                    if (fails.length === 0) {
+                        toastr.success('下载成功');
+                    } else {
+                        toastr.warning(`下载成功(${fails.length}个文件下载失败)`);
+                    }
+                }, () => {
+                    $(self).removeAttr('disabled');
+                    toastr.error('批量下载失败');
+                });
+            });
         }
         }
         _initImTypeSetRela() {
         _initImTypeSetRela() {
             const self = this;
             const self = this;
@@ -3894,7 +3930,7 @@ $(document).ready(() => {
                             return !changeBills || curChange.is_import;
                             return !changeBills || curChange.is_import;
                         },
                         },
                         visible: function (key, opt) {
                         visible: function (key, opt) {
-                            return false;
+                            return is_debug && stage.status === 1;
                         }
                         }
                     },
                     },
                 }
                 }
@@ -3938,7 +3974,7 @@ $(document).ready(() => {
                             return !changeBills || curChange.is_import;
                             return !changeBills || curChange.is_import;
                         },
                         },
                         visible: function (key, opt) {
                         visible: function (key, opt) {
-                            return false;
+                            return is_debug && stage.status === 1;
                         }
                         }
                     },
                     },
                     'autoUseAll': {
                     'autoUseAll': {

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

@@ -45,7 +45,7 @@ $(document).ready(function() {
                     html.push(`<td class="text-center"></td>`);
                     html.push(`<td class="text-center"></td>`);
                 } else {
                 } else {
                     html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
                     html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
-                    html.push(`<td class="text-center">${node.management || ''}</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></td>`);
                 }
                 }
                 // 操作
                 // 操作
                 html.push(`<td>`);
                 html.push(`<td>`);
@@ -79,10 +79,21 @@ $(document).ready(function() {
             },
             },
             refreshAddButton: function() {
             refreshAddButton: function() {
                 const select = this.getSelectNode();
                 const select = this.getSelectNode();
-                $('[href="#add-folder"]').attr('disabled', select && select.tree_level >= 4);
+                // $('[href="#add-folder"]').attr("disabled", select && (select.tree_level >= 4 || !select.is_folder));
+                if (select && (select.tree_level >= 4 || !select.is_folder)) {
+                    $('[href="#add-folder"]').hide();
+                } else {
+                    $('[href="#add-folder"]').show();
+                }
+                if (select && !select.is_folder) {
+                    $('[href="#add-project"]').hide();
+                } else {
+                    $('[href="#add-project"]').show();
+                }
             },
             },
             refreshTreeTable: function(result) {
             refreshTreeTable: function(result) {
                 ProjectTree.loadDatas(result);
                 ProjectTree.loadDatas(result);
+                if (ProjectTree.nodes.length > 0 && $('#no-project').length > 0) window.location.reload();
                 Utils.reloadTable();
                 Utils.reloadTable();
             },
             },
             refreshRow: function(result) {
             refreshRow: function(result) {
@@ -160,6 +171,13 @@ $(document).ready(function() {
             $('[name=std_id]').attr('tree_id', treeId);
             $('[name=std_id]').attr('tree_id', treeId);
             $('#set-std').modal('show');
             $('#set-std').modal('show');
         });
         });
+        $('body').on('click', 'a[name=set-management]', function(e) {
+            const treeId = $(this).parent().parent().attr('tree_id');
+            const node = ProjectTree.getItems(treeId);
+            if (node.is_folder) return;
+            $('#sm-management').attr('tree_id', treeId);
+            $('#set-management').modal('show');
+        });
         return { ProjectTree, TableObj, ...Utils };
         return { ProjectTree, TableObj, ...Utils };
     })({
     })({
         treeSetting: { id: 'id', pid: 'tree_pid', level: 'tree_level', order: 'tree_order', rootId: '-1' },
         treeSetting: { id: 'id', pid: 'tree_pid', level: 'tree_level', order: 'tree_order', rootId: '-1' },
@@ -216,7 +234,7 @@ $(document).ready(function() {
     });
     });
     $('#edit-project-ok').click(function() {
     $('#edit-project-ok').click(function() {
         const nameObj = $('#edit-project-name');
         const nameObj = $('#edit-project-name');
-        const name = name.val();
+        const name = nameObj.val();
         if (!name || name.length > 100) return;
         if (!name || name.length > 100) return;
         postData('/subproj/save', { id: nameObj.attr('tree_id'), name }, function(result) {
         postData('/subproj/save', { id: nameObj.attr('tree_id'), name }, function(result) {
             projectTreeObj.refreshRow(result);
             projectTreeObj.refreshRow(result);
@@ -234,6 +252,16 @@ $(document).ready(function() {
             $('[name=std_id]').attr('tree_id', '');
             $('[name=std_id]').attr('tree_id', '');
         });
         });
     });
     });
+    $('#set-management-ok').click(function() {
+        const select = $('#sm-management');
+        const id = select.attr('tree_id');
+        const management = select.val();
+        postData('/subproj/save', { id, management }, function(result) {
+            projectTreeObj.refreshRow(result);
+            $('#set-management').modal('hide');
+            $('#sm-management').attr('tree_id', '');
+        });
+    });
 
 
     let timer = null;
     let timer = null;
     let oldSearchVal = null;
     let oldSearchVal = null;
@@ -307,7 +335,7 @@ $(document).ready(function() {
                         <label class="custom-control-label" for="budgetview${mem.uid}"></label></div></td>`);
                         <label class="custom-control-label" for="budgetview${mem.uid}"></label></div></td>`);
             const editBudget = mem.budget_permission.indexOf(permissionConst.budget.edit.value) >= 0;
             const editBudget = mem.budget_permission.indexOf(permissionConst.budget.edit.value) >= 0;
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
-                        <input type="checkbox" ptype="budgetEdit" sptype="edit" id="budgetedit${mem.uid}" uid="${mem.uid}" class="custom-control-input" ${(editBudget ? 'checked' : '')}>
+                        <input type="checkbox" ptype="budget" sptype="edit" id="budgetedit${mem.uid}" uid="${mem.uid}" class="custom-control-input" ${(editBudget ? 'checked' : '')}>
                         <label class="custom-control-label" for="budgetedit${mem.uid}"></label></div></td>`);
                         <label class="custom-control-label" for="budgetedit${mem.uid}"></label></div></td>`);
             // 电子文档
             // 电子文档
 
 
@@ -319,7 +347,7 @@ $(document).ready(function() {
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" ptype="file" sptype="upload" id="fileupload${mem.uid}" uid="${mem.uid}" class="custom-control-input" ${(fileUpload ? 'checked' : '')}>
                         <input type="checkbox" ptype="file" sptype="upload" id="fileupload${mem.uid}" uid="${mem.uid}" class="custom-control-input" ${(fileUpload ? 'checked' : '')}>
                         <label class="custom-control-label" for="fileupload${mem.uid}"></label></div></td>`);
                         <label class="custom-control-label" for="fileupload${mem.uid}"></label></div></td>`);
-            const fileEdit = mem.file_permission.indexOf(permissionConst.file.edit.value) >= 0;
+            const fileEdit = mem.file_permission.indexOf(permissionConst.file.filing.value) >= 0;
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
             html.push(`<td><div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" ptype="file" sptype="eidt" uid="${mem.uid}" id="fileedit${mem.uid}" class="custom-control-input" ${(fileEdit ? 'checked' : '')}>
                         <input type="checkbox" ptype="file" sptype="eidt" uid="${mem.uid}" id="fileedit${mem.uid}" class="custom-control-input" ${(fileEdit ? 'checked' : '')}>
                         <label class="custom-control-label" for="fileedit${mem.uid}"></label></div></td>`);
                         <label class="custom-control-label" for="fileedit${mem.uid}"></label></div></td>`);
@@ -385,9 +413,9 @@ $(document).ready(function() {
             if (pType === 'budget' && spType === 'view') {
             if (pType === 'budget' && spType === 'view') {
                 mem.budget_permission.push(parseInt(permissionConst.budget.view.value));
                 mem.budget_permission.push(parseInt(permissionConst.budget.view.value));
             } else if (pType === 'budget' && spType === 'edit') {
             } else if (pType === 'budget' && spType === 'edit') {
-                mem.budget_permission.push(parseInt(permissionConst.budget.view.value));
+                mem.budget_permission.push(parseInt(permissionConst.budget.edit.value));
                 if (mem.budget_permission.indexOf(permissionConst.budget.view.value) < 0) {
                 if (mem.budget_permission.indexOf(permissionConst.budget.view.value) < 0) {
-                    mem.budget_permission.push(parseInt(permissionConst.budget.edit.value));
+                    mem.budget_permission.push(parseInt(permissionConst.budget.view.value));
                     $(`#budgetview${id}`)[0].checked = true;
                     $(`#budgetview${id}`)[0].checked = true;
                 }
                 }
             } else if (pType === 'file' && spType === 'view') {
             } else if (pType === 'file' && spType === 'view') {
@@ -399,7 +427,7 @@ $(document).ready(function() {
                     $(`#fileview${id}`)[0].checked = true;
                     $(`#fileview${id}`)[0].checked = true;
                 }
                 }
             } else if (pType === 'file' && spType === 'edit') {
             } else if (pType === 'file' && spType === 'edit') {
-                mem.file_permission.push(parseInt(permissionConst.file.view.value));
+                mem.file_permission.push(parseInt(permissionConst.file.edit.value));
                 if (mem.file_permission.indexOf(permissionConst.file.view.value) < 0) {
                 if (mem.file_permission.indexOf(permissionConst.file.view.value) < 0) {
                     mem.file_permission.push(parseInt(permissionConst.file.view.value));
                     mem.file_permission.push(parseInt(permissionConst.file.view.value));
                     $(`#fileview${id}`)[0].checked = true;
                     $(`#fileview${id}`)[0].checked = true;
@@ -420,7 +448,7 @@ $(document).ready(function() {
             } else if (pType === 'file' && spType === 'upload') {
             } else if (pType === 'file' && spType === 'upload') {
                 mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.upload.value), 1);
                 mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.upload.value), 1);
             } else if (pType === 'file' && spType === 'edit') {
             } else if (pType === 'file' && spType === 'edit') {
-                mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.edit.value), 1);
+                mem.file_permission.splice(mem.file_permission.indexOf(permissionConst.file.filing.value), 1);
             } else if (pType === 'manage' && spType === 'rela') {
             } else if (pType === 'manage' && spType === 'rela') {
                 mem.manage_permission = [];
                 mem.manage_permission = [];
             }
             }

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

@@ -109,7 +109,7 @@ function findNode (key, value, arr) {
 }
 }
 function getPId(level) {
 function getPId(level) {
     if (level !== 1) {
     if (level !== 1) {
-        const p = findNode('level', level - 1, levelNodes);
+        const p = findNode('show_level', level - 1, levelNodes);
         if (p) {
         if (p) {
             return p.lid
             return p.lid
         } else {
         } else {
@@ -413,7 +413,7 @@ $(document).ready(() => {
             const node = zTree.getNodeByParam('id', c.id);
             const node = zTree.getNodeByParam('id', c.id);
             const parent = node.getParentNode();
             const parent = node.getParentNode();
             if (c.level > 0) defaultLevel.push({id: c.id, level: c.level});
             if (c.level > 0) defaultLevel.push({id: c.id, level: c.level});
-            if (parent.lid === 2) {
+            if (parent.lid != 1) {
                 selfLevel.push({id: c.id, level: node.getPath().length - 1});
                 selfLevel.push({id: c.id, level: node.getPath().length - 1});
             }
             }
         }
         }

+ 23 - 3
app/router.js

@@ -40,6 +40,9 @@ module.exports = app => {
     // 修订
     // 修订
     const reviseCheck = app.middlewares.reviseCheck();
     const reviseCheck = app.middlewares.reviseCheck();
     const budgetCheck = app.middlewares.budgetCheck();
     const budgetCheck = app.middlewares.budgetCheck();
+    const subProjectCheck = app.middlewares.subProjectCheck();
+    // 支付审批中间件
+    const paymentTenderCheck = app.middlewares.paymentTenderCheck();
     // 登入登出相关
     // 登入登出相关
     app.get('/login', 'loginController.index');
     app.get('/login', 'loginController.index');
     app.get('/login/:code', 'loginController.index');
     app.get('/login/:code', 'loginController.index');
@@ -701,6 +704,7 @@ module.exports = app => {
     app.post('/subproj/dragTo', sessionAuth, projectManagerCheck, 'subProjController.dragTo');
     app.post('/subproj/dragTo', sessionAuth, projectManagerCheck, 'subProjController.dragTo');
     app.post('/subproj/del', sessionAuth, projectManagerCheck, 'subProjController.del');
     app.post('/subproj/del', sessionAuth, projectManagerCheck, 'subProjController.del');
     app.post('/subproj/save', sessionAuth, projectManagerCheck, 'subProjController.save');
     app.post('/subproj/save', sessionAuth, projectManagerCheck, 'subProjController.save');
+    app.post('/subproj/rela/save', sessionAuth, 'subProjController.saveRela');
     app.post('/subproj/rela', sessionAuth, 'subProjController.rela');
     app.post('/subproj/rela', sessionAuth, 'subProjController.rela');
     app.post('/subproj/member', sessionAuth, projectManagerCheck, 'subProjController.member');
     app.post('/subproj/member', sessionAuth, projectManagerCheck, 'subProjController.member');
     app.post('/subproj/memberSave', sessionAuth, projectManagerCheck, 'subProjController.memberSave');
     app.post('/subproj/memberSave', sessionAuth, projectManagerCheck, 'subProjController.memberSave');
@@ -716,14 +720,30 @@ module.exports = app => {
     app.post('/budget/:id/decimal', sessionAuth, budgetCheck, 'budgetController.decimal');
     app.post('/budget/:id/decimal', sessionAuth, budgetCheck, 'budgetController.decimal');
     // 电子档案
     // 电子档案
     app.get('/file', sessionAuth, 'fileController.index');
     app.get('/file', sessionAuth, 'fileController.index');
+    app.get('/sp/:id/file', sessionAuth, subProjectCheck, 'fileController.file');
+    app.post('/sp/:id/permission', sessionAuth, projectManagerCheck, subProjectCheck, 'fileController.getFilingTypePermission');
+    app.post('/sp/:id/permission/save', sessionAuth, projectManagerCheck, subProjectCheck, 'fileController.saveFilingTypePermission');
+    app.post('/sp/:id/filing/add', sessionAuth, subProjectCheck, 'fileController.addFiling');
+    app.post('/sp/:id/filing/save', sessionAuth, subProjectCheck, 'fileController.saveFiling');
+    app.post('/sp/:id/filing/del', sessionAuth, subProjectCheck, 'fileController.delFiling');
+    app.post('/sp/:id/filing/move', sessionAuth, subProjectCheck, 'fileController.moveFiling');
+    app.post('/sp/:id/file/load', sessionAuth, subProjectCheck, 'fileController.loadFile');
+    app.post('/sp/:id/file/upload', sessionAuth, subProjectCheck, 'fileController.uploadFile');
+    app.post('/sp/:id/file/del', sessionAuth, subProjectCheck, 'fileController.delFile');
+    app.post('/sp/:id/file/save', sessionAuth, subProjectCheck, 'fileController.saveFile');
+    app.post('/sp/:id/file/rela', sessionAuth, subProjectCheck, 'fileController.relaFile');
+    app.post('/sp/:id/file/rela/tender', sessionAuth, subProjectCheck, 'fileController.loadValidRelaTender');
+    app.post('/sp/:id/file/rela/files', sessionAuth, subProjectCheck, 'fileController.loadRelaFiles');
 
 
     // 支付审批
     // 支付审批
     app.get('/payment', sessionAuth, 'paymentController.index');
     app.get('/payment', sessionAuth, 'paymentController.index');
     app.post('/payment/permission/save', sessionAuth, 'paymentController.permissionSave');
     app.post('/payment/permission/save', sessionAuth, 'paymentController.permissionSave');
-    app.get('/payment/:pid/detail/:id', sessionAuth, 'paymentController.detail');
+    app.get('/payment/:id/detail/:did', sessionAuth, 'paymentController.detail');
     app.post('/payment/save', sessionAuth, 'paymentController.save');
     app.post('/payment/save', sessionAuth, 'paymentController.save');
-    app.post('/payment/load', sessionAuth, 'paymentController.listLoad');
-    app.get('/payment/:pid/process', sessionAuth, 'paymentController.process');
+    app.post('/payment/list/load', sessionAuth, 'paymentController.listLoad');
+    app.get('/payment/:id/process', sessionAuth, paymentTenderCheck, 'paymentController.process');
+    app.post('/payment/:id/process/save', sessionAuth, paymentTenderCheck, 'paymentController.processSave');
+    app.post('/payment/:id/rpt', sessionAuth, paymentTenderCheck, 'paymentController.rptList');
 
 
     // 企业微信回调
     // 企业微信回调
     app.get('/wx/work/callback/command', 'wechatController.command');
     app.get('/wx/work/callback/command', 'wechatController.command');

+ 134 - 0
app/service/file.js

@@ -0,0 +1,134 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const path = require('path');
+
+module.exports = app => {
+    class Filing extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @param {String} tableName - 表名
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'file';
+        }
+
+        analysisFiles(files) {
+            const helper = this.ctx.helper;
+            const userId = this.ctx.session.sessionUser.accountId;
+            const ossPath = this.ctx.app.config.fujianOssPath;
+            files.forEach(x => {
+                x.viewpath = helper.canPreview(x.fileext) ? ossPath + x.filepath : '';
+                x.filepath = ossPath + x.filepath;
+                x.fileext_str = helper.fileExtStr(x.fileext);
+                x.canEdit = x.user_id === userId;
+            });
+        }
+
+        async getFiles(condition) {
+            condition.orders = [['create_time', 'desc']];
+            const result = await this.getAllDataByCondition(condition);
+            this.analysisFiles(result);
+            return result;
+        }
+
+        async addFiles(filing, fileInfo, user) {
+            const conn = await this.db.beginTransaction();
+            const result = {};
+            try {
+                const insertData = fileInfo.map(x => {
+                    return {
+                        id: this.uuid.v4(), spid: filing.spid, filing_id: filing.id, filing_type: filing.filing_type,
+                        user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
+                        filename: x.filename, fileext: x.fileext, filesize: x.filesize, filepath: x.filepath,
+                    };
+                });
+                await conn.insert(this.tableName, insertData);
+                const count = await conn.count(this.tableName, { filing_id: filing.id, is_deleted: 0 });
+                await conn.update(this.ctx.service.filing.tableName, { id: filing.id, file_count: count });
+                await conn.commit();
+                result.files = { id: insertData.map(x => { return x.id; })};
+                result.filing = { id: filing.id, file_count: count };
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+            result.files = await this.getFiles({ where: result.files });
+            return result;
+        }
+
+        async delFiles(files) {
+            if (files.length === 0) return;
+
+            const fileDatas = await this.getAllDataByCondition({ where: { id: files } });
+            const filing = await this.ctx.service.filing.getDataById(fileDatas[0].filing_id);
+            const result = {};
+
+            const conn = await this.db.beginTransaction();
+            try {
+                const updateData = fileDatas.map(x => { return { id: x.id, is_deleted: 1 }; });
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                const count = await conn.count(this.tableName, { filing_id: filing.id, is_deleted: 0 });
+                await conn.update(this.ctx.service.filing.tableName, { id: filing.id, file_count: count });
+                await conn.commit();
+                result.del = files;
+                result.filing = { id: filing.id, file_count: count };
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+            return result;
+        }
+
+        async relaFiles(filing, fileInfo, user) {
+            const conn = await this.db.beginTransaction();
+            const result = {};
+            try {
+                const insertData = fileInfo.map(x => {
+                    return {
+                        id: this.uuid.v4(), spid: filing.spid, filing_id: filing.id, filing_type: filing.filing_type,
+                        user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
+                        filename: x.filename, fileext: x.fileext, filesize: x.filesize, filepath: x.filepath,
+                        is_rela: 1, rela_info: x.rela_info,
+                    };
+                });
+                await conn.insert(this.tableName, insertData);
+                const count = await conn.count(this.tableName, { filing_id: filing.id, is_deleted: 0 });
+                await conn.update(this.ctx.service.filing.tableName, { id: filing.id, file_count: count });
+                await conn.commit();
+                result.files = { id: insertData.map(x => { return x.id; })};
+                result.filing = { id: filing.id, file_count: count };
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+            result.files = await this.getFiles({ where: result.files });
+            return result;
+        }
+
+        async saveFile(id, filename){
+            const file = await this.getDataById(id);
+            if (!file) throw '文件不存在';
+            if (file.user_id !== this.ctx.session.sessionUser.accountId) throw '您无权编辑该文件';
+
+            const info = path.parse(filename);
+            const updateData = { id, filename: info.name, fileext: info.ext};
+            await this.defaultUpdate(updateData);
+            return updateData;
+        }
+    }
+
+    return Filing;
+};

+ 203 - 0
app/service/filing.js

@@ -0,0 +1,203 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+const rootId = '-1';
+const filingType = [
+    { value: 1, name: '立项文件' },
+    { value: 2, name: '招标投标、合同协议文件' },
+    { value: 3, name: '勘察、设计文件' },
+    { value: 4, name: '征地、拆迁、移民文件' },
+    { value: 5, name: '项目管理文件' },
+    { value: 6, name: '施工文件' },
+    { value: 7, name: '信息系统开发文件' },
+    { value: 8, name: '设备文件' },
+    { value: 9, name: '监理文件' },
+    { value: 10, name: '科研项目文件' },
+    { value: 11, name: '生产技术准备、试运行文件' },
+    { value: 12, name: '竣工验收文件' },
+];
+
+module.exports = app => {
+    class Filing extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @param {String} tableName - 表名
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'filing';
+            this.filingType = filingType;
+        }
+
+        get allFilingType () {
+            return filingType.map(x => { return x.value });
+        }
+
+        async initFiling(spid, transaction) {
+            const count = await this.count({ spid });
+            if (count > 0) return;
+
+            const insertData = [];
+            for (const [i, f] of filingType.entries()) {
+                insertData.push({
+                    id: this.uuid.v4(), tree_pid: -1, tree_level: 1, tree_order: i + 1,
+                    spid, add_user_id: this.ctx.session.sessionUser.accountId, is_fixed: 1,
+                    filing_type: f.value, name: f.name,
+                });
+            }
+            if (transaction) {
+                await transaction.insert(this.tableName, insertData);
+            } else {
+                await this.db.insert(this.tableName, insertData);
+            }
+        }
+
+        async getValidFiling(spid, filingType) {
+            const condition = { spid, is_deleted: 0 };
+            if (filingType !== 'all') condition.filing_type = filingType;
+            if (!filingType || filingType.length === 0) return [];
+            return await this.getAllDataByCondition({ where: condition });
+        }
+
+
+        async getPosterityData(id){
+            const result = [];
+            let cur = await this.getAllDataByCondition({ where: { tree_pid: id } });
+            let iLevel = 1;
+            while (cur.length > 0 && iLevel < 6) {
+                result.push(...cur);
+                cur = await this.getAllDataByCondition({ where: { tree_pid: cur.map(x => { return x.id })} });
+                iLevel += 1;
+            }
+            return result;
+        }
+
+        _checkFixed(data) {
+            if (data.is_fixed) throw '固定分类,不可编辑';
+        }
+
+        async getNewName(spid, name = '新增文件类别') {
+            const data = await this.db.query(`SELECT * FROM ${this.tableName} WHERE spid = '${spid}' AND name LIKE '${name}%'`);
+            if (data.length === 0) return name;
+
+            const _ = this._;
+            const names = data.map(x => { return _.toInteger(x.name.replace(name, '')) });
+            const filterNames = names.filter(x => { return x > 0 });
+            const max = filterNames.reduce((pre, cur) => { return Math.max(pre, cur); }, 0);
+            return max >= 0 ? name + (max + 1) : name;
+        }
+
+        async add(data) {
+            const parent = await this.getDataById(data.tree_pid);
+            if (!parent) throw '添加数据结构错误';
+            if (parent.file_count > 0) throw `分类【${parent.name}】下存在文件,不可添加子分类`;
+
+            const sibling = await this.getAllDataByCondition({ where: { tree_pid: parent.id }, orders: [['tree_order', 'asc']]});
+            const preChild = data.tree_pre_id ? sibling.find(x => { x.id === data.tree_pre_id; }) : null;
+
+            const conn = await this.db.beginTransaction();
+            try {
+                // 获取当前用户信息
+                const sessionUser = this.ctx.session.sessionUser;
+                // 获取当前项目信息
+                const sessionProject = this.ctx.session.sessionProject;
+
+                const tree_order = preChild ? preChild.tree_order + 1 : (sibling.length > 0 ? sibling[sibling.length - 1].tree_order + 1 : 1);
+                const name = await this.getNewName(parent.spid);
+                const insertData = {
+                    id: this.uuid.v4(), spid: parent.spid, add_user_id: sessionUser.accountId,
+                    tree_pid: parent.id, tree_level: parent.tree_level + 1, tree_order,
+                    name, filing_type: parent.filing_type,
+                };
+                const operate = await conn.insert(this.tableName, insertData);
+                if (operate.affectedRows === 0) throw '新增文件夹失败';
+
+                const updateData = [];
+                if (preChild) {
+                    sibling.forEach(x => {
+                        if (x.tree_order >= tree_order) updateData.push({ id: x.id, tree_order: x.tree_order + 1 });
+                    });
+                }
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+
+                await conn.commit();
+                return { create: [insertData], update: updateData };
+            } catch (error) {
+                await conn.rollback();
+                throw error;
+            }
+        }
+        async save(data) {
+            const filing = await this.getDataById(data.id);
+            this._checkFixed(filing);
+
+            const result = await this.db.update(this.tableName, data);
+            if (result.affectedRows > 0) {
+                return data;
+            } else {
+                throw '更新数据失败';
+            }
+        }
+
+        async del(data) {
+            const filing = await this.getDataById(data.id);
+            this._checkFixed(filing);
+
+            const posterity = await this.getPosterityData(data.id);
+            const delData = posterity.map(x => {return { id: x.id, is_deleted: 1 }; });
+            delData.push({ id: data.id, is_deleted: 1});
+
+            const sibling = await this.getAllDataByCondition({ where: { tree_pid: filing.tree_pid } });
+            const updateData = [];
+            sibling.forEach(x => {
+                if (x.tree_order > filing.tree_order) updateData.push({ id: x.id, tree_order: x.tree_order - 1});
+            });
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.updateRows(this.tableName, delData);
+                if (updateData.length > 0) conn.updateRows(this.tableName, updateData);
+
+                await conn.commit();
+                return { delete: delData.map(x => { return x.id }), update: updateData };
+            } catch(err) {
+                await conn.rollback();
+                throw error;
+            }
+        }
+
+        async move(data) {
+            const filing = await this.getDataById(data.id);
+            const silbing = await this.getAllDataByCondition({ where: { tree_pid: filing.tree_pid, is_deleted: 0 } });
+            const updateData = [{ id: filing.id, tree_order: data.tree_order }];
+            if (data.tree_order < filing.tree_order) {
+                silbing.forEach(x => {
+                    if (x.id === filing.id) return;
+                    if (x.tree_order < data.tree_order) return;
+                    if (x.tree_order > filing.tree_order) return;
+                    updateData.push({id: x.id, tree_order: x.tree_order + 1});
+                });
+            } else {
+                silbing.forEach(x => {
+                    if (x.id === filing.id) return;
+                    if (x.tree_order < filing.tree_order) return;
+                    if (x.tree_order > data.tree_order) return;
+                    updateData.push({id: x.id, tree_order: x.tree_order - 1});
+                });
+            }
+            await this.db.updateRows(this.tableName, updateData);
+            return { update: updateData };
+        }
+    }
+
+    return Filing;
+};

+ 5 - 0
app/service/payment_detail.js

@@ -6,6 +6,11 @@ module.exports = app => {
             super(ctx);
             super(ctx);
             this.tableName = 'payment_detail';
             this.tableName = 'payment_detail';
         }
         }
+
+        async hadDetail(trId) {
+            const result = await this.count({ tr_id: trId });
+            return result !== 0;
+        }
     }
     }
     return PaymentDetail;
     return PaymentDetail;
 };
 };

+ 87 - 0
app/service/payment_shenpi_audit.js

@@ -0,0 +1,87 @@
+'use strict';
+
+/**
+ * 版本数据模型
+ *
+ * @author CaiAoLin
+ * @date 2017/10/25
+ * @version
+ */
+const shenpiConst = require('../const/shenpi');
+
+module.exports = app => {
+
+    class PaymentShenpiAudit extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'payment_shenpi_audit';
+        }
+
+        async getShenpiAudit(tr_id, status) {
+            if (status === shenpiConst.sp_status.gdspl) {
+                return await this.getAuditList(this.ctx.tender.id, tr_id, status);
+            } else if (status === shenpiConst.sp_status.gdzs) {
+                return await this.getAudit(this.ctx.tender.id, tr_id, status);
+            }
+        }
+
+        async getAudit(tid, tr_id, status) {
+            const sql = 'SELECT sp.audit_id, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+                ' WHERE sp.tid = ? AND sp.tr_id = ? AND sp.sp_status = ?';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tid, tr_id, status];
+            return await this.db.queryOne(sql, sqlParam);
+        }
+
+        async getAuditList(tid, tr_id, status) {
+            const sql = 'SELECT sp.audit_id, pa.name FROM ?? AS sp LEFT JOIN ?? AS pa ON sp.audit_id = pa.id' +
+                ' WHERE sp.tid = ? AND sp.tr_id = ? AND sp.sp_status = ? ORDER BY sp.id ASC';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, tid, tr_id, status];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        async addAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertData = {
+                    tid: this.ctx.tender.id,
+                    tr_id: data.tr_id,
+                    sp_status: data.status,
+                    audit_id: data.audit_id,
+                };
+                await transaction.insert(this.tableName, insertData);
+                await transaction.commit();
+                return await this.getShenpiAudit(data.tr_id, data.status);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async removeAudit(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const delData = {
+                    tid: this.ctx.tender.id,
+                    tr_id: data.tr_id,
+                    sp_status: data.status,
+                    audit_id: data.audit_id,
+                };
+                await transaction.delete(this.tableName, delData);
+                await transaction.commit();
+                return await this.getShenpiAudit(data.tr_id, data.status);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+
+    return PaymentShenpiAudit;
+};

+ 1 - 1
app/service/payment_tender.js

@@ -19,7 +19,7 @@ module.exports = app => {
         async getList(uid) {
         async getList(uid) {
             const sql = 'SELECT pt.*, pa.name as user_name FROM ?? as pt LEFT JOIN ?? as pa ON pt.`uid` = pa.`id` WHERE pt.`uid` = ?';
             const sql = 'SELECT pt.*, pa.name as user_name FROM ?? as pt LEFT JOIN ?? as pa ON pt.`uid` = pa.`id` WHERE pt.`uid` = ?';
             const params = [this.tableName, this.ctx.service.projectAccount.tableName, uid];
             const params = [this.tableName, this.ctx.service.projectAccount.tableName, uid];
-            return this.db.query(sql, params);
+            return await this.db.query(sql, params);
         }
         }
 
 
         async addTender(projectId, uid, folderId, name) {
         async addTender(projectId, uid, folderId, name) {

+ 130 - 0
app/service/payment_tender_rpt.js

@@ -0,0 +1,130 @@
+'use strict';
+
+/**
+ * 决策大屏用户查看权限-数据模型
+ *
+ * @author ellisran
+ * @date 2021/09/23
+ * @version
+ */
+const accountGroup = require('../const/account_group').group;
+const paymentConst = require('../const/payment');
+module.exports = app => {
+    class paymentTenderRpt extends app.BaseService {
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'payment_tender_rpt';
+        }
+
+        async getProcessList(id) {
+            const sql = 'SELECT ptr.*, pa.name as user_name FROM ?? as ptr LEFT JOIN ?? as pa ON ptr.`uid` = pa.`id` WHERE ptr.`tender_id` = ?';
+            const params = [this.tableName, this.ctx.service.projectAccount.tableName, id];
+            return await this.db.query(sql, params);
+        }
+
+        async checkAndUpdateList(tenderRptList, rptProjectList) {
+            if (tenderRptList.length > 0) {
+                const updateDatas = [];
+                const delDatas = [];
+                for (const tr of tenderRptList) {
+                    const rptInfo = this._.find(rptProjectList, { ID: tr.rpt_id });
+                    // 判断是否已经新建过报表次
+                    const had_rpt = await this.ctx.service.paymentDetail.hadDetail(tr.id);
+                    if (tr.is_del === 0 && !rptInfo) {
+                        if (had_rpt) {
+                            updateDatas.push({
+                                id: tr.id,
+                                is_del: 1,
+                            });
+                            tr.is_del = 1;
+                        } else {
+                            delDatas.push(tr.id);
+                        }
+                    } else if (rptInfo && tr.rpt_name !== rptInfo.name) {
+                        updateDatas.push({
+                            id: tr.id,
+                            rpt_name: rptInfo.name,
+                        });
+                        tr.rpt_name = rptInfo.name;
+                    }
+                    rptInfo.had_rpt = had_rpt;
+                }
+                if (updateDatas.length > 0) await this.db.updateRows(this.tableName, updateDatas);
+                if (delDatas.length > 0) {
+                    this._.remove(tenderRptList, function(item) {
+                        return delDatas.indexOf(item.id) !== -1;
+                    });
+                    await this.db.delete(this.tableName, { id: delDatas });
+                }
+            }
+            return [tenderRptList, rptProjectList];
+        }
+
+        async setRpt(tid, rpt_list) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const originList = await this.getAllDataByCondition({ where: { tender_id: tid, is_del: 0 } });
+                const insertData = [];
+                let deleteData = [];
+                if (originList.length === 0 && rpt_list.length !== 0) {
+                    // 添加到tender_rpt
+                    for (const rpt of rpt_list) {
+                        insertData.push({
+                            tender_id: tid,
+                            rpt_id: rpt.id,
+                            rpt_name: rpt.name,
+                            uid: this.ctx.session.sessionUser.accountId,
+                            in_time: new Date(),
+                        });
+                    }
+                } else if (originList.length !== 0 && rpt_list.length === 0) {
+                    // 删除原有
+                    deleteData = this._.map(originList, 'id');
+                } else if (originList.length !== 0 && rpt_list.length !== 0) {
+                    const orginRptIds = this._.map(originList, 'rpt_id');
+                    const newIds = this._.map(rpt_list, 'id');
+                    console.log(orginRptIds, newIds);
+                    const insertRptIds = this._.difference(newIds, orginRptIds);
+                    const deleteRptIds = this._.difference(orginRptIds, newIds);
+                    if (deleteRptIds.length > 0) {
+                        for (const id of deleteRptIds) {
+                            const orginInfo = this._.find(originList, { rpt_id: id });
+                            deleteData.push(orginInfo.id);
+                        }
+                    }
+                    console.log(insertRptIds, deleteData);
+                    for (const id of insertRptIds) {
+                        const info = this._.find(rpt_list, { id });
+                        insertData.push({
+                            tender_id: tid,
+                            rpt_id: id,
+                            rpt_name: info.name,
+                            uid: this.ctx.session.sessionUser.accountId,
+                            in_time: new Date(),
+                        });
+                    }
+                }
+                if (insertData.length > 0) await transaction.insert(this.tableName, insertData);
+                if (deleteData.length > 0) await transaction.delete(this.tableName, { id: deleteData });
+                await transaction.commit();
+                return await this.getProcessList(tid);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async setStatus(id, sp_status) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                await transaction.update(this.tableName, { id, sp_status });
+                await transaction.commit();
+                return await this.ctx.service.paymentShenpiAudit.getShenpiAudit(id, sp_status);
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+    }
+    return paymentTenderRpt;
+};

+ 5 - 4
app/service/rpt_gather_memory.js

@@ -325,10 +325,11 @@ module.exports = app => {
             for (const stage of stages) {
             for (const stage of stages) {
                 if (stage.status !== auditConst.stage.status.checked) continue;
                 if (stage.status !== auditConst.stage.status.checked) continue;
 
 
-                const finalAudit = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times, stage.status);
-                if (!finalAudit) continue;
-
-                const sTime = moment(moment(finalAudit.end_time).format('YYYY-MM-DD'), 'YYYY-MM-DD');
+                // const finalAudit = await this.ctx.service.stageAudit.getLastestAuditor(stage.id, stage.times, stage.status);
+                // if (!finalAudit) continue;
+                //
+                // const sTime = moment(moment(finalAudit.end_time).format('YYYY-MM-DD'), 'YYYY-MM-DD');
+                const sTime = stage.period ? (stage.period.split(' ~ ')[1] ? stage.period.split(' ~ ')[1] : s.period)  : '';
                 stage.checked_day = sTime;
                 stage.checked_day = sTime;
                 stage.checked_time = moment(finalAudit.end_time);
                 stage.checked_time = moment(finalAudit.end_time);
                 if (sTime.isBetween(beginTime, endTime, null, '[]')) {
                 if (sTime.isBetween(beginTime, endTime, null, '[]')) {

+ 4 - 3
app/service/stage_audit.js

@@ -435,7 +435,7 @@ module.exports = app => {
                         tp_history: JSON.stringify(this.ctx.stage.tp_history),
                         tp_history: JSON.stringify(this.ctx.stage.tp_history),
                         cache_time_r: this.ctx.stage.cache_time_l,
                         cache_time_r: this.ctx.stage.cache_time_l,
                     });
                     });
-                    await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, auditConst.status.checking, audit, nextAudit, ledgerTp, stageTp);
+                    await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, auditConst.status.checking, nextAudit, audit, ledgerTp, stageTp);
                     // 多人协同,取消下一审批人存在的锁定
                     // 多人协同,取消下一审批人存在的锁定
                     await this.ctx.service.stageAuditAss.cancelLock(this.ctx.stage, nextAudit.aid, transaction);
                     await this.ctx.service.stageAuditAss.cancelLock(this.ctx.stage, nextAudit.aid, transaction);
 
 
@@ -475,7 +475,8 @@ module.exports = app => {
                         cache_time_r: this.ctx.stage.cache_time_l,
                         cache_time_r: this.ctx.stage.cache_time_l,
                         his_id,
                         his_id,
                     });
                     });
-                    await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, checkData.checkType, audit, nextAudit, ledgerTp, stageTp);
+
+                    await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, checkData.checkType, nextAudit, audit, ledgerTp, stageTp);
                     await this.ctx.service.stagePay.cacheOrder(this.ctx.stage, transaction);
                     await this.ctx.service.stagePay.cacheOrder(this.ctx.stage, transaction);
 
 
                     // 添加短信通知-审批通过提醒功能
                     // 添加短信通知-审批通过提醒功能
@@ -698,7 +699,7 @@ module.exports = app => {
                     tp_history: JSON.stringify(this.ctx.stage.tp_history),
                     tp_history: JSON.stringify(this.ctx.stage.tp_history),
                     cache_time_r: this.ctx.stage.cache_time_l,
                     cache_time_r: this.ctx.stage.cache_time_l,
                 });
                 });
-                await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, checkData.checkType, audit, preAuditor, ledgerTp, stageTp);
+                await this.ctx.service.tenderCache.updateStageCache4Flow(transaction, this.ctx.stage, checkData.checkType, preAuditor, audit, ledgerTp, stageTp);
                 await transaction.update(this.tableName, {
                 await transaction.update(this.tableName, {
                     id: audit.id,
                     id: audit.id,
                     status: checkData.checkType,
                     status: checkData.checkType,

+ 1 - 1
app/service/stage_detail_att.js

@@ -76,7 +76,7 @@ module.exports = app => {
                         // a.viewpath = a.filepath.replace(/^app|\/app/, '');
                         // a.viewpath = a.filepath.replace(/^app|\/app/, '');
                         a.viewpath = this.ctx.app.config.fujianOssPath + a.filepath;
                         a.viewpath = this.ctx.app.config.fujianOssPath + a.filepath;
                     }
                     }
-                    delete a.filepath;
+                    a.filepath = this.ctx.app.config.fujianOssPath + a.filepath;
                     a.username = (await this.getUserTemp(a.uid)).name;
                     a.username = (await this.getUserTemp(a.uid)).name;
                 }
                 }
             }
             }

+ 49 - 5
app/service/sub_proj_permission.js

@@ -29,7 +29,7 @@ module.exports = app => {
                 file: {
                 file: {
                     view: { title: '查看', value: 1 },
                     view: { title: '查看', value: 1 },
                     upload: { title: '上传文件', value: 2 },
                     upload: { title: '上传文件', value: 2 },
-                    edit: { title: '文件类别编辑', value: 3 },
+                    filing: { title: '文件类别编辑', value: 3 },
                 },
                 },
                 manage: {
                 manage: {
                     rela: { title: '关联标段', value: 1 },
                     rela: { title: '关联标段', value: 1 },
@@ -37,8 +37,17 @@ module.exports = app => {
             };
             };
         }
         }
 
 
+        get adminPermission () {
+            return {
+                budget_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.budget, 'value'),
+                file_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.file, 'value'),
+                manage_permission: this.ctx.helper.mapAllSubField(this.PermissionConst.manage, 'value'),
+                filing_type: 'all',
+            }
+        }
+
         async showSubTab(uid, type) {
         async showSubTab(uid, type) {
-            const sql = `SELECT count(*) FROM ${this.tableName} WHERE ${type}_permission <> '' AND uid = ?`;
+            const sql = `SELECT count(*) as count FROM ${this.tableName} WHERE ${type}_permission <> '' AND uid = ?`;
             const result = await this.db.queryOne(sql, [uid]);
             const result = await this.db.queryOne(sql, [uid]);
             return result.count;
             return result.count;
         }
         }
@@ -56,6 +65,7 @@ module.exports = app => {
                 x.budget_permission = x.budget_permission ? _.map(x.budget_permission.split(','), _.toInteger) : [];
                 x.budget_permission = x.budget_permission ? _.map(x.budget_permission.split(','), _.toInteger) : [];
                 x.file_permission = x.file_permission ? _.map(x.file_permission.split(','), _.toInteger) : [];
                 x.file_permission = x.file_permission ? _.map(x.file_permission.split(','), _.toInteger) : [];
                 x.manage_permission = x.manage_permission ? _.map(x.manage_permission.split(','), _.toInteger) : [];
                 x.manage_permission = x.manage_permission ? _.map(x.manage_permission.split(','), _.toInteger) : [];
+                x.filing_type = x.filing_type ? _.map(x.filing_type.split(','), _.toInteger): [];
             });
             });
         }
         }
 
 
@@ -84,12 +94,18 @@ module.exports = app => {
         }
         }
 
 
         async getBudgetUserPermission(bid) {
         async getBudgetUserPermission(bid) {
-            const _ = this.ctx.helper._;
-            const result = await this.getDataByCondition({uid: this.ctx.session.sessionUser.accountId, bid});
+            const subProj = await this.service.subProject.getDataByCondition({ budget_id: bid });
+            const result = await this.getDataByCondition({ spid: subProj.id, uid: this.ctx.session.sessionUser.accountId });
             if (result) this.parsePermission(result);
             if (result) this.parsePermission(result);
             return result;
             return result;
         }
         }
 
 
+        async getSubProjectUserPermission(spid, uid) {
+            const result = await this.getDataByCondition({ spid, uid });
+            if (result) this.parsePermission(result);
+            return result;
+        };
+
         async savePermission(subProjectId, member) {
         async savePermission(subProjectId, member) {
             const orgMember = await this.getAllDataByCondition({ where: { spid: subProjectId } });
             const orgMember = await this.getAllDataByCondition({ where: { spid: subProjectId } });
             const dm = [], um = [], im = [];
             const dm = [], um = [], im = [];
@@ -108,11 +124,12 @@ module.exports = app => {
             }
             }
             for (const m of member) {
             for (const m of member) {
                 im.push({
                 im.push({
+                    id: this.uuid.v4(),
                     spid: subProjectId, pid: this.ctx.session.sessionProject.id, uid: m.uid,
                     spid: subProjectId, pid: this.ctx.session.sessionProject.id, uid: m.uid,
                     budget_permission: m.budget_permission.join(','),
                     budget_permission: m.budget_permission.join(','),
                     file_permission: m.file_permission.join(','),
                     file_permission: m.file_permission.join(','),
                     manage_permission: m.manage_permission.join(',')
                     manage_permission: m.manage_permission.join(',')
-                })
+                });
             }
             }
             const conn = await this.db.beginTransaction();
             const conn = await this.db.beginTransaction();
             try {
             try {
@@ -125,6 +142,33 @@ module.exports = app => {
                 throw err;
                 throw err;
             }
             }
         }
         }
+
+        async getFilingType(subProjectId) {
+            const permissionConst = {}, prefix = 'f';
+            for (const p in this.PermissionConst.file) {
+                const fp = this.PermissionConst.file[p];
+                permissionConst[prefix + fp.value] = fp.title;
+            }
+            const result = await this.db.query(`SELECT spp.id, p.name, p.role, p.company, p.mobile, spp.file_permission, spp.filing_type 
+                FROM ${this.tableName} spp LEFT JOIN ${this.ctx.service.projectAccount.tableName} p
+                On spp.uid = p.id WHERE spp.spid = ? and file_permission <> ''`, [subProjectId]);
+            result.forEach(x => {
+                const filePermission = x.file_permission.split(',');
+                x.file_permission = filePermission.map(x => {
+                    return permissionConst[prefix + x] || '';
+                }).join(',');
+            });
+            return result;
+        }
+
+        // 资料归集,授权固定分类
+        async saveFilingType(data) {
+            const updateData = [];
+            data.forEach(x => {
+                updateData.push({ id: x.id, filing_type: x.filing_type });
+            });
+            if (updateData.length > 0) await this.db.updateRows(this.tableName, updateData);
+        }
     }
     }
 
 
     return subProjPermission;
     return subProjPermission;

+ 63 - 18
app/service/sub_project.js

@@ -34,7 +34,7 @@ module.exports = app => {
                 const pb = permission.find(y => { return x.id === y.spid});
                 const pb = permission.find(y => { return x.id === y.spid});
                 if (!pb) return false;
                 if (!pb) return false;
                 x.user_permission = pb;
                 x.user_permission = pb;
-                return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.manage_permission.length > 0;
+                return x.user_permission.budget_permission.length > 0 || x.user_permission.file_permission.length > 0 || x.user_permission.manage_permission.length > 0;
             });
             });
             return result;
             return result;
         }
         }
@@ -50,35 +50,47 @@ module.exports = app => {
         }
         }
 
 
         async getBudgetProject(pid, uid, admin) {
         async getBudgetProject(pid, uid, admin) {
-            const sql = `SELECT sp.*, b.std_id From ${this.tableName} sp LEFT JOIN ${this.ctx.service.budget.tableName} b ON sp.budget_id = b.id  
-                WHERE sp.project_id = ? AND sp.is_delete = 0`;
-            let result = await this.db.query(sql, [pid]);
+            let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
 
 
-            const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
+            const adminPermission = this.ctx.service.subProjPermission.adminPermission;
+            const permission = admin ? [] : await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             result = result.filter(x => {
             result = result.filter(x => {
                 if (!x.is_folder && !x.budget_id) return false;
                 if (!x.is_folder && !x.budget_id) return false;
-                if (x.is_folder || admin) return true;
-                const pb = permission.find(y => { return x.id === y.spid});
-                if (!pb) return false;
-                x.permission = pb.budget_permission;
-                x.manage_permission = pb.manage_permission;
-                return x.budget_permission.length > 0;
+                if (x.is_folder) return true;
+                if (admin) {
+                    x.permission = adminPermission.budget_permission;
+                    x.manage_permission = adminPermission.manage_permission;
+                    return true;
+                } else {
+                    const pb = permission.find(y => { return x.id === y.spid});
+                    if (!pb) return false;
+                    x.permission = pb.budget_permission;
+                    x.manage_permission = pb.manage_permission;
+                    return x.permission.length > 0;
+                }
             });
             });
-            console.log(result.length);
             return this._filterEmptyFolder(result);
             return this._filterEmptyFolder(result);
         }
         }
 
 
         async getFileProject(pid, uid, admin) {
         async getFileProject(pid, uid, admin) {
             let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
             let result = await this.getAllDataByCondition({ where: { project_id: pid, is_delete: 0 } });
 
 
+            const adminPermission = this.ctx.service.subProjPermission.adminPermission;
             const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             const permission = await this.ctx.service.subProjPermission.getUserPermission(pid, uid);
             result = result.filter(x => {
             result = result.filter(x => {
-                if (x.is_folder || admin) return true;
-                const pb = permission.find(y => { return x.id === y.spid});
-                if (!pb) return false;
-                x.permission = pb.file_permission;
-                x.manage_permission = pb.manage_permission;
-                return x.file_permission.length > 0;
+                if (!x.is_folder && !x.management) return false;
+                if (x.is_folder) return true;
+                if (admin) {
+                    x.permission = adminPermission.file_permission;
+                    x.manage_permission = adminPermission.manage_permission;
+                    return true;
+                } else {
+                    const pb = permission.find(y => { return x.id === y.spid});
+                    if (!pb) return false;
+                    x.permission = pb.file_permission;
+                    x.manage_permission = pb.manage_permission;
+                    return x.permission.length > 0;
+                }
             });
             });
             return this._filterEmptyFolder(result);
             return this._filterEmptyFolder(result);
         }
         }
@@ -251,6 +263,39 @@ module.exports = app => {
                 await conn.rollback();
                 await conn.rollback();
                 throw error;
                 throw error;
             }
             }
+
+        }
+
+        async setManagement(data) {
+            const subProject = await this.getDataById(data.id);
+            if (subProject.management === data.management) return data;
+
+            const users = await this.ctx.service.projectAccount.getAllDataByCondition({ where: { project_id: subProject.project_id, company: data.management }});
+            const orgMember = await this.ctx.service.subProjPermission.getAllDataByCondition({ where: { spid: subProject.id } });
+            const dm = [], um = [], im = [];
+            const filing_type = this.ctx.service.filing.allFilingType.join(','), file_permission = '1,2';
+            for (const u of users) {
+                const nm = orgMember.find(x => { return u.id === x.uid; });
+                if (nm) {
+                    if (!nm.file_permission) um.push({ id: nm.id, file_permission, filing_type });
+                } else {
+                    im.push({ id: this.uuid.v4(), spid: subProject.id, pid: subProject.project_id, uid: u.id, file_permission, filing_type });
+                }
+            }
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.update(this.tableName, { id: subProject.id, management: data.management });
+                await this.ctx.service.filing.initFiling(subProject.id, conn);
+                if (dm.length > 0) await conn.delete(this.ctx.service.subProjPermission.tableName, { id: dm });
+                if (um.length > 0) await conn.updateRows(this.ctx.service.subProjPermission.tableName, um);
+                if (im.length > 0) await conn.insert(this.ctx.service.subProjPermission.tableName, im);
+
+                await conn.commit();
+                return data;
+            } catch (error) {
+                await conn.rollback();
+                throw error;
+            }
         }
         }
     }
     }
 
 

+ 2 - 1
app/service/tender_cache.js

@@ -59,7 +59,8 @@ module.exports = app => {
                 tender.stage_status = auditConst.stage.status.checked;
                 tender.stage_status = auditConst.stage.status.checked;
                 tender.stage_count = tender.stage_complete_count;
                 tender.stage_count = tender.stage_complete_count;
                 tender.stage_complete_count = tender.stage_complete_count;
                 tender.stage_complete_count = tender.stage_complete_count;
-                tender.cur_flow = JSON.parse(cache.stage_flow_cur_info || cache.stage_flow_pre_info);
+                tender.cur_flow = (cache.stage_status !== auditConst.stage.status.uncheck || cache.stage_flow_cur_uid !== uid)
+                    ? JSON.parse(cache.stage_flow_pre_info) : JSON.parse(cache.stage_flow_cur_info || cache.stage_flow_pre_info);
                 tender.pre_flow = cache.stage_flow_pre_info ? JSON.parse(cache.stage_flow_pre_info) : null;
                 tender.pre_flow = cache.stage_flow_pre_info ? JSON.parse(cache.stage_flow_pre_info) : null;
                 tender.stage_tp = JSON.parse(cache.stage_flow_cur_tp || cache.stage_flow_pre_tp);
                 tender.stage_tp = JSON.parse(cache.stage_flow_cur_tp || cache.stage_flow_pre_tp);
                 tender.progress = {
                 tender.progress = {

+ 1 - 1
app/view/advance/detail.ejs

@@ -328,7 +328,7 @@
                                                                             class="pull-right
                                                                             class="pull-right
                                                                                         <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                         <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                             <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                                             <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
-                                                                            <%- auditor.status === auditConst.status.checkNoPre ? auditors.find(item => item.order === auditor.sort-1).name : '' %>
+                                                                            <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                         </span>
                                                                         </span>
                                                                     </p>
                                                                     </p>
                                                                     <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                     <p class="text-muted mb-0"><%- auditor.role %></p>

+ 5 - 5
app/view/advance/modal_audit.ejs

@@ -184,7 +184,7 @@
                                                             class="pull-right
                                                             class="pull-right
                                                                             <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                             <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                             <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                             <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
-                                                            <%- auditor.status === auditConst.status.checkNoPre ? history[index-1].name : '' %>
+                                                            <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                         </span>
                                                         </span>
                                                     </p>
                                                     </p>
                                                     <p class="text-muted mb-0"><%- auditor.role %></p>
                                                     <p class="text-muted mb-0"><%- auditor.role %></p>
@@ -368,7 +368,7 @@
                                                                     class="pull-right
                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
-                                                                    <%- auditor.status === auditConst.status.checkNoPre ? history[index-1].name : '' %>
+                                                                    <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
                                                                 </span>
                                                             </p>
                                                             </p>
                                                             <p class="text-muted mb-0"><%- auditor.role %></p>
                                                             <p class="text-muted mb-0"><%- auditor.role %></p>
@@ -535,7 +535,7 @@
                                                                     id="inlineRadio2"
                                                                     id="inlineRadio2"
                                                                     value="<%- auditConst.status.checkNoPre %>">
                                                                     value="<%- auditConst.status.checkNoPre %>">
                                                                 <label class="form-check-label" for="inlineRadio2">退回上一审批人
                                                                 <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                    <%- auditors.find(item => item.order === auditor.sort-1).name %></label>
+                                                                    <%- history.find(item => item.sort === auditor.sort-1).name %></label>
                                                             </div>
                                                             </div>
                                                             <% } %>
                                                             <% } %>
                                                         </div>
                                                         </div>
@@ -581,7 +581,7 @@
                                                                     class="pull-right
                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
                                                                     <%- auditor.status === auditConst.status.checkNo ? advance.user.name : '' %>
-                                                                    <%- auditor.status === auditConst.status.checkNoPre ? history[index-1].name : '' %>
+                                                                    <%- auditor.status === auditConst.status.checkNoPre ? history.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
                                                                 </span>
                                                             </p>
                                                             </p>
                                                             <p class="text-muted mb-0"><%- auditor.role %></p>
                                                             <p class="text-muted mb-0"><%- auditor.role %></p>
@@ -609,7 +609,7 @@
                                                                             id="inlineRadio2"
                                                                             id="inlineRadio2"
                                                                             value="<%- auditConst.status.checkNoPre %>">
                                                                             value="<%- auditConst.status.checkNoPre %>">
                                                                         <label class="form-check-label" for="inlineRadio2">退回上一审批人
                                                                         <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                            <%- auditors.find(item => item.order === auditor.sort-1).name %></label>
+                                                                            <%- history.find(item => item.sort === auditor.sort-1).name %></label>
                                                                     </div>
                                                                     </div>
                                                                     <% } %>
                                                                     <% } %>
                                                                 </div>
                                                                 </div>

+ 1 - 1
app/view/budget/list_modal.ejs

@@ -25,7 +25,7 @@
     <div class="modal-dialog" role="document">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
-                <h5 class="modal-title">选择决算标段</h5>
+                <h5 class="modal-title">选择关联标段</h5>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">
                 <h5>可选标段</h5>
                 <h5>可选标段</h5>

+ 1 - 0
app/view/change/information_modal.ejs

@@ -207,6 +207,7 @@
                     </div>
                     </div>
                 </div>
                 </div>
                 <div class="modal-footer">
                 <div class="modal-footer">
+                    <button type="button" class="btn btn-sm btn-primary" id="select-all-revise">选择所有新增部位/清单</button>
                     <!--<button type="button" class="add-list-btn btn btn-sm btn-outline-primary">添加空白清单</button>-->
                     <!--<button type="button" class="add-list-btn btn btn-sm btn-outline-primary">添加空白清单</button>-->
                     <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal" id="cancel-list-btn">取消</button>
                     <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal" id="cancel-list-btn">取消</button>
                     <button type="button" class="add-list-btn btn btn-primary btn-sm">添加</button>
                     <button type="button" class="add-list-btn btn btn-primary btn-sm">添加</button>

+ 67 - 0
app/view/file/file.ejs

@@ -0,0 +1,67 @@
+<div class="panel-content">
+    <div class="panel-title fluid">
+        <div class="title-main  d-flex justify-content-between">
+            <div>资料归集</div>
+            <div class="ml-auto">
+                <% if (ctx.session.sessionUser.is_admin) { %>
+                <a class="btn btn-sm btn-outline-primary mr-1" href="#filing-permission" data-toggle="modal" data-target="#filing-permission">授权用户</a>
+                <% } %>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0 row">
+                <div class="col-3">
+                    <div class="d-flex flex-row">
+                        <% if (canFiling) { %>
+                        <div class="p-2"><a href="javascript: void(0);" id="add-slibing">添加同级</a></div>
+                        <div class="p-2"><a href="javascript: void(0);" id="add-child">添加子级</a></div>
+                        <% } else { %>
+                        <div class="p-2 ml-2">分类目录</div>
+                        <% } %>
+                    </div>
+                    <div>
+                        <ul id="filing" class="ztree"></ul>
+                    </div>
+                </div>
+                <div class="col-9" id="file-view" style="display: none">
+                    <div class="d-flex flex-row">
+                        <% if (canUpload) { %>
+                        <div class="py-2 pr-2"><a href="#add-file" data-toggle="modal" data-target="#add-file">上传文件</a></div>
+                        <div class="p-2" id="rela-file-btn"><a href="#rela-file" data-toggle="modal" data-target="#rela-file">导入文件</a></div>
+                        <div class="p-2"><a href="#batch-del-file" data-toggle="modal" data-target="#batch-del-file">批量删除</a></div>
+                        <% } %>
+                        <div class="p-2"><a href="javascript: void(0)" id="batch-download">批量下载</a></div>
+                        <div class="p-2">
+                            <span id="showPage">
+                                <a href="javascript:void(0);" class="page-select ml-3" content="pre"><i class="fa fa-chevron-left"></i></a>
+                                <span id="curPage">1</span>/<span id="curTotalPage">10</span>
+                                <a href="javascript:void(0);" class="page-select mr-3" content="next"><i class="fa fa-chevron-right"></i></a>
+                            </span>
+                        </div>
+                    </div>
+                    <table class="table table-bordered">
+                        <thead>
+                        <tr>
+                            <th>选择</th>
+                            <th width="40%">文件名称</th>
+                            <th>上传人</th>
+                            <th>上传时间</th>
+                            <th>文件类型</th>
+                        </tr>
+                        </thead>
+                        <tbody id="file-list">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const canFiling = <%- canFiling %>;
+    const filing = JSON.parse(unescape('<%- escape(JSON.stringify(filing)) %>'));
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const whiteList = JSON.parse('<%- JSON.stringify(ctx.app.config.multipart.whitelist) %>');
+</script>

+ 174 - 0
app/view/file/file_modal.ejs

@@ -0,0 +1,174 @@
+<% if (ctx.session.sessionUser.is_admin) { %>
+<div class="modal" id="filing-permission">
+    <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">授权用户</h5>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4">
+                        <div class="d-flex justify-content-center bg-graye">
+                            <div class="p-2">文档类别</div>
+                        </div>
+                        <div class="modal-height-400">
+                            <div class="category">
+                                <ul>
+                                    <% for (const ft of filingTypes) { %>
+                                    <li>
+                                        <div class="form-check">
+                                            <input class="form-check-input" name="cbft" type="checkbox" value="<%- ft.value %>" id="ftCheck<%- ft.value %>">
+                                            <label class="form-check-label" for="ftCheck<%- ft.value %>"></label>
+                                            <span name="ftName" ftid="<%- ft.value %>"><%- ft.name %></span>
+                                        </div>
+                                    </li>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="col-8">
+                        <div class="d-flex flex-row bg-graye">
+                            <div class="p-2">
+                                <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="dropdown-up" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                    添加用户
+                                </button>
+                                <div class="dropdown-menu" aria-labelledby="dropdown-up" style="width:220px">
+                                    <dl class="list-unstyled book-list" id="puList">
+                                    </dl>
+                                </div>
+                            </div>
+                            <div class="p-2"><a href="javascript: void(0);" id="batch-del-filing" class="text-danger">批量删除</a></div>
+                            <div class="p-2"><a href="javascript: void(0);" id="sync-filing">同步授权至其他类别</a></div>
+                        </div>
+                        <div class="modal-height-400">
+                            <table class="table table-bordered">
+                                <thead>
+                                <tr><th>选择</th><th>用户名</th><th>授权时间</th><th>权限</th><th>操作</th></tr>
+                                </thead>
+                                <tbody id="filing-valid">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="filing-permission-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>
+<% if (canUpload) { %>
+<!--上传附件-->
+<div class="modal fade" id="add-file" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">上传附件</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="" data-original-title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="" id="upload-file" multiple>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="button" class="btn btn-primary btn-sm" id="add-file-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="batch-del-file" data-backdrop="static" style="display: none;" aria-hidden="true">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">批量删除确认</h5>
+            </div>
+            <div class="modal-body">
+                <p>确认删除当前文件类别?</p>
+                <p>删除后,数据无法恢复,请谨慎操作。</p>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-danger" id="batch-del-file-ok">确定删除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="rela-file" data-backdrop="static" style="display: none;" aria-hidden="true">
+    <div class="modal-dialog modal-lgx" role="document">
+        <div class="modal-content">
+            <div class="modal-header d-flex justify-content-between align-items-center">
+                <h5 class="modal-title">引用文件</h5>
+                <div id="rela-filing-hint">当前目录:项目管理文件/第一合同段/计量台账(自己新增的节点)</div>
+            </div>
+            <div class="modal-body">
+                <div class="row">
+                    <div class="col-4">
+                        <div class="d-flex justify-content-center bg-graye">
+                            <div class="p-2">标段列表</div>
+                        </div>
+                        <div class="modal-height-400">
+                            <ul id="rela-tender" class="ztree"></ul>
+                        </div>
+                    </div>
+                    <div class="col-8">
+                        <div class="d-flex justify-content-between align-items-center">
+                            <div>
+                                <div class="d-flex flex-row">
+                                    <select class="form-control form-control-sm mr-2" id="tf-type" style="width: 100px">
+                                    </select>
+                                    <select class="form-control form-control-sm mr-2" id="tf-sub-type" style="width: 120px">
+                                    </select>
+                                    <select class="form-control form-control-sm" id="tf-stage" style="width: 80px">
+                                    </select>
+                                </div>
+                            </div>
+                            <div id="cur-tender-hint">当前标段,已选14个文件</div>
+                        </div>
+                        <div class="modal-height-400">
+                            <table class="table table-bordered mt-3">
+                                <thead>
+                                <tr><th>选择</th><th>文件名称</th><th>文件分类</th></tr>
+                                </thead>
+                                <tbody id="rf-files">
+                                </tbody>
+                            </table>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal-footer d-flex justify-content-between align-items-center">
+                <div id="rela-file-hint">已选1个标段,共14个文件</div>
+                <div>
+                    <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-sm btn-primary" id="rela-file-ok">确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>
+<% if (canFiling) { %>
+<div class="modal fade" id="del-filing" data-backdrop="static" style="display: none;" aria-hidden="true">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">删除确认</h5>
+            </div>
+            <div class="modal-body">
+                <p>如存在子类别,数据文件会一并删除,删除后,数据无法恢复,请谨慎操作。</p>
+                <h6>确认删除当前文件类别?</h6>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-danger" id="del-filing-ok">确定删除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>

+ 1 - 1
app/view/file/modal.ejs

@@ -3,7 +3,7 @@
     <div class="modal-dialog" role="document">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
         <div class="modal-content">
             <div class="modal-header">
             <div class="modal-header">
-                <h5 class="modal-title">选择决算标段</h5>
+                <h5 class="modal-title">选择关联标段</h5>
             </div>
             </div>
             <div class="modal-body">
             <div class="modal-body">
                 <h5>可选标段</h5>
                 <h5>可选标段</h5>

+ 3 - 3
app/view/material/audit_modal.ejs

@@ -219,7 +219,7 @@
                                                                             class="pull-right
                                                                             class="pull-right
                                                                             <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                             <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                         <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
                                                                         <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
-                                                                        <%- auditor.status === auditConst.status.checkNoPre ? (ctx.material.auditors2.find(item => item.order === auditor.sort-1) ? ctx.material.auditors2.find(item => item.order === auditor.sort-1).name : '') : '' %>
+                                                                        <%- auditor.status === auditConst.status.checkNoPre ? auditors.find(item => item.sort === auditor.sort-1).name : '' %>
                                                         </span>
                                                         </span>
                                                                 </p>
                                                                 </p>
                                                                 <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                 <p class="text-muted mb-0"><%- auditor.role %></p>
@@ -409,7 +409,7 @@
                                                                                     class="pull-right
                                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
-                                                                                <%- auditor.status === auditConst.status.checkNoPre ? ctx.material.auditors2.find(item => item.order === auditor.sort-1).name : '' %>
+                                                                                <%- auditor.status === auditConst.status.checkNoPre ? auditors.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
                                                                 </span>
                                                                         </p>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
@@ -624,7 +624,7 @@
                                                                                     class="pull-right
                                                                                     class="pull-right
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                     <%- auditConst.statusClass[auditor.status] %>"><%- auditor.status !== auditConst.status.uncheck ? auditConst.statusString[auditor.status] : ''%>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
                                                                                 <%- auditor.status === auditConst.status.checkNo ? ctx.material.user.name : '' %>
-                                                                                <%- auditor.status === auditConst.status.checkNoPre ? ctx.material.auditors2.find(item => item.order === auditor.sort-1).name : '' %>
+                                                                                <%- auditor.status === auditConst.status.checkNoPre ? auditors.find(item => item.sort === auditor.sort-1).name : '' %>
                                                                 </span>
                                                                 </span>
                                                                         </p>
                                                                         </p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>
                                                                         <p class="text-muted mb-0"><%- auditor.role %></p>

+ 53 - 270
app/view/payment/process.ejs

@@ -84,288 +84,62 @@
                         </div>
                         </div>
                         <div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">
                         <div class="tab-pane fade" id="profile" role="tabpanel" aria-labelledby="profile-tab">
                             <div class="row">
                             <div class="row">
-                                <div class="col-4">
-                                    <div class="my-3"><a href="#add-rpt" data-toggle="modal" data-target="#add-rpt">添加表单</a></div>
+                                <div class="col-4 mt-3">
                                     <table class="table table-bordered">
                                     <table class="table table-bordered">
                                         <thead>
                                         <thead>
                                         <tr>
                                         <tr>
                                             <th>名称</th>
                                             <th>名称</th>
                                             <th>添加人</th>
                                             <th>添加人</th>
-                                            <th>表单角色</th>
                                         </tr>
                                         </tr>
                                         </thead>
                                         </thead>
-                                        <tbody>
-                                        <tr>
-                                            <td>指挥部工程结算款</td>
-                                            <td>管理员</td>
-                                            <td>6</td>
-                                        </tr>
-                                        <tr>
-                                            <td>指挥部工程结算付款</td>
-                                            <td>管理员</td>
-                                            <td>6</td>
-                                        </tr>
-                                        <tr>
-                                            <td>指挥部监理、设计等预付款</td>
-                                            <td>管理员</td>
-                                            <td>7</td>
-                                        </tr>
-                                        <tr>
-                                            <td>指挥部监理、设计等结算款</td>
-                                            <td>邓莹洁</td>
-                                            <td>4</td>
-                                        </tr>
-                                        <tr>
-                                            <td>招标文件合同送审单</td>
-                                            <td>邓莹洁</td>
-                                            <td>4</td>
+                                        <tbody id="tender_rpt_table">
+                                        <% for (const tr of tenderRptList) { %>
+                                        <% if (tr.is_del === 0) { %>
+                                        <tr data-id="<%- tr.id %>">
+                                            <td><%- tr.rpt_name  %></td>
+                                            <td><%- tr.user_name %></td>
                                         </tr>
                                         </tr>
+                                        <% } %>
+                                        <% } %>
                                         </tbody>
                                         </tbody>
                                     </table>
                                     </table>
+                                    <div class="my-3"><a href="#add-rpt" data-toggle="modal" data-target="#add-rpt">添加表单</a></div>
                                 </div>
                                 </div>
                                 <div class="col-8">
                                 <div class="col-8">
-                                    <ul class="nav nav-tabs my-2" id="myTab" role="tablist">
-                                        <li class="nav-item">
-                                            <a class="nav-link active" id="home-tab" data-toggle="tab" href="#role" role="tab" aria-controls="role" aria-selected="true">表单角色</a>
-                                        </li>
-                                        <li class="nav-item">
-                                            <a class="nav-link" id="profile-tab" data-toggle="tab" href="#process" role="tab" aria-controls="process" aria-selected="false">审批流程</a>
-                                        </li>
-                                    </ul>
-                                    <div class="tab-content" id="myTabContent">
-                                        <div class="tab-pane fade show active" id="role" role="tabpanel" aria-labelledby="home-tab">
-                                            <div class="row">
-                                                <div class="col-6">
-                                                    <table class="table table-bordered">
-                                                        <thead>
-                                                        <tr>
-                                                            <th>序号</th>
-                                                            <th>表单角色</th>
-                                                            <th>不参与审批</th>
-                                                            <th>签字签章</th>
-                                                        </tr>
-                                                        </thead>
-                                                        <tbody>
-                                                        <tr>
-                                                            <td>1</td>
-                                                            <td>监理公司</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>2</td>
-                                                            <td>造价咨询单位</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>3</td>
-                                                            <td>经办人</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>4</td>
-                                                            <td>合同管理人</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>5</td>
-                                                            <td>相关科室负责人</td>
-                                                            <td><input type="checkbox" checked></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>6</td>
-                                                            <td>计划财务科科长</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>7</td>
-                                                            <td>分管领导</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        </tbody>
-                                                    </table>
-                                                </div>
-                                                <div class="col-6">
-                                                    <table class="table table-bordered">
-                                                        <thead>
-                                                        <tr>
-                                                            <th>序号</th>
-                                                            <th>表单角色</th>
-                                                            <th>不参与审批</th>
-                                                            <th>签字签章</th>
-                                                            <th>操作</th>
-                                                        </tr>
-                                                        </thead>
-                                                        <tbody>
-                                                        <tr>
-                                                            <td>1</td>
-                                                            <td>监理公司</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>2</td>
-                                                            <td>造价咨询单位</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>3</td>
-                                                            <td>经办人</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>4</td>
-                                                            <td>合同管理人</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>5</td>
-                                                            <td>相关科室负责人</td>
-                                                            <td><input type="checkbox" checked></td>
-                                                            <td><span class="mr-2">张三</span><a href="#">更改</a></td>
-                                                            <td><a href="#">预览</a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>6</td>
-                                                            <td>计划财务科科长</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>7</td>
-                                                            <td>分管领导</td>
-                                                            <td><input type="checkbox"></td>
-                                                            <td></td>
-                                                            <td></td>
-                                                        </tr>
-                                                        </tbody>
-                                                    </table>
-                                                </div>
-                                            </div>
-                                        </div>
-                                        <div class="tab-pane fade" id="process" role="tabpanel" aria-labelledby="profile-tab">
-                                            <div class="row">
-                                                <div class="col-6">
-                                                    <table class="table table-bordered">
-                                                        <thead>
-                                                        <tr>
-                                                            <th>审批流程</th>
-                                                            <th>表单角色</th>
-                                                            <th><b class="text-danger">*&nbsp;</b>审批人</th>
-                                                            <th>更换顺序</th>
-                                                        </tr>
-                                                        </thead>
-                                                        <tbody>
-                                                        <tr>
-                                                            <td>一审</td>
-                                                            <td>监理公司</td>
-                                                            <td><select class="form-control form-control-sm"><option>请选择审批人</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>二审</td>
-                                                            <td>造价咨询单位</td>
-                                                            <td><select class="form-control form-control-sm"><option>李四</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>三审</td>
-                                                            <td>经办人</td>
-                                                            <td><select class="form-control form-control-sm"><option>王五</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>四审</td>
-                                                            <td>合同管理人</td>
-                                                            <td><select class="form-control form-control-sm"><option>老六</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>五审</td>
-                                                            <td>计划财务科科长</td>
-                                                            <td><select class="form-control form-control-sm"><option>邓莹洁</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>六审</td>
-                                                            <td>分管领导</td>
-                                                            <td><select class="form-control form-control-sm"><option>仁温书</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        </tbody>
-                                                    </table>
-                                                    <div class="my-3"><a href="#">添加流程</a></div>
-                                                </div>
-                                                <div class="col-6">
-                                                    <table class="table table-bordered">
-                                                        <thead>
-                                                        <tr>
-                                                            <th>审批流程</th>
-                                                            <th>表单角色</th>
-                                                            <th><b class="text-danger">*&nbsp;</b>审批人</th>
-                                                            <th>更换顺序</th>
-                                                        </tr>
-                                                        </thead>
-                                                        <tbody>
-                                                        <tr>
-                                                            <td>一审</td>
-                                                            <td>监理公司</td>
-                                                            <td><select class="form-control form-control-sm"><option>请选择审批人</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>二审</td>
-                                                            <td>造价咨询单位</td>
-                                                            <td><select class="form-control form-control-sm"><option>李四</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>三审</td>
-                                                            <td>经办人</td>
-                                                            <td><select class="form-control form-control-sm"><option>王五</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>四审</td>
-                                                            <td>合同管理人</td>
-                                                            <td><select class="form-control form-control-sm"><option>老六</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>五审</td>
-                                                            <td>计划财务科科长</td>
-                                                            <td><select class="form-control form-control-sm"><option>邓莹洁</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>六审</td>
-                                                            <td>分管领导</td>
-                                                            <td><select class="form-control form-control-sm"><option>仁温书</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        <tr>
-                                                            <td>七审</td>
-                                                            <td>主管领导(新添加的流程)</td>
-                                                            <td><select class="form-control form-control-sm"><option>请选择审批人</option></select></td>
-                                                            <td><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a><a href="" class="btn btn-sm btn-link text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></td>
-                                                        </tr>
-                                                        </tbody>
-                                                    </table>
-                                                    <div class="my-3"><a href="#">添加流程</a></div>
+                                    <div class="row">
+                                        <div class="col-8 mt-3">
+                                            <div class="card" id="process_set" <% if (tenderRptList.length === 0) { %>style="display: none" <% } %>>
+                                                <div class="card-body">
+                                                    <h5 class="card-title"></h5>
+                                                    <div class="form-group">
+                                                        <div class="form-group form-check">
+                                                            <% for (const st in shenpi.sp_status_list) { %>
+                                                                <div class="custom-control custom-checkbox custom-control-inline">
+                                                                    <input type="radio" class="custom-control-input" value="<%- shenpi.sp_status_list[st].status %>" name="tender_process" id="tender_process_<%- shenpi.sp_status_list[st].status %>">
+                                                                    <label class="custom-control-label" for="tender_process_<%- shenpi.sp_status_list[st].status %>"><%- shenpi.sp_status_list[st].name %></label>
+                                                                </div>
+                                                            <% } %>
+                                                        </div>
+                                                    </div>
+                                                    <div class="alert alert-warning mb-0 mt-3" role="alert"></div>
+                                                    <div class="lc-show mt-3"></div>
+                                                    <!--<div class="d-flex justify-content-start align-items-center mt-3">-->
+                                                        <!--<div class="mr-2"><a href="#">一审</a></div>-->
+                                                        <!--<div>-->
+                                                            <!--<select class="form-control form-control-sm" id="exampleFormControlSelect1">-->
+                                                                <!--<option>选择审批人</option>-->
+                                                            <!--</select>-->
+                                                        <!--</div>-->
+                                                    <!--</div>-->
+                                                    <!--<div class="d-flex justify-content-start align-items-center mt-3">-->
+                                                        <!--<div class="mr-2"><a href="#">一审</a></div>-->
+                                                        <!--<div class="mr-2">邓莹洁</div>-->
+                                                        <!--<div><i class="fa fa-remove text-danger" aria-hidden="true"></i></div>-->
+                                                    <!--</div>-->
+                                                    <!--<div class="d-flex justify-content-start align-items-center mt-3">-->
+                                                        <!--<div class="mr-2"><a href="#"><i class="fa fa-plus" aria-hidden="true"></i></a></div>-->
+                                                        <!--<div><a href="#">添加流程</a></div>-->
+                                                    <!--</div>-->
                                                 </div>
                                                 </div>
                                             </div>
                                             </div>
                                         </div>
                                         </div>
@@ -379,3 +153,12 @@
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
+<script>
+    const tenderId = parseInt('<%- ctx.tender.id %>');
+    const sp_status = JSON.parse('<%- JSON.stringify(shenpi.sp_status) %>');
+    const sp_status_list = JSON.parse('<%- JSON.stringify(shenpi.sp_status_list) %>');
+    const accountGroup = JSON.parse(unescape('<%- escape(JSON.stringify(accountGroup)) %>'));
+    const accountList = JSON.parse(unescape('<%- escape(JSON.stringify(accountList)) %>'));
+    const cur_uid = parseInt('<%- ctx.tender.uid %>');
+    let tenderRptList = JSON.parse(unescape('<%- escape(JSON.stringify(tenderRptList)) %>'));
+</script>

+ 8 - 13
app/view/payment/process_modal.ejs

@@ -8,30 +8,25 @@
                 <table id="rpt-table" class="table table-bordered">
                 <table id="rpt-table" class="table table-bordered">
                     <thead>
                     <thead>
                     <tr>
                     <tr>
-                        <th class="text-center"><input type="checkbox">选择</th>
+                        <th class="text-center"><input type="checkbox" id="select_all_rpt_checkbox"> 选择</th>
                         <th class="text-center">表单名</th>
                         <th class="text-center">表单名</th>
                     </tr>
                     </tr>
                     </thead>
                     </thead>
-                    <tbody>
+                    <tbody id="rpt_table">
+                    <% for (const rpt of rptProjectList) { %>
                     <tr>
                     <tr>
-                        <td class="text-center"><input type="checkbox"></td>
-                        <td>指挥部工程结算款</td>
-                    </tr>
-                    <tr>
-                        <td class="text-center"><input type="checkbox"></td>
-                        <td>指挥部工程结算付款</td>
-                    </tr>
-                    <tr>
-                        <td class="text-center"><input type="checkbox"></td>
-                        <td>指挥部监理、设计等预付款</td>
+                        <td class="text-center"><input type="checkbox" name="rptId[]" data-name="<%- rpt.name %>" value="<%- rpt.ID %>"
+                                                       <% if (ctx.helper._.findIndex(tenderRptList, { rpt_id: rpt.ID }) !== -1) { %>checked<% } %> <% if (rpt.had_rpt) { %>disabled<% } %>></td>
+                        <td><%- rpt.name %></td>
                     </tr>
                     </tr>
+                    <% } %>
                     </tbody>
                     </tbody>
                 </table>
                 </table>
             </div>
             </div>
             <div class="modal-footer">
             <div class="modal-footer">
                 <input type="hidden" id="add_tender_folder_id" />
                 <input type="hidden" id="add_tender_folder_id" />
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-primary" id="new_tender_btn">确定添加</button>
+                <button type="button" class="btn btn-sm btn-primary" id="add_rpt_btn">确定添加</button>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>

+ 2 - 12
app/view/stage/audit_modal.ejs

@@ -585,12 +585,7 @@
                                                                 id="inlineRadio2"
                                                                 id="inlineRadio2"
                                                                 value="<%- auditConst.status.checkNoPre %>">
                                                                 value="<%- auditConst.status.checkNoPre %>">
                                                             <label class="form-check-label" for="inlineRadio2">退回上一审批人
                                                             <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                <% for (const [i, a] of auditors.entries()) { %>
-                                                                    <% if (a.aid === ctx.stage.curAuditor.aid) { %>
-                                                                        <%- auditors[i-1].name %>
-                                                                        <% break; %>
-                                                                    <% } %>
-                                                                <% } %>
+                                                                <%- auditors.find(item => item.sort === auditor.sort-1).name %>
                                                             </label>
                                                             </label>
                                                         </div>
                                                         </div>
                                                         <% } %>
                                                         <% } %>
@@ -664,12 +659,7 @@
                                                                 id="inlineRadio2"
                                                                 id="inlineRadio2"
                                                                 value="<%- auditConst.status.checkNoPre %>">
                                                                 value="<%- auditConst.status.checkNoPre %>">
                                                             <label class="form-check-label" for="inlineRadio2">退回上一审批人
                                                             <label class="form-check-label" for="inlineRadio2">退回上一审批人
-                                                                <% for (const [i, a] of auditors.entries()) { %>
-                                                                    <% if (a.aid === ctx.stage.curAuditor.aid) { %>
-                                                                        <%- auditors[i-1].name %>
-                                                                        <% break; %>
-                                                                    <% } %>
-                                                                <% } %>
+                                                                <%- auditors.find(item => item.sort === auditor.sort-1).name %>
                                                             </label>
                                                             </label>
                                                         </div>
                                                         </div>
                                                         <% } %>
                                                         <% } %>

+ 7 - 2
app/view/stage/modal.ejs

@@ -626,13 +626,18 @@
                 <div class="form-group">
                 <div class="form-group">
                     <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
                     <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
                     <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <label for="formGroupExampleInput">单个文件大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
-                    <input type="file" class="" id="upload-im-file" multiple>
                     <% } %>
                     <% } %>
+                    <div class="d-flex">
+                        <% if (!ctx.tender.isTourist || (ctx.tender.isTourist && ctx.tender.touristPermission.file) || stage.filePermission) { %>
+                        <input type="file" class="" id="upload-im-file" multiple>
+                        <% } %>
+                        <div class="ml-auto"><button class="btn btn-sm btn-primary" id="batch-download-im-file">批量下载</button></div>
+                    </div>
                 </div>
                 </div>
                 <div class="modal-height-500" style="overflow:auto;">
                 <div class="modal-height-500" style="overflow:auto;">
                     <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
                     <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
                         <thead>
                         <thead>
-                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="52">操作</th></tr>
+                        <tr><th width="40px">选择</th><th width="200">文件名</th><th>上传人</th><th>上传时间</th><th width="52">操作</th></tr>
                         </thead>
                         </thead>
                         <tbody id="im-attList">
                         <tbody id="im-attList">
                         </tbody>
                         </tbody>

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

@@ -14,12 +14,12 @@
         <div class="sjs-height-0" style="background-color: #fff">
         <div class="sjs-height-0" style="background-color: #fff">
             <div class="c-body">
             <div class="c-body">
                 <% if (!projectList || projectList.length === 0) { %>
                 <% if (!projectList || projectList.length === 0) { %>
-                <div class="jumbotron">
+                <div class="jumbotron" id="no-project">
                     <h3 class="display-6">还没有项目数据</h3>
                     <h3 class="display-6">还没有项目数据</h3>
                 </div>
                 </div>
                 <% } else { %>
                 <% } else { %>
                 <table class="table table-bordered">
                 <table class="table table-bordered">
-                    <tr class="text-center"><th style="min-width: 200px">项目名称</th><th>创建时间</th><th>概预算标准</th><th>管理单位</th><th>操作</th></tr>
+                    <tr class="text-center"><th style="min-width: 200px">项目名称</th><th>概预算标准</th><th>创建时间</th><th>管理单位</th><th>操作</th></tr>
                     <tbody id="projectList">
                     <tbody id="projectList">
                     </tbody>
                     </tbody>
                 </table>
                 </table>

+ 34 - 9
app/view/sub_proj/modal.ejs

@@ -15,12 +15,6 @@
                             <input type="text" class="form-control form-control-sm" id="project-name" placeholder="请输入项目名称" onchange="NameChange(this);">
                             <input type="text" class="form-control form-control-sm" id="project-name" placeholder="请输入项目名称" onchange="NameChange(this);">
                         </div>
                         </div>
                     </div>
                     </div>
-                    <div class="form-group row">
-                        <label for="text" class="col-sm-3 col-form-label-sm">管理单位</label>
-                        <div class="col-sm-9">
-                            <input type="text" class="form-control form-control-sm" id="management" placeholder="请输入管理名称">
-                        </div>
-                    </div>
                 </form>
                 </form>
             </div>
             </div>
             <div class="modal-footer">
             <div class="modal-footer">
@@ -130,7 +124,7 @@
             <div class="modal-header">
             <div class="modal-header">
                 <h5 class="modal-title">成员管理</h5>
                 <h5 class="modal-title">成员管理</h5>
             </div>
             </div>
-            <div class="modal-body">
+            <div class="modal-body" style="overflow-y: auto; height: 480px">
                 <div class="dropdown">
                 <div class="dropdown">
                     <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                     <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                         添加用户
                         添加用户
@@ -160,7 +154,7 @@
                             <th rowspan="2">成员名称</th>
                             <th rowspan="2">成员名称</th>
                             <th rowspan="2">角色/职位</th>
                             <th rowspan="2">角色/职位</th>
                             <th colspan="2">动态投资</th>
                             <th colspan="2">动态投资</th>
-                            <th colspan="3">电子档案</th>
+                            <th colspan="3">资料归集</th>
                             <th rowspan="2">关联标段</th>
                             <th rowspan="2">关联标段</th>
                             <th rowspan="2">移除</th>
                             <th rowspan="2">移除</th>
                         </tr>
                         </tr>
@@ -169,7 +163,7 @@
                             <th>编辑</th>
                             <th>编辑</th>
                             <th>查看</th>
                             <th>查看</th>
                             <th>导入</th>
                             <th>导入</th>
-                            <th>编辑</th>
+                            <th>编辑分类</th>
                         </tr>
                         </tr>
                         </thead>
                         </thead>
                         <tbody id="member-list">
                         <tbody id="member-list">
@@ -185,6 +179,37 @@
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
+<div class="modal" id="set-management" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">设置管理单位</h5>
+            </div>
+            <div class="modal-body">
+                <form>
+                    <div class="form-group row">
+                        <label for="text" class="col-sm-2 col-form-labelcol-form-label-sm">管理单位</label>
+                        <div class="col-sm-10">
+                            <select id="sm-management" class="form-control form-control-sm">
+                                <% if (companys.length > 0) { %>
+                                <% for( const c of companys) { %>
+                                <option value="<%- c.name %>"><%- c.name %></option>
+                                <% } %>
+                                <% } else {%>
+                                <option value="">暂无任何参建单位,请先在项目信息-账号设置-参建单位下添加</option>
+                                <% } %>
+                            </select>
+                        </div>
+                    </div>
+                </form>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="set-management-ok">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 <script>
 <script>
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');
     const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');

+ 5 - 5
config/menu.js

@@ -31,9 +31,9 @@ const menu = {
         display: true,
         display: true,
         url: '/list',
         url: '/list',
         children: null,
         children: null,
-        caption: '项目',
+        caption: '标段管理',
     },
     },
-    sub_project: {
+    subproj: {
         name: '项目管理',
         name: '项目管理',
         icon: 'fa-tags',
         icon: 'fa-tags',
         display: true,
         display: true,
@@ -43,7 +43,7 @@ const menu = {
     },
     },
     file: {
     file: {
         name: '资料归集',
         name: '资料归集',
-        icon: 'fa-credit-card',
+        icon: 'fa-file-zip-o',
         display: true,
         display: true,
         url: '/file',
         url: '/file',
         children: null,
         children: null,
@@ -58,10 +58,10 @@ const menu = {
         caption: '动态投资',
         caption: '动态投资',
     },
     },
     management: {
     management: {
-        name: '项目管理',
+        name: '项目管理系统',
         icon: 'fa-cubes',
         icon: 'fa-cubes',
         display: false,
         display: false,
-        caption: '项目管理',
+        caption: '项目管理系统',
         children: null,
         children: null,
     },
     },
     payment: {
     payment: {

+ 19 - 4
config/web.js

@@ -356,6 +356,7 @@ const JsFiles = {
                     '/public/js/zh_calc.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/path_tree.js',
                     '/public/js/zip_oss.js',
                     '/public/js/zip_oss.js',
+                    '/public/js/shares/ali_oss.js',
                     '/public/js/stage_im.js',
                     '/public/js/stage_im.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tender_select.js',
                     '/public/js/shares/tender_select.js',
@@ -1020,7 +1021,7 @@ const JsFiles = {
                 mergeFiles: [
                 mergeFiles: [
                     '/public/js/zh_calc.js',
                     '/public/js/zh_calc.js',
                 ],
                 ],
-                mergeFile: 'index',
+                mergeFile: 'datacollect_index',
             },
             },
         },
         },
         subProject:{
         subProject:{
@@ -1050,9 +1051,23 @@ const JsFiles = {
                     '/public/js/path_tree.js',
                     '/public/js/path_tree.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/shares/tenders2tree.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
-                    '/public/js/file.js',
+                    '/public/js/file_list.js',
                 ],
                 ],
-                mergeFile: 'file',
+                mergeFile: 'file_list',
+            },
+            file: {
+                files: [
+                    '/public/js/axios/axios.min.js', '/public/js/file-saver/FileSaver.js', '/public/js/js-xlsx/jszip.min.js',
+                    '/public/js/moment/moment.min.js', '/public/js/ztree/jquery.ztree.core.js', '/public/js/ztree/jquery.ztree.exedit.js',
+                ],
+                mergeFiles: [
+                    '/public/js/shares/ali_oss.js',
+                    '/public/js/shares/drag_tree.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/shares/tenders2tree.js',
+                    '/public/js/file_detail.js',
+                ],
+                mergeFile: 'file_detail',
             },
             },
         },
         },
         budget: {
         budget: {
@@ -1120,7 +1135,7 @@ const JsFiles = {
                 mergeFiles: [
                 mergeFiles: [
                     '/public/js/payment_index.js',
                     '/public/js/payment_index.js',
                 ],
                 ],
-                mergeFile: 'index',
+                mergeFile: 'payment_index',
             },
             },
             process: {
             process: {
                 files: [],
                 files: [],

+ 3 - 1
sql/update20230329.sql

@@ -32,4 +32,6 @@ CREATE TABLE `zh_global_log` (
   `is_response` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '是否返回数据',
   `is_response` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '是否返回数据',
   `run_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '运行时长,毫秒',
   `run_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '运行时长,毫秒',
   PRIMARY KEY (`id`)
   PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+
+ALTER TABLE `zh_project` ADD `after_login_url` VARCHAR(255) NULL DEFAULT NULL COMMENT '登录后跳转页url' AFTER `can_api`;