Explorar o código

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

Tony Kang hai 4 semanas
pai
achega
cdfda1d39e
Modificáronse 80 ficheiros con 3129 adicións e 372 borrados
  1. 16 0
      app/base/base_service.js
  2. 1 1
      app/const/sp_page_show.js
  3. 3 1
      app/controller/budget_controller.js
  4. 45 5
      app/controller/change_controller.js
  5. 44 0
      app/controller/contract_controller.js
  6. 189 2
      app/controller/financial_controller.js
  7. 3 1
      app/controller/pay_controller.js
  8. 40 45
      app/controller/quality_controller.js
  9. 3 3
      app/controller/stage_controller.js
  10. 1 2
      app/controller/tender_controller.js
  11. 12 2
      app/extend/context.js
  12. 3 2
      app/lib/ledger.js
  13. 2 2
      app/lib/revise_price.js
  14. 2 0
      app/lib/rpt_data_analysis.js
  15. 6 4
      app/lib/sum_load.js
  16. 166 20
      app/public/js/budget_compare.js
  17. 112 78
      app/public/js/change_information.js
  18. 4 5
      app/public/js/change_revise.js
  19. 128 1
      app/public/js/contract_detail.js
  20. 1 1
      app/public/js/div_resizer.js
  21. 297 0
      app/public/js/financial_pay_tender.js
  22. 35 0
      app/public/js/financial_transfer.js
  23. 14 3
      app/public/js/global.js
  24. 4 3
      app/public/js/ledger_check.js
  25. 9 6
      app/public/js/path_tree.js
  26. 49 20
      app/public/js/quality_info.js
  27. 48 30
      app/public/js/quality_rule.js
  28. 2 2
      app/public/js/quality_tender.js
  29. 24 5
      app/public/js/setting_s2b.js
  30. 1 1
      app/public/js/shares/cs_tools.js
  31. 3 3
      app/public/js/stage.js
  32. 1 0
      app/public/js/tender_list_base.js
  33. 4 1
      app/router.js
  34. 2 2
      app/service/change_pos.js
  35. 175 0
      app/service/contract_tree.js
  36. 4 4
      app/service/financial_transfer.js
  37. 1 1
      app/service/ledger.js
  38. 18 1
      app/service/phase_pay.js
  39. 1 1
      app/service/phase_pay_detail.js
  40. 107 36
      app/service/quality.js
  41. 1 1
      app/service/quality_file.js
  42. 8 7
      app/service/quality_rule.js
  43. 8 0
      app/service/report.js
  44. 26 2
      app/service/report_memory.js
  45. 9 1
      app/service/stage_bills_final.js
  46. 10 1
      app/service/stage_pos_final.js
  47. 1 1
      app/service/sub_proj_permission.js
  48. 8 7
      app/service/tender_info.js
  49. 16 1
      app/view/budget/compare.ejs
  50. 66 0
      app/view/budget/compare_modal.ejs
  51. 2 2
      app/view/change/apply_modal.ejs
  52. 5 0
      app/view/change/information.ejs
  53. 2 2
      app/view/change/plan_modal.ejs
  54. 2 2
      app/view/change/project_modal.ejs
  55. 28 8
      app/view/contract/detail.ejs
  56. 14 0
      app/view/dashboard/workspace.ejs
  57. 1 1
      app/view/file/file.ejs
  58. 1 1
      app/view/file/index.ejs
  59. 1 1
      app/view/file/manage.ejs
  60. 1 1
      app/view/file/template.ejs
  61. 1 1
      app/view/financial/pay_list.ejs
  62. 1 1
      app/view/financial/pay_stage.ejs
  63. 2 2
      app/view/financial/pay_stage_modal.ejs
  64. 103 0
      app/view/financial/pay_tender.ejs
  65. 5 1
      app/view/financial/transfer.ejs
  66. 23 0
      app/view/financial/transfer_modal.ejs
  67. 4 4
      app/view/quality/sub_memu_list.ejs
  68. 28 8
      app/view/quality/tender.ejs
  69. 1 0
      app/view/report/index_sign.ejs
  70. 17 13
      app/view/revise/index.ejs
  71. 5 3
      app/view/setting/s2b.ejs
  72. 2 0
      app/view/setting/s2b_modal.ejs
  73. 1 1
      app/view/sp_setting/permission.ejs
  74. 1 1
      app/view/spss/check_tz.ejs
  75. 1 1
      app/view/sub_proj/modal.ejs
  76. 9 0
      app/view/tender/list_sub_menu_list.ejs
  77. 1 1
      app/view/tender/tender_sub_menu.ejs
  78. 2 2
      config/menu.js
  79. 25 0
      config/web.js
  80. 1107 0
      sql/update.sql

+ 16 - 0
app/base/base_service.js

@@ -101,6 +101,22 @@ class BaseService extends Service {
     }
 
     /**
+     * 主动分隔插入数据
+     *
+     * @param transaction 事务
+     * @param tableName 表名
+     * @param datas 插入数据
+     * @param split 分隔条数,默认5000
+     * @returns {Promise<void>}
+     */
+    async insertDatasSplit(transaction, tableName, datas, split = 5000) {
+        const conn = transaction || this.db;
+        for (let i = 0; i < datas.length; i += split) {
+            await conn.insert(tableName, datas.slice(i, i + split));
+        }
+    }
+
+    /**
      * 根据id删除数据
      *
      * @param {Number} id - 数据库中的id

+ 1 - 1
app/const/sp_page_show.js

@@ -18,7 +18,7 @@ const managerPageControl = [
     { title: '项目概况', name: 'openInfo', value: pageStatus.show, type: 'checkbox' },
     { title: '决策大屏', name: 'openDataCollect', value: pageStatus.show, type: 'checkbox', tip: '开启后,前台配置用户权限后方可显示' },
     { title: '合同管理', name: 'openContract', value: pageStatus.show, type: 'checkbox' },
-    { title: '资料归集', name: 'openFile', value: pageStatus.show, type: 'checkbox' },
+    { title: '资料管理', name: 'openFile', value: pageStatus.show, type: 'checkbox' },
     { title: '动态投资', name: 'openBudget', value: pageStatus.show, type: 'checkbox', tip: '开启后,前台配置用户权限后方可显示' },
     { title: '支付审批', name: 'openPayment', value: pageStatus.show, type: 'checkbox', tip: '开启后,前台配置用户权限后方可显示' },
     { title: '资金监管', name: 'openFinancial', value: pageStatus.show, type: 'checkbox' },

+ 3 - 1
app/controller/budget_controller.js

@@ -23,6 +23,8 @@ const ValidDskProject = {
     zb: [4, 18, 19],
 };
 const DSK = require('../lib/dsk');
+const sendToWormhole = require('stream-wormhole');
+const streamToArray = require('stream-to-array');
 
 module.exports = app => {
     class BudgetController extends app.BaseController {
@@ -351,7 +353,7 @@ module.exports = app => {
                 const parts = await streamToArray(stream);
                 // 转化为buffer
                 const buffer = Buffer.concat(parts);
-                const bills = relaService.importYbp(ctx.budget, buffer.toString(), needGcl);
+                const bills = await relaService.importYbp(ctx.budget, buffer.toString(), needGcl);
                 ctx.body = { err: 0, msg: '', data: bills };
             } catch (err) {
                 await sendToWormhole(stream);

+ 45 - 5
app/controller/change_controller.js

@@ -931,17 +931,49 @@ module.exports = app => {
             }
         }
 
+        async _getLedgerColumn() {
+            const tender = this.ctx.tender;
+            const ledgerColumn = [
+                'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+                'code', 'b_code', 'name', 'unit', 'unit_price', 'quantity', 'total_price',
+                'sgfh_qty', 'sgfh_expr', 'sgfh_tp', 'memo', 'features', 'drawing_code', 'node_type', 'ex_qty1', 'ex_tp1', 'ccid'];
+            if (tender.info.display.ledger.deal) ledgerColumn.push('deal_qty', 'deal_tp');
+            if (tender.info.display.ledger.dgnQty) ledgerColumn.push('dgn_qty1', 'dgn_qty2');
+            if (tender.info.display.ledger.clQty) ledgerColumn.push('sjcl_qty', 'qtcl_qty', 'sjcl_expr', 'qtcl_expr', 'sjcl_tp', 'qtcl_tp');
+            const posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'sgfh_qty', 'sgfh_expr', 'add_stage_order', 'drawing_code', 'quantity', 'ex_qty1', 'ccid'];
+            if (tender.info.display.ledger.clQty) posColumn.push('sjcl_qty', 'qtcl_qty', 'sjcl_expr', 'qtcl_expr');
+
+            const extraFields = await spreadSetting.getExtraFields(this.ctx, tender.id);
+            if (extraFields.length > 0) {
+                ledgerColumn.push(...extraFields);
+                posColumn.push(...extraFields);
+            }
+
+            return [ledgerColumn, posColumn];
+        }
+
         async defaultBills(ctx) {
             try {
+                const filterField = true;
+                const [ledgerColumn, posColumn] = await this._getLedgerColumn();
                 // 判断是否台账修订中,修订中则不获取changeLedger及changePos值
                 const lastRevise = await ctx.service.ledgerRevise.getLastestRevise(ctx.tender.id);
                 const data = JSON.parse(ctx.request.body.data);
                 const readySettle = await ctx.service.settle.getReadySettle(ctx.tender.id);
                 if (data.from === 'revise') await ctx.service.change.checkSettleUpdate(ctx.tender.id, readySettle);
-                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
-                const changeLedgerData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changeLedger.getData(ctx.tender.id);
-                const posData = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
-                const changePosData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changePos.getPosData({ tid: ctx.tender.id });
+
+                const ledgerData = await ctx.service.ledger.getAllDataByCondition({ columns: ledgerColumn, where: { tender_id: ctx.tender.id } });
+                ledgerColumn.push('formc');
+                const changeLedgerData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changeLedger.getAllDataByCondition({ columns: ledgerColumn, where: { tender_id: ctx.tender.id} });
+                const posData = await ctx.service.pos.getPosData({tid: ctx.tender.id}, posColumn);
+                posColumn.push('formc');
+                const changePosData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changePos.getPosData({ tid: ctx.tender.id }, posColumn);
+
+                // const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                // const changeLedgerData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changeLedger.getData(ctx.tender.id);
+                // const posData = await ctx.service.pos.getPosData({ tid: ctx.tender.id });
+                // const changePosData = lastRevise && lastRevise.status !== audit.revise.status.checked && data.from !== 'revise' ? [] : await ctx.service.changePos.getPosData({ tid: ctx.tender.id });
+
                 const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: ctx.tender.id } });
                 // const settleStatus = ctx.service.settle.settleStatus;
                 const settleBills = readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: readySettle.id } }) : [];
@@ -1003,7 +1035,15 @@ module.exports = app => {
                     bodyData.changeLedgerList = changeLedgerData;
                     bodyData.changePosList = changePosData;
                 }
-                ctx.body = { err: 0, msg: '', data: bodyData };
+                // hpack压缩数据
+                const hpack = true;
+                if (hpack) {
+                    bodyData.bills = this.ctx.helper.hpackArr(bodyData.bills);
+                    bodyData.pos = this.ctx.helper.hpackArr(bodyData.pos);
+                    ctx.body = { err: 0, msg: '', data: bodyData, hpack: ['bills', 'pos'] };
+                } else {
+                    ctx.body = { err: 0, msg: '', data: bodyData };
+                }
             } catch (err) {
                 console.log(err);
                 this.log(err);

+ 44 - 0
app/controller/contract_controller.js

@@ -1,4 +1,9 @@
 'use strict';
+const stdDataAddType = {
+    withParent: 1,
+    child: 2,
+    next: 3,
+};
 const auditConst = require('../const/audit');
 const contractConst = require('../const/contract');
 const moment = require('moment');
@@ -260,6 +265,8 @@ module.exports = app => {
                 const contractOptions = ctx.helper._.cloneDeep(ctx.contractOptions);
                 contractOptions.contract_type = ctx.contract_type;
                 const contractTreeAudits = await ctx.service.contractTreeAudit.getAllDataByCondition({ where: contractOptions });
+                const [stdBills, stdChapters] = ctx.contract.tender ? await this.ctx.service.valuation.getValuationStdList(
+                    ctx.contract.valuation, ctx.contract.measure_type) : await ctx.service.budgetStd.getStdList(ctx.subProject.std_id, 'yu');
                 const renderData = {
                     contractTreeAudits,
                     audit_permission: ctx.contract_audit_permission,
@@ -269,6 +276,7 @@ module.exports = app => {
                     contractConst,
                     whiteList,
                     thisUrl: `/sp/${ctx.subProject.id}` + (ctx.contract_tender ? `/contract/tender/${ctx.contract.id}/detail` : '/contract/detail'),
+                    stdChapters,
                 };
                 if (ctx.session.sessionUser.is_admin) {
                     const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
@@ -350,6 +358,9 @@ module.exports = app => {
                     case 'paste-block':
                         responseData.data = await this._pasteBlock(ctx, data.postData, options);
                         break;
+                    case 'add-std':
+                        responseData.data = await this._addStd(ctx, data.postData, options);
+                        break;
                     case 'add-contract':
                         responseData.data = await ctx.service.contract.add(options, data.postData.select, data.postData.contract);
                         break;
@@ -418,6 +429,39 @@ module.exports = app => {
                 (!data.block || data.block.length <= 0)) throw '参数错误';
             return await ctx.service.contractTree.pasteBlockData(options, data.id, data.block);
         }
+        /**
+         * 从标准项目表添加数据
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async _addStd(ctx, data, options) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            // todo 校验项目是否使用该库的权限
+            if (!(ctx.session.sessionUser.is_admin || ctx.audit_permission.permission_edit)) {
+                throw '没有操作权限';
+            }
+            let stdLib,
+                addType;
+            switch (data.stdType) {
+                case 'xmj':
+                    stdLib = ctx.service.stdXmj;
+                    addType = stdDataAddType.withParent;
+                    break;
+                default:
+                    throw '未知标准库';
+            }
+            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
+            switch (addType) {
+                // case stdDataAddType.child:
+                //     return await ctx.service.contractTree.addStdNodeAsChild(options, data.id, stdData);
+                // case stdDataAddType.next:
+                //     return await ctx.service.contractTree.addStdNode(options, data.id, stdData);
+                case stdDataAddType.withParent:
+                    return await ctx.service.contractTree.addStdNodeWithParent(options, data.id, stdData, stdLib);
+                default:
+                    throw '未知添加方式';
+            }
+        }
 
         /**
          * 上传附件

+ 189 - 2
app/controller/financial_controller.js

@@ -244,6 +244,15 @@ module.exports = app => {
                     case 'save-transfer':
                         responseData.data = await ctx.service.financialTransfer.saveTransfer(data.postData);
                         break;
+                    case 'edit-transfer-time':
+                        if (!ctx.session.sessionUser.is_admin) throw '没有权限修改划拨年月';
+                        if (!data.postData.id || !data.postData.t_time) throw '数据错误';
+                        const node = await ctx.service.financialTransfer.getDataById(data.postData.id);
+                        if (!node) {
+                            throw '该资金划拨不存在';
+                        }
+                        await ctx.service.financialTransfer.defaultUpdate(data.postData);
+                        break;
                     default:
                         throw '未知操作';
                 }
@@ -899,7 +908,7 @@ module.exports = app => {
                 unitList,
                 moment,
                 auditType: auditConst.auditType,
-                preUrl: '/sp/' + ctx.subProject.id + '/financial/pay' + (from ? '/list' : '/stage'),
+                preUrl: '/sp/' + ctx.subProject.id + '/financial/pay' + (from !== null ? '/' + from : '/stage'),
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.pay),
                 pageInfo,
             };
@@ -1110,6 +1119,184 @@ module.exports = app => {
         }
 
         /**
+         * 变更管理 页面 (Get)
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async payTender(ctx) {
+            try {
+                await this._filterPayTender(ctx);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect(`/sp/${ctx.subProject.id}/dashboard`);
+            }
+        }
+
+        async _filterPayTender(ctx) {
+            const financialPermission = await ctx.service.subProjPermission.getFinancailPermission(ctx.subProject.permission.fund_trans_permission, ctx.subProject.permission.fund_pay_permission);
+            if (!financialPermission.pay_show) {
+                throw '没有查看权限';
+            }
+            const fptAudits = await ctx.service.financialPayTenderAudit.getAllDataByCondition({ where: { spid: ctx.subProject.id, uid: ctx.session.sessionUser.accountId } });
+            const fptAuditTids = ctx.helper._.map(fptAudits, 'tid');
+            const fptReportTids = ctx.helper._.map(ctx.helper._.filter(fptAudits, { is_report: 1 }), 'tid');
+            const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
+            const filterTids = ctx.session.sessionUser.is_admin ? null : fptAuditTids;
+            const tenderCondition = { spid: ctx.subProject.id, filter_fund: 0 };
+            let hadTender = false;
+            if (ctx.session.sessionUser.is_admin) {
+                hadTender = true;
+            } else if (fptAuditTids.length !== 0) {
+                hadTender = true;
+                tenderCondition.id = fptAuditTids;
+            } else {
+                hadTender = false;
+                tenderCondition.id = -1;
+            }
+            const categoryData = await this.ctx.service.category.getAllCategory(ctx.subProject);
+            const tenders = hadTender ? await ctx.service.tender.getAllDataByCondition({ where: tenderCondition, columns: ['id', 'name', 'category'] }) : [];
+            const allPays = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, null, 0, filterTids);
+            const transfersCondition = { spid: ctx.subProject.id };
+            if (filterTids) {
+                transfersCondition.tid = filterTids;
+            }
+            const allTransfers = await ctx.service.financialTransferTender.getAllDataByCondition({ where: transfersCondition });
+            const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'company_id', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
+            const user = accountList.find(item => item.id === ctx.session.sessionUser.accountId) || null;
+            const userCompany = user ? ctx.helper._.find(unitList, { name: user.company }) : null;
+            if (!userCompany) {
+                throw '请联系管理员添加用户所在单位信息';
+            }
+            const allPayStages = await ctx.service.financialPayStage.getListByStatus(ctx.subProject.id, null, userCompany ? userCompany.id : 0, null, 0);
+            for (const t of tenders) {
+                t.category = t.category && t.category !== '' ? JSON.parse(t.category) : null;
+                const tenderPays = ctx.helper._.filter(allPays, { tid: t.id });
+                let payAmount = 0;
+                for (const p of tenderPays) {
+                    payAmount = ctx.helper.add(payAmount, p.total_price);
+                    p.stage = ctx.helper._.find(allPayStages, { id: p.fpsid }) || null;
+                    p.tenderName = t ? t.name : '';
+                    p.order = p.stage ? p.stage.order : 0;
+                    const userInfo = ctx.helper._.find(accountList, { id: p.uid });
+                    p.username = userInfo ? userInfo.name : '';
+                    if (p.status !== auditConst.financial.status.checked || !p.final_auditor_str) {
+                        p.curAuditors = await ctx.service.financialPayAudit.getAuditorsByStatus(p.id, p.status, p.times);
+                        if (p.status === auditConst.financial.status.checked && p.curAuditors.length > 0) {
+                            const final_auditor_str = p.curAuditors[0].audit_type === auditConst.auditType.key.common
+                                ? p.curAuditors[0].name + (p.curAuditors[0].role ? '-' + p.curAuditors[0].role : '')
+                                : ctx.helper.transFormToChinese(p.curAuditors[0].audit_order) + '审';
+                            await ctx.service.financialPay.defaultUpdate({ final_auditor_str }, { where: { id: p.id } });
+                        }
+                    }
+                    if (p.status !== auditConst.financial.status.checked) {
+                        p.entities = await ctx.service.financialPayContract.getEntities(p.id);
+                    }
+                }
+                t.pay_amount = payAmount;
+                t.pay_num = tenderPays.length;
+                const tenderTransfers = ctx.helper._.filter(allTransfers, { tid: t.id });
+                let transferAmount = 0;
+                for (const tr of tenderTransfers) {
+                    transferAmount = ctx.helper.add(transferAmount, tr.hb_tp);
+                }
+                t.transfer_amount = transferAmount;
+            }
+            // const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject, ['id', 'account', 'name', 'company', 'company_id', 'role', 'enable', 'is_admin', 'account_group', 'mobile']);
+            // const user = accountList.find(item => item.id === ctx.session.sessionUser.accountId) || null;
+            // const userCompany = user ? ctx.helper._.find(unitList, { name: user.company }) : null;
+            // if (!userCompany) {
+            //     throw '请联系管理员添加用户所在单位信息';
+            // }
+            // const userOrderList = await ctx.service.financialPayStage.getUserOrderList(ctx.subProject.id, userCompany ? userCompany.id : 0);
+            // const userCompanyList = await ctx.service.financialPayStage.getUserCompanyList(ctx.subProject.id, userCompany ? userCompany.id : 0, unitList);
+            // // const payList = await ctx.service.financialPayStage.getListByStatus(ctx.subProject.id, companyInfo ? companyInfo.id : null, userCompany ? userCompany.id : 0, qi);
+            const filter = JSON.parse(JSON.stringify(auditConst.financial.filter));
+            // filter.count = [];
+            // filter.count[filter.status.pending] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, filter.status.pending, filterTids, used);
+            // filter.count[filter.status.uncheck] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, filter.status.uncheck, filterTids, used);
+            // filter.count[filter.status.checking] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, filter.status.checking, filterTids, used);
+            // filter.count[filter.status.checked] = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, filter.status.checked, filterTids, used);
+            // const payList = await ctx.service.financialPay.getListByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, status, filterTids, used, 1);
+            // const total = await ctx.service.financialPay.getCountByStatus(ctx.subProject.id, fpsidList.length > 0 ? fpsidList : null, status, filterTids, used);
+            // // 分页相关
+            // const page = ctx.page;
+            // const pageSize = ctx.pageSize;
+            // const pageInfo = {
+            //     page,
+            //     pageSizeSelect: 1,
+            //     pageSize,
+            //     total_num: total,
+            //     total: Math.ceil(total / pageSize),
+            //     queryData: JSON.stringify(ctx.urlInfo.query),
+            // };
+            // for (const pay of payList) {
+            //     const t = ctx.helper._.find(tenders, { id: pay.tid });
+            //     pay.tenderName = t ? t.name : '';
+            //     const userInfo = ctx.helper._.find(accountList, { id: pay.uid });
+            //     pay.username = userInfo ? userInfo.name : '';
+            //     if (pay.status !== auditConst.financial.status.checked || !pay.final_auditor_str) {
+            //         pay.curAuditors = await ctx.service.financialPayAudit.getAuditorsByStatus(pay.id, pay.status, pay.times);
+            //         if (pay.status === auditConst.financial.status.checked && pay.curAuditors.length > 0) {
+            //             const final_auditor_str = pay.curAuditors[0].audit_type === auditConst.auditType.key.common
+            //                 ? pay.curAuditors[0].name + (pay.curAuditors[0].role ? '-' + pay.curAuditors[0].role : '')
+            //                 : ctx.helper.transFormToChinese(pay.curAuditors[0].audit_order) + '审';
+            //             await ctx.service.financialPay.defaultUpdate({ final_auditor_str }, { where: { id: pay.id } });
+            //         }
+            //     }
+            //     if (pay.status !== auditConst.financial.status.checked) {
+            //         pay.entities = await ctx.service.financialPayContract.getEntities(pay.id);
+            //     }
+            //     pay.stage = pay.fpsid ? await ctx.service.financialPayStage.getOnePayStage(pay.fpsid) : null;
+            // }
+            const renderData = {
+                categoryData,
+                tenders,
+                financialPermission,
+                usedList: financialConst.used,
+                auditConst: auditConst.financial,
+                filter,
+                allPays,
+                fptAuditTids,
+                fptReportTids,
+                unitList,
+                moment,
+                auditType: auditConst.auditType,
+                // preUrl: '/sp/' + ctx.subProject.id + '/financial/pay' + (from === 'list' ? '/list/?from=' + from : '/stage'),
+                jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.payTender),
+                // pageInfo,
+            };
+            await this.layout('financial/pay_tender.ejs', renderData);
+        }
+
+        async payTenderSave(ctx) {
+            try {
+                const responseData = {
+                    err: 0, msg: '', data: {},
+                };
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.type) {
+                    throw '提交数据错误';
+                }
+                const payStage = await ctx.service.financialPayStage.getDataById(ctx.params.fpsid);
+                if (!payStage) {
+                    throw '资金支付单位期不存在';
+                }
+                switch (data.type) {
+                    case 'tender':
+                        if (!data.tid) throw '未选择标段';
+                        responseData.data = await ctx.service.financialPay.addPay(ctx.subProject.id, payStage, data.updateData);
+                        break;
+                    default: throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: {} };
+            }
+        }
+
+        /**
          * 获取审批界面所需的 原报、审批人数据等
          * @param ctx
          * @return {Promise<void>}
@@ -1142,7 +1329,7 @@ module.exports = app => {
                     from,
                     fileList,
                     authMobile: auth_mobile,
-                    returnUrl: `/sp/${ctx.subProject.id}/financial/pay/${from === 'list' ? 'list' : 'stage/' + ctx.financialPay.fpsid}`,
+                    returnUrl: `/sp/${ctx.subProject.id}/financial/pay/${from !== undefined ? from : 'stage/' + ctx.financialPay.fpsid}`,
                     preUrl: `/sp/${ctx.subProject.id}/financial/pay`,
                     preUrl2: `/sp/${ctx.subProject.id}/financial/pay/` + ctx.financialPay.id,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.financial.payDetail),

+ 3 - 1
app/controller/pay_controller.js

@@ -34,10 +34,12 @@ module.exports = app => {
                     if (p.audit_status !== audit.common.status.checked) await this.ctx.service.phasePay.loadUser(p);
                     relaStage.push(...p.rela_stage);
                 }
+                const relaStageOrder = this.ctx.helper._.orderBy(relaStage, ['stage_order'], ['desc']);
+                const maxOrder = relaStageOrder.length > 0 ? relaStageOrder[0].stage_order : 0;
                 const stages = await this.ctx.service.stage.getAllDataByCondition({ where: { tid: ctx.tender.id }, orders: [['order', 'AEC']] });
                 const validStages = stages.filter(s => {
                     if (s.status !== audit.stage.status.checked) return false;
-                    return !relaStage.find(r => { return s.id === r.stage_id; });
+                    return !relaStage.find(r => { return s.id === r.stage_id; }) && s.order > maxOrder;
                 });
                 this.ctx.service.phasePay.calculatePhasePay(phasePays);
                 const renderData = {

+ 40 - 45
app/controller/quality_controller.js

@@ -25,15 +25,13 @@ module.exports = app => {
 
         async tender(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
 
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.quality.tender),
                 };
-                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 accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
                 renderData.accountList = accountList;
                 const unitList = await ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 renderData.accountGroup = unitList.map(item => {
@@ -42,8 +40,8 @@ module.exports = app => {
                 });
                 renderData.accountInfo = await this.ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
                 renderData.tenderList = await ctx.service.tender.getSpecList(ctx.service.tenderPermission, 'quality', ctx.session.sessionUser.is_admin ? 'all' : '');
-                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
-                // renderData.selfCategoryLevel = this.ctx.subProject.permission.self_category_level;
+                renderData.categoryData = await this.ctx.service.category.getAllCategory(this.ctx.subProject);
+                renderData.selfCategoryLevel = this.ctx.subProject.permission.self_category_level;
                 renderData.permissionConst = ctx.service.tenderPermission.partPermissionConst('quality');
                 renderData.permissionBlock = ctx.service.tenderPermission.partPermissionBlock('quality');
                 await this.layout('quality/tender.ejs', renderData, 'quality/tender_modal.ejs');
@@ -78,7 +76,7 @@ module.exports = app => {
 
         async info(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
                 const renderData = {
                     thirdParty: {
                         gxby: ctx.session.sessionProject.gxby_status,
@@ -90,13 +88,13 @@ module.exports = app => {
             } catch (err) {
                 ctx.log(err);
                 ctx.postError(err, '无法查看质量管理数据');
-                ctx.redirect('/quality');
+                ctx.redirect(`/sp/${ctx.subProject.id}/quality`);
             }
         }
 
         async flaw(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.quality.flaw),
                 };
@@ -104,13 +102,13 @@ module.exports = app => {
             } catch (err) {
                 ctx.log(err);
                 ctx.postError(err, '无法查看质量管理数据');
-                ctx.redirect('/quality');
+                ctx.redirect(`/sp/${ctx.subProject.id}/quality`);
             }
         }
 
         async lab(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.quality.lab),
                 };
@@ -118,40 +116,40 @@ module.exports = app => {
             } catch (err) {
                 ctx.log(err);
                 ctx.postError(err, '无法查看质量管理数据');
-                ctx.redirect('/quality');
+                ctx.redirect(`/sp/${ctx.subProject.id}/quality`);
             }
         }
 
         async _loadXmjData(spec) {
             const xmj = await this.ctx.service.ledger.getAllDataByCondition({
                 where: { tender_id: this.ctx.tender.id },
-                columns: ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'name'],
+                columns: ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name'],
             });
-            const quality = spec && spec.loadStatus ? await this.ctx.service.quality.getAllDataByCondition({
-                where: { tid: this.ctx.tender.id, rela_type: 'xmj' },
-                columns: ['rela_id', 'gxby_status', 'gxby_date', 'dagl_status'],
-            }) : [];
-            this.ctx.helper.assignRelaData(xmj, [
-                { data: quality, fields: ['gxby_status', 'gxby_date', 'dagl_status'], prefix: '', relaId: 'rela_id' },
-            ]);
-            return xmj;
+            // const quality = spec && spec.loadStatus ? await this.ctx.service.quality.getAllDataByCondition({
+            //     where: { tid: this.ctx.tender.id, rela_type: 'xmj' },
+            //     columns: ['rela_id', 'gxby_status', 'gxby_date', 'dagl_status'],
+            // }) : [];
+            // this.ctx.helper.assignRelaData(xmj, [
+            //     { data: quality, fields: ['gxby_status', 'gxby_date', 'dagl_status'], prefix: '', relaId: 'rela_id' },
+            // ]);
+            return xmj; // .filter(x => { return !x.b_code; });
         }
         async _loadPosData(condition, spec) {
             const pos = await this.ctx.service.pos.getAllDataByCondition({
                 where: condition,
-                columns: ['id', 'tid', 'lid', 'tree_id', 'tree_pid', 'level', 'order', 'full_path', 'is_leaf', 'code', 'b_code', 'name'],
+                columns: ['id', 'tid', 'lid', 'name'],
             });
-            const quality = spec && spec.loadStatus ? await this.ctx.service.quality.getAllDataByCondition({
-                where: { tid: this.ctx.tender.id, rela_type: 'pos' },
-                columns: ['rela_id', 'gxby_status', 'gxby_date', 'dagl_status'],
-            }) : [];
-            this.ctx.helper.assignRelaData(pos, [
-                { data: quality, fields: ['gxby_status', 'gxby_date', 'dagl_status'], prefix: '', relaId: 'rela_id' },
-            ]);
+            // const quality = spec && spec.loadStatus ? await this.ctx.service.quality.getAllDataByCondition({
+            //     where: { tid: this.ctx.tender.id, rela_type: 'pos' },
+            //     columns: ['rela_id', 'gxby_status', 'gxby_date', 'dagl_status'],
+            // }) : [];
+            // this.ctx.helper.assignRelaData(pos, [
+            //     { data: quality, fields: ['gxby_status', 'gxby_date', 'dagl_status'], prefix: '', relaId: 'rela_id' },
+            // ]);
             return pos;
         }
-        async _loadDetailData(id) {
-            const result = await this.ctx.service.quality.getDataByCondition({ tid: this.ctx.tender.id, rela_id: id });
+        async _loadDetailData(rela_type, rela_id, rela_name) {
+            const result = await this.ctx.service.quality.getDataByCondition({ tid: this.ctx.tender.id, rela_type, rela_id, rela_name });
             if (!result) return result;
             await this.ctx.service.quality.loadQualityDetail(result);
             await this.ctx.service.quality.checkQualityStatusAndSave(result);
@@ -165,12 +163,9 @@ module.exports = app => {
                     return this._loadXmjData(data.spec);
                 case 'pos':
                     return this._loadPosData({ tid: this.ctx.tender.id }, data.spec);
-                case 'pos1':
-                    if (result.pos) return result.pos.filter(p => { return p.level === 1; });
-                    return this._loadPosData({ tid: this.ctx.tender.id, level: 1 }, data.spec);
                 case 'detail':
-                    if (!data.id) throw '缺少参数';
-                    return this._loadDetailData(data.id);
+                    if (!data.rela_id && !data.rela_type) throw '缺少参数';
+                    return this._loadDetailData(data.rela_type, data.rela_id, data.rela_name);
                 case 'quality':
                     return await this.ctx.service.quality.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
                 case 'lab':
@@ -241,7 +236,7 @@ module.exports = app => {
                         break;
                     default: throw '请求错误';
                 }
-                const quality = await this._loadDetailData(data.rela_id);
+                const quality = await this._loadDetailData(data.rela_type, data.rela_id, data.rela_name);
                 ctx.body = { err: 0, msg: '', data: quality };
             } catch (err) {
                 ctx.log(err);
@@ -280,7 +275,7 @@ module.exports = app => {
                     const filepath = `${ctx.session.sessionProject.id}/${ctx.tender.id}/quality/${blockType}/${ctx.moment().format('YYYYMMDD')}/${create_time + '_' + index + fileInfo.ext}`;
 
                     // 保存文件
-                    await ctx.app.oss.put(filepath, stream);
+                    await ctx.fujianOss.put(ctx.fujianOssPath + filepath, stream);
                     await sendToWormhole(stream);
 
                     // 插入到stage_pay对应的附件列表中
@@ -332,9 +327,9 @@ module.exports = app => {
 
         async rule(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
-                const ruleGroups = await ctx.service.qualityRule.getRuleGroups(ctx.session.sessionProject.id);
-                const tenderList = await ctx.service.tender.getAllDataByCondition({ where: { project_id: ctx.session.sessionProject.id } });
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
+                const ruleGroups = await ctx.service.qualityRule.getRuleGroups(ctx.subProject.id);
+                const tenderList = await ctx.service.tender.getAllDataByCondition({ where: { spid: ctx.subProject.id } });
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.quality.rule),
                     ruleGroups,
@@ -348,12 +343,12 @@ module.exports = app => {
             } catch (err) {
                 ctx.log(err);
                 ctx.postError(err, '无法查看质量管理数据');
-                ctx.redirect('/quality');
+                ctx.redirect(`/sp/${ctx.subProject.id}/quality`);
             }
         }
         async ruleSave(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.group && !data.rule && !data.quality) throw '参数错误';
                 if (data.group) {
@@ -382,7 +377,7 @@ module.exports = app => {
         }
         async pushStatus(ctx) {
             try {
-                if (!ctx.session.sessionProject.page_show.quality) throw '该功能已关闭';
+                if (!ctx.subProject.page_show.quality) throw '该功能已关闭';
                 const data = JSON.parse(ctx.request.body.data);
                 if (!data.push_type) throw '参数错误';
                 let count;

+ 3 - 3
app/controller/stage_controller.js

@@ -279,7 +279,7 @@ module.exports = app => {
                 { data: extraData, fields: this.ledgerExtraColumn, prefix: '', relaId: 'id' },
                 { data: importData, fields: ['is_import'], prefix: '', relaId: 'lid' },
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'postil', 'ex_stage_qty1', 'ex_stage_tp1'], prefix: '', relaId: 'lid' },
-                { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used', 'ex_stage_qty1', 'ex_stage_tp1'], prefix: 'pre_', relaId: 'lid' },
+                { data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'used', 'ex_stage_qty1', 'ex_stage_tp1', 'used_time', 'update_time'], prefix: 'pre_', relaId: 'lid' },
                 { data: pcData, fields: ['contract_pc_tp', 'qc_pc_tp', 'pc_tp', 'org_price'], prefix: '', relaId: 'lid' },
                 { data: changeData, fields: ['qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'due_', relaId: 'gcl_id' },
                 { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'lid' },
@@ -314,7 +314,7 @@ module.exports = app => {
                 { data: memoData, fields: this.posMemoColumn, prefix: '', relaId: 'id'},
                 { data: extraData, fields: this.posExtraColumn, prefix: '', relaId: 'id'},
                 { data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'qc_minus_qty', 'postil', 'ex_stage_qty1'], prefix: '', relaId: 'pid' },
-                { data: preStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'ex_stage_qty1'], prefix: 'pre_', relaId: 'pid' },
+                { data: preStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'ex_stage_qty1', 'used_time', 'update_time'], prefix: 'pre_', relaId: 'pid' },
                 { data: settleStatus, fields: ['settle_status'], prefix: '', relaId: 'pid' },
             ]);
             return posData;
@@ -536,7 +536,7 @@ module.exports = app => {
                 if (projRela.banOver && ctx.tender.info.ledger_check.over) {
                     checkData.checkOverRange(ctx.tender.info.checkOverInfo);
                 }
-                if (this.ctx.tender.info.calc_type === 'up' && this.ctx.subProject.page_show.correctCalcContractTp) {
+                if (this.ctx.tender.info.calc_type === 'up' && !this.ctx.subProject.page_show.correctCalcContractTp) {
                     checkData.checkBillsTp([
                         { qty: 'contract_qty', tp: 'contract_tp' }, { qty: 'qc_qty', tp: 'qc_tp' },
                     ], this.ctx.tender.info.decimal, x => { return x.is_tp; });

+ 1 - 2
app/controller/tender_controller.js

@@ -411,7 +411,7 @@ module.exports = app => {
                         tender.total_price = sum.total_price;
                         tender.deal_tp = sum.deal_tp;
                     }
-                    tender.sum = ctx.helper.add(tender.total_price, tender.change_tp);
+                    tender.sum = ctx.tender.info.calc_type === 'tp' ? ctx.helper.add(tender.total_price, tender.change_valuation_tp) : ctx.helper.add(tender.total_price, tender.change_tp);
                     tender.gather_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.qc_tp, lastStage.pc_tp]);
                     tender.end_contract_tp = ctx.helper.sum([lastStage.contract_tp, lastStage.pre_contract_tp, lastStage.contract_pc_tp]);
                     tender.end_qc_tp = ctx.helper.sum([lastStage.qc_tp, lastStage.pre_qc_tp, lastStage.qc_pc_tp]);
@@ -737,7 +737,6 @@ module.exports = app => {
             ctx.redirect(ctx.request.header.referer);
         }
 
-
         /**
          * 设置标段计量类型并调整到标段概况(Get)
          *

+ 12 - 2
app/extend/context.js

@@ -68,14 +68,24 @@ module.exports = {
         }
     },
 
-    get hisOssPath() {
-        return this.app.config.hisOssPath;
+    // 所有附件都存储于fujianOss
+    get fujianOss() {
+        return this.app.fujianOss;
+    },
+
+    get fujianOssPath() {
+        return this.app.config.fujianOssFolder;
     },
 
+    // his和stash都存储于hisOss下,路径不同
     get hisOss() {
         return this.app.hisOss;
     },
 
+    get hisOssPath() {
+        return this.app.config.hisOssPath;
+    },
+
     get stashOssPath() {
         return this.app.config.stashOssPath;
     },

+ 3 - 2
app/lib/ledger.js

@@ -880,9 +880,10 @@ class checkData {
         }
     }
 
-    _checkMultiCondition(data, condition) {
+    _checkMultiCondition(status, check, condition) {
         let result = true;
         for (const c of condition) {
+            const data = !c.source || c.source === '3f' ? status : check;
             result = c.rela && c.rela === 'or'
                 ? (result || this.valueCheck[c.check](data[c.field], c.value, c.operation))
                 : (result && this.valueCheck[c.check](data[c.field], c.value, c.operation));
@@ -917,7 +918,7 @@ class checkData {
         return 0; // 合法
     }
     _checkMulti3f(bills, checkData, statusData, limitOption){
-        if (!this._checkMultiCondition(statusData, limitOption.condition)) return;
+        if (!this._checkMultiCondition(statusData, checkData, limitOption.condition)) return;
 
         if (bills.is_tp) return this._check3fMultiTp(checkData, limitOption.limit, limitOption);
         return this._check3fMultiQty(checkData, limitOption.limit, limitOption, bills.unit);

+ 2 - 2
app/lib/revise_price.js

@@ -399,7 +399,7 @@ class revisePriceCalc {
         const self = this;
         billsTree.calculateAll(node => {
             if (node.children && node.children.length > 0) return;
-            if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty || node.cur_ex_stage_qty1)) {
+            if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty || node.cur_ex_stage_qty1 || node.cur_qc_minus_qty)) {
                 const cur_contract_tp = calcType === 'up'
                     ? helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp)
                     : self._calcContractTpByTp(node, decimal);
@@ -488,7 +488,7 @@ class revisePriceCalc {
         const self = this;
         billsTree.calculateAll(node => {
             if (node.children && node.children.length > 0) return;
-            if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty)) {
+            if (node.cur_id && (node.cur_contract_qty || node.cur_qc_qty || node.cur_qc_minus_qty || node.cur_ex_stage_qty1)) {
                 const cur_contract_tp = calcType === 'up'
                     ? helper.mul(node.cur_contract_qty, node.unit_price, decimal.tp)
                     : self._calcContractTpByTp(node, decimal);

+ 2 - 0
app/lib/rpt_data_analysis.js

@@ -1066,6 +1066,7 @@ const auditSelect = {
             const tPrefix = 'as' + i + '_';
             fields.push({ source: sPrefix + 'contract_qty', target: tPrefix + 'contract_qty' });
             fields.push({ source: sPrefix + 'contract_tp', target: tPrefix + 'contract_tp' });
+            fields.push({ source: sPrefix + 'contract_expr', target: tPrefix + 'contract_expr' });
             fields.push({ source: sPrefix + 'qc_qty', target: tPrefix + 'qc_qty' });
             fields.push({ source: sPrefix + 'qc_tp', target: tPrefix + 'qc_tp' });
             fields.push({ source: sPrefix + 'gather_qty', target: tPrefix + 'gather_qty' });
@@ -1083,6 +1084,7 @@ const auditSelect = {
             const sPrefix = 'r' + o + '_';
             const tPrefix = 'as' + i + '_';
             fields.push({ source: sPrefix + 'contract_qty', target: tPrefix + 'contract_qty' });
+            fields.push({ source: sPrefix + 'contract_expr', target: tPrefix + 'contract_expr' });
             fields.push({ source: sPrefix + 'qc_qty', target: tPrefix + 'qc_qty' });
             fields.push({ source: sPrefix + 'gather_qty', target: tPrefix + 'gather_qty' });
         }

+ 6 - 4
app/lib/sum_load.js

@@ -367,7 +367,9 @@ class gatherStageGclTree extends loadGclBaseTree {
                 b_code: d.b_code,
                 name: d.name,
                 unit: d.unit,
-                unit_price: d.unit_price,
+                unit_price: d.unit_price || 0,
+                quantity: d.quantity || 0,
+                total_price: d.total_price || 0,
                 org_contract_qty: d.contract_qty || 0,
                 org_contract_tp: d.contract_tp || 0,
                 org_qc_qty: d.qc_qty || 0,
@@ -409,7 +411,7 @@ class gatherStageGclTree extends loadGclBaseTree {
             let end_contract_qty = node.contract_qty;
             activeQty = this.ctx.helper.add(activeQty, node.pre_qc_minus_qty);
             end_contract_qty = this.ctx.helper.add(end_contract_qty, node.pre_contract_qty);
-            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, decimal.tp);
+            const end_contract_tp = this.ctx.helper.mul(this.ctx.helper.div(end_contract_qty, activeQty), node.total_price, info.decimal.tp);
             return this.ctx.helper.sub(end_contract_tp, node.pre_contract_tp);
         } else if (info.calc_type === 'up') {
             if (correct) {
@@ -417,7 +419,7 @@ class gatherStageGclTree extends loadGclBaseTree {
                 let end_contract_qty = node.contract_qty;
                 activeQty = this.ctx.helper.add(activeQty, node.pre_qc_minus_qty);
                 end_contract_qty = this.ctx.helper.add(end_contract_qty, node.pre_contract_qty);
-                const end_contract_tp = this.ctx.helper.mul(end_contract_qty, node.total_price, decimal.tp);
+                const end_contract_tp = this.ctx.helper.mul(end_contract_qty, node.total_price, info.decimal.tp);
                 return activeQty === end_contract_qty ? this.ctx.helper.sub(end_contract_tp, node.pre_contract_tp) : this.ctx.helper.mul(node.unit_price, node.contract_qty, info.decimal.tp);
             } else {
                 return this.ctx.helper.mul(node.unit_price, node.contract_qty, info.decimal.tp);
@@ -622,7 +624,7 @@ class sumLoad {
         const stageBills = await this.ctx.service.stageBills.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
         const importLid = await this.ctx.service.stageImportChange.getLeafXmjImportLid(this.ctx.stage.id, select.id);
         const settleStatusBills = this.ctx.stage.readySettle ? await this.ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: this.ctx.stage.readySettle.id }}) : [];
-        const preStageBills = await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender.data, this.ctx.stage.preCheckedStage.order);
+        const preStageBills = this.ctx.stage.preCheckedStage ? await this.ctx.service.stageBillsFinal.getFinalData(this.ctx.tender.data, this.ctx.stage.preCheckedStage.order) : [];
         this.ctx.helper.assignRelaData(posterity, [
             { data: extraData, fields: ['is_tp'], prefix: '', relaId: 'id' },
             { data: importLid, fields: ['is_import'], prefix: '', relaId: 'lid' },

+ 166 - 20
app/public/js/budget_compare.js

@@ -1,17 +1,48 @@
 'use strict';
+/**
+ * 从cookie中读取缓存的列显示设置,没有则取默认
+ * @returns {*[]}
+ */
+function customColDisplay () {
+    const defaultSetting = [
+        { title: '投资估算', fields: ['gu_dgn_qty', 'gu_dgn_price', 'gu_tp'], visible: true },
+        { title: '设计概算', fields: ['gai_dgn_qty', 'gai_dgn_price', 'gai_tp'], visible: true },
+        { title: '施工图预算', fields: ['yu_dgn_qty', 'yu_dgn_price', 'yu_tp'], visible: true },
+        { title: '招标预算', fields: ['zb_dgn_qty', 'zb_dgn_price', 'zb_tp'], visible: true },
+        { title: '台账', fields: ['dgn_qty', 'dgn_price', 'total_price'], visible: true },
+        { title: '预估决算', fields: ['final_dgn_qty', 'final_dgn_price', 'final_tp'], visible: true },
+    ];
+    const settingStr = Cookies.get(ckColSetting);
+    if (settingStr) {
+        const customSetting = JSON.parse(settingStr);
+        for (const ds of defaultSetting) {
+            const cs = customSetting.find(x => {return x.title === ds.title});
+            if (cs) ds.visible = cs.visible;
+        }
+    }
+    return defaultSetting;
+}
 
 /**
- *
- *
- * @author Mai
- * @date
- * @version
+ * 根据列显示设置,调整setting中的列是否显示
+ * @param setting
+ * @param customDisplay
  */
+function customizeTreeSetting(setting, customDisplay) {
+    for (const cd of customDisplay) {
+        for (const c of setting.cols) {
+            if (cd.fields.indexOf(c.field) !== -1) {
+                c.defaultVisible = cd.visible;
+            }
+        }
+    }
+}
 
 $(document).ready(() => {
     const compareTypeKey = 'budget-compareType';
     const stackedBarCoverKey = 'budget-stackedBarCover';
     const stackedBarKey = 'budget-stackedBar';
+    const comparePhaseKey = 'budget-comparePhase';
     autoFlashHeight();
     const compareSpread = SpreadJsObj.createNewSpread($('#cost-compare')[0]);
     const compareSheet = compareSpread.getActiveSheet();
@@ -19,6 +50,14 @@ $(document).ready(() => {
     const getStackedBarTip = function (data) {
         return data.stackedBarTips.join('\n');
     };
+    const comparePhase = [
+        { key: 'gu', name: '投资估算', dgn1: 'gu_dgn_qty1', dgn2: 'gu_dgn_qty2', tp: 'gu_tp'},
+        { key: 'gai', name: '设计概算', dgn1: 'gai_dgn_qty1', dgn2: 'gai_dgn_qty2', tp: 'gai_tp'},
+        { key: 'yu', name: '施工图预算', dgn1: 'yu_dgn_qty1', dgn2: 'yu_dgn_qty2', tp: 'yu_tp'},
+        { key: 'zb', name: '招标预算', dgn1: 'zb_dgn_qty1', dgn2: 'zb_dgn_qty2', tp: 'zb_tp'},
+        { key: 'tz', name: '台账', dgn1: 'dgn_qty1', dgn2: 'dgn_qty2', tp: 'total_price'},
+        { key: 'final', name: '预估决算', dgn1: 'final_dgn_qty1', dgn2: 'final_dgn_qty2', tp: 'final_tp'},
+    ];
     const spreadSetting = {
         cols: [
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'},
@@ -42,9 +81,9 @@ $(document).ready(() => {
             {title: '预估决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80, bc_type: 'number', visible: false},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number', bc_type: 'number', visible: false},
-            {title: '数据对比', colSpan: '1', rowSpan: '2', field: 'stackedBar', hAlign: 0, width: 300, cellType: 'stackedBar', stackedBarCover: false, bc_type: 'grid', visible: false, getTip: getStackedBarTip},
-            {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'grow_dgn_qty', hAlign: 2, width: 80},
-            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'grow_tp', hAlign: 2, width: 80, type: 'Number'},
+            {title: '数据对比', colSpan: '1', rowSpan: '2', field: 'stackedBar', hAlign: 0, width: 300, cellType: 'stackedBar', stackedBarCover: false, bc_type: 'grid', defaultVisible: true, visible: false, getTip: getStackedBarTip},
+            {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'compare_dgn_qty', hAlign: 2, width: 80},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'compare_tp', hAlign: 2, width: 80, type: 'Number'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -61,6 +100,37 @@ $(document).ready(() => {
     sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.jz);
     let sfSelect;
     const compareObj = {
+        comparePhase1: 'gai',
+        comparePhase2: 'final',
+        getCompareSelectHtml() {
+            const html = [];
+            for (const cp of comparePhase) {
+                html.push(`<option value="${cp.key}">${cp.name}</option>`);
+            }
+            return html.join('');
+        },
+        initCompare() {
+            const selectHtml = this.getCompareSelectHtml();
+            $('#compare1').html(selectHtml);
+            $('#compare2').html(selectHtml);
+            const comparePhase = Cookies.get(comparePhaseKey).split(',');
+            this.setComparePhase(comparePhase[0] || this.comparePhase1, comparePhase[1] || this.comparePhase2);
+        },
+        getCompareHint(phase1, phase2) {
+            if (!phase1) phase1 = this.comparePhase1;
+            if (!phase2) phase2 = this.comparePhase2;
+            const cp1 = comparePhase.find(x => { return x.key === phase1; });
+            const cp2 = comparePhase.find(x => { return x.key === phase2; });
+            return `增减幅度=(${cp2.name}-${cp1.name})/${cp1.name}*100%`;
+        },
+        setComparePhase(phase1, phase2) {
+            compareObj.comparePhase1 = phase1;
+            compareObj.comparePhase2 = phase2;
+            $('#compare-hint').html(compareObj.getCompareHint());
+            compareObj.reCalculateCompareData();
+            $('#compare-set').modal('hide');
+            Cookies.set(comparePhaseKey, phase1 + ',' + phase2);
+        },
         curFinalId() {
             return this.finalInfo ? this.finalInfo.id : undefined;
         },
@@ -74,11 +144,7 @@ $(document).ready(() => {
             }
         },
         initShowType() {
-            const type = this.compareType;
-            spreadSetting.cols.forEach(x => {
-                if (!x.bc_type) return;
-                x.visible = x.bc_type === type;
-            });
+            this.reCalcColVisible();
             const colIndex = spreadSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
             spreadSetting.cols[colIndex].stackedBarCover = parseInt(this.stackedBarCover);
         },
@@ -203,6 +269,7 @@ $(document).ready(() => {
             const expandTag = getLocalCache('revise-compare-level');
             if (expandTag) compareObj.expand(compareTree, expandTag);
             this.calcStackedBar(compareTree);
+            this.reCalculateCompareData(compareTree);
             SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
         },
         loadFinalData(result, msg) {
@@ -221,9 +288,28 @@ $(document).ready(() => {
             const expandTag = getLocalCache('revise-compare-level');
             if (expandTag) compareObj.expand(finalTree, expandTag);
             this.calcStackedBar(finalTree);
+            this.reCalculateCompareData(finalTree);
             SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, finalTree);
             if (sfSelect) sfSelect.reloadSelect(this.finalInfo.tender);
         },
+        reCalculateCompareData(calcTree) {
+            const tree = calcTree || compareSheet.zh_tree;
+            if (!tree) return;
+
+            const cp1 = comparePhase.find(x => { return x.key === this.comparePhase1; });
+            const cp2 = comparePhase.find(x => { return x.key === this.comparePhase2; });
+            treeCalc.calculateAll(tree, function(node) {
+                node.compare_dgn_qty1 = node[cp1.dgn1] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.dgn1], node[cp1.dgn1]), node[cp1.dgn1], 4), 100) : 0;
+                node.compare_dgn_qty2 = node[cp1.dgn2] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.dgn1], node[cp1.dgn1]), node[cp1.dgn1], 4), 100) : 0;
+                node.compare_dgn_qty = node.compare_dgn_qty1
+                    ? (node.compare_dgn_qty2 ? node.compare_dgn_qty1 + '/' + node.compare_dgn_qty2 : node.compare_dgn_qty1)
+                    : (node.compare_dgn_qty2 ? '/' + node.compare_dgn_qty2 : '');
+                node.compare_tp = node[cp1.tp] ? ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node[cp2.tp], node[cp1.tp]), node[cp1.tp], 4), 100) : 0;
+            });
+
+            const index = spreadSetting.cols.findIndex(x => { return x.field === 'compare_dgn_qty'});
+            SpreadJsObj.reloadColData(compareSheet, index, 2);
+        },
         loadCacheData(){
             let stackedBarCache = getLocalCache(stackedBarKey);
             if (stackedBarCache === null) stackedBarCache = 'gai_tp,total_price,final_tp';
@@ -239,19 +325,25 @@ $(document).ready(() => {
             const colIndex = spreadSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
             SpreadJsObj.reloadColData(compareSheet, colIndex);
         },
+        reCalcColVisible() {
+            const type = this.compareType;
+            spreadSetting.cols.forEach(x => {
+                if (!x.bc_type) return;
+                x.visible = x.bc_type === type && x.defaultVisible;
+            });
+        },
         setCompareType(type) {
             this.compareType = type || 'number';
             $('[name=showType]').removeClass('active');
             $(`[tag=${this.compareType}]`).addClass('active');
             if (this.compareType === 'grid') {
-                $('.ml-auto').show();
+                $('.ml-grid').show();
+                $('.ml-number').hide();
             } else {
-                $('.ml-auto').hide();
+                $('.ml-grid').hide();
+                $('.ml-number').show();
             }
-            spreadSetting.cols.forEach(x => {
-                if (!x.bc_type) return;
-                x.visible = x.bc_type === type;
-            });
+            this.reCalcColVisible();
             setLocalCache(compareTypeKey, this.compareType);
         },
         setStackedBarCover(cover){
@@ -262,7 +354,9 @@ $(document).ready(() => {
             setLocalCache(stackedBarCoverKey, this.stackedBarCover);
         }
     };
+    customizeTreeSetting(spreadSetting, customColDisplay());
     compareObj.loadCacheData();
+    compareObj.initCompare();
     SpreadJsObj.initSheet(compareSheet, spreadSetting);
 
     function compareCode(str1, str2, symbol = '-') {
@@ -339,7 +433,6 @@ $(document).ready(() => {
         });
     })('a[name=showLevel]', compareSheet);
 
-
     class sfObject {
         constructor() {
             const self = this;
@@ -459,4 +552,57 @@ $(document).ready(() => {
             }
         });
     });
+
+    $('#row-view').on('show.bs.modal', function () {
+        const html = [], customDisplay = customColDisplay();
+        for (const cd of customDisplay) {
+            html.push('<tr>');
+            html.push('<td>', cd.title, '</td>');
+            html.push('<td>', '<input type="checkbox"' + (cd.visible ? ' checked=""' : '') + '>', '</td>');
+            html.push('</tr>');
+        }
+        $('#row-view-list').html(html.join(''));
+    });
+    $('#row-view-ok').click(function () {
+        const customDisplay = customColDisplay();
+        const cvl = $('#row-view-list').children();
+        for (const cv of cvl) {
+            const title = $(cv).children()[0].innerHTML;
+            const check = $('input', cv)[0].checked;
+            const cd = customDisplay.find(function (c) {
+                return c.title === title;
+            });
+            cd.visible = check;
+        }
+        customizeTreeSetting(spreadSetting, customDisplay);
+        compareObj.loadCacheData();
+        SpreadJsObj.refreshColumnVisible(compareSheet);
+        Cookies.set(ckColSetting, JSON.stringify(customDisplay), 30*24*60*60*1000);
+        $('#row-view').modal('hide');
+    });
+
+    $('#compare-set').on('show.bs.modal', function() {
+        $('#compare1').val(compareObj.comparePhase1);
+        $('#compare2').val(compareObj.comparePhase2);
+        $('#compare-active-hint').html(compareObj.getCompareHint());
+    });
+    $('#compare1').change(() => {
+        const phase1 = $('#compare1').val();
+        const phase2 = $('#compare2').val();
+        $('#compare-active-hint').html(compareObj.getCompareHint(phase1, phase2));
+    });
+    $('#compare2').change(() => {
+        const phase1 = $('#compare1').val();
+        const phase2 = $('#compare2').val();
+        $('#compare-active-hint').html(compareObj.getCompareHint(phase1, phase2));
+    });
+    $('#compare-set-ok').click(() => {
+        compareObj.setComparePhase($('#compare1').val(), $('#compare2').val());
+    });
+    $('#export-excel').click(function() {
+        const excelSetting = JSON.parse(JSON.stringify(compareSheet.zh_setting));
+        const gridIndex = excelSetting.cols.findIndex(x => { return x.field === 'stackedBar'});
+        if (gridIndex >= 0) excelSetting.cols.splice(gridIndex, 1);
+        SpreadExcelObj.exportSimpleXlsxSheet(excelSetting, compareSheet.zh_tree.nodes, $('.sidebar-title').attr('data-original-title') + "-造价对比.xlsx");
+    });
 });

+ 112 - 78
app/public/js/change_information.js

@@ -839,25 +839,23 @@ $(document).ready(() => {
         },
         makeNewChangeList: function(newOrder = changeOrder) {
             // 先加载台账数据
-            if (!newOrder) {
+            if (!newOrder && !readOnly) {
                 const newChangeList2 = [];
                 for (const gcl of changeListData) {
-                    if (!newOrder) {
-                        if (gcl.leafXmjs !== undefined && gcl.leafXmjs !== null) {
-                            const cl = _.filter(changeList, function (item) {
-                                return _.findIndex(gcl.leafXmjs, { gcl_id: item.gcl_id }) !== -1;
-                            });
-                            if (cl.length > 0) {
-                                for (const l of gcl.leafXmjs) {
-                                    const c = _.find(cl, { gcl_id: l.gcl_id, mx_id: l.mx_id || '' });
-                                    if (c) newChangeList2.push(c);
-                                }
+                    if (gcl.leafXmjs !== undefined && gcl.leafXmjs !== null) {
+                        const cl = _.filter(changeList, function (item) {
+                            return _.findIndex(gcl.leafXmjs, { gcl_id: item.gcl_id }) !== -1;
+                        });
+                        if (cl.length > 0) {
+                            for (const l of gcl.leafXmjs) {
+                                const c = _.find(cl, { gcl_id: l.gcl_id, mx_id: l.mx_id || '' });
+                                if (c) newChangeList2.push(c);
                             }
-                        } else {
-                            // 签约清单
-                            const c = _.find(changeList, { lid: gcl.id });
-                            if (c) newChangeList2.push(c);
                         }
+                    } else {
+                        // 签约清单
+                        const c = _.find(changeList, { lid: gcl.id });
+                        if (c) newChangeList2.push(c);
                     }
                 }
                 // 按台账去重新排序changeList,空白清单居后
@@ -990,51 +988,6 @@ $(document).ready(() => {
     const preUrl = window.location.pathname.split('/').slice(0, 4).join('/');
     let changeListData;
     let gclGatherData;
-    postData(preUrl + '/defaultBills', {}, function (result) {
-        ledgerList = result.bills;
-        posList = result.pos;
-
-        gclGatherModel.loadLedgerData(result.bills);
-        gclGatherModel.loadPosData(result.pos);
-        gclGatherData = gclGatherModel.gatherGclData();
-        gclGatherData = _.filter(gclGatherData, function (item) {
-            return item.leafXmjs && item.leafXmjs.length !== 0;
-        });
-        for (const ggd in gclGatherData) {
-            if (gclGatherData[ggd].leafXmjs && gclGatherData[ggd].leafXmjs.length === 0) {
-                gclGatherData.splice(ggd, 1);
-            }
-            gclGatherData[ggd].code = gclGatherData[ggd].b_code;
-            let hadcid = 0;
-            for (const xmj of gclGatherData[ggd].leafXmjs) {
-                const changeLedger = _.find(changeLedgerList, { id: xmj.gcl_id });
-                const changePos = _.find(changePosList, { id: xmj.mx_id, lid: xmj.gcl_id });
-                if (changeLedger || changePos) {
-                    xmj.cid = 1;
-                    xmj.ccid = changeLedger ? changeLedger.ccid : changePos.ccid;
-                    hadcid = 1;
-                }
-            }
-
-            if (hadcid !== 0) gclGatherData[ggd].cid = 1;
-        }
-        console.log(gclGatherData);
-        // 数组去重
-        const dealBillList = result.dealBills;
-        for (const db of gclGatherData) {
-            const exist_index = dealBillList.findIndex(function (item) {
-                return item.code === db.code && item.name === db.name && item.unit === db.unit && item.unit_price === db.unit_price;
-            });
-            if (exist_index !== -1) {
-                dealBillList.splice(exist_index, 1);
-            }
-        }
-        changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
-        if (!readOnly || shenpiPower) {
-            checkAndChange(changeListData);
-        }
-        showMode(changeMode);
-    });
 
     changeSpread.bind(spreadNS.Events.SelectionChanged, changeSpreadObj.selectionChanged);
     changeSpread.bind(spreadNS.Events.topRowChanged, changeSpreadObj.topRowChanged);
@@ -5530,24 +5483,95 @@ $(document).ready(() => {
                 }
                 firstLoad = false;
             }
-            const {changeLedgers, changePoss} = makeLedgerAndPosData();
-            ledgerTree.loadDatas(changeLedgers);
-            pos.loadDatas(changePoss);
-            treeCalc.calculateAll(ledgerTree);
-            console.log(ledgerTree);
-            SpreadJsObj.loadSheetData(ledgerSheet, SpreadJsObj.DataType.Tree, ledgerTree);
-            ledgerTree.expandByCustom(() => { return true; });
-            SpreadJsObj.refreshTreeRowVisible(ledgerSheet);
-            SpreadJsObj.loadTopAndSelect(ledgerSheet, ckBillsSpread);
-            ledgerTreeSpreadObj.showHideAudit($('#show-table-detail').is(':checked'));
-            posSpreadObj.loadCurPosData();
-            SpreadJsObj.resetTopAndSelect(posSheet);
-            ledgerTreeSpreadObj.loadExprToInput();
-            ledgerSpread.refresh();
-            posSpread.refresh();
+            if (readOnly) {
+                postData(preUrl + '/defaultBills', {}, function (result) {
+                    ledgerList = result.bills;
+                    posList = result.pos;
+                    showLedgerAndPos();
+                });
+            } else {
+                showLedgerAndPos();
+            }
         }
     }
 
+    function showLedgerAndPos() {
+        const {changeLedgers, changePoss} = makeLedgerAndPosData();
+        ledgerTree.loadDatas(changeLedgers);
+        pos.loadDatas(changePoss);
+        treeCalc.calculateAll(ledgerTree);
+        console.log(ledgerTree);
+        SpreadJsObj.loadSheetData(ledgerSheet, SpreadJsObj.DataType.Tree, ledgerTree);
+        ledgerTree.expandByCustom(() => { return true; });
+        SpreadJsObj.refreshTreeRowVisible(ledgerSheet);
+        SpreadJsObj.loadTopAndSelect(ledgerSheet, ckBillsSpread);
+        ledgerTreeSpreadObj.showHideAudit($('#show-table-detail').is(':checked'));
+        posSpreadObj.loadCurPosData();
+        SpreadJsObj.resetTopAndSelect(posSheet);
+        ledgerTreeSpreadObj.loadExprToInput();
+        ledgerSpread.refresh();
+        posSpread.refresh();
+    }
+
+    function getAndCheck(showTips = false) {
+        postData(preUrl + '/defaultBills', {}, function (result) {
+            ledgerList = result.bills;
+            posList = result.pos;
+            // if (!readOnly) {
+            console.time('hello');
+            gclGatherModel.loadLedgerData(result.bills);
+            gclGatherModel.loadPosData(result.pos);
+            gclGatherData = gclGatherModel.gatherGclData();
+            gclGatherData = _.filter(gclGatherData, function (item) {
+                return item.leafXmjs && item.leafXmjs.length !== 0;
+            });
+            for (const ggd in gclGatherData) {
+                if (gclGatherData[ggd].leafXmjs && gclGatherData[ggd].leafXmjs.length === 0) {
+                    gclGatherData.splice(ggd, 1);
+                }
+                gclGatherData[ggd].code = gclGatherData[ggd].b_code;
+                let hadcid = 0;
+                for (const xmj of gclGatherData[ggd].leafXmjs) {
+                    const changeLedger = _.find(changeLedgerList, { id: xmj.gcl_id });
+                    const changePos = _.find(changePosList, { id: xmj.mx_id, lid: xmj.gcl_id });
+                    if (changeLedger || changePos) {
+                        xmj.cid = 1;
+                        xmj.ccid = changeLedger ? changeLedger.ccid : changePos.ccid;
+                        hadcid = 1;
+                    }
+                }
+
+                if (hadcid !== 0) gclGatherData[ggd].cid = 1;
+            }
+            console.log(gclGatherData);
+            // 数组去重
+            const dealBillList = result.dealBills;
+            for (const db of gclGatherData) {
+                const exist_index = dealBillList.findIndex(function (item) {
+                    return item.code === db.code && item.name === db.name && item.unit === db.unit && item.unit_price === db.unit_price;
+                });
+                if (exist_index !== -1) {
+                    dealBillList.splice(exist_index, 1);
+                }
+            }
+            changeListData = gclGatherData.concat(dealBillList).sort(sortByCode);
+            checkAndChange(changeListData, showTips);
+            console.timeEnd('hello');
+            // }
+            showMode(changeMode);
+        });
+    }
+
+    if (!readOnly) {
+        getAndCheck();
+    } else {
+        showMode(changeMode);
+    }
+
+    $('#checkAndChange').click(function () {
+        getAndCheck(true);
+    });
+
     // 显示层次
     (function (select, sheet) {
         $(select).click(function () {
@@ -6254,7 +6278,7 @@ function calcChangePrice() {
         });
     }
 }
-function checkAndChange(changeListData) {
+function checkAndChange(changeListData, showTips = false) {
     // 根据已添加的清单显示
     if (changeList.length > 0 && changeList[0]) {
         const removeList = [];
@@ -6281,7 +6305,7 @@ function checkAndChange(changeListData) {
                     // 针对旧数据获取清单信息
                     listinfo = changeListData[clinfo.lid - 1];
                     if (listinfo === undefined) {
-                        toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
+                        if (!showTips) toastr.warning('台账清单列表已不存在'+ clinfo.code +',已更新变更清单列表');
                         if (changeStatus !== auditConst.status.revise) {
                             removeList.push(clinfo);
                         } else {
@@ -6328,13 +6352,23 @@ function checkAndChange(changeListData) {
             }, function () {
             });
         }
-        if(removeList.length > 0) {
+        if(removeList.length > 0 && (!readOnly || shenpiPower)) {
             console.log(removeList);
             _.pullAll(changeList, removeList);
             postData(window.location.pathname + '/save', { type:'remove_list', updateData: removeList }, function (result) {
             }, function () {
             });
         }
+        if (showTips && ((removeList.length > 0 && (!readOnly || shenpiPower)) || updateList.length > 0)) {
+            if ((removeList.length > 0 && (!readOnly || shenpiPower))) {
+                toastr.error('已移除'+ removeList.length +'条变更清单数据');
+            }
+            if (updateList.length > 0) {
+                toastr.success('已更新'+ updateList.length +'条变更清单数据');
+            }
+        } else if (showTips) {
+            toastr.warning('当前变更清单数据无变化');
+        }
     }
 }
 

+ 4 - 5
app/public/js/change_revise.js

@@ -57,7 +57,6 @@ $(document).ready(() => {
     autoFlashHeight();
     billsSpreadSetting.cols.unshift({ title: '变更清单', colSpan: '1', rowSpan: '2', field: 'is_change', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isChangeList', });
     const billsIndex = _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'drawing_code' }) : (_.findIndex(billsSpreadSetting.cols, { field: 'ex_tp1' }) !== -1 ? _.findIndex(billsSpreadSetting.cols, { field: 'ex_tp1' }) + 1 : billsSpreadSetting.cols.length - 1);
-    console.log(billsSpreadSetting.cols);
     billsSpreadSetting.cols.splice(billsIndex, 0, { title: '计价', colSpan: '1', rowSpan: '2', field: 'is_valuation', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: 'readOnly.isValuation', getValue: 'getValue.isValuation' });
     billsSpreadSetting.cols.splice(billsIndex + 1, 0, {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, readOnly: 'readOnly.isSettle', getValue: 'getValue.camount'},);
     billsSpreadSetting.cols.splice(billsIndex + 2, 0, {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ca_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},);
@@ -727,7 +726,7 @@ $(document).ready(() => {
                             unit_price: gclInfo.unit_price,
                             oamount: xmjInfo.quantity,
                             oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
-                            bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                            bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
                             xmj_code: xmjInfo.code || '',
                             xmj_jldy: xmjInfo.jldy || '',
                             xmj_dwgc: xmjInfo.dwgc || '',
@@ -868,7 +867,7 @@ $(document).ready(() => {
                             unit_price: gclInfo.unit_price,
                             oamount: xmjInfo.quantity,
                             oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
-                            bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                            bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
                             xmj_code: xmjInfo.code || '',
                             xmj_jldy: xmjInfo.jldy || '',
                             xmj_dwgc: xmjInfo.dwgc || '',
@@ -1212,7 +1211,7 @@ $(document).ready(() => {
                                 unit_price: gclInfo.unit_price,
                                 oamount: xmjInfo.quantity,
                                 oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
-                                bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                                bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
                                 xmj_code: xmjInfo.code || '',
                                 xmj_jldy: xmjInfo.jldy || '',
                                 xmj_dwgc: xmjInfo.dwgc || '',
@@ -1968,7 +1967,7 @@ $(document).ready(() => {
                         unit_price: gclInfo.unit_price,
                         oamount: xmjInfo.quantity,
                         oamount2: oldCInfo ? oldCInfo.oamount2 : xmjInfo.quantity,
-                        bwmx: xmjInfo.bwmx || xmjInfo.jldy,
+                        bwmx: xmjInfo.bwmx || xmjInfo.jldy || '',
                         xmj_code: xmjInfo.code || '',
                         xmj_jldy: xmjInfo.jldy || '',
                         xmj_dwgc: xmjInfo.dwgc || '',

+ 128 - 1
app/public/js/contract_detail.js

@@ -1,5 +1,6 @@
 const copyBlockTag = 'zh.calc.copyBlock';
 $(document).ready(function() {
+    let stdXmj;
     const ckBillsSpread = '/contract/' + window.location.pathname.split('/')[2] + '/' + contractConst.typeMap[contract_type] + '/detail/contract-billsSelect';
     autoFlashHeight();
     const contractSpreadSetting = {
@@ -2052,6 +2053,91 @@ $(document).ready(function() {
         });
     });
 
+    const stdLibCellDoubleClick = function (updateData, stdNode, stdTree) {
+        if (!permission_edit) return;
+        const mainSheet = contractSheet;
+        if (!stdTree || !mainSheet.zh_tree) { return; }
+
+        const mainTree = mainSheet.zh_tree;
+        const sel = mainSheet.getSelections()[0];
+        const mainNode = mainTree.nodes[sel.row];
+        if (!stdNode) return;
+
+        // if (updateData.postData.stdType === 'gcl') {
+        //     if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
+        //         toastr.warning('非最底层项目下,不应添加节点');
+        //         return;
+        //     }
+        // } else {
+        //     const stdNodes = stdTree.getAllParents(stdNode);
+        //     for (const node of stdNodes) {
+        //         const parent = mainTree.datas.find(x => { return x.code === node.code && x.name === node.name; });
+        //     }
+        // }
+
+        console.log(mainTree.getNodeKey(mainNode), stdTree.getNodeKey(stdNode), stdNode.list_id, updateData.postData.stdType);
+
+        postData(window.location.pathname + '/update', {
+            postType: 'add-std',
+            postData: {
+                id: mainTree.getNodeKey(mainNode),
+                tender_id: mainNode.tender_id,
+                stdType: updateData.postData.stdType,
+                stdLibId: stdNode.list_id,
+                stdNode: stdTree.getNodeKey(stdNode)
+            }
+        }, function (result) {
+            const refreshNode = mainTree.loadPostData(result);
+            contractTreeSpreadObj.refreshTree(mainSheet, refreshNode);
+            if (sel) {
+                if (refreshNode.create && refreshNode.create.length > 0) {
+                    mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
+                } else {
+                    const node = _.find(mainTree.nodes, {code: stdNode.code, name: stdNode.name});
+                    if (node) {
+                        mainSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                        SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(node)]);
+                    }
+                }
+            }
+            contractTreeSpreadObj.refreshOperationValid(mainSheet);
+            contractSpread.focus();
+        });
+    };
+    const stdXmjSetting = {
+        selector: '#std-xmj',
+        stdType: 'xmj',
+        libs: stdChapters,
+        treeSetting: {
+            id: 'chapter_id',
+            pid: 'pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'list_id', 'chapter_id'],
+        },
+        spreadSetting: {
+            cols: [
+                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true, cellType: 'tree'},
+                {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+            ],
+            treeCol: 0,
+            emptyRows: 0,
+            headRows: 1,
+            headRowHeight: [32],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+            headColWidth: [30],
+            selectedBackColor: '#fffacd',
+        },
+        spreadClassCss: 'sjs-contract',
+        cellDoubleClick: stdLibCellDoubleClick,
+        page: 'contract',
+        tid: window.location.pathname.split('/')[2],
+    };
+
     // 显示层次
     (function (select, sheet) {
         $(select).click(function () {
@@ -2082,15 +2168,55 @@ $(document).ready(function() {
             }, 100);
         });
     })('a[name=showLevel]', contractSheet);
+
     $.divResizer({
-        select: '#contract-resize',
+        select: '#right-spr',
         callback: function () {
             contractSpread.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
+            const width = (($('#right-view').width()/$('#right-view').parent('div').width())*100).toFixed();
+        }
+    });
+    $.divResizer({
+        select: '#contract-resize',
+        callback: function () {
             let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
             $(".sp-wrap").height(bcontent-30);
+            updateSjsHeight();
+            contractSpread.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
             // posSpread.refresh();
         }
     });
+    // 展开收起月信息价并浏览器记住本期展开收起
+    $('a', '.right-nav').bind('click', function () {
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('#right-view .tab-content .tab-pane').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#std-xmj') {
+                if (!stdXmj) {
+                    stdXmj = $.stdLib(stdXmjSetting);
+                }
+                updateSjsHeight();
+                stdXmj.spread.refresh();
+            }
+        } else {
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        contractSpread.refresh();
+    });
+    function updateSjsHeight() {
+        $('.sjs-contract').height($('.sjs-height-1').height() - getObjHeight($('.sjs-bar')));
+    }
+    function getObjHeight(select) {
+        return select.length > 0 ? select.outerHeight() : 0;
+    }
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -2106,6 +2232,7 @@ $(document).ready(function() {
             }
             autoFlashHeight();
             contractSpread.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
         }
     });
 });

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

@@ -41,7 +41,7 @@
             const rType = obj.attr('r-type'), aType = obj.attr('a-type');
             const div1 = $(obj.attr('div1')), div2 = $(obj.attr('div2'));
             const parent = div1.parent();
-            if (aType !== 'percent') {
+            if (aType !== 'percent' && parent[rType]() > 0) {
                 orgSize1 = div1[rType]();
                 orgSize2 = div2[rType]();
                 const parentSize = parent[rType]() * 0.99;

+ 297 - 0
app/public/js/financial_pay_tender.js

@@ -0,0 +1,297 @@
+'use strict';
+
+const tenderListSpec = (function(){
+    function getTenderNodeHtml(node, arr, pid) {
+        const html = [];
+        html.push('<tr pid="' + pid + '" data-tid="'+ (!node.cid ? node.id : -1) +'"' + (!node.cid ? 'class="tender-info" data-id="'+ node.id +'" style="cursor: pointer;"' : '') + '>');
+        // 名称
+        html.push('<td style="min-width: 150px;" class="in-' + node.level + '">');
+        if (node.cid) {
+            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" cid="'+ node.sort_id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ');
+            html.push((node.level === 1 ? '<b>' : ''), node.name, (node.level === 1 ? '</b>' : ''));
+        } else {
+            html.push('<span class="text-muted mr-2">');
+            html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+            html.push('</span>');
+            html.push(node.name);
+        }
+        html.push('</td>');
+        // 累计划拨
+        html.push('<td class="text-right" style="width: 15%">');
+        if (!node.cid) {
+            html.push(node.transfer_amount ? node.transfer_amount : '');
+        }
+        html.push('</td>');
+        // 累计支付
+        html.push('<td class="text-right" style="width: 15%">');
+        if (!node.cid) {
+            html.push(node.pay_amount ? node.pay_amount : '');
+        }
+        html.push('</td>');
+        // 支付次数
+        html.push('<td class="text-center" style="width: 15%">');
+        if (!node.cid) {
+            html.push(node.pay_num ? node.pay_num : '');
+        }
+        html.push('</td>');
+        html.push('</tr>');
+        return html.join('');
+    }
+    function getTenderTreeHeaderHtml() {
+        const html = [];
+        const left = $('#sub-menu').css('display') === 'none' ? 56 : 176;
+        html.push('<table class="table table-hover table-bordered" id="progress-table">')
+        html.push('<thead style="position: sticky;left:'+ left +'px;top: 0;">', '<tr>');
+        html.push('<th class="text-center" style="min-width: 150px;">', '标段名称', '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '累计划拨', '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '累计支付', '</th>');
+        html.push('<th class="text-center" style="width: 15%">', '支付次数', '</th>');
+        html.push('</tr>', '</thead>');
+        return html.join('');
+    }
+    return { getTenderNodeHtml, getTenderTreeHeaderHtml }
+})();
+
+
+
+let auditUtils;
+$(function () {
+    autoFlashHeight();
+
+    $('#company_select').change(function () {
+        const company_id = parseInt($(this).val()) || 0;
+        setSelectValue('company', company_id);
+    });
+
+    $('#order_select').change(function () {
+        const qi = parseInt($(this).val()) || 0;
+        setSelectValue('qi', qi);
+    });
+
+    function setSelectValue(select, value) {
+        const routes = [];
+        const company_id = select === 'company' ? value : parseInt($('#company_select').val());
+        if (company_id) {
+            const companyInfo = _.find(userCompanyList, { id: company_id });
+            if (companyInfo) routes.push('company=' + companyInfo.name);
+        }
+        const qi = select === 'qi' ? value : parseInt($('#order_select').val());
+        if (qi) {
+            routes.push('qi=' + qi);
+        }
+        if (getLocalCache('account-pageSize')) {
+            routes.push('pageSize=' + getLocalCache('account-pageSize'));
+        }
+        window.location.href = `/sp/${spid}/financial/pay/stage` + (routes.length ? '?' + routes.join('&') : '');
+    }
+
+    $('body').on('click', '.c-body .tender-info', function () {
+        $('.c-body .tender-info').removeClass('table-warning');
+        $(this).addClass('table-warning');
+        const tid = parseInt($(this).attr('data-id'));
+        if (!tid) {
+            toastr.warning('未选择标段');
+            return;
+        }
+        console.log(tid);
+        const tenderPays = _.filter(pays, { tid });
+        console.log(tenderPays);
+        $('#qi_select').val('0');
+        const qi_options = ['<option value="0">筛选期数</option>'];
+        // 只取order大于0的并去除重复并倒序排序
+        const qis = _.orderBy(_.uniq(_.map(tenderPays, 'order')).filter(o => o > 0), null, 'desc');
+        for (const order of qis) {
+            qi_options.push('<option value="' + order + '">第' + order + '期</option>');
+        }
+        $('#qi_select').html(qi_options.join(''));
+        $('#used_selected').text('资金用途:全部');
+        // used_select清除如果存在用途为'<a class="dropdown-item to-log-link" data-val="" href="javascript:void(0);">全部</a>'这条remove掉
+        $('#used_select').find('a.to-log-link[data-val=""]').remove();
+        payListHtml(tenderPays, 0);
+    });
+
+    $('body').on('change', '#qi_select', function () {
+        const order = parseInt($(this).val());
+        const tid = parseInt($('.c-body .tender-info.table-warning').attr('data-id'));
+        if (!tid) {
+            toastr.warning('未选择标段');
+            return;
+        }
+        const tenderPays = _.filter(pays, { tid });
+        let newTenderPays = tenderPays;
+        if (order !== 0) {
+            newTenderPays = _.filter(tenderPays, { order });
+        }
+        const used = $('#used_selected').attr('data-val') || '';
+        if (used !== '') {
+            newTenderPays = _.filter(newTenderPays, { used });
+        }
+        const status = parseInt($('#status_selected').attr('data-val')) || 0;
+        payListHtml(newTenderPays, status);
+    });
+
+    $('body').on('click', '#used_select .to-log-link', function () {
+        const used = $(this).attr('data-val') || '';
+        $('#used_selected').attr('data-val', used);
+        $('#used_selected').text('资金用途:' + (used === '' ? '全部' : used));
+        if (used !== '' && $('#used_select').find('a.to-log-link[data-val=""]').length === 0) {
+            $('#used_select').prepend('<a class="dropdown-item to-log-link" data-val="" href="javascript:void(0);">全部</a>');
+        } else if (used === '') {
+            $('#used_select').find('a.to-log-link[data-val=""]').remove();
+        }
+        const tid = parseInt($('.c-body .tender-info.table-warning').attr('data-id'));
+        if (!tid) {
+            toastr.warning('未选择标段');
+            return;
+        }
+        let tenderPays = _.filter(pays, { tid });
+        const order = parseInt($('#qi_select').val()) || 0;
+        if (order !== 0) {
+            tenderPays = _.filter(tenderPays, { order });
+        }
+        if (used !== '') {
+            tenderPays = _.filter(tenderPays, { used });
+        }
+        const status = parseInt($('#status_selected').attr('data-val')) || 0;
+        payListHtml(tenderPays, status);
+    });
+
+    $('body').on('click', '#status_select .to-log-link', function () {
+        const status = parseInt($(this).attr('data-val')) || 0;
+        const tid = parseInt($('.c-body .tender-info.table-warning').attr('data-id'));
+        if (!tid) {
+            toastr.warning('未选择标段');
+            return;
+        }
+        let tenderPays = _.filter(pays, { tid });
+        const order = parseInt($('#qi_select').val()) || 0;
+        if (order !== 0) {
+            tenderPays = _.filter(tenderPays, { order });
+        }
+        const used = $('#used_selected').attr('data-val') || '';
+        if (used !== '') {
+            tenderPays = _.filter(tenderPays, { used });
+        }
+        payListHtml(tenderPays, status);
+    });
+
+    function payListHtml(tenderPays, status = 0) {
+        const status_options = [];
+        for (const fs in filter.status) {
+            const f = filter.status[fs];
+            let num = 0;
+            switch(f) {
+                case filter.status.pending:
+                    num = _.filter(tenderPays, function (item) {
+                        return (item.status === auditConst.status.checking && item.curAuditors && item.curAuditors.findIndex(x => { return x.aid === user_id; }) >= 0 && item.curAuditors.find(x => { return x.aid === user_id; }).status === auditConst.status.checking) || (item.uid === user_id && (item.status === auditConst.status.uncheck || item.status === auditConst.status.checkNo));
+                    }).length;
+                    break;
+                case filter.status.uncheck:
+                    num = _.filter(tenderPays, function (item) {
+                        return item.status === auditConst.status.uncheck || item.status === auditConst.status.checkNo;
+                    }).length;
+                    break;
+                case filter.status.checking:
+                    num = _.filter(tenderPays, function (item) {
+                        return item.status === auditConst.status.checking;
+                    }).length;
+                    break;
+                case filter.status.checked:
+                    num = _.filter(tenderPays, function (item) {
+                        return item.status === auditConst.status.checked;
+                    }).length;
+                    break;
+                default:
+                    break;
+            }
+            if (status !== f) status_options.push('<a class="dropdown-item to-log-link" data-val="' + f + '" href="javascript:void(0);">' + filter.statusString[f] + '(' + num + ')</a>');
+        }
+        if (status !== 0) status_options.unshift('<a class="dropdown-item to-log-link" data-val="0" href="javascript:void(0);">全部</a>');
+        $('#status_select').html(status_options.join(''));
+        $('#status_selected').attr('data-val', status);
+        $('#status_selected').text('审批状态:' + (status === 0 ? '全部' : filter.statusString[status]));
+        let newTenderPays = tenderPays;
+        if (status !== 0) {
+            newTenderPays = _.filter(tenderPays, function (item) {
+                switch(status) {
+                    case filter.status.pending:
+                        return (item.status === auditConst.status.checking && item.curAuditors && item.curAuditors.findIndex(x => { return x.aid === user_id; }) >= 0 && item.curAuditors.find(x => { return x.aid === user_id; }).status === auditConst.status.checking) || (item.uid === user_id && (item.status === auditConst.status.uncheck || item.status === auditConst.status.checkNo));
+                    case filter.status.uncheck:
+                        return item.status === auditConst.status.uncheck || item.status === auditConst.status.checkNo;
+                    case filter.status.checking:
+                        return item.status === auditConst.status.checking;
+                    case filter.status.checked:
+                        return item.status === auditConst.status.checked;
+                    default:
+                        return true;
+                }
+            });
+        }
+        let html = '';
+        for (const pay of newTenderPays) {
+            let statusHtml = '';
+            if (pay.status === auditConst.status.checked && pay.final_auditor_str) {
+                statusHtml = `<a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="${pay.id}">${pay.final_auditor_str}</a>`;
+            } else if (pay.curAuditors.length > 0) {
+                if (pay.curAuditors[0].audit_type === auditType.key.common) {
+                    statusHtml = `<a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="${pay.id}">${pay.curAuditors[0].name}${ pay.curAuditors[0].role !== '' && pay.curAuditors[0].role !== null? '-' + pay.curAuditors[0].role : ''}</a>`;
+                } else {
+                    statusHtml = `<a href="#sp-list" data-toggle="modal" data-target="#sp-list" c-id="${pay.id}">${transFormToChinese(pay.curAuditors[0].audit_order) + '审'}</a>`;
+                }
+            }
+            let operationHtml = '';
+            if (pay.status === auditConst.status.uncheck && pay.uid === user_id) {
+                operationHtml = `<a href="/sp/${spid}/financial/pay/${pay.id}/detail?from=tender" class="btn ${auditConst.statusButtonClass[pay.status]} btn-sm">${auditConst.statusButton[pay.status]}</a>`;
+            } else if (pay.status === auditConst.status.checkNo && pay.curAuditors && pay.uid === user_id) {
+                operationHtml = `<a href="/sp/${spid}/financial/pay/${pay.id}/detail?from=tender" class="btn ${auditConst.statusButtonClass[pay.status]} btn-sm">${auditConst.statusButton[pay.status]}</a>`;
+            } else if (pay.status === auditConst.status.checking && pay.curAuditors && pay.curAuditors.findIndex(x => { return x.aid === user_id; }) >= 0) {
+                const curAudit = pay.curAuditors.find(x => { return x.aid === user_id });
+                if (curAudit.status === auditConst.status.checking) {
+                    operationHtml = `<a href="/sp/${spid}/financial/pay/${pay.id}/detail?from=tender" class="btn ${auditConst.statusButtonClass[pay.status]} btn-sm">${auditConst.statusButton[pay.status]}</a>`;
+                } else {
+                    operationHtml = `<span class="${auditConst.auditStringClass[curAudit.status]}">${auditConst.auditString[curAudit.status]}</span>`;
+                }
+            } else {
+                operationHtml = `<span class="${auditConst.auditStringClass[pay.status]}">${auditConst.auditString[pay.status]}</span>`;
+            }
+            html += `<tr class="text-center" data-tid="${pay.order}">
+                            <td class="">${pay.stage ? '<a href="/sp/'+ spid + '/financial/pay/stage/' + pay.stage.id + '?from=tender" class="account-page-size">第' + pay.stage.order + '期</a>' : '' }</td>
+                            <td class=""><a href="/sp/${spid}/financial/pay/${pay.id}/detail?from=tender">${pay.code}</td>
+                            <td class="">${moment(pay.create_time).format('YYYY-MM-DD')}</td>
+                            <td class="">${pay.username}</td>
+                            <td class="">${pay.used}</td>
+                            <td class="">${pay.entities}</td>
+                            <td class="text-right">${pay.total_price}</td>
+                            <td class="text-left ${auditConst.auditProgressClass[pay.status]}">
+                                ${statusHtml}
+                                ${auditConst.auditProgress[pay.status]}
+                            </td>
+                            <td>
+                                ${operationHtml}
+                            </td>
+                        </tr>`;
+        }
+        $('#pay-list').html(html);
+    }
+
+    setTimeout(function () {
+        $('.c-body .tender-info').eq(0).click();// 需要延时加载
+    }, 500);
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+        }
+    });
+});

+ 35 - 0
app/public/js/financial_transfer.js

@@ -9,6 +9,41 @@ $(function () {
         $('#add-transfer-form').submit();
     });
 
+    let editPeriod = null;
+    $('.edit-time').on('click', function () {
+        const t_time = $(this).data('time');
+        const ftid = parseInt($(this).data('ftid'));
+        console.log(t_time, ftid);
+        $('#edit-transfer-date').val('');
+        $('#edit-ftid').val(ftid);
+        $('#edit-transfer-date').val(t_time);
+        editPeriod = !editPeriod ? $('#edit-transfer-date').datepicker().data('datepicker') : editPeriod;
+        editPeriod.clear();
+        editPeriod.selectDate(new Date(t_time));
+    });
+
+    $('#edit-ok').click(function () {
+        const id = parseInt($('#edit-ftid').val());
+        if (!id) {
+            toastr.error('获取资金划拨信息失败');
+            return;
+        }
+        const trInfo = _.find(transferList, { id });
+        if (!trInfo) {
+            toastr.error('获取资金划拨信息失败');
+            return;
+        }
+        const t_time = $('#edit-transfer-date').val();
+        if (t_time === '') {
+            toastr.error('请选择划拨年月');
+            return false;
+        }
+        postData(window.location.pathname + '/update', {postType: 'edit-transfer-time', postData: { id, t_time }}, function (result) {
+            window.location.reload();
+        })
+    });
+
+
     $('body').on('click', '#transfer-list .del-transfer-btn', function () {
         const trid = $(this).data('id');
         deleteAfterHint(function () {

+ 14 - 3
app/public/js/global.js

@@ -28,6 +28,7 @@ function autoFlashHeight(){
     $(".sjs-height-4").height($(window).height()-cHeader-pBarz-110+75);
     $(".sjs-height-5").height($(window).height()-cHeader-sBar-492+55);/*492*/
     $(".sjs-height-6").height($(window).height()-cHeader-34-sBar1);
+    $(".sjs-height-7").height($(window).height()-cHeader-34-41);
     $(".sp-wrap").height(bcontent-30);
     /*侧栏高度*/
     $(".sjs-sh-1").height($(window).height()-cHeader-sBar1-92+55);
@@ -212,7 +213,7 @@ $(function(){
     })
 });
 
-function setChangeFilterData(orderLocal) {
+function setChangeFilterData(orderLocal, filterString = '') {
     const filterData = [];
     const orderSetting = getLocalCache(orderLocal);
     if(orderSetting) {
@@ -223,8 +224,8 @@ function setChangeFilterData(orderLocal) {
     if (getLocalCache('account-pageSize')) {
         filterData.push('pageSize=' + getLocalCache('account-pageSize'));
     }
-    let filterString = '';
-    if (filterData.length > 0) filterString = filterString + '?' + filterData.join('&');
+    filterString = filterString ? filterString + '&' : filterString + '?';
+    if (filterData.length > 0) filterString = filterString + filterData.join('&');
     return filterString;
 }
 
@@ -431,6 +432,16 @@ const postDataWithFile = function (url, formData, successCallback, errorCallBack
     });
 };
 
+const postDataWithFileAsync = function (url, formData, showWaiting = true) {
+    return new Promise(function (resolve, reject) {
+        postDataWithFile(url, formData, result => {
+            resolve(result);
+        }, err => {
+            reject(err);
+        }, showWaiting);
+    });
+};
+
 const postDataWithFileProgress = function (url, formData, successCallback, errorCallBack) {
     showUploadFileProgress();
     $.ajax({

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

@@ -421,9 +421,10 @@ const ledgerCheckUtil = {
                 return valueCheck.date(checkDate, operation);
             }
         };
-        const checkMultiCondition = function (data, condition) {
+        const checkMultiCondition = function (status, check, condition) {
             let result = true;
             for (const c of condition) {
+                const data = !c.source || c.source === '3f' ? status : check;
                 result = c.rela && c.rela === 'or'
                     ? (result || valueCheck[c.check](data[c.field], c.value, c.operation))
                     : (result && valueCheck[c.check](data[c.field], c.value, c.operation));
@@ -431,7 +432,7 @@ const ledgerCheckUtil = {
             return result;
         };
         const checkMulti3f = function(bills, checkData, statusData, limitOption) {
-            if (!checkMultiCondition(statusData, limitOption.condition)) return;
+            if (!checkMultiCondition(statusData, checkData, limitOption.condition)) return;
 
             if (bills.is_tp) return check3fTp(checkData, limitOption.limit, limitOption);
             return check3fQty(checkData, limitOption.limit, limitOption, bills.unit);
@@ -461,7 +462,7 @@ const ledgerCheckUtil = {
                     }
                 }
             } else {
-                for (const lo of limitOption) {
+                for (const lo of limitOption.options) {
                     const checkResult = checkMulti3f(bills, bills, leafXmj, lo);
                     getErrorInfo(bills, lo, checkResult);
                 }

+ 9 - 6
app/public/js/path_tree.js

@@ -1286,7 +1286,8 @@ const createNewPathTree = function (type, setting) {
             }
             this.sortByLevelConverse(reCalcNodes);
             for (const node of reCalcNodes) {
-                treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+                // treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+                treeCalc.calculateNode(this, node);
             }
             result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes;
             return result;
@@ -1404,7 +1405,8 @@ const createNewPathTree = function (type, setting) {
             }
             this.sortByLevelConverse(reCalcNodes);
             for (const node of reCalcNodes) {
-                treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+                // treeCalc.calculateNode(this, node, this.setting.calcFields, this.setting.calcFun);
+                treeCalc.calculateNode(this, node);
             }
             result.update = result.update ? result.update.concat(reCalcNodes) : reCalcNodes;
             return result;
@@ -2304,8 +2306,8 @@ const treeCalc = {
         const setting = tree.setting;
         return Math.max.apply(Math, tree.datas.map(function (o) { return o[setting.level] }));
     },
-    calculateNode: function (tree, node) {
-        if (node.children && node.children.length > 0) {
+    calculateNode: function (tree, node, customFun) {
+        if (node.children && node.children.length > 0 && tree.setting.calcFields) {
             const gather = {};
             node.children.forEach(c => {
                 for (const cf of tree.setting.calcFields) {
@@ -2332,6 +2334,7 @@ const treeCalc = {
         if (tree.setting.calcFun) {
             tree.setting.calcFun(node);
         }
+        if (customFun) customFun(node);
     },
     calculateLevelNode: function (tree, level) {
         const setting = tree.setting;
@@ -2340,13 +2343,13 @@ const treeCalc = {
             this.calculateNode(tree, node);
         }
     },
-    calculateAll: function (tree) {
+    calculateAll: function (tree, customFun) {
         const [maxLevel, levelMap] = this.mapTreeNode(tree);
         for (let i = maxLevel; i >= 0; i--) {
             const levelNodes = levelMap[i];
             if (levelNodes && levelNodes.length > 0) {
                 for (const node of levelNodes) {
-                    this.calculateNode(tree, node);
+                    this.calculateNode(tree, node, customFun);
                 }
             }
         }

+ 49 - 20
app/public/js/quality_info.js

@@ -19,26 +19,55 @@ $(document).ready(() => {
     SpreadJsObj.initSheet(xmjSheet, xmjSpreadSetting);
     const xmjTree = createNewPathTree('gather', { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
 
-    postData('load', { filter: 'xmj;pos1', spec: { loadStatus: 1 } }, function(result) {
-        result.xmj.forEach(x => { x.rela_type = 'xmj'; });
-        xmjTree.loadDatas(result.xmj);
-        const posIndex = {};
-        for (const p of result.pos1) {
-            if (!posIndex[p.lid]) posIndex[p.lid] = [];
-            posIndex[p.lid].push(p);
-        }
-        for (const pi in posIndex) {
-            const xmj = xmjTree.nodes.find(x => { return x.id === pi; });
-            if (!xmj || (xmj.children && xmj.children.length > 0)) continue;
+    postData('load', { filter: 'xmj;pos;quality', spec: { loadStatus: 1 } }, function(result) {
+        const ledgerTree = createNewPathTree('ledger', { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+        const pos = new PosData({id: 'id', ledgerId: 'lid',});
+        ledgerTree.loadDatas(result.xmj);
+        pos.loadDatas(result.pos);
 
-            const posRange = posIndex[pi];
-            posRange.sort((a, b) => { return a.order - b.order; });
-            for (const p of posRange) {
-                if (p.b_code) continue;
-                xmjTree.addNode({ id: p.id, code: p.code, name: p.name, rela_type: 'pos' }, xmj);
+        const recursiveLoad = function (node, parent, type = 'xmj') {
+            if (type === 'xmj') {
+                const cur = !node.b_code ? xmjTree.addNode({ id: node.id, tender_id: node.tender_id, code: node.code, name: node.name, rela_type: type, rela_id: node.id, rela_name: '', }, parent) : parent;
+                if (node.children && node.children.length > 0) {
+                    for (const child of node.children) {
+                        recursiveLoad(child, cur);
+                    }
+                } else {
+                    const posRange = pos.getLedgerPos(node.id) || [];
+                    for (const pos of posRange) {
+                        recursiveLoad(pos, cur, 'pos');
+                    }
+                }
             }
+            if (type === 'pos') {
+                if (!parent) return;
+                const cur = parent.children ? parent.children.find(x => { return x.name === node.name }) : null;
+                if (!cur) {
+                    xmjTree.addNode({ id: node.id, tender_id: node.tid, rela_id: parent.id, code: '', name: node.name, rela_type: 'pos', rela_name: node.name, pid: [node.id]}, parent);
+                } else {
+                    cur.pid.push(node.id);
+                }
+            }
+        };
+        for (const node of ledgerTree.children) {
+            recursiveLoad(node);
         }
         xmjTree.sortTreeNode(false);
+
+        const qualityIndex = [];
+        for (const q of result.quality) {
+            const key = q.rela_type + ';' + q.rela_id + ';' + q.rela_name;
+            qualityIndex[key] = q;
+        }
+        for (const x of xmjTree.nodes) {
+            const qua = qualityIndex[x.rela_type + ';' + x.rela_id + ';' + x.rela_name];
+            if (qua) {
+                x.gxby_status = qua.gxby_status;
+                x.gxby_date = qua.gxby_date;
+                x.dagl_status = qua.dagl_status;
+            }
+        }
+
         SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
     });
 
@@ -52,7 +81,7 @@ $(document).ready(() => {
         }
         getFilterData() {
             if (!this.node) return null;
-            const result = { rela_type: this.node.rela_type, rela_id: this.node.id };
+            const result = { rela_type: this.node.rela_type, rela_id: this.node.rela_id, rela_name: this.node.rela_name };
             if (this.node.quality) result.quality_id = this.node.quality.id;
             return result;
         }
@@ -864,7 +893,7 @@ $(document).ready(() => {
                 $('#quality-detail').show();
             }
             if (!node.quality || force) {
-                node.quality = (await postDataAsync('load', { filter: 'detail', id: node.id })).detail;
+                node.quality = (await postDataAsync('load', { filter: 'detail', rela_type: node.rela_type, rela_id: node.rela_id, rela_name: node.rela_name, })).detail;
                 if (node.quality) {
                     node.gxby_status = node.quality.gxby_status;
                     node.gxby_date = node.quality.gxby_date;
@@ -899,10 +928,10 @@ $(document).ready(() => {
                     if (node.children && node.children.length > 0) {
                         const posterity = xmjTree.getPosterity(node);
                         for (const p of posterity) {
-                            if (!node.children || node.children.length === 0) select.push(node.id);
+                            if (!node.children || node.children.length === 0) select.push({ rela_type: node.rela_type, rela_id: node.rela_id, rela_name: node.rela_name, pid: node.pid });
                         }
                     } else {
-                        select.push(node.id);
+                        select.push({ rela_type: node.rela_type, rela_id: node.rela_id, rela_name: node.rela_name, pid: node.pid });
                     }
                     postData('push', {push_type: 'inc', select}, function(result) {
                         if (!result) {

+ 48 - 30
app/public/js/quality_rule.js

@@ -539,39 +539,57 @@ $(() => {
     let tenderId;
     const loadTenderData = function(tid) {
         tenderId = tid;
-        postData(`/quality/tender/${tid}/load`, {filter: 'xmj;pos1;quality'}, function(result) {
-            const items = getSelectGroup();
-            result.quality.forEach(q => {
-                const index = items.findIndex( i => { return i.value === q.group_id; } );
-                if (index < 0) return;
-                if (q.rela_type === 'xmj') {
-                    const x = result.xmj.find(x => { return x.id === q.rela_id; });
-                    if (x) x.group_id = q.group_id;
-                } else if (q.rela_type === 'pos') {
-                    const x = result.pos1.find(x => { return x.id === q.rela_id; });
-                    if (x) x.group_id = q.group_id;
-                }
-            });
+        postData(`/sp/${spid}/quality/tender/${tid}/load`, {filter: 'xmj;pos;quality'}, function(result) {
+            const ledgerTree = createNewPathTree('ledger', { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+            const pos = new PosData({id: 'id', ledgerId: 'lid',});
+            ledgerTree.loadDatas(result.xmj);
+            pos.loadDatas(result.pos);
 
-            xmjTree.loadDatas(result.xmj);
-            xmjTree.nodes.forEach(x => { x.rela_type = 'xmj'; });
-            const posIndex = {};
-            for (const p of result.pos1) {
-                if (!posIndex[p.lid]) posIndex[p.lid] = [];
-                posIndex[p.lid].push(p);
+            const recursiveLoad = function (node, parent, type = 'xmj') {
+                if (type === 'xmj') {
+                    const cur = !node.b_code ? xmjTree.addNode({ id: node.id, tender_id: node.tender_id, code: node.code, name: node.name, rela_type: type, rela_id: node.id, rela_name: '', }, parent) : parent;
+                    if (node.children && node.children.length > 0) {
+                        for (const child of node.children) {
+                            recursiveLoad(child, cur);
+                        }
+                    } else {
+                        const posRange = pos.getLedgerPos(node.id) || [];
+                        for (const pos of posRange) {
+                            recursiveLoad(pos, cur, 'pos');
+                        }
+                    }
+                }
+                if (type === 'pos') {
+                    if (!parent) return;
+                    const cur = parent.children ? parent.children.find(x => { return x.name === node.name }) : null;
+                    if (!cur) {
+                        xmjTree.addNode({ id: node.id, tender_id: node.tid, rela_id: parent.id, code: '', name: node.name, rela_type: 'type', rela_name: node.name, pid: [node.id]}, parent);
+                    } else {
+                        cur.pid.push(node.id);
+                    }
+                }
+            };
+            xmjTree.clearDatas();
+            for (const node of ledgerTree.children) {
+                recursiveLoad(node);
             }
-            for (const pi in posIndex) {
-                const xmj = xmjTree.nodes.find(x => { return x.id === pi; });
-                if (!xmj || (xmj.children && xmj.children.length > 0)) continue;
+            xmjTree.sortTreeNode(false);
 
-                const posRange = posIndex[pi];
-                posRange.sort((a, b) => { return a.order - b.order; });
-                for (const p of posRange) {
-                    if (p.b_code) continue;
-                    xmjTree.addNode({ id: p.id, tender_id: p.tid, code: p.code, name: p.name, group_id: p.group_id, rela_type: 'pos' }, xmj);
+            const items = getSelectGroup();
+            const qualityIndex = [];
+            for (const q of result.quality) {
+                const key = q.rela_type + ';' + q.rela_id + ';' + q.rela_name;
+                qualityIndex[key] = q;
+            }
+            for (const x of xmjTree.nodes) {
+                const qua = qualityIndex[x.rela_type + ';' + x.rela_id + ';' + x.rela_name];
+                if (qua) {
+                    const index = items.findIndex( i => { return i.value === qua.group_id; } );
+                    if (index < 0) return;
+                    x.group_id = qua.group_id;
                 }
             }
-            xmjTree.sortTreeNode(false);
+
             SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
         });
     };
@@ -596,11 +614,11 @@ $(() => {
         if (col.field !== 'group_id') return;
 
         const node = SpreadJsObj.getSelectObject(info.sheet);
-        const updateData = { rela_type: node.rela_type, rela_id: node.id, group_id: info.editingText || '' };
+        const updateData = { rela_type: node.rela_type, rela_id: node.rela_id, rela_name: node.rela_name, group_id: info.editingText || '' };
         if (updateData.group_id === node.group_id || (!updateData.group_id && !node.group_id)) return;
 
         // 更新至服务器
-        postData(`/quality/tender/${node.tender_id}/rule/save`, { quality: updateData }, function (result) {
+        postData(`/sp/${spid}/quality/tender/${node.tender_id}/rule/save`, { quality: updateData }, function (result) {
             node.group_id = result.group_id;
             SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
         }, function() {

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

@@ -13,7 +13,7 @@ const tenderListSpec = (function(){
             html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
             html.push('</span>');
             //html.push('<a href="/tender/' + node.id + '">', node[c.field], '</a>');
-            html.push(`<a href="/quality/tender/${node.id}/info" name="name" style="min-width: 200px;word-break:break-all;" id="${node.id}">${node.name}</a>`);
+            html.push(`<a href="/sp/${spid}/quality/tender/${node.id}/info" name="name" style="min-width: 200px;word-break:break-all;" id="${node.id}">${node.name}</a>`);
         }
         html.push('</td>');
 
@@ -35,7 +35,7 @@ const tenderListSpec = (function(){
     function getTenderTreeHeaderHtml() {
         const html = [];
         const left = $('#sub-menu').css('display') === 'none' ? 56 : 176;
-        html.push('<table class="table table-hover table-bordered" id="progress-table">');
+        html.push('<table class="table table-hover table-bordered">');
         html.push('<thead style="position: sticky;left:'+ left +'px;top: 0;" class="text-center">', '<tr>');
         html.push('<th style="min-width: 50%">',  '标段名称',  tenderListOrder.getOrderButton('name'), '</th>');
         html.push('<th style="width: 15%">', '创建时间',  tenderListOrder.getOrderButton('create_time'), '</th>');

+ 24 - 5
app/public/js/setting_s2b.js

@@ -32,12 +32,18 @@ $(() => {
             }
             $('#limitOptions').html(html.join(''));
         };
+        const refreshLimit = function() {
+            if (!curLimit) {
+                $('#limitOptions').html('');
+            } else {
+                loadLimitOption(curLimit);
+                $('dd[limitId]').removeClass('bg-warning');
+                $(`dd[limitId=${curLimit.limit_id}]`).addClass('bg-warning');
+            }
+        };
         const setCurLimit = function(limit) {
             curLimit = limit;
-            if (!limit) return;
-            loadLimitOption(limit);
-            $('dd[limitId]').removeClass('bg-warning');
-            $(`dd[limitId=${curLimit.limit_id}]`).addClass('bg-warning');
+            refreshLimit();
         };
         const getCurLimit = function() {
             return curLimit;
@@ -72,6 +78,12 @@ $(() => {
         const delLimit = function(limit_id){
             postData('/setting/limit/save', {del: limit_id}, function(result) {
                 $(`dd[limitId=${result.del}]`).remove();
+                const limitIndex = limits.findIndex(x => { return x.limit_id === limit_id; });
+                limits.splice(limitIndex, 1);
+                if (curLimit.limit_id === limit_id) {
+                    curLimit = null;
+                    refreshLimit();
+                }
             });
         };
 
@@ -103,7 +115,7 @@ $(() => {
         let limit_id, limit_name;
         const addCheckHtml = function(check) {
             const html = [];
-            html.push('<tr>', `<td locInfo="${check.alias}&^&${check.field}&^&${check.check}&^&${check.operation}&^&${check.value}">${check.alias} ${check.operation} ${check.value}</td>`,
+            html.push('<tr>', `<td locInfo="${check.alias}&^&${check.field}&^&${check.check}&^&${check.operation}&^&${check.value}&^&${check.source}">${check.alias} ${check.operation} ${check.value}</td>`,
                 '<td><a href="javascript: void(0);" class="mr-1" data-toggle="tooltip" data-placement="bottom" data-original-title="删除" name="loc-del"><i class="fa fa-trash-o fa-fw text-danger"></i></a></td>', '</tr>');
             $('#loc-list').append(html.join(''));
         };
@@ -136,6 +148,11 @@ $(() => {
         });
 
         const showOptionModal = function(option, limit) {
+            if (!option && !limit) {
+                toastr.warning('请先新增或选择配置后,再新增判断');
+                return;
+            }
+
             limit_id = option ? option.limit_id : limit.limit_id;
             limit_name = option ? option.name : limit.name;
             $('#lo-id').val(option ? option.id : '');
@@ -146,6 +163,7 @@ $(() => {
             $('#loc-list').html('');
             if (option) {
                 for (const c of option.condition) {
+                    if (!c.source) c.source = '3f';
                     addCheckHtml(c);
                 }
             }
@@ -169,6 +187,7 @@ $(() => {
                         check: info[2],
                         operation: info[3],
                         value: parseInt(info[4]),
+                        source: info[5],
                     });
                 }
                 const id = $('#lo-id').val();

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

@@ -1729,7 +1729,7 @@ const showSelectTab = function(select, spread, afterShow) {
             '        </div>\n' +
             '    </div>' +
             '</div>\n' +
-            `<div id="std-${setting.stdType}-spread" class="sjs-sh"></div>\n`
+            `<div id="std-${setting.stdType}-spread" class="${setting.spreadClassCss ? setting.spreadClassCss : 'sjs-sh'}"></div>\n`
         );
         autoFlashHeight();
         const pathTree = createNewPathTree('base', setting.treeSetting);

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

@@ -233,7 +233,7 @@ $(document).ready(() => {
     };
     let detail, searchLedger, checkedChanges;
     const tpCalcFields = [{qty: 'qc_qty', tp: 'qc_tp'}];
-    if (tenderInfo.calc_type === 'up' && correctCalcContractTp) tpCalcFields.shift({qty: 'contract_qty', tp: 'contract_tp'});
+    if (tenderInfo.calc_type === 'up' && !correctCalcContractTp) tpCalcFields.unshift({qty: 'contract_qty', tp: 'contract_tp'});
     const checkOption = {
         sibling: { enable: 0 },
         empty_code: { enable: 0 },
@@ -844,7 +844,7 @@ $(document).ready(() => {
             },
         },
     ];
-    sjsSettingObj.setIsTpCol(ledgerSpreadSetting.cols, [ {field: 'is_tp'} ]);
+    // sjsSettingObj.setIsTpCol(ledgerSpreadSetting.cols, [ {field: 'is_tp'} ]);
     ledgerSpreadSetting.readOnly = function (data) {
         if (!data) return false;
         return data.lock || stageTreeSpreadObj.assistReadOnly;
@@ -1413,7 +1413,7 @@ $(document).ready(() => {
                 const tpUnit = ['总额', '元', '台', '个', '处', '月', '棵', '套', '组', '辆', '项', '座', '根', '块', '系统'];
                 if (node.pre_used === 1 ||
                     (node.children && node.children.length > 0) ||
-                    (tpUnit.indexOf(node.unit) < 0) ||
+                    // (tpUnit.indexOf(node.unit) < 0) ||
                     (posRange && posRange.length > 0) /*|| !checkZero(node.qc_qty)*/) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
                     return;

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

@@ -363,6 +363,7 @@ $(document).ready(() => {
     bindTenderUrl();
     localHideList();
     tenderTreeShowLevel.initShowLevel();
+    console.log($("#progress-table"));
     if ($("#progress-table").length > 0) {
         $("#progress-table").colResizable({
             liveDrag: true,

+ 4 - 1
app/router.js

@@ -452,6 +452,8 @@ module.exports = app => {
     app.post('/sp/:id/financial/pay/:fpid/audit/check/again', sessionAuth, subProjectCheck, financialCheck, financialPayCheck, 'financialController.checkPayAgain');
     app.get('/sp/:id/financial/pay/list', sessionAuth, subProjectCheck, financialCheck, 'financialController.payList');
     app.post('/sp/:id/financial/pay/list/save', sessionAuth, subProjectCheck, financialCheck, 'financialController.payListSave');
+    app.get('/sp/:id/financial/pay/tender', sessionAuth, subProjectCheck, financialCheck, 'financialController.payTender');
+    app.post('/sp/:id/financial/pay/tender/save', sessionAuth, subProjectCheck, financialCheck, 'financialController.payTenderSave');
     app.get('/sp/:id/financial/summary', sessionAuth, subProjectCheck, financialCheck, 'financialController.summary');
     app.post('/sp/:id/financial/summary/load', sessionAuth, subProjectCheck, financialCheck, 'financialController.summaryLoad');
 
@@ -461,7 +463,7 @@ module.exports = app => {
     // app.get('/sp/:id/drawing/as-built', sessionAuth, subProjectCheck, 'drawingController.built');
 
     // 质量管理
-    app.get('/sp/:id/quality', sessionAuth, subProjectCheck, 'qualityController.tender');
+    app.get('/sp/:id/quality/tender', sessionAuth, subProjectCheck, 'qualityController.tender');
     app.post('/sp/:id/quality/member', sessionAuth, subProjectCheck, projectManagerCheck, 'qualityController.member');
     app.post('/sp/:id/quality/memberSave', sessionAuth, subProjectCheck, projectManagerCheck, 'qualityController.memberSave');
     app.get('/sp/:id/quality/tender/:tid/info', sessionAuth, subProjectCheck, tenderCheck, tenderPermissionCheck, 'qualityController.info');
@@ -591,6 +593,7 @@ module.exports = app => {
     app.post('/tender/:id/revise/:rid/info/upload-excel/:ueType', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.uploadExcel');
     app.post('/tender/:id/revise/:rid/info/check', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.checkData');
     app.post('/tender/:id/revise/:rid/info/deal2sgfh', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.deal2sgfh');
+    app.post('/tender/:id/revise/:rid/info/sgfh2deal', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.sgfh2deal');
     app.post('/tender/:id/revise/:rid/file/upload', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.uploadFile');
     app.post('/tender/:id/revise/:rid/file/delete', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.deleteFile');
     app.post('/tender/:id/revise/:rid/file/save', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, reviseCheck, 'reviseController.saveFile');

+ 2 - 2
app/service/change_pos.js

@@ -23,11 +23,11 @@ module.exports = app => {
             this.tableName = 'change_pos';
         }
 
-        async getPosData(condition) {
+        async getPosData(condition, columns) {
             if (!condition.tid) throw '查询计量单元缺少必要信息';
             return await this.db.select(this.tableName, {
                 where: condition,
-                columns: ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty',
+                columns: columns || ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty',
                     'qtcl_qty', 'in_time', 'porder', 'add_stage', 'sgfh_expr', 'sjcl_expr', 'qtcl_expr', 'real_qty',
                     'dagl_status', 'dagl_url', 'gxby_status', 'gxby_limit', 'dagl_limit', 'dagl_status',
                     'ex_memo1', 'ex_memo2', 'ex_memo3', 'ccid', 'formc', 'ex_qty1'],

+ 175 - 0
app/service/contract_tree.js

@@ -7,6 +7,7 @@
 const BaseService = require('../base/base_service');
 const contractConst = require('../const/contract');
 const rootId = -1;
+const billsUtils = require('../lib/bills_utils');
 
 module.exports = app => {
 
@@ -1036,6 +1037,180 @@ module.exports = app => {
 
             return data;
         }
+
+        /**
+         * 添加节点,并同步添加父节点
+         * @param {Number} tenderId - 标段id
+         * @param {Number} selectId - 选中节点id
+         * @param {Object} stdData - 节点数据
+         * @param {StandardLib} stdLib - 标准库
+         * @return {Promise<void>}
+         */
+        async addStdNodeWithParent(options, kid, stdData, stdLib) {
+            if (!options[this.setting.type]) throw '参数有误';
+            const select = kid ? await this.getDataByKid(options, kid) : null;
+            if (!select) throw '新增子节点数据错误';
+            if (select && select.c_code) throw '合同无法新增子节点';
+            // 查询完整标准清单,并按层次排序
+            const fullLevel = await stdLib.getFullLevelDataByFullPath(stdData.list_id, stdData.full_path);
+            fullLevel.sort(function(x, y) {
+                return x.level - y.level;
+            });
+
+            let isNew = false,
+                node,
+                firstNew,
+                updateParent,
+                addResult;
+            const expandIds = [];
+            this.transaction = await this.db.beginTransaction();
+            try {
+                // 从最顶层节点依次查询是否存在,否则添加
+                for (let i = 0, len = fullLevel.length; i < len; i++) {
+                    const stdNode = fullLevel[i];
+
+                    if (isNew) {
+                        const newData = {
+                            name: stdNode.name,
+                            unit: stdNode.unit,
+                        };
+                        newData.code = stdNode.code ? stdNode.code : '';
+                        newData.is_leaf = (i === len - 1) ? 1 : 0;
+                        [addResult, node] = await this._addChildNodeData(options, node, newData);
+                    } else {
+                        const parent = node;
+                        const condition = this._.cloneDeep(options);
+                        condition.code = stdNode.code;
+                        condition.name = stdNode.name;
+                        node = await this.getDataByCondition(condition);
+                        if (!node) {
+                            // let children = await this.getChildrenByParentId(options, parent[this.setting.pid]);
+                            // if (children.length === 0) {
+                            //     throw '原台账节点为子项时不能添加它的子项';
+                            // }
+                            isNew = true;
+                            const newData = {
+                                name: stdNode.name,
+                                unit: stdNode.unit,
+                            };
+                            newData.code = stdNode.code ? stdNode.code : '';
+                            newData.is_leaf = (i === len - 1) ? 1 : 0;
+                            [addResult, node] = await this._addChildAutoOrder(options, parent, newData);
+                            if (parent && parent.is_leaf) {
+                                await this.transaction.update(this.tableName, { id: parent.id, is_leaf: 0 });
+                                updateParent = parent;
+                            }
+                            firstNew = node;
+                        } else {
+                            expandIds.push(node[this.setting.pid]);
+                        }
+                    }
+                }
+                await this.transaction.commit();
+            } catch (err) {
+                await this.transaction.rollback();
+                throw err;
+            }
+
+            // 查询应返回的结果
+            let createData = [],
+                updateData = [];
+            if (firstNew) {
+                createData = await this.getDataByFullPath(options, firstNew[this.setting.fullPath] + '%');
+                updateData = await this.getNextsData(options, firstNew[this.setting.pid], firstNew[this.setting.order]);
+                if (updateParent) {
+                    updateData.push(await this.getDataByCondition({ id: updateParent.id }));
+                }
+            }
+            return { create: createData, update: updateData };
+        }
+
+        /**
+         * 根据 父节点id 获取子节点
+         * @param tenderId
+         * @param nodeId
+         * @return {Promise<*>}
+         */
+        async getChildrenByParentId(options, pid) {
+            const sql = 'SELECT * FROM ?? WHERE ' + this.ctx.helper._getOptionsSql(options) + ' AND ' + this.setting.pid + ' = ? ORDER BY `order` ASC';
+            const sqlParam = [this.tableName, pid];
+            const data = await this.db.query(sql, sqlParam);
+
+            const sql1 = 'SELECT * FROM ?? WHERE ' + this.ctx.helper._getOptionsSql(options) + ' AND ' + this.setting.pid + ' = ? ORDER BY `order` ASC';
+            const sqlParam1 = [this.ctx.service.contract.tableName, pid];
+            const data1 = await this.db.query(sql1, sqlParam1);
+            // data和data1合并且按order排序
+            const resultData = data.concat(data1).sort((a, b) => a.order - b.order);
+            return resultData;
+        }
+
+        /**
+         * 根据parentData, data新增数据(新增为parentData的最后一个子项)
+         * @param {Number} tenderId - 标段id
+         * @param {Object} parentData - 父项数据
+         * @param {Object} data - 新增节点,初始数据
+         * @return {Promise<*>} - 新增结果
+         * @private
+         */
+        async _addChildNodeData(options, parentData, data) {
+            if (!data) {
+                data = {};
+            }
+            const pid = parentData ? parentData[this.setting.kid] : rootId;
+
+            const maxId = await this._getMaxLid(options);
+            data.id = this.uuid.v4();
+            data[this.setting.spid] = options.spid || null;
+            data[this.setting.pid] = pid;
+            data[this.setting.kid] = maxId + 1;
+            data[this.setting.type] = options[this.setting.type];
+            data[this.setting.mid] = options.tid || null;
+            data[this.setting.level] = parentData ? parentData[this.setting.level] + 1 : 1;
+            if (data[this.setting.order] === undefined) {
+                data[this.setting.order] = 1;
+            }
+            data.full_path = parentData ? parentData.full_path + '-' + data[this.setting.kid] : '' + data[this.setting.kid];
+            if (data[this.setting.isLeaf] === undefined) {
+                data[this.setting.isLeaf] = true;
+            }
+            const result = await this.transaction.insert(this.tableName, data);
+
+            this._cacheMaxLid(options, maxId + 1);
+
+            return [result, data];
+        }
+
+        /**
+         * 根据parentData, data新增数据(自动排序)
+         * @param tenderId
+         * @param parentData
+         * @param data
+         * @return {Promise<void>}
+         * @private
+         */
+        async _addChildAutoOrder(options, parentData, data) {
+            const self = this;
+            const findPreData = function(list, a) {
+                if (!list || list.length === 0) { return null; }
+                for (let i = 0, iLen = list.length; i < iLen; i++) {
+                    if (billsUtils.compareCode(list[i].code, a.code) > 0) {
+                        return i > 0 ? list[i - 1] : null;
+                    }
+                }
+                return list[list.length - 1];
+            };
+
+            const pid = parentData ? parentData[this.setting.kid] : rootId;
+            const children = await this.getChildrenByParentId(options, pid);
+            const preData = findPreData(children, data);
+            if (!preData || children.indexOf(preData) < children.length - 1) {
+                await this._updateChildrenOrder(options, pid, preData ? preData.order + 1 : 1);
+            }
+            data.order = preData ? preData.order + 1 : 1;
+            const [addResult, node] = await this._addChildNodeData(options, parentData, data);
+
+            return [addResult, node];
+        }
     }
     return ContractTree;
 };

+ 4 - 4
app/service/financial_transfer.js

@@ -38,10 +38,10 @@ module.exports = app => {
         }
 
         async addTransfer(spid, date, remark) {
-            const node = await this.getDataByCondition({ spid, t_time: date });
-            if (node) {
-                throw '资金划拨年月已存在,请勿重复';
-            }
+            // const node = await this.getDataByCondition({ spid, t_time: date });
+            // if (node) {
+            //     throw '资金划拨年月已存在,请勿重复';
+            // }
             let pre_hb_tp = 0;
             const ftList = await this.getAllDataByCondition({ where: { spid } });
             if (ftList.length > 0) {

+ 1 - 1
app/service/ledger.js

@@ -710,7 +710,7 @@ module.exports = app => {
                 await transaction.insert(this.tableName, datas);
                 if (this.ctx.tender.data.measure_type === measureType.tz.value
                     && cacheTree.pos && cacheTree.pos.length > 0) {
-                    await transaction.insert(this.ctx.service.pos.tableName, cacheTree.pos);
+                    await this.insertDatasSplit(transaction, this.ctx.service.pos.tableName, cacheTree.pos);
                 }
                 await transaction.commit();
                 this._cacheMaxLid(this.ctx.tender.id, cacheTree.keyNodeId);

+ 18 - 1
app/service/phase_pay.js

@@ -81,7 +81,7 @@ module.exports = app => {
          */
         async getAllPhasePay (tid, sort = 'ASC') {
             const result = await this.getAllDataByCondition({
-                where: {tid: tid},
+                where: { tid: tid },
                 orders: [['phase_order', sort]],
             });
             this.analysisPhasePay(result);
@@ -512,6 +512,23 @@ module.exports = app => {
                 }
             }
         }
+
+        async checkStageRelaPhasePay(stage) {
+            if (this.ctx.stageRelaPhasePay !== undefined) return;
+
+            const allPhase = await this.getAllPhasePay(stage.tid);
+            for (const phase of allPhase) {
+                if (phase.rela_stage.find(x => { return x.stage_id === stage.id; })) {
+                    await this.doCheckPhase(phase);
+                    this.ctx.stageRelaPhasePay = phase;
+                    return;
+                }
+                const fs = this.ctx.helper._.orderBy(phase.rela_stage.filter(x => { return x.stage_order < stage.order; }), ['stage_order'], ['desc']);
+                phase.maxRelaStageOrder = fs.length > 0 ? fs[0].stage_order : 0;
+            }
+            const fp = this.ctx.helper._.orderBy(allPhase.filter(x => { return x.maxRelaStageOrder > 0; }), ['maxRelaStageOrder'], ['desc']);
+            this.ctx.stageRelaPhasePay = fp.length > 0 ? fp[0] : null;
+        }
     }
 
     return PhasePay;

+ 1 - 1
app/service/phase_pay_detail.js

@@ -500,7 +500,7 @@ class PhasePayDetail extends TreeService {
         try {
             const result = await conn.insert(this.tableName, insertData);
             if (children.length === 0) {
-                await conn.update(this.tableName, { id: select.id, is_leaf: false });
+                await conn.update(this.tableName, { id: select.id, tree_is_leaf: false });
             }
             await conn.commit();
         } catch(err) {

+ 107 - 36
app/service/quality.js

@@ -8,6 +8,8 @@
  * @version
  */
 
+const Ledger = require('../lib/ledger');
+
 class RuleCheck {
     sortCondition(condition) {
         const fieldKey = ['gongxu_name', 'yinbi_name', 'file_count', 'file_name'];
@@ -279,7 +281,7 @@ module.exports = app => {
 
         async getQuality(filter) {
             if (filter.quality_id) return await this.getDataByCondition({ id: filter.quality_id });
-            if (filter.rela_type && filter.rela_id) return await this.getDataByCondition({ tid: filter.tid || this.ctx.tender.id, rela_id: filter.rela_id, rela_name: filter.rela_name });
+            if (filter.rela_type && filter.rela_id) return await this.getDataByCondition({ tid: filter.tid || this.ctx.tender.id, rela_type: filter.rela_type, rela_id: filter.rela_id, rela_name: filter.rela_name });
             throw '参数错误';
         }
 
@@ -421,8 +423,14 @@ module.exports = app => {
         }
 
         async pushIncStatus(select) {
-            const quality = await this.ctx.service.quality.getAllDataByCondition({ where: { tid: this.ctx.tender.id, rela_id: select, is_used: 1 } });
-            if (quality.length === 0) return 0;
+            const quality = [];
+            for (const s of select) {
+                const sq = await this.ctx.service.quality.getDataByCondition({ tid: this.ctx.tender.id, rela_type: s.rela_type, rela_id: s.rela_id, rela_name: s.rela_name });
+                if (sq && sq.is_used) {
+                    quality.push(sq);
+                    if (sq.rela_type === 'pos') sq.rela_pid = s.pid;
+                }
+            }
             const xmjInsert = [], xmjUpdate = [], posInsert = [], posUpdate = [];
             const xmjQuality = [], posQuality = [];
             quality.forEach(q => {
@@ -437,19 +445,27 @@ module.exports = app => {
                 const xe = xmjExtra.find(x => { return x.id === xq.rela_id; });
                 const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${xq.tid}&${xq.rela_type}id=${xq.rela_id}`;
                 if (xe) {
-                    xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
+                    // xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
+                    xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, dagl_status: xq.dagl_status, wbs_url });
                 } else {
-                    xmjInsert.push({ id: xq.rela_id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
+                    // xmjInsert.push({ id: xq.rela_id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
+                    xmjInsert.push({ id: xq.rela_id, tid: this.ctx.tender.id, gxby_status: xq.gxby_status, dagl_status: xq.dagl_status, wbs_url });
                 }
             }
-            const posExtra = posQuality.length > 0 ? await this.ctx.service.posExtra.getAllDataByCondition({ where: { id: posQuality.map(x => { return x.rela_id; }) } }) : [];
+
             for (const pq of posQuality) {
-                const pe = posExtra.find(x => { return x.id === pq.rela_id; });
-                const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${pq.tid}&${pq.rela_type}id=${pq.rela_id}`;
-                if (pe) {
-                    posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
-                } else {
-                    posInsert.push({ id: pq.rela_id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
+                if (!pq.rela_pid || pq.rela_pid.length === 0) continue;
+                const posExtra = await this.ctx.service.posExtra.getAllDataByCondition({ where: { id: pq.rela_pid } });
+                for (const pid of pq.rela_pid) {
+                    const pe = posExtra.find(x => { return x.id === pid; });
+                    const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${pq.tid}&${pq.rela_type}id=${pid}`;
+                    if (pe) {
+                        // posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
+                        posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, dagl_status: pq.dagl_status, wbs_url });
+                    } else {
+                        // posInsert.push({ id: pid, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
+                        posInsert.push({ id: pid, tid: this.ctx.tender.id, gxby_status: pq.gxby_status, dagl_status: pq.dagl_status, wbs_url });
+                    }
                 }
             }
             const conn = await this.db.beginTransaction();
@@ -466,41 +482,96 @@ module.exports = app => {
             }
             return xmjInsert.length + xmjUpdate.length + posInsert.length + posUpdate.length;
         }
+        async getQualityTree(quality) {
+            const ledgerData = await this.ctx.service.ledger.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id }});
+            const posData = await this.ctx.service.pos.getAllDataByCondition({ where: { tid: this.ctx.tender.id }});
+
+            const qualityTree = new Ledger.filterGatherTree(this.ctx, { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1 });
+            const ledgerTree = new Ledger.billsTree(this.ctx, {id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1});
+            const pos = new Ledger.pos({ id: 'id', ledgerId: 'lid' });
+            ledgerTree.loadDatas(ledgerData);
+            pos.loadDatas(posData);
+            const recursiveLoad = function (node, parent, type = 'xmj') {
+                if (type === 'xmj') {
+                    const cur = !node.b_code ? qualityTree.addNode({ id: node.id, tender_id: node.tender_id, code: node.code, name: node.name, rela_type: type, rela_id: node.id, rela_name: '', }, parent) : parent;
+                    if (node.children && node.children.length > 0) {
+                        for (const child of node.children) {
+                            recursiveLoad(child, cur);
+                        }
+                    } else {
+                        const posRange = pos.getLedgerPos(node.id) || [];
+                        for (const pos of posRange) {
+                            recursiveLoad(pos, cur, 'pos');
+                        }
+                    }
+                }
+                if (type === 'pos') {
+                    if (!parent) return;
+                    const cur = parent.children ? parent.children.find(x => { return x.name === node.name }) : null;
+                    if (!cur) {
+                        qualityTree.addNode({ id: node.id, tender_id: node.tid, rela_id: parent.id, code: '', name: node.name, rela_type: 'pos', rela_name: node.name, pid: [node.id]}, parent);
+                    } else {
+                        cur.pid.push(node.id);
+                    }
+                }
+            };
+            for (const node of ledgerTree.children) {
+                recursiveLoad(node);
+            }
+            qualityTree.sortTreeNode(false);
+
+            if (quality) {
+                const qualityIndex = [];
+                for (const q of quality) {
+                    const key = q.rela_type + ';' + q.rela_id + ';' + q.rela_name;
+                    qualityIndex[key] = q;
+                }
+                for (const x of qualityTree.nodes) {
+                    const qua = qualityIndex[x.rela_type + ';' + x.rela_id + ';' + x.rela_name];
+                    if (qua) x.quality = qua;
+                }
+            }
+            return qualityTree;
+        }
         async pushAllStatus() {
             const quality = await this.ctx.service.quality.getAllDataByCondition({ where: { tid: this.ctx.tender.id, is_used: 1 } });
             if (quality.length === 0) return 0;
-            const xmjQuality = [], posQuality = [];
-            quality.forEach(q => {
-                if (q.rela_type === 'xmj') {
-                    xmjQuality.push(q);
-                } else {
-                    posQuality.push(q);
-                }
-            });
+            const qualityTree = await this.getQualityTree(quality);
+
             const xmjInsert = [], xmjUpdate = [], posInsert = [], posUpdate = [];
             const xmjExtra = await this.ctx.service.ledgerExtra.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
-            for (const xq of xmjQuality) {
-                const xe = xmjExtra.find(x => { return x.id === xq.rela_id; });
-                const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${xq.tid}&${xq.rela_type}id=${xq.rela_id}`;
-                if (xe) {
-                    xmjUpdate.push({ id: xe.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
-                } else {
-                    xmjInsert.push({ id: xq.rela_id, tid: this.ctx.tender.id, gxby_status: xq.gxby_status, gxby_date: xq.gxby_date, dagl_status: xq.dagl_status, wbs_url });
-                }
-            }
             const posExtra = await this.ctx.service.posExtra.getAllDataByCondition({ where: { tid: this.ctx.tender.id } });
-            for (const pq of posQuality) {
-                const pe = posExtra.find(x => { return x.id === pq.rela_id; });
-                const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${pq.tid}&${pq.rela_type}id=${pq.rela_id}`;
-                if (pe) {
-                    posUpdate.push({ id: pe.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
+            for (const node of qualityTree.nodes) {
+                if (!node.quality) continue;
+
+                if (node.rela_type === 'xmj') {
+                    const xe = xmjExtra.find(x => { return x.id === node.rela_id; });
+                    const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${this.ctx.tender.id}&xmjid=${node.rela_id}`;
+                    if (xe) {
+                        // xmjUpdate.push({ id: xe.id, gxby_status: node.quality.gxby_status, gxby_date: node.quality.gxby_date, dagl_status: node.quality.dagl_status, wbs_url });
+                        xmjUpdate.push({ id: xe.id, gxby_status: node.quality.gxby_status, dagl_status: node.quality.dagl_status, wbs_url });
+                    } else {
+                        // xmjInsert.push({ id: node.rela_id, tid: this.ctx.tender.id, gxby_status: node.quality.gxby_status, gxby_date: node.quality.gxby_date, dagl_status: node.quality.dagl_status, wbs_url });
+                        xmjInsert.push({ id: node.rela_id, tid: this.ctx.tender.id, gxby_status: node.quality.gxby_status, dagl_status: node.quality.dagl_status, wbs_url });
+                    }
                 } else {
-                    posInsert.push({ id: pq.rela_id, tid: this.ctx.tender.id, gxby_status: pq.gxby_status, gxby_date: pq.gxby_date, dagl_status: pq.dagl_status, wbs_url });
+                    for (const pid of node.pid) {
+                        const pe = posExtra.find(x => { return x.id === pid; });
+                        const wbs_url = `/3f/zh/info?pid=${this.ctx.session.sessionProject.id}&tid=${this.ctx.tender.id}&posid=${pid}`;
+                        if (pe) {
+                            // posUpdate.push({ id: pe.id, gxby_status: node.quality.gxby_status, gxby_date: node.quality.gxby_date, dagl_status: node.quality.dagl_status, wbs_url });
+                            posUpdate.push({ id: pe.id, gxby_status: node.quality.gxby_status, dagl_status: node.quality.dagl_status, wbs_url });
+                        } else {
+                            // posInsert.push({ id: pid, tid: this.ctx.tender.id, gxby_status: node.quality.gxby_status, gxby_date: node.quality.gxby_date, dagl_status: node.quality.dagl_status, wbs_url });
+                            posInsert.push({ id: pid, tid: this.ctx.tender.id, gxby_status: node.quality.gxby_status, dagl_status: node.quality.dagl_status, wbs_url });
+                        }
+                    }
                 }
             }
             const conn = await this.db.beginTransaction();
             try {
-                const defaultData = { gxby_status: -1, gxby_date: null, dagl_status: -1 };
+                // const defaultData = { gxby_status: -1, gxby_date: null, dagl_status: -1 };
+                const defaultData = { gxby_status: -1, dagl_status: -1 };
                 await conn.update(this.ctx.service.ledgerExtra.tableName, defaultData, { where: {tid: this.ctx.tender.id } });
                 await conn.update(this.ctx.service.posExtra.tableName, defaultData, { where: {tid: this.ctx.tender.id } });
 

+ 1 - 1
app/service/quality_file.js

@@ -28,7 +28,7 @@ module.exports = app => {
         analysisFiles(files) {
             const helper = this.ctx.helper;
             const userId = this.ctx.session.sessionUser.accountId;
-            const ossPath = this.ctx.app.config.ossUrl;
+            const ossPath = this.ctx.app.config.fujianOssPath;
             files.forEach(x => {
                 x.viewpath = helper.canPreview(x.fileext) ? ossPath + x.filepath : '';
                 x.filepath = ossPath + x.filepath;

+ 8 - 7
app/service/quality_rule.js

@@ -23,9 +23,9 @@ module.exports = app => {
             this.tableName = 'quality_rule';
         }
 
-        async getGroupList(pid) {
-            const sql = `SELECT group_id, group_name FROM ${this.tableName} WHERE pid = ? GROUP BY group_id`;
-            return await this.db.query(sql, [pid]);
+        async getGroupList(spid) {
+            const sql = `SELECT group_id, group_name FROM ${this.tableName} WHERE spid = ? GROUP BY group_id`;
+            return await this.db.query(sql, [spid]);
         }
 
         analysisiRule(data) {
@@ -47,10 +47,10 @@ module.exports = app => {
             return result;
         }
 
-        async getRuleGroups(pid) {
+        async getRuleGroups(spid) {
             const result = await this.getAllDataByCondition({
                 columns: ['id', 'group_id', 'group_name', 'name', 'condition', 'push_status', 'memo'],
-                where: { pid },
+                where: { spid },
             });
             return this.analysisRuleGroups(result);
         }
@@ -104,7 +104,7 @@ module.exports = app => {
             }
 
             const newRule = {
-                group_id: this.uuid.v4(), pid: this.ctx.session.sessionProject.id,
+                group_id: this.uuid.v4(), pid: this.ctx.session.sessionProject.id, spid: this.ctx.subProject.id,
                 user_id: this.ctx.session.sessionUser.accountId,
                 group_name: group_name || '新增规则组', name: '新增规则', condition: '[]', push_status: '[]',
             };
@@ -133,6 +133,7 @@ module.exports = app => {
                 await this.db.update(this.tableName, data);
             } else {
                 data.pid = this.ctx.session.sessionProject.id;
+                data.spid = this.ctx.subProject.id;
                 data.user_id = this.ctx.session.sessionUser.accountId;
                 data.name = data.name || '新增规则';
                 data.condition = data.condition ? JSON.stringify(data.condition) : '[]';
@@ -155,7 +156,7 @@ module.exports = app => {
             if (!group) throw '您选择的规则组不存在,请刷新页面再试';
             const insertData = rules.map(rule => {
                 return {
-                    pid: rule.pid, user_id: this.ctx.session.sessionUser.accountId,
+                    pid: rule.pid, spid: rule.spid, user_id: this.ctx.session.sessionUser.accountId,
                     group_id: group.group_id, group_name: group.group_name,
                     name: rule.name, condition: rule.condition, push_status: rule.push_status,
                 };

+ 8 - 0
app/service/report.js

@@ -509,6 +509,14 @@ module.exports = app => {
                             runnableRst.push(service.reportMemory.getSpContractPay(params.tender_id, 2));
                             runnableKey.push(filter);
                             break;
+                        case 'mem_phase_pay':
+                            runnableRst.push(service.reportMemory.getPhasePay(params.tender_id, params.stage_id));
+                            runnableKey.push(filter);
+                            break;
+                        case 'mem_phase_pay_detail':
+                            runnableRst.push(service.reportMemory.getPhasePayDetail(params.tender_id, params.stage_id));
+                            runnableKey.push(filter);
+                            break;
                         default:
                             break;
                     }

+ 26 - 2
app/service/report_memory.js

@@ -742,7 +742,7 @@ module.exports = app => {
                         filterStageBills.push(stageBillsIndex[prop]);
                     }
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: filterStageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'r' + role.flowOrder + '_', relaId: 'lid'}
+                        {data: filterStageBills, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty', 'contract_expr'], prefix: 'r' + role.flowOrder + '_', relaId: 'lid'}
                     ]);
                 }
 
@@ -836,7 +836,7 @@ module.exports = app => {
                         filterStagePos.push(stagePosIndex[prop]);
                     }
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: filterStagePos, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'r' + role.flowOrder + '_', relaId: 'pid'}
+                        {data: filterStagePos, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty', 'contract_expr'], prefix: 'r' + role.flowOrder + '_', relaId: 'pid'}
                     ]);
                 }
                 this.pos.loadDatas(posData);
@@ -1736,6 +1736,30 @@ module.exports = app => {
             await this.ctx.service.tender.checkTender(tid);
             return await this._getContractPay({ spid: this.ctx.tender.spid, contract_type });
         }
+
+        async getPhasePay(tid, sid) {
+            await this.ctx.service.tender.checkTender(tid);
+            await this.ctx.service.stage.checkStage(sid);
+            await this.ctx.service.phasePay.checkStageRelaPhasePay(this.ctx.stage);
+
+            return this.ctx.stageRelaPhasePay || {};
+        }
+
+        async getPhasePayDetail(tid, sid) {
+            await this.ctx.service.tender.checkTender(tid);
+            await this.ctx.service.stage.checkStage(sid);
+            await this.ctx.service.phasePay.checkStageRelaPhasePay(this.ctx.stage);
+
+            if (!this.ctx.stageRelaPhasePay) return [];
+            const data = await this.ctx.service.phasePayDetail.getDetailData(this.ctx.stageRelaPhasePay);
+            const payTree = new Ledger.baseTree(this.ctx, {
+                id: 'tree_id', pid: 'tree_pid', order: 'tree_order',
+                level: 'tree_level', isLeaf: 'tree_is_leaf', fullPath: 'tree_full_path',
+                rootId: -1, calcField: [],
+            });
+            payTree.loadDatas(data);
+            return payTree.getDefaultDatas();
+        }
     }
 
     return ReportMemory;

+ 9 - 1
app/service/stage_bills_final.js

@@ -75,6 +75,7 @@ module.exports = app => {
             if (!transaction || !tender || !stage) {
                 throw '数据错误';
             }
+            const time = new Date();
             const cur = await this.ctx.service.stageBills.getLastestStageData2(tender.id, stage.id);
             const curPc = await this.ctx.service.stageBillsPc.getAllDataByCondition({ where: { sid: stage.id } });
             const pre = await this.getFinalData(tender, stage.order - 1);
@@ -100,8 +101,11 @@ module.exports = app => {
                     c.negative_qc_qty = this.ctx.helper.add(c.negative_qc_qty, p.negative_qc_qty);
                     c.positive_qc_tp = this.ctx.helper.add(c.positive_qc_tp, p.positive_qc_tp);
                     c.negative_qc_tp = this.ctx.helper.add(c.negative_qc_tp, p.negative_qc_tp);
-                    c.used = p.used || !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty)
+                    const curUsed = !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty)
                         || !this.ctx.helper.checkZero(c.contract_tp) || ! this.ctx.helper.checkZero(c.qc_minus_qty);
+                    c.used = p.used || curUsed;
+                    if (c.used) c.used_time = p.used && p.used_time ? p.used_time : time;
+                    if (curUsed) c.update_time = time;
 
                     c.ex_stage_qty1 = this.ctx.helper.add(c.ex_stage_qty1, p.ex_stage_qty1);
                     c.ex_stage_tp1 = this.ctx.helper.add(c.ex_stage_tp1, p.ex_stage_tp1);
@@ -109,6 +113,10 @@ module.exports = app => {
                 } else {
                     c.used = !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty)
                         || !this.ctx.helper.checkZero(c.contract_tp) || ! this.ctx.helper.checkZero(c.qc_minus_qty);
+                    if (c.used) {
+                        c.used_time = time;
+                        c.update_time = time;
+                    }
                 }
                 const cp = curPc.find(x => { return x.lid === c.lid; });
                 if (cp) {

+ 10 - 1
app/service/stage_pos_final.js

@@ -59,6 +59,7 @@ module.exports = app => {
             }
             const cur = await this.ctx.service.stagePos.getLastestStageData2(tender.id, stage.id);
             const pre = stage.order > 1 ? await this.getFinalData(tender, stage.order - 1) : [];
+            const time = new Date();
             if ((!cur || cur.length === 0) && (!pre || pre.length === 0)) return;
             for (const c of cur) {
                 delete c.id;
@@ -76,11 +77,19 @@ module.exports = app => {
                     c.qc_minus_qty = this.ctx.helper.add(c.qc_minus_qty, p.qc_minus_qty);
                     c.positive_qc_qty = this.ctx.helper.add(c.positive_qc_qty, p.positive_qc_qty);
                     c.negative_qc_qty = this.ctx.helper.add(c.negative_qc_qty, p.negative_qc_qty);
-                    c.used = p.used || !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty) || !this.ctx.helper.checkZero(c.qc_minus_qty);
+                    const curUsed = !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty) || !this.ctx.helper.checkZero(c.qc_minus_qty);
+                    c.used = p.used || curUsed;
+                    if (c.used) c.used_time = p.used && p.used_time ? p.used_time : time;
+                    if (curUsed) c.update_time = time;
+
                     c.ex_stage_qty1 = this.ctx.helper.add(c.ex_stage_qty1, p.ex_stage_qty1);
                     pre.splice(pre.indexOf(p), 1);
                 } else {
                     c.used = !this.ctx.helper.checkZero(c.contract_qty) || !this.ctx.helper.checkZero(c.qc_qty) || !this.ctx.helper.checkZero(c.qc_minus_qty);
+                    if (c.used) {
+                        c.used_time = time;
+                        c.update_time = time;
+                    }
                 }
             }
             for (const p of pre) {

+ 1 - 1
app/service/sub_proj_permission.js

@@ -79,7 +79,7 @@ module.exports = app => {
                         '5、查看所有合同:未授权节点情况下可以查看包括其他单位人员添加的合同',
                         '注:查看合同第3、4、5必须选择其一,否则无法查看本项目合同管理',
                     ] },
-                { key: 'file', name: '资料归集', field: 'file_permission' },
+                { key: 'file', name: '资料管理', field: 'file_permission' },
                 { key: 'budget', name: '动态投资', field: 'budget_permission' },
                 {
                     key: 'financial', name: '资金监管', children: [

+ 8 - 7
app/service/tender_info.js

@@ -263,7 +263,7 @@ module.exports = app => {
                     const contract_tp = this.ctx.helper.mul(b.unit_price, sb.contract_qty, decimal.tp);
                     const qc_tp = this.ctx.helper.mul(b.unit_price, sb.qc_qty, decimal.tp);
                     const positive_qc_tp = this.ctx.helper.mul(b.unit_price, sb.positive_qc_qty, decimal.tp);
-                    const negative_qc_tp = this.ctx.helper.mul(b.unit_price, sb.positive_qc_qty, decimal.tp);
+                    const negative_qc_tp = this.ctx.helper.mul(b.unit_price, sb.negative_qc_qty, decimal.tp);
                     const ex_stage_tp1 = this.ctx.helper.mul(b.unit_price, sb.ex_stage_qty1, decimal.tp);
                     if (contract_tp == sb.contract_tp && qc_tp === sb.qc_tp && ex_stage_tp1 === sb.ex_stage_tp1
                         && positive_qc_tp === sb.positive_qc_tp && negative_qc_tp === sb.negative_qc_tp) continue;
@@ -278,8 +278,8 @@ module.exports = app => {
                             times: stage.times, order: 0,
                             contract_qty: sb.contract_qty, contract_expr: sb.contract_expr, contract_tp,
                             qc_qty: sb.qc_qty, qc_tp, qc_minus_qty: sb.qc_minus_qty,
-                            positive_qc_qty: sb.positive_qc_qty, positive_qc_tp: sb.positive_qc_tp,
-                            negative_qc_qty: sb.negative_qc_qty, negative_qc_tp: sb.negative_qc_tp,
+                            positive_qc_qty: sb.positive_qc_qty, positive_qc_tp: positive_qc_tp,
+                            negative_qc_qty: sb.negative_qc_qty, negative_qc_tp: negative_qc_tp,
                             ex_stage_qty1: sb.ex_stage_qty1, ex_stage_tp1,
                             postil: sb.postil,
                         });
@@ -312,9 +312,10 @@ module.exports = app => {
                     const contract_tp = preSb ? this.ctx.helper.sub(end_contract_tp, preSb.contract_tp) : end_contract_tp;
                     const qc_tp = this.ctx.helper.mul(b.unit_price, sb.qc_qty, decimal.tp);
                     const positive_qc_tp = this.ctx.helper.mul(b.unit_price, sb.positive_qc_qty, decimal.tp);
-                    const negative_qc_tp = this.ctx.helper.mul(b.unit_price, sb.positive_qc_qty, decimal.tp);
+                    const negative_qc_tp = this.ctx.helper.mul(b.unit_price, sb.negative_qc_qty, decimal.tp);
                     const ex_stage_tp1 = this.ctx.helper.mul(b.unit_price, sb.ex_stage_qty1, decimal.tp);
-                    if (contract_tp == sb.contract_tp && qc_tp === sb.qc_tp && ex_stage_tp1 === sb.ex_stage_tp1) continue;
+                    if (contract_tp == sb.contract_tp && qc_tp === sb.qc_tp && ex_stage_tp1 === sb.ex_stage_tp1
+                        && positive_qc_tp === sb.positive_qc_tp && negative_qc_tp === sb.negative_qc_tp) continue;
 
                     if (sb.times === stage.times && sb.order === 0) {
                         updateStageBills.push({
@@ -326,8 +327,8 @@ module.exports = app => {
                             times: stage.times, order: 0,
                             contract_qty: sb.contract_qty, contract_expr: sb.contract_expr, contract_tp,
                             qc_qty: sb.qc_qty, qc_tp, qc_minus_qty: sb.qc_minus_qty,
-                            positive_qc_qty: sb.positive_qc_qty, positive_qc_tp: sb.positive_qc_tp,
-                            negative_qc_qty: sb.negative_qc_qty, negative_qc_tp: sb.negative_qc_tp,
+                            positive_qc_qty: sb.positive_qc_qty, positive_qc_tp: positive_qc_tp,
+                            negative_qc_qty: sb.negative_qc_qty, negative_qc_tp: negative_qc_tp,
                             ex_stage_qty1: sb.ex_stage_qty1, ex_stage_tp1,
                             postil: sb.postil,
                         });

+ 16 - 1
app/view/budget/compare.ejs

@@ -34,8 +34,11 @@
                 </div>
                 <div class="d-inline-block ml-2" id="final-info">
                 </div>
+                <div class="d-inline-block ml-3">
+                    <button class="btn btn-sm btn-primary" id="export-excel">导出Excel</button>
+                </div>
             </div>
-            <div class="ml-auto">
+            <div class="ml-auto ml-grid">
                 <div class="d-inline-block">
                     <div class="dropdown">
                         <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dp-stackedBar" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
@@ -88,6 +91,17 @@
                     </div>
                 </div>
             </div>
+            <div class="ml-auto ml-number">
+                <div class="d-inline-block ml-2">
+                    <div class="alert text-warning p-1" id="compare-hint">增减幅度=(预估决算-设计概算)/设计概算*100%</div>
+                </div>
+                <div class="d-inline-block border-right">
+                    <button href="#compare-set" class="btn btn-sm btn-ligth text-primary" data-toggle="modal" data-target="#compare-set"><i class="fa fa-cog"></i></button>
+                </div>
+                <div class="d-inline-block">
+                    <button href="#row-view" class="btn btn-sm btn-ligth text-primary" data-toggle="modal" data-target="#row-view"><i class="fa fa-table"></i> 列显示</button>
+                </div>
+            </div>
         </div>
     </div>
     <div class="content-wrap">
@@ -102,4 +116,5 @@
 <script>
     const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
     const tenderList = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
+    const ckColSetting = 'budget-col-visible-1.0.0';
 </script>

+ 66 - 0
app/view/budget/compare_modal.ejs

@@ -20,4 +20,70 @@
             </div>
         </div>
     </div>
+</div>
+<!--列设置-->
+<div class="modal fade" id="row-view" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">列显示</h5>
+            </div>
+            <div class="modal-body">
+                <table class="table table-bordered table-sm">
+                    <thead>
+                    <tr><th>列项</th><th width="90">显示</th></tr>
+                    </thead>
+                    <tbody id="row-view-list">
+                    <tr><td>投资估算</td><td><input type="checkbox" checked=""></td></tr>
+                    <tr><td>设计概算</td><td><input type="checkbox" checked=""></td></tr>
+                    <tr><td>施工图预算</td><td><input type="checkbox" checked=""></td></tr>
+                    <tr><td>招标预算</td><td><input type="checkbox" checked=""></td></tr>
+                    <tr><td>台账</td><td><input type="checkbox" checked=""></td></tr>
+                    <tr><td>预估决算</td><td><input type="checkbox"></td></tr>
+                    </tbody>
+                </table>
+            </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="row-view-ok">确认</button>
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal fade" id="compare-set" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">设置</h5>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label class="mb-2">增减幅度</label>
+                    <div class="col-6 mb-2">
+                        <div class="input-group input-group-sm">
+                            <div class="input-group-prepend">
+                                <span class="input-group-text" style="width:80px">对比阶段1</span>
+                            </div>
+                            <select class="form-control form-control-sm" id="compare1">
+                            </select>
+                        </div>
+                    </div>
+                    <div class="col-6 mb-2">
+                        <div class="input-group input-group-sm">
+                            <div class="input-group-prepend">
+                                <span class="input-group-text" style="width:80px">对比阶段2</span>
+                            </div>
+                            <select class="form-control form-control-sm" id="compare2">
+                            </select>
+                        </div>
+                    </div>
+                    <div class="alert-warning ml-3 p-1 pl-2" id="compare-active-hint">增减幅度=(预估决算-设计概算)/设计概算*100%</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="compare-set-ok">确认</button>
+            </div>
+        </div>
+    </div>
 </div>

+ 2 - 2
app/view/change/apply_modal.ejs

@@ -113,7 +113,7 @@
                                     <% for (const rule of codeRule) { %>
                                         <% preview.push(rule.preview); %>
                                     <% } %>
-                                    <%- preview.join(tender.c_connector !== null && tender.c_connector !== '3' ? ruleConst.connectorString[tender.c_connector] : ''); %>
+                                    <%- preview.join(c_connector !== null && c_connector !== '3' ? ruleConst.connectorString[c_connector] : ''); %>
                                 <% } %>
                             </span>
                         </h5>
@@ -134,7 +134,7 @@
                             <select class="form-control form-control-sm connector-change">
                                 <option disabled selected>请选择</option>
                                 <% for (const index in ruleConst.connectorString) { %>
-                                    <option value="<%- index %>" <% if (tender.c_connector !== null && tender.c_connector === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
+                                    <option value="<%- index %>" <% if (c_connector !== null && parseInt(c_connector) === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
                                 <% } %>
                             </select>
                         </div>

+ 5 - 0
app/view/change/information.ejs

@@ -15,6 +15,11 @@
                         </div>
                     </div>
                 </div>
+                <% if (ctx.change.readOnly && (ctx.helper._.findIndex(ctx.change.auditors, { uid: ctx.session.sessionUser.accountId }) !== -1 || ctx.session.sessionUser.is_admin)) { %>
+                <div class="d-inline-block ml-auto">
+                    <a href="javascript: void(0);" class="btn btn-outline-primary btn-sm mr-2" id="checkAndChange">检查并更新变更清单</a>
+                </div>
+                <% } %>
                 <div class="pull-right mr-3" id="sp-btn">
                     <% if (ctx.change.cancancel) { %>
                         <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm mr-2">撤回</a>

+ 2 - 2
app/view/change/plan_modal.ejs

@@ -113,7 +113,7 @@
                                     <% for (const rule of codeRule) { %>
                                         <% preview.push(rule.preview); %>
                                     <% } %>
-                                    <%- preview.join(tender.c_connector !== null && tender.c_connector !== '3' ? ruleConst.connectorString[tender.c_connector] : ''); %>
+                                    <%- preview.join(c_connector !== null && c_connector !== '3' ? ruleConst.connectorString[c_connector] : ''); %>
                                 <% } %>
                             </span>
                         </h5>
@@ -134,7 +134,7 @@
                             <select class="form-control form-control-sm connector-change">
                                 <option disabled selected>请选择</option>
                                 <% for (const index in ruleConst.connectorString) { %>
-                                    <option value="<%- index %>" <% if (tender.c_connector !== null && tender.c_connector === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
+                                    <option value="<%- index %>" <% if (c_connector !== null && parseInt(c_connector) === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
                                 <% } %>
                             </select>
                         </div>

+ 2 - 2
app/view/change/project_modal.ejs

@@ -113,7 +113,7 @@
                                     <% for (const rule of codeRule) { %>
                                         <% preview.push(rule.preview); %>
                                     <% } %>
-                                    <%- preview.join(tender.c_connector !== null && tender.c_connector !== '3' ? ruleConst.connectorString[tender.c_connector] : ''); %>
+                                    <%- preview.join(c_connector !== null && c_connector !== '3' ? ruleConst.connectorString[c_connector] : ''); %>
                                 <% } %>
                             </span>
                         </h5>
@@ -134,7 +134,7 @@
                             <select class="form-control form-control-sm connector-change">
                                 <option disabled selected>请选择</option>
                                 <% for (const index in ruleConst.connectorString) { %>
-                                    <option value="<%- index %>" <% if (tender.c_connector !== null && tender.c_connector === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
+                                    <option value="<%- index %>" <% if (c_connector !== null && parseInt(c_connector) === parseInt(index)) { %>selected<% } %>><%- ruleConst.connectorString[index] %></option>
                                 <% } %>
                             </select>
                         </div>

+ 28 - 8
app/view/contract/detail.ejs

@@ -45,16 +45,27 @@
             </div>
         </div>
     </div>
-    <div class="content-wrap row">
-        <div class="c-header p-0 col-12"></div>
-        <!--核心内容(两栏)-->
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
         <div class="row w-100 sub-content">
-            <!--左栏-->
-            <div class="c-body" id="left-view" style="width: 100%">
-                <!--0号台账模式-->
-                <div class="sjs-height-1" style="overflow: hidden" id="contract-spread">
+            <div class="col-12 c-body">
+                <!--上部分-->
+                <div class="sjs-height-1 row w-100 sub-content">
+                    <!--左栏-->
+                    <div class="c-body" id="left-view" style="width: 100%">
+                        <div class="sjs-height-1" style="overflow: hidden" id="contract-spread"></div>
+                    </div>
+                    <div class="c-body" id="right-view" style="display: none; width: 30%">
+                        <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                        <div class="tab-content">
+                            <div id="std-xmj" class="tab-pane">
+                            </div>
+                        </div>
+                    </div>
                 </div>
-                <div class="bcontent-wrap">
+                <!--下部分-->
+                <div class="bcontent-wrap mt-1">
                     <div id="contract-resize" class="resize-y" id="top-spr" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
                     <div class="bc-bar mb-1">
                         <ul class="nav nav-tabs">
@@ -186,6 +197,14 @@
                     </div>
                 </div>
             </div>
+            <div class="side-menu">
+                <!--右侧菜单-->
+                <ul class="nav flex-column right-nav">
+                    <li class="nav-item">
+                        <a class="nav-link" content="#std-xmj" href="javascript: void(0);">项目节</a>
+                    </li>
+                </ul>
+            </div>
         </div>
     </div>
 </div>
@@ -200,5 +219,6 @@
     const contractConst = JSON.parse(unescape('<%- escape(JSON.stringify(contractConst)) %>'));
     let contractTreeAudits = JSON.parse(unescape('<%- escape(JSON.stringify(contractTreeAudits)) %>'));
     const thisUrl = JSON.parse(unescape('<%- escape(JSON.stringify(thisUrl)) %>'));
+    const stdChapters = JSON.parse(unescape('<%- escape(JSON.stringify(stdChapters)) %>'));
     let contractPays = [];
 </script>

+ 14 - 0
app/view/dashboard/workspace.ejs

@@ -630,6 +630,20 @@
             },
             formatter: function (p) {
                 _param = p
+                let html = `<div style="line-height:20px;">`;  // 行距控制
+                html += `<div style="font-weight:bold;margin-bottom:4px;">${p[0].axisValue}</div>`;
+
+                p.forEach(item => {
+                    html += `
+              <div style="display:flex; justify-content:space-between; align-items:center;">
+                <span>${item.marker}${item.seriesName}</span>
+                <span style="margin-left:12px; text-align:right; min-width:80px;">${item.value}</span>
+              </div>
+            `;
+                });
+
+                html += `</div>`;
+                return html;
             }
         },
         legend: {},

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

@@ -1,7 +1,7 @@
 <div class="panel-content">
     <div class="panel-title fluid">
         <div class="title-main  d-flex justify-content-between">
-            <div>资料归集/<%- ctx.subProject.name %><span class="ml-4" id="file-count"></span></div>
+            <div>资料管理/<%- ctx.subProject.name %><span class="ml-4" id="file-count"></span></div>
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.is_admin) { %>
                 <a href="/sp/<%- ctx.subProject.id %>/fm" class="btn btn-outline-primary btn-sm ml-1">管理分类</a>

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

@@ -1,7 +1,7 @@
 <div class="panel-content">
     <div class="panel-title fluid">
         <div class="title-main  d-flex justify-content-between">
-            <div>资料归集</div>
+            <div>资料管理</div>
             <div class="d-inline-block ml-1" id="show-level"></div>
             <div class="ml-auto"></div>
         </div>

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

@@ -2,7 +2,7 @@
     <div class="panel-title fluid">
         <div class="title-main d-flex justify-content-between">
             <div class="d-flex">
-                资料归集/<%- ctx.subProject.name %>
+                资料管理/<%- ctx.subProject.name %>
                 (当前状态:<span class="<%- ctx.subProject.lock_file ? 'text-success' : 'text-warning'%>"><%- ctx.subProject.lock_file ? '锁定' : '未锁定'%></span>)
                 <div class="d-flex">
                     <div class="alert alert-warning p-1 mt-1"><i class="fa Example of exclamation-circle fa-exclamation-circle "></i> 请先锁定再编辑;锁定时其他人不可操作资料归集,仅可查看;编辑完成后需解锁。</div>

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

@@ -1,7 +1,7 @@
 <div class="panel-content">
     <div class="panel-title fluid">
         <div class="title-main  d-flex justify-content-between">
-            <div>资料归集模板库</div>
+            <div>资料管理模板库</div>
         </div>
     </div>
     <div class="content-wrap">

+ 1 - 1
app/view/financial/pay_list.ejs

@@ -7,7 +7,7 @@
                 <div class="btn-group group-tab">
                     <a class="btn btn-sm btn-light account-page-size" href="/sp/<%- ctx.subProject.id %>/financial/pay/stage">支付列表</a>
                     <a class="btn btn-sm btn-light active" href="javascript:void(0);">汇总列表</a>
-                    <a class="btn btn-sm btn-light" href="/financial/pay">标段统计</a>
+                    <a class="btn btn-sm btn-light" href="/sp/<%- ctx.subProject.id %>/financial/pay/tender">标段统计</a>
                 </div>
                 <div class="d-inline-block col-sm-2">
                     <div class="input-group input-group-sm pr-1">

+ 1 - 1
app/view/financial/pay_stage.ejs

@@ -7,7 +7,7 @@
                 <div class="btn-group group-tab">
                     <a class="btn btn-sm btn-light active" href="javascript:void(0);">支付列表</a>
                     <a class="btn btn-sm btn-light account-page-size" href="/sp/<%- ctx.subProject.id %>/financial/pay/list">汇总列表</a>
-                    <a class="btn btn-sm btn-light" href="/financial/pay">标段统计</a>
+                    <a class="btn btn-sm btn-light" href="/sp/<%- ctx.subProject.id %>/financial/pay/tender">标段统计</a>
                 </div>
                 <div class="d-inline-block col-sm-3">
                     <div class="input-group input-group-sm pr-1">

+ 2 - 2
app/view/financial/pay_stage_modal.ejs

@@ -106,7 +106,7 @@
                         <div class="p-2">标段列表</div>
                         <div id="shenpi-tender-list"></div>
                     </div>
-                    <div class="col-4">
+                    <div class="col-4" style="min-height: 400px;max-height: 700px;overflow:auto;">
                         <div class="d-flex flex-row bg-graye">
                             <div class="px-2 pt-1 dropdown">
                                 <button class="btn btn-outline-primary btn-sm dropdown-toggle" type="button" id="report_audit_dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@@ -141,7 +141,7 @@
                             </tbody>
                         </table>
                     </div>
-                    <div class="col-4">
+                    <div class="col-4" style="min-height: 400px;max-height: 700px;overflow:auto;">
                         <div class="d-flex flex-row bg-graye">
                             <div class="p-2"><a href="javascript:void(0);" id="set-other-tenders">设置流程至其他已勾选标段</a></div>
                         </div>

+ 103 - 0
app/view/financial/pay_tender.ejs

@@ -0,0 +1,103 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main  d-flex">
+            <% include ./sub_mini_menu.ejs %>
+            <div class="col-11 pl-0">
+                <div class="btn-group group-tab">
+                    <a class="btn btn-sm btn-light account-page-size" href="/sp/<%- ctx.subProject.id %>/financial/pay/stage">支付列表</a>
+                    <a class="btn btn-sm btn-light" href="/sp/<%- ctx.subProject.id %>/financial/pay/list">汇总列表</a>
+                    <a class="btn btn-sm btn-light active" href="javascript:void(0);">标段统计</a>
+                </div>
+            </div>
+            <div class="d-inline-block ml-auto">
+<!--                <a href="#batch-sp" data-toggle="modal" data-target="#batch-sp" class="btn btn-success btn-sm pull-right mr-2">批量审批</a>-->
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="d-flex sjs-height-0" style="background-color: #fff">
+            <div class="col-4">
+                <div class="d-flex my-2">
+                    <div class="d-inline-block ml-1" id="show-level"></div>
+                </div>
+                <div class="sjs-height-7" style="overflow: auto;">
+                    <div class="c-body"></div>
+                </div>
+            </div>
+            <div class="col">
+                <div class="d-flex my-2">
+                    <div class="col-sm-2 pl-0">
+                        <div class="input-group input-group-sm pr-1">
+                            <select class="form-control form-control-sm col-auto" id="qi_select">
+                                <option value="0">筛选期数</option>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="">
+                        <div class="input-group input-group-sm pr-1">
+                            <div class="btn-group">
+                                <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="used_selected" data-value="">资金用途:全部</button>
+                                <div class="dropdown-menu" aria-labelledby="used_selected" id="used_select">
+                                    <% for (const u of usedList) { %>
+                                        <a class="dropdown-item to-log-link" href="javascript:void(0)" data-val="<%- u %>"><%- u %></a>
+                                    <% } %>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="">
+                        <div class="input-group input-group-sm pr-1">
+                            <div class="btn-group">
+                                <button type="button" class="btn btn-sm btn-light text-primary dropdown-toggle" data-toggle="dropdown" id="status_selected" data-value="">审批状态:全部</button>
+                                <div class="dropdown-menu" aria-labelledby="status_selected" id="status_select">
+                                    <% for (const fs in filter.status) { %>
+                                        <% const f = filter.status[fs]; %>
+                                        <a class="dropdown-item to-log-link" data-val="<%- f %>" href="javascript:void(0);"><%- filter.statusString[f] %>()</a>
+                                    <% } %>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="ml-auto">
+<!--                        <a href="#batch-sp" data-toggle="modal" data-target="#batch-sp" class="btn btn-success btn-sm pull-right mr-2">批量审批</a>-->
+                    </div>
+                </div>
+                <div class="sjs-height-7" style="overflow: auto;">
+                    <table class="table table-bordered text-center">
+                        <thead>
+                        <tr>
+                            <th width="100px">关联期数</th>
+                            <th width="200px">支付编号</th>
+                            <th width="150px">申请时间</th>
+                            <th width="100px">申请人</th>
+                            <th width="200px">资金用途</th>
+                            <th width="300px">收款单位</th>
+                            <th width="150px">支付金额</th>
+                            <th width="200px">审批进度</th>
+                            <th width="150px">操作</th>
+                        </tr>
+                        </thead>
+                        <tbody id="pay-list">
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const user_id = <%- ctx.session.sessionUser.accountId %>;
+    const is_admin = <%- ctx.session.sessionUser.is_admin %>;
+    const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenders)) %>'));
+    const pays = JSON.parse(unescape('<%- escape(JSON.stringify(allPays)) %>'));
+    const fptReportTids = JSON.parse(unescape('<%- escape(JSON.stringify(fptReportTids)) %>'));
+    const auditConst = JSON.parse(unescape('<%- escape(JSON.stringify(auditConst)) %>'));
+    const auditType = JSON.parse(unescape('<%- escape(JSON.stringify(auditType)) %>'));
+    const filter = JSON.parse(unescape('<%- escape(JSON.stringify(filter)) %>'));
+    const selfCategoryLevel = '';
+    const pid = '<%- ctx.session.sessionProject.id %>';
+    const subProid = '<%- ctx.subProject.id %>';
+    const uphlname = 'user_' + user_id + '_pro_' + pid + '_sub_' + subProid + '_pay_tender_category_hide_list';
+</script>

+ 5 - 1
app/view/financial/transfer.ejs

@@ -35,7 +35,11 @@
                     <% for (const [i, t] of transferList.entries()) { %>
                     <tr class="text-center" data-id="<%- t.id %>">
                         <td class=""><%- (transferList.length - i) %></td>
-                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender"><%- t.t_time %></a></td>
+                        <td class=""><a href="/sp/<%- ctx.subProject.id %>/financial/transfer/<%- t.id %>/tender"><%- t.t_time %></a>
+                            <% if (ctx.session.sessionUser.is_admin) { %>
+                                <a href="#edit" class="edit-time" data-ftid="<%- t.id %>" data-time="<%- t.t_time %>" data-toggle="modal" data-target="#edit"><i class="fa fa-pencil-square-o "></i></a>
+                            <% } %>
+                        </td>
                         <td class="text-right"><%- t.total_price %></td>
                         <td class="text-right"><%- t.pre_hb_tp %></td>
                         <td class="text-right"><%- ctx.helper.add(t.total_price, t.pre_hb_tp) %></td>

+ 23 - 0
app/view/financial/transfer_modal.ejs

@@ -29,6 +29,29 @@
         </form>
     </div>
 </div>
+<% if (ctx.session.sessionUser.is_admin) { %>
+<div class="modal fade" id="edit" 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>划拨年月</label>
+                    <input class="datepicker-here form-control form-control-sm" name="date" id="edit-transfer-date" readonly placeholder="点击选择划拨年月" data-view="months" data-min-view="months" data-date-format="yyyy-MM" data-language="zh" type="text">
+                </div>
+            </div>
+            <div class="modal-footer">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>" />
+                <input type="hidden" name="ftid" id="edit-ftid" value="">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary btn-sm" id="edit-ok" >确定修改</button>
+            </div>
+        </div>
+    </div>
+</div>
+<% } %>
 <% } %>
 <!--附件-->
 <div class="modal fade" id="transfer-file" data-backdrop="static" style="z-index: 1049">

+ 4 - 4
app/view/quality/sub_memu_list.ejs

@@ -1,4 +1,4 @@
-<nav-menu title="返回" url="/quality" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
-<nav-menu title="工程资料" url="/quality/tender/<%= ctx.tender.id %>/info%>" ml="3" active="<%= ctx.url.indexOf('/info') %>"></nav-menu>
-<!--<nav-menu title="缺陷管理" url="/quality/tender/<%= ctx.tender.id %>/flaw%>" ml="3" active="<%= ctx.url.indexOf('/flaw') %>"></nav-menu>-->
-<!--<nav-menu title="试验报告" url="/quality/tender/<%= ctx.tender.id %>/lab%>" ml="3" active="<%= ctx.url.indexOf('/lab') %>"></nav-menu>-->
+<nav-menu title="返回" url="/sp/<%- ctx.subProject.id %>/quality/tender" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
+<nav-menu title="工程资料" url="/sp/<%- ctx.subProject.id %>/quality/tender/<%= ctx.tender.id %>/info%>" ml="3" active="<%= ctx.url.indexOf('/info') %>"></nav-menu>
+<!--<nav-menu title="缺陷管理" url="/sp/<%- ctx.subProject.id %>/quality/tender/<%= ctx.tender.id %>/flaw%>" ml="3" active="<%= ctx.url.indexOf('/flaw') %>"></nav-menu>-->
+<!--<nav-menu title="试验报告" url="/sp/<%- ctx.subProject.id %>/quality/tender/<%= ctx.tender.id %>/lab%>" ml="3" active="<%= ctx.url.indexOf('/lab') %>"></nav-menu>-->

+ 28 - 8
app/view/quality/tender.ejs

@@ -1,18 +1,18 @@
+<% include ../tender/list_sub_menu.ejs %>
 <div class="panel-content">
     <div class="panel-title fluid">
         <div class="title-main  d-flex justify-content-between">
-            <div>
-                <div class="d-inline-block mr-2">
-                    <button type="button" class="btn btn-sm btn-light dropdown-toggle text-primary" data-toggle="dropdown">展开/收起</button>
-                    <div class="dropdown-menu">
-                        <a class="dropdown-item tree-toggle" href="javascript:void(0);" data-item="open">展开所有</a>
-                        <a class="dropdown-item tree-toggle" href="javascript:void(0);" data-item="hide">收起所有</a>
-                    </div>
+            <% include ../tender/list_sub_mini_menu.ejs %>
+            <div class="d-inline-block mr-2">
+                <button type="button" class="btn btn-sm btn-light dropdown-toggle text-primary" data-toggle="dropdown">展开/收起</button>
+                <div class="dropdown-menu">
+                    <a class="dropdown-item tree-toggle" href="javascript:void(0);" data-item="open">展开所有</a>
+                    <a class="dropdown-item tree-toggle" href="javascript:void(0);" data-item="hide">收起所有</a>
                 </div>
             </div>
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.is_admin) { %>
-                <a class="btn btn-sm btn-primary mr-2" href="/quality/rule">设置状态规则</a>
+                <a class="btn btn-sm btn-primary mr-2" href="/sp/<%- ctx.subProject.id %>/quality/rule">设置状态规则</a>
                 <% } %>
             </div>
         </div>
@@ -27,8 +27,28 @@
 <script>
     const tenders = JSON.parse(unescape('<%- escape(JSON.stringify(tenderList)) %>'));
     const category = JSON.parse(unescape('<%- escape(JSON.stringify(categoryData)) %>'));
+    const selfCategoryLevel = '<%- (selfCategoryLevel || '') %>';
     const is_admin = <%- ctx.session.sessionUser.is_admin %>;
 
     const pid = '<%- ctx.session.sessionProject.id %>';
     const uphlname = 'user_<%- ctx.session.sessionUser.accountId %>_pro_<% ctx.session.sessionProject.id %>_category_hide_list';
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'list.menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+                $('.c-body table thead').css('left', '56px');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+                $('.c-body table thead').css('left', '176px');
+            }
+            autoFlashHeight();
+        }
+    });
 </script>

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

@@ -181,6 +181,7 @@
     const can_netcasign = <%- can_netcasign %>;
     const oss_path = JSON.parse('<%- JSON.stringify(ossPath) %>');
     let signLogList = JSON.parse('<%- JSON.stringify(signLogList) %>');
+    let CURRENT_SELECTED_BIZ_TYPE = '';
 
     const FOLDER_SEPERATER = '->';
 

+ 17 - 13
app/view/revise/index.ejs

@@ -45,26 +45,27 @@
                 <!--修订中-->
                 <table class="table table-bordered">
                     <thead>
-                    <tr>
-                        <th></th>
-                        <th>创建时间</th>
-                        <th>提交人</th>
-                        <th>状态</th>
-                        <th>完成时间</th>
-                        <th>操作</th>
+                    <tr class="text-center">
+                        <th width="60px"></th>
+                        <th width="150px">创建时间</th>
+                        <th width="150px">提交人</th>
+                        <th width="150px">状态</th>
+                        <th width="150px">完成时间</th>
+                        <th>修订详情</th>
+                        <th width="200px">操作</th>
                     </tr>
                     </thead>
                     <% for (const lr of ledgerRevise) { %>
                     <tr>
-                        <td><%- lr.corder %></td>
-                        <td><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD HH:mm:ss') : '' %></td>
-                        <td><%- lr.user_name %></td>
+                        <td class="text-center"><%- lr.corder %></td>
+                        <td class="text-center"><%- lr.in_time ? ctx.moment(lr.in_time).format('YYYY-MM-DD HH:mm:ss') : '' %></td>
+                        <td class="text-center"><%- lr.user_name %></td>
                         <% if (!lr.valid) {%>
-                        <td class="text-danger">
+                        <td class="text-danger text-center">
                             作废
                         </td>
                         <% } else { %>
-                        <td class="<%- auditConst.auditProgressClass[lr.status] %>">
+                        <td class="<%- auditConst.auditProgressClass[lr.status] %> text-center">
                             <% if (lr.curAuditor) { %>
                             <% const curAuditor = lr.curAuditors[0]; %>
                             <% if (curAuditor.audit_type === auditType.key.common) { %>
@@ -76,7 +77,10 @@
                             <%- auditConst.auditProgress[lr.status] %>
                         </td>
                         <% } %>
-                        <td><%- lr.end_time ? ctx.moment(lr.end_time).format('YYYY-MM-DD') : '' %></td>
+                        <td class="text-center"><%- lr.end_time ? ctx.moment(lr.end_time).format('YYYY-MM-DD') : '' %></td>
+                        <td style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; positive: relative; max-width: 450px" title="<%- lr.content %>">
+                            <%- lr.content %>
+                        </td>
                         <td>
                             <% if (lr.valid) { %>
                             <% if (lr.status === auditConst.status.uncheck && lr.uid === ctx.session.sessionUser.accountId) { %>

+ 5 - 3
app/view/setting/s2b.ejs

@@ -198,9 +198,11 @@
 <script>
     const limitList = JSON.parse('<%- JSON.stringify(limitList) %>');
     const locInfo = {
-        gxby_status: { check: 'num', field: 'gxby_status', alias: '工序报验'},
-        dagl_status: { check: 'num', field: 'dagl_status', alias: '档案管理'},
-        gxby_date_ago: { check: 'date', field: 'gxby_date', alias: '完工日期 距今'},
+        gxby_status: { check: 'num', field: 'gxby_status', alias: '工序报验', source: '3f'},
+        dagl_status: { check: 'num', field: 'dagl_status', alias: '档案管理', source: '3f'},
+        gxby_date_ago: { check: 'date', field: 'gxby_date', alias: '完工日期 距今', source: '3f'},
+        pre_used_date_ago: { check: 'date', field: 'pre_used_time', alias: '首次计量日期 距今', source: 'jl'},
+        pre_update_date_ago: { check: 'date', field: 'pre_update_time', alias: '最后计量日期 距今', source: 'jl'},
     }
     const updateStatusLimit = function (obj, type) {
         const data = { type };

+ 2 - 0
app/view/setting/s2b_modal.ejs

@@ -42,6 +42,8 @@
                                 <option value="gxby_status">工序报验</option>
                                 <option value="dagl_status">档案管理</option>
                                 <!--<option value="gxby_date_ago">完工日期</option>-->
+                                <option value="pre_used_date_ago">首次计量日期</option>
+                                <option value="pre_update_date_ago">最后计量日期</option>
                             </select>
                         </div>
                         <div class="d-inline-block ml-2">

+ 1 - 1
app/view/sp_setting/permission.ejs

@@ -32,7 +32,7 @@
                                     <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=datacollect<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action <%- (ptype === 'datacollect' ? 'active' : '') %>">决策大屏</a>
                                     <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=info<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action <%- (ptype === 'info' ? 'active' : '') %>">项目概况</a>
                                     <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=contract<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action  <%- (ptype === 'contract' ? 'active' : '') %>">合同管理</a>
-                                    <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=file<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action  <%- (ptype === 'file' ? 'active' : '') %>">资料归集</a>
+                                    <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=file<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action  <%- (ptype === 'file' ? 'active' : '') %>">资料管理</a>
                                     <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=budget<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action  <%- (ptype === 'budget' ? 'active' : '') %>">动态投资</a>
                                     <dd class="list-group-item">资金监管</dd>
                                     <a href="/sp/<%- ctx.subProject.id %>/setting/user/permission?ptype=fund_trans<%- (keyword ? '&keyword=' + keyword : '')%>" class="list-group-item list-group-item-action  <%- (ptype === 'fund_trans' ? 'active' : '') %>"><span class="ml-4"></span>资金划拨</a>

+ 1 - 1
app/view/spss/check_tz.ejs

@@ -38,7 +38,7 @@
             <div class="sjs-height-1" id="bills-spread">
             </div>
             <div class="bcontent-wrap" id="main-bottom">
-                <div id="main-resize" class="resize-y"  r-Type="height" div1="#bills-spread" div2="#main-bottom" store-id="compare-main" store-version="1.0.0" min="100"></div>
+                <div id="main-resize" class="resize-y"  r-Type="height" div1="#bills-spread" div2="#main-bottom" store-id="check-tz" store-version="1.0.0" min="100"></div>
                 <div class="bc-bar mb-1">
                     <ul class="nav nav-tabs">
                         <li class="nav-item">

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

@@ -154,7 +154,7 @@
                             <th rowspan="2" class="align-middle">成员名称</th>
                             <th rowspan="2" class="align-middle">职位</th>
                             <th colspan="2">动态投资</th>
-                            <th colspan="4">资料归集</th>
+                            <th colspan="4">资料管理</th>
                             <th rowspan="2" class="align-middle">关联标段</th>
                             <th rowspan="2" class="align-middle">移除</th>
                         </tr>

+ 9 - 0
app/view/tender/list_sub_menu_list.ejs

@@ -23,6 +23,15 @@
     </ul>
 </div>
 <% } %>
+<% if (ctx.subProject.page_show.quality) { %>
+<div class="nav-box">
+    <ul class="nav-list list-unstyled">
+        <li class="<% if (ctx.url === '/sp/' + ctx.subProject.id + '/quality/tender') { %>active<% } %>">
+            <a href="/sp/<%- ctx.subProject.id %>/quality/tender"><span class="ml-3">质量管理</span></a>
+        </li>
+    </ul>
+</div>
+<% } %>
 <% if (ctx.subProject.page_show.openConstruction) { %>
 <div class="nav-box">
     <ul class="nav-list list-unstyled">

+ 1 - 1
app/view/tender/tender_sub_menu.ejs

@@ -15,7 +15,7 @@
             </ul>
         </div>
         <div class="nav-box">
-            <h3><i class="fa fa-list-alt fa-fw"></i> 0号台账</h3>
+            <h3><i class="fa fa-list-alt fa-fw"></i> 台账管理</h3>
             <ul class="nav-list list-unstyled sub-list">
                 <li <% if (ctx.url === '/tender/' + ctx.tender.id + '/ledger') { %>class="active"<% } %>><a href="/tender/<%- ctx.tender.id %>/ledger"><span>台账分解</span></a></li>
                 <% if (ctx.tender.data.measure_type === 1 && ctx.subProject.page_show.bwtz) { %>

+ 2 - 2
config/menu.js

@@ -111,11 +111,11 @@ const menu = {
         notIncludedUrl: ['/contract/tender'],
     },
     file: {
-        name: '资料归集',
+        name: '资料管理',
         icon: 'fa-file-zip-o',
         display: true,
         children: null,
-        caption: '资料归集',
+        caption: '资料管理',
         controller: 'file',
         controllers: ['file', 'fm'],
     },

+ 25 - 0
config/web.js

@@ -1450,13 +1450,16 @@ const JsFiles = {
                 files: [
                     '/public/js/moment/moment.min.js',
                     '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
                     '/public/js/decimal.min.js',
                     '/public/js/component/menu.js',
+                    '/public/js/file-saver/FileSaver.js',
                 ],
                 mergeFiles: [
                     '/public/js/sub_menu.js',
                     '/public/js/spreadjs_rela/spreadjs_zh.js',
                     '/public/js/shares/sjs_setting.js',
+                    '/public/js/shares/export_excel.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
                     '/public/js/shares/tenders2tree.js',
@@ -1960,6 +1963,27 @@ const JsFiles = {
                 ],
                 mergeFile: 'financial_pay_list',
             },
+            payTender: {
+                files: [
+                    '/public/js/decimal.min.js',
+                    '/public/js/math.min.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/moment/moment.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/PinYinOrder.bundle.js',
+                    '/public/js/colResizable/colResizable-1.6.min.js',
+                    '/public/js/shares/tender_list_order.js',
+                    '/public/js/shares/show_level.js',
+                    '/public/js/tender_showhide.js',
+                    '/public/js/tender_list_base.js',
+                    '/public/js/financial_pay_tender.js',
+                ],
+                mergeFile: 'financial_pay_tender',
+            },
             payDetail: {
                 files: [
                     '/public/js/decimal.min.js',
@@ -2147,6 +2171,7 @@ const JsFiles = {
                     '/public/js/moment/moment.min.js',
                 ],
                 mergeFiles: [
+                    '/public/js/sub_menu.js',
                     '/public/js/PinYinOrder.bundle.js',
                     '/public/js/shares/tender_list_order.js',
                     '/public/js/shares/show_level.js',

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1107 - 0
sql/update.sql