Browse Source

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang 5 năm trước cách đây
mục cha
commit
4748fe785c
72 tập tin đã thay đổi với 3036 bổ sung292 xóa
  1. 26 1
      app/controller/change_controller.js
  2. 2 3
      app/controller/ledger_audit_controller.js
  3. 3 0
      app/controller/material_controller.js
  4. 1 1
      app/controller/setting_controller.js
  5. 27 1
      app/controller/stage_controller.js
  6. 8 0
      app/controller/stage_extra_controller.js
  7. 259 0
      app/controller/wap_controller.js
  8. 25 1
      app/extend/helper.js
  9. 18 4
      app/lib/rpt_data_analysis.js
  10. 1 1
      app/lib/stage_im.js
  11. 17 7
      app/middleware/session_auth.js
  12. 5 1
      app/middleware/tender_check.js
  13. BIN
      app/public/css/logo.png
  14. 43 10
      app/public/css/main.css
  15. 11 11
      app/public/css/spreadjs/sheets/gc.spread.sheets.excelsmartcost.css
  16. 1 0
      app/public/css/undraw_all_the_data_h4ki.svg
  17. 1 0
      app/public/css/undraw_order_a_car_3tww.svg
  18. 1 0
      app/public/css/undraw_work_chat_erdt.svg
  19. 23 0
      app/public/css/wap/main.css
  20. BIN
      app/public/images/favicon.ico
  21. BIN
      app/public/images/loginlogo.png
  22. BIN
      app/public/images/logo.png
  23. 102 25
      app/public/js/ledger.js
  24. 4 4
      app/public/js/ledger_search.js
  25. 5 4
      app/public/js/material.js
  26. 21 4
      app/public/js/se_bonus.js
  27. 95 0
      app/public/js/shares/cs_tools.js
  28. 6 6
      app/public/js/tender_list.js
  29. 13 13
      app/public/js/tender_list_info.js
  30. 6 6
      app/public/js/tender_list_manage.js
  31. 1 1
      app/public/js/tender_list_progress.js
  32. 8 7
      app/public/js/tender_showhide.js
  33. 97 0
      app/public/js/wap/global.js
  34. 245 0
      app/public/js/wap/list.js
  35. 57 0
      app/public/js/wap/login.js
  36. 3 0
      app/public/report/js/rpt_custom.js
  37. 9 0
      app/router.js
  38. 13 0
      app/service/material.js
  39. 14 11
      app/service/report_memory.js
  40. 2 1
      app/service/rpt_gather_memory.js
  41. 14 0
      app/service/stage.js
  42. 30 4
      app/service/stage_audit.js
  43. 0 1
      app/service/stage_bills.js
  44. 1 1
      app/service/stage_bonus.js
  45. 3 1
      app/service/stage_pay.js
  46. 100 0
      app/view/change/info_modal.ejs
  47. 2 1
      app/view/layout/layout.ejs
  48. 106 0
      app/view/ledger/audit_modal.ejs
  49. 0 2
      app/view/ledger/bwtz.ejs
  50. 5 0
      app/view/ledger/explode.ejs
  51. 4 3
      app/view/ledger/explode_modal.ejs
  52. 66 54
      app/view/login/login.ejs
  53. 44 32
      app/view/login/login_port.ejs
  54. 106 0
      app/view/material/audit_modal.ejs
  55. 3 2
      app/view/material/info.ejs
  56. 82 0
      app/view/revise/info_modal.ejs
  57. 1 1
      app/view/setting/user_modal.ejs
  58. 86 0
      app/view/shares/check_data_modal.ejs
  59. 230 2
      app/view/stage/audit_modal.ejs
  60. 0 2
      app/view/stage/bwtz.ejs
  61. 1 1
      app/view/stage_extra/bonus_modal.ejs
  62. 87 0
      app/view/wap/dashboard.ejs
  63. 80 0
      app/view/wap/list.ejs
  64. 83 0
      app/view/wap/login.ejs
  65. 549 0
      app/view/wap/tender.ejs
  66. 14 0
      builder_report_index_define.js
  67. 2 0
      config/web.js
  68. 0 41
      sql/update.sql
  69. 9 1
      sql/update20200325.sql
  70. 44 0
      sql/update20200511.sql
  71. 3 0
      sql/update20200513.sql
  72. 108 20
      test/app/service/report_memory_temp.test.js

+ 26 - 1
app/controller/change_controller.js

@@ -83,7 +83,7 @@ module.exports = app => {
             // 分页相关
             const pageInfo = {
                 page,
-                total: parseInt(total / app.config.pageSize),
+                total: Math.ceil(total / app.config.pageSize),
                 queryData: JSON.stringify(ctx.urlInfo.query),
             };
 
@@ -419,6 +419,31 @@ module.exports = app => {
                         auditList3[i].max_sort = auditList2.length - 1;
                     }
                     renderData.auditList3 = auditList3;
+
+                    // 展示页右侧审批流程列表
+                    const auditList5 = await ctx.service.changeAudit.getListByBack(change.cid, change.times);
+                    const auditList4 = [];
+                    for (let time = 1; time <= change.times; time++) {
+                        const auditTimeList = [];
+                        let max_sort = 1;
+                        for (const al of auditList5) {
+                            if (al.times === time) {
+                                auditTimeList.push(al);
+                                if (al.usite > max_sort) {
+                                    max_sort = al.usite;
+                                }
+                            }
+                        }
+                        for (const i in auditTimeList) {
+                            auditTimeList[i].max_sort = max_sort;
+                        }
+                        if (auditTimeList.length > 0) {
+                            auditList4.push(auditTimeList);
+                        }
+                    }
+                    renderData.auditList4 = auditList4;
+                    console.log(auditList4)
+
                     changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     renderData.changeList = changeList;
                     let ototalCost = 0;

+ 2 - 3
app/controller/ledger_audit_controller.js

@@ -203,11 +203,10 @@ module.exports = app => {
 
                 await ctx.service.ledgerAudit.start(ctx.tender.id, ctx.tender.data.ledger_times);
 
-                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+                ctx.body = {err: 0, msg: '', data: {url: '/tender/' + ctx.tender.id + '/ledger'}}; //ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
             } catch (err) {
                 this.log(err);
-                ctx.session.postError = err.toString();
-                ctx.redirect('/tender/' + ctx.tender.id + '/ledger');
+                ctx.body = this.ajaxErrorBody(err, '上报失败,请刷新页面重试');
             }
         }
 

+ 3 - 0
app/controller/material_controller.js

@@ -260,6 +260,9 @@ module.exports = app => {
                 renderData.calcBase = await ctx.service.stage.getMaterialCalcBase(stage_list, ctx.tender.info);
                 // }
 
+                // 取当前期截止上期含税金额
+                renderData.pre_tp_hs = await ctx.service.material.getPreTpHs(ctx.tender.id, ctx.material.order);
+
                 renderData.materialType = JSON.stringify(materialConst);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.material.info);
                 await this.layout('material/info.ejs', renderData, 'material/info_modal.ejs');

+ 1 - 1
app/controller/setting_controller.js

@@ -137,7 +137,7 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async userUnbind(ctx) {
-            const projectData = ctx.session.sessionProjectData;
+            const projectData = ctx.session.sessionProject;
 
             try {
                 if (projectData.id === undefined) {

+ 27 - 1
app/controller/stage_controller.js

@@ -190,6 +190,24 @@ module.exports = app => {
             }
         }
 
+        _checkUniqData(data, field) {
+            const groupData = this.ctx.helper._.groupBy(data, field);
+            const surplus = [];
+            for (const gd in groupData) {
+                if (groupData[gd].length > 1) {
+                    const first = groupData[gd][0];
+                    for (let i = 1, iLen = groupData[gd].length; i < iLen; i++) {
+                        const check = groupData[gd][i];
+                        if (this.ctx.helper._.isMatch({field: check[field], sid: check.sid, order: check.order, times: check.times},
+                                {field: first[field], sid: first.sid, order: first.order, times: first.times})) {
+                            surplus.push(groupData[gd][i].id);
+                        }
+                    }
+                }
+            }
+            return surplus;
+        }
+
         async _getStageLedgerData(ctx) {
             const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
             const dgnData = await ctx.service.stageBillsDgn.getDgnData(ctx.tender.id);
@@ -203,6 +221,10 @@ module.exports = app => {
                 curStageData = await ctx.service.stageBills.getAuditorStageData(ctx.tender.id, ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
             } else {
                 curStageData = await ctx.service.stageBills.getLastestStageData(ctx.tender.id, ctx.stage.id);
+                const surplus = this._checkUniqData(curStageData, 'lid');
+                if (surplus.length > 0) {
+                    await ctx.service.stageBills.deleteById(surplus);
+                }
             }
             // 查询截止上期数据
             if (ctx.stage.order > 1) {
@@ -227,6 +249,11 @@ module.exports = app => {
                     ctx.stage.id, ctx.stage.curTimes, ctx.stage.curOrder);
             } else {
                 curStageData = await ctx.service.stagePos.getLastestStageData2(ctx.tender.id, ctx.stage.id);
+
+                const surplus = this._checkUniqData(curStageData, 'pid');
+                if (surplus.length > 0) {
+                    await ctx.service.stagePos.deleteById(surplus);
+                }
             }
             //console.timeEnd('cur');
             // 查询截止上期数据
@@ -687,7 +714,6 @@ module.exports = app => {
                 }
                 await this.layout('stage/pay.ejs', renderData, 'stage/pay_modal.ejs');
             } catch (err) {
-                console.log(err);
                 this.log(err);
                 ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
             }

+ 8 - 0
app/controller/stage_extra_controller.js

@@ -114,6 +114,12 @@ module.exports = app => {
                     }
                 }
                 const preData = await ctx.service.stageBonus.getPreStageData(ctx.stage.order);
+                for (const d of preData) {
+                    for (const pf of d.proof_file) {
+                        delete pf.filepath;
+                        pf.username = (await ctx.service.projectAccount.getAccountInfoById(pf.uid)).name;
+                    }
+                }
                 ctx.body = {err: 0, msg: '', data: data.concat(preData)};
             } catch (error) {
                 ctx.helper.log(error);
@@ -203,6 +209,8 @@ module.exports = app => {
                         throw '未发现上传文件!';
                     }
                     if (!bonus) bonus = await ctx.service.stageBonus.getStageDataById(parts.field.bonus_id);
+                    if (!bonus || bonus.sid !== ctx.stage.id) throw '该奖罚金,当前不允许上传附件';
+
                     const fileInfo = path.parse(stream.filename);
                     const dirName = 'app/public/upload/extra/' + moment().format('YYYYMMDD');
                     const fileName = create_time + '_' + index + fileInfo.ext;

+ 259 - 0
app/controller/wap_controller.js

@@ -0,0 +1,259 @@
+'use strict';
+
+/**
+ * 登录页面控制器
+ *
+ * @author CaiAoLin
+ * @date 2017/11/15
+ * @version
+ */
+const URL = require('url');
+const maintainConst = require('../const/maintain');
+const auditConst = require('../const/audit');
+
+module.exports = app => {
+
+    class WapController extends app.BaseController {
+
+        /**
+         * 登录页面
+         *
+         * @param {Object} ctx - egg全局页面
+         * @return {void}
+         */
+        async index(ctx) {
+            const errorMessage = ctx.session.loginError;
+            // 显示完删除
+            ctx.session.loginError = null;
+            // 获取系统维护信息
+            const maintainData = await ctx.service.maintain.getDataById(1);
+
+            if (!ctx.app.config.is_debug) {
+                await ctx.service.maintain.syncMaintainData();
+            }
+            let projectData = '';
+            if (ctx.session.wapTenderID) {
+                const tenderData = await ctx.service.tender.getDataById(ctx.session.wapTenderID);
+                if (tenderData) projectData = await ctx.service.project.getDataById(tenderData.project_id);
+            }
+
+            const renderData = {
+                maintainData,
+                maintainConst,
+                errorMessage,
+                projectData,
+            };
+            await ctx.render('wap/login.ejs', renderData);
+        }
+
+        /**
+         * 登录操作
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async login(ctx) {
+            let loginType = ctx.request.body.type;
+
+            try {
+                loginType = parseInt(loginType);
+                const result = await ctx.service.projectAccount.accountLogin(ctx.request.body, loginType);
+
+                if (!result) {
+                    throw '用户名或密码错误';
+                }
+
+                if (result === 2) {
+                    throw '该账号已被停用,请联系销售人员';
+                }
+
+                // 调用 rotateCsrfSecret 刷新用户的 CSRF token
+                ctx.rotateCsrfSecret();
+
+                // 判断是否已经有对应用户信息,没有则跳转初始化页面
+                const needBoot = await ctx.service.customer.isNeedBoot(ctx.request.body);
+                const url = needBoot ? '/boot' : '/wap/dashboard';
+
+                const query = URL.parse(ctx.request.header.referer, true).query;
+                let referer = '';
+                if (ctx.session.wapTenderID) {
+                    referer = query.referer ? query.referer + '#shenpi' : url;
+                    ctx.session.wapTenderID = null;
+                }
+                ctx.redirect(referer ? referer : url);
+            } catch (error) {
+                this.log(error);
+                ctx.session.loginError = error;
+                ctx.redirect('/wap');
+            }
+        }
+
+        /**
+         * 退出登录
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async logout(ctx) {
+            // 删除session并跳转
+            ctx.session = null;
+            ctx.redirect('/wap');
+        }
+
+
+        /**
+         * 待办页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async dashboard(ctx) {
+            // 获取待审批的期
+            const auditStages = await ctx.service.stageAudit.getAuditStageByWap(ctx.session.sessionUser.accountId);
+            for (const audit of auditStages) {
+                await this.ctx.service.stage.checkStageGatherData(audit);
+                audit.gather_tp = ctx.helper.add(audit.contract_tp, audit.qc_tp);
+                audit.end_contract_tp = ctx.helper.add(audit.contract_tp, audit.pre_contract_tp);
+                audit.end_qc_tp = ctx.helper.add(audit.qc_tp, audit.pre_qc_tp);
+                audit.end_gather_tp = ctx.helper.add(audit.end_contract_tp, audit.end_qc_tp);
+                audit.pre_gather_tp = ctx.helper.add(audit.pre_contract_tp, audit.pre_qc_tp);
+            }
+            const renderData = {
+                auditStages,
+            };
+            await ctx.render('wap/dashboard.ejs', renderData);
+        }
+
+        /**
+         * 标段列表页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async list(ctx) {
+            try {
+                // 获取用户新建标段权利
+                const accountInfo = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
+                const userPermission = accountInfo !== undefined && accountInfo.permission !== '' ? JSON.parse(accountInfo.permission) : null;
+
+                const tenderList = await this.ctx.service.tender.getList('', userPermission);
+
+                for (const t of tenderList) {
+                    if (t.user_id === this.ctx.session.sessionUser.accountId && (
+                        t.ledger_status === auditConst.ledger.status.checkNo || t.ledger_status === auditConst.ledger.status.uncheck)) {
+                        const sum = await this.ctx.service.ledger.addUp({tender_id: t.id/*, is_leaf: true*/});
+                        t.total_price = sum.total_price;
+                        t.deal_tp = sum.deal_tp;
+                    }
+                    if (t.ledger_status === auditConst.ledger.status.checked) {
+                        t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, true);
+                        if (t.lastStage) {
+                            await this.ctx.service.stage.checkStageGatherData(t.lastStage);
+                        }
+                    }
+                }
+                const categoryData = await this.ctx.service.category.getAllCategory(this.ctx.session.sessionProject.id);
+                const valuations = await this.ctx.service.valuation.getProjectValidValuation(this.ctx.session.sessionProject.id);
+                const renderData = {
+                    tenderList,
+                    categoryData,
+                    auditConst,
+                    userPermission,
+                    valuations,
+                    uid: this.ctx.session.sessionUser.accountId,
+                    pid: this.ctx.session.sessionProject.id,
+                };
+                await ctx.render('wap/list.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                this.ctx.redirect('/wap/dashboard');
+            }
+        }
+
+        /**
+         * 标段详细页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async tender(ctx) {
+            try {
+                const tender = ctx.tender.data;
+                if (tender.user_id === this.ctx.session.sessionUser.accountId && (
+                    tender.ledger_status === auditConst.ledger.status.checkNo || tender.ledger_status === auditConst.ledger.status.uncheck)) {
+                    const sum = await this.ctx.service.ledger.addUp({tender_id: tender.id/*, is_leaf: true*/});
+                    tender.total_price = sum.total_price;
+                    tender.deal_tp = sum.deal_tp;
+                }
+                const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
+                const lastStage = stages.length > 0 ? stages[0] : null; //await ctx.service.stage.getLastestStage(ctx.tender.id);
+                if (lastStage) {
+                    await this.ctx.service.stage.checkStageGatherData(lastStage);
+                    tender.gather_tp = ctx.helper.add(lastStage.contract_tp, lastStage.qc_tp);
+                    tender.end_contract_tp = ctx.helper.add(lastStage.contract_tp, lastStage.pre_contract_tp);
+                    tender.end_qc_tp = ctx.helper.add(lastStage.qc_tp, lastStage.pre_qc_tp);
+                    tender.end_gather_tp = ctx.helper.add(tender.end_contract_tp, tender.end_qc_tp);
+                    tender.pre_gather_tp = ctx.helper.add(lastStage.pre_contract_tp, lastStage.pre_qc_tp);
+                    tender.yf_tp = lastStage.yf_tp;
+                    tender.qc_ratio = ctx.helper.mul(ctx.helper.div(tender.end_qc_tp, ctx.tender.info.deal_param.contractPrice, 2), 100);
+                    tender.sum = ctx.helper.add(tender.total_price, tender.end_qc_tp);
+                    tender.pre_ratio = ctx.helper.mul(ctx.helper.div(tender.pre_gather_tp, tender.sum, 2), 100);
+                    tender.cur_ratio = ctx.helper.mul(ctx.helper.div(tender.gather_tp, tender.sum, 2), 100);
+                    tender.other_tp = ctx.helper.sub(ctx.helper.sub(tender.sum, tender.pre_gather_tp), tender.gather_tp);
+                    tender.other_ratio = Math.max(0, 100 - tender.pre_ratio - tender.cur_ratio);
+                }
+                const monthProgress = [];
+                for (const s of stages) {
+                    if (s.s_time) {
+                        let progress = monthProgress.find(function(x) {
+                            return x.month === s.s_time;
+                        });
+                        if (!progress) {
+                            progress = { month: s.s_time };
+                            monthProgress.push(progress);
+                        }
+                        progress.tp = ctx.helper.add(ctx.helper.add(progress.tp, s.contract_tp), s.qc_tp);
+                    }
+                }
+                monthProgress.sort(function(x, y) {
+                    return Date.parse(x.month) - Date.parse(y.month);
+                });
+                let sum = 0;
+                for (const p of monthProgress) {
+                    p.ratio = ctx.helper.mul(ctx.helper.div(p.tp, tender.sum, 4), 100);
+                    sum = ctx.helper.add(sum, p.tp);
+                    p.end_tp = sum;
+                    p.end_ratio = ctx.helper.mul(ctx.helper.div(p.end_tp, tender.sum, 4), 100);
+                }
+                const renderData = {
+                    tender,
+                    stages,
+                    auditConst: auditConst.stage,
+                    monthProgress,
+                    stagesEcharts: JSON.parse(JSON.stringify(stages)).reverse(),
+                };
+                if (stages.length > 0) {
+                    for (const s of stages) {
+                        // s.curAuditor = null;
+                        // 根据期状态返回展示用户
+                        s.curAuditor = await ctx.service.stageAudit.getAuditorByStatus(s.id, s.status, s.times);
+                        if (s.status === auditConst.stage.status.checkNoPre) {
+                            s.curAuditor2 = await ctx.service.stageAudit.getAuditorByStatus(s.id, auditConst.stage.status.checking);
+                        }
+                    }
+                    renderData.stage = stages[0];
+                    renderData.stage.user = await ctx.service.projectAccount.getAccountInfoById(renderData.stages[0].user_id);
+                    renderData.stage.auditors = await ctx.service.stageAudit.getAuditors(renderData.stages[0].id, renderData.stages[0].times);
+                    // 获取审批流程中左边列表
+                    renderData.stage.auditors2 = await ctx.service.stageAudit.getAuditGroupByList(renderData.stages[0].id, renderData.stages[0].times);
+                }
+                await ctx.render('wap/tender.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/wap/list');
+            }
+        }
+    }
+
+    return WapController;
+};

+ 25 - 1
app/extend/helper.js

@@ -958,4 +958,28 @@ module.exports = {
         }
         return o;
     },
-};
+
+    /**
+     * 短链接生成
+     * @param url
+     * @returns {*}
+     */
+    async urlToShort(url) {
+        const apiUrl = 'https://api.uomg.com/api/long2dwz';
+        const data = {
+            dwzapi: 'tcn',
+            url,
+        };
+        const result = await this.sendRequest(apiUrl, data, 'get');
+        return result && result.code && result.code === 1 ? result.ae_url : url;
+    },
+
+    /**
+     * 判断是否wap访问
+     * @param request
+     * @returns {*}
+     */
+    isWap(request) {
+        return request.url.indexOf('/wap/') !== -1;
+    },
+}

+ 18 - 4
app/lib/rpt_data_analysis.js

@@ -1012,14 +1012,12 @@ const gatherSelectConverse = {
         table: ['mem_gather_stage_bills'],
     },
     _commonConverse: function (helper, data, count) {
-        console.log(count);
-        console.log(data);
         const result = [];
         const reg = new RegExp('^t_[0-9]+_');
         for (let i = 0; i < count; i++) {
             const curReg = new RegExp('^t_' + i + '_');
-            for (const d of data) {
-                const nd = {};
+            for (const [i, d] of data.entries()) {
+                const nd = { cross_index: i + 1};
                 for (const prop in d) {
                     if (reg.test(prop)) {
                         if (curReg.test(prop)) {
@@ -1032,6 +1030,22 @@ const gatherSelectConverse = {
                 result.push(nd);
             }
         }
+        // for (const d of data) {
+        //     for (let i = 0; i < count; i++) {
+        //         const curReg = new RegExp('^t_' + i + '_');
+        //         const nd = {};
+        //         for (const prop in d) {
+        //             if (reg.test(prop)) {
+        //                 if (curReg.test(prop)) {
+        //                     nd[prop.replace(curReg, 't_')] = d[prop];
+        //                 }
+        //             } else {
+        //                 nd[prop] = d[prop];
+        //             }
+        //         }
+        //         result.push(nd);
+        //     }
+        // }
         return result;
     },
     fun: function (ctx, data, fieldsKey, options, csRela) {

+ 1 - 1
app/lib/stage_im.js

@@ -259,7 +259,7 @@ class StageIm {
                 }
             }
             im.calc_memo = memo.join('\n');
-        } else if (im.check) {
+        } else if (im.gclBills && im.gclBills.length > 0) {
             const memo = [];
             for (const [i, b] of im.gclBills.entries()) {
                 if (b.pos && b.pos.length > 0) {

+ 17 - 7
app/middleware/session_auth.js

@@ -50,14 +50,24 @@ module.exports = options => {
                     data: '',
                 };
             } else if (this.session === null) {
-                return this.redirect('/login?referer=' + this.url);
+                if (this.helper.isWap(this.request)) {
+                    this.session.wapTenderID = this.params.id ? this.params.id : null;
+                    return this.redirect('/wap/login?referer=' + this.url);
+                } else {
+                    return this.redirect('/login?referer=' + this.url);
+                }
             } else {
-                this.session.message = {
-                    type: messageType.ERROR,
-                    icon: 'exclamation-circle',
-                    message: '登录信息异常,请重新登录',
-                };
-                return this.redirect('/login?referer=' + this.url);
+                if (this.helper.isWap(this.request)) {
+                    this.session.wapTenderID = this.params.id ? this.params.id : null;
+                    return this.redirect('/wap/login?referer=' + this.url);
+                } else {
+                    this.session.message = {
+                        type: messageType.ERROR,
+                        icon: 'exclamation-circle',
+                        message: '登录信息异常,请重新登录',
+                    };
+                    return this.redirect('/login?referer=' + this.url);
+                }
             }
         }
         yield next;

+ 5 - 1
app/middleware/tender_check.js

@@ -93,7 +93,11 @@ module.exports = options => {
                 }));
             }
             // 重定向值标段管理
-            this.redirect('/list');
+            if (this.helper.isWap(this.request)) {
+                this.redirect('/wap/list');
+            } else {
+                this.redirect('/list');
+            }
         }
     };
 };

BIN
app/public/css/logo.png


+ 43 - 10
app/public/css/main.css

@@ -6,7 +6,7 @@ body {
     background: #e4e7ea
 }
 .dropdown-menu {
-    font-size: 0.9rem
+    font-size: 12px
 }
 .btn.disabled, .btn:disabled {
   color:#999
@@ -136,13 +136,17 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
   overflow-y: auto;
 }
 .form-signin {
-    max-width: 500px;
-    margin: 150px auto;
+    max-width:400px;
+    margin-left:auto;
     background: #fff;
     padding:30px;
     border:1px solid #ddd;
     border-radius: .25rem
 }
+
+.bg-404{
+    background:#dcdcdc url("404.png") bottom center no-repeat;
+}
 .has-danger {
     -webkit-animation: shake 1s .2s ease both;
     -moz-animation: shake 1s .2s ease both;
@@ -476,12 +480,36 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 }
 /*登陆相关*/
 .login-body{
-  background:linear-gradient(#33425b,#192948);
-  height:960px
+  background:linear-gradient(#192948,#33425b);
+  height:100%
 }
 .login-infoinput {
   margin-top:15%
 }
+.login-body .container{
+  position:absolute;
+  top:10%;
+  margin:0 auto;
+  left:0;
+  right:0;
+}
+.login-bg.img-1{
+  background:url("undraw_all_the_data_h4ki.svg") no-repeat;
+}
+.login-bg.img-2{
+  background:url("undraw_order_a_car_3tww.svg") no-repeat;
+}
+.login-bg.img-3{
+  background:url("undraw_work_chat_erdt.svg") no-repeat;
+}
+.login-body .login-bg{
+  width: 100%;
+  height: 100%;
+  background-position:25% 95%;
+  background-size: 40% auto;
+  display: inline-block;
+  opacity: .8
+}
 /*侧栏主菜单*/
 .nav-top,.nav-bottom{
   width: 55px
@@ -651,7 +679,6 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
     height: calc(100vh - 10rem);
     overflow-y: auto;
 }
-
 /*草图编辑器*/
 .img-view{
   height:400px;
@@ -904,16 +931,22 @@ legend {
   font-size:12px;
   padding:.1rem .5rem;
   }
-  .side-menu{
+.side-menu{
     right:0;
     top:35px;
   }
-  .custom-control-label::before{
-    top:.1rem;
-  }
+.custom-control-label::before{
+  top:.1rem;
+}
 .custom-control-label::after{
   top:.1rem;
 }
+.dropdown-menu .custom-control-label::before{
+  top:.5rem;
+}
+.dropdown-menu .custom-control-label::after{
+  top:.5rem;
+}
 .form-control-sm{
   height:calc(1.4125rem + 2px);
 }

+ 11 - 11
app/public/css/spreadjs/sheets/gc.spread.sheets.excelsmartcost.css

@@ -4,7 +4,7 @@
     z-index: 2013;
 }
 .gc-grayArea {
-    background-color: white;
+    background-color: #f1f1f1;
 }
 .gc-corner-hover {
     background-color: white;
@@ -59,9 +59,9 @@
     background-image: none;
     background-color:#e9ecef;
     border-style:solid;
-    border-left-color: #cccccc !important;
-    border-right-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
+    border-left-color: #dee2e6 !important;
+    border-right-color: #dee2e6 !important;
+    border-bottom-color: #dee2e6 !important;
 }
 .gc-columnHeader-hover {
     color: #444444;
@@ -95,9 +95,9 @@
     background-color: #e9ecef;
     background-image: none;
     border-style:solid;
-    border-top-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
-    border-right-color: #cccccc !important;
+    border-top-color: #efefef !important;
+    border-bottom-color: #d5ded5 !important;
+    border-right-color: #dee2e6 !important;
 }
 .gc-rowHeader-hover {
     color: #73a2e3;
@@ -113,9 +113,9 @@
     background-color: #dddfe1;
     background-image: none;
     border-style:solid;
-    border-top-color: #cccccc !important;
-    border-bottom-color: #cccccc !important;
-    border-right-color: #cccccc !important;
+    border-top-color: #dee2e6 !important;
+    border-bottom-color: #dee2e6 !important;
+    border-right-color: #dee2e6 !important;
 }
 .gc-rowHeader-highlight {
     color: #73a2e3;
@@ -145,7 +145,7 @@
     border-color: #217346;
 }
 .gc-gridlineColor {
-    border-color: #cccccc;
+    border-color: #dee2e6;
 }
 .gc-group {
     background-color: white;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
app/public/css/undraw_all_the_data_h4ki.svg


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
app/public/css/undraw_order_a_car_3tww.svg


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 0
app/public/css/undraw_work_chat_erdt.svg


+ 23 - 0
app/public/css/wap/main.css

@@ -0,0 +1,23 @@
+/*bootstrap 初始化*/
+.bg-dark{
+  background-color:#192948!important;
+}
+.pb-6,.py-6{
+ padding-bottom: 4.5rem !important;
+}
+.pt-6,.py-6{
+ padding-top: 4.5rem !important;
+}
+/**/
+.login-body{
+  background: linear-gradient(#192948,#33425b)
+}
+.in-1{padding-left:5px!important}
+.in-2{padding-left:21px!important}
+.in-3{padding-left:42px!important}
+.in-4{padding-left:63px!important}
+.in-5{padding-left:84px!important}
+.in-6{padding-left:105px!important}
+.bg-gray {
+    background-color: #bbb !important;
+}

BIN
app/public/images/favicon.ico


BIN
app/public/images/loginlogo.png


BIN
app/public/images/logo.png


+ 102 - 25
app/public/js/ledger.js

@@ -56,6 +56,45 @@ $(document).ready(function() {
     });
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
 
+
+    const showSideTools = function (show) {
+        const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
+        if (show) {
+            right.show();
+            autoFlashHeight();
+            /**
+             * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+             * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+             * 故需要通过最终的parent.width再计算一次left.width
+             *
+             * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+             * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+             *
+             */
+                //left.css('width', parent.width() - right.outerWidth());
+                //left.css('width', parent.width() - right.outerWidth());
+            const percent = 100 - right.outerWidth() /parent.width() * 100;
+            left.css('width', percent + '%');
+        } else {
+            left.width(parent.width());
+            right.hide();
+        }
+    };
+    const errorList = $.cs_errorList({
+        tabSelector: '#error-list-tab',
+        selector: '#error-list',
+        relaSpread: ledgerSpread,
+        storeKey: 'ledger-error-' + getTenderId(),
+        afterLocated:  function () {
+            posOperationObj.loadCurPosData();
+        },
+        afterShow: function () {
+            ledgerSpread.refresh();
+            if (posSpread) posSpread.refresh();
+        },
+        showSideTools: showSideTools,
+    });
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -86,6 +125,9 @@ $(document).ready(function() {
             if (searchLedger) {
                 searchLedger.spread.refresh();
             }
+            if (errorList) {
+                errorList.spread.refresh();
+            }
         }
     });
 
@@ -1882,29 +1924,6 @@ $(document).ready(function() {
     $('a', '#side-menu').bind('click', function (e) {
         e.preventDefault();
         const tab = $(this), tabPanel = $(tab.attr('content'));
-        const showSideTools = function (show) {
-            const left = $('#left-view'), right = $('#right-view'), parent = left.parent();
-            if (show) {
-                right.show();
-                autoFlashHeight();
-                /**
-                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
-                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
-                 * 故需要通过最终的parent.width再计算一次left.width
-                 *
-                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
-                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
-                 *
-                 */
-                //left.css('width', parent.width() - right.outerWidth());
-                //left.css('width', parent.width() - right.outerWidth());
-                const percent = 100 - right.outerWidth() /parent.width() * 100;
-                left.css('width', percent + '%');
-            } else {
-                left.width(parent.width());
-                right.hide();
-            }
-        };
         // 展开工具栏、切换标签
         if (!tab.hasClass('active')) {
             const close = $('.active', '#side-menu').length === 0;
@@ -1948,7 +1967,7 @@ $(document).ready(function() {
                     dealBills.loadData();
                 }
                 dealBills.spread.refresh();
-            } else if (tab.attr('content') === '#search' && !searchLedger) {
+            } else if (tab.attr('content') === '#search') {
                 if (!searchLedger) {
                     searchLedger = $.billsSearch({
                         selector: '#search',
@@ -1976,6 +1995,8 @@ $(document).ready(function() {
                     });
                 }
                 searchLedger.spread.refresh();
+            } else if (tab.attr('content') === '#error-list') {
+                errorList.spread.refresh();
             }
         } else { // 收起工具栏
             tab.removeClass('active');
@@ -2581,6 +2602,60 @@ $(document).ready(function() {
 
         SpreadExcelObj.exportSimpleXlsxSheet(setting, data, "台账分解.xlsx");
     });
+
+    const dataChecker = DataChecker({
+        loadUrl: window.location.pathname + '/load',
+        loadData: {},
+        checkFun: function (data, progress) {
+            ledgerTree.loadDatas(data.bills);
+            treeCalc.calculateAll(ledgerTree);
+            SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
+
+            pos.loadDatas(data.pos);
+            posOperationObj.loadCurPosData();
+
+            const checkFields = ['sgfh_qty', 'qtcl_qty', 'sjcl_qty', 'quantity'], result = [];
+            const iLen = data.bills.length;
+            for (const [i, b] of data.bills.entries()) {
+                const pr = _.filter(data.pos, {lid: b.id});
+                if (pr && pr.length > 0) {
+                    const checkData = {}, calcData = {};
+                    for (const field of checkFields) {
+                        checkData[field] = b[field] ? b[field] : 0;
+                    }
+                    for (p of pr) {
+                        for (const field of checkFields) {
+                            calcData[field] = ZhCalc.add(calcData[field], p[field]);
+                        }
+                    }
+                    for (const field of checkFields) {
+                        calcData[field] = calcData[field] ? calcData[field] : 0;
+                    }
+                    if (!_.isMatch(checkData, calcData)) {
+                        result.push({
+                            ledger_id: b.ledger_id,
+                            b_code: b.b_code,
+                            name: b.name,
+                            serialNo: ledgerTree.getNodeIndex(ledgerTree.getItems(b.ledger_id)) + 1,
+                            error: {checkData: checkData, calcData: calcData}
+                        })
+                    }
+                    progress(parseInt((i+1)/iLen*100));
+                }
+            }
+            return result;
+        },
+        errorList: errorList,
+    });
+    $('[name=audit-start]').submit(function (e) {
+        if (checkAuditorFrom()) {
+            $(this).parent().parent().parent().modal('hide');
+            const formData = new FormData();
+            dataChecker.checkAndPost(this.action, formData);
+            $('#hide-all').hide();
+        }
+        return false;
+    });
 });
 
 // 检查上报情况
@@ -2588,7 +2663,9 @@ function checkAuditorFrom () {
     if ($('#auditors li').length === 0) {
         toastr.error('请先选择审批人,再上报数据');
         return false;
+    } else {
+        $('#hide-all').show();
+        return true;
     }
-    $('#hide-all').show();
 }
 

+ 4 - 4
app/public/js/ledger_search.js

@@ -201,12 +201,12 @@
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
 
-        $('input', this.obj).bind('keydown', function (e) {
+        $('input', obj).bind('keydown', function (e) {
             if (e.keyCode == 13) searchBills();
         });
-        $('button', this.obj).bind('click', () => {searchBills()});
-        $('#over', this.obj).bind('change', () => {searchOver()});
-        $('#empty', this.obj).bind('change', () => {searchLess()});
+        $('button', obj).bind('click', () => {searchBills()});
+        $('#over', obj).bind('change', () => {searchOver()});
+        $('#empty', obj).bind('change', () => {searchLess()});
         resultSpread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) {
             const sheet = info.sheet;
             const data = sheet.zh_data;

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

@@ -74,7 +74,7 @@ DatePickerCellType.prototype.updateEditor = function (editorContext, cellStyle,
 function resetTpTable() {
     const rate = $('#changeRate').val();
     const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
-    const jzbqhs = ZhCalc.round(ZhCalc.mul(ZhCalc.add(pre_tp, m_tp), 1+rate/100), 2);
+    const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
     $('#tp_set').find('td').eq(1).text(ZhCalc.round(m_tp, 2));
     $('#tp_set').find('td').eq(2).text(ZhCalc.round(ZhCalc.add(pre_tp, m_tp), 2));
     $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
@@ -334,7 +334,7 @@ $(document).ready(() => {
                 select.m_spread = materialCol.getValue.m_spread(select);
                 select.m_tp = materialCol.getValue.m_tp(select);
 
-                console.log(select);
+                // console.log(select);
 
                 // 更新至服务器
                 postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
@@ -441,6 +441,7 @@ $(document).ready(() => {
                         }
                     }
                     materialData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
                 }
                 if (materialData.code === '' || materialData.code === null) {
                     toastMessageUniq(getPasteHint(hint.codeEmpty, hintRow));
@@ -460,7 +461,7 @@ $(document).ready(() => {
                 SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
                 return;
             }
-            console.log(data);
+            // console.log(data);
             // 更新至服务器
             postData(window.location.pathname + '/save', { type:'paste', updateData: data }, function (result) {
                 materialBillsData = result.info;
@@ -536,7 +537,7 @@ $(document).ready(() => {
             const rate = parseInt($(this).val());
             postData(window.location.pathname + '/save', { type:'rate', rate: rate }, function (result) {
                 const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
-                const jzbqhs = ZhCalc.round(ZhCalc.mul(ZhCalc.add(pre_tp, m_tp), 1+rate/100), 2);
+                const jzbqhs = ZhCalc.round(ZhCalc.add(pre_tp_hs, bqhs), 2);
                 $('#rate_set').find('td').eq(1).text(bqhs !== 0 ? bqhs : '');
                 $('#rate_set').find('td').eq(2).text(jzbqhs !== 0 ? jzbqhs : '');
             });

+ 21 - 4
app/public/js/se_bonus.js

@@ -56,7 +56,7 @@ $(document).ready(() => {
         font: '12px 微软雅黑',
         readOnly: readOnly,
         imageClick: function (data, hitinfo) {
-            if (!data || readOnly) return;
+            if (!data) return;
 
             const setting = hitinfo.sheet.zh_setting;
             if (!setting) return;
@@ -64,11 +64,12 @@ $(document).ready(() => {
             if (!col) return;
 
             if (col.field === 'proof_file') {
+                fileObj.canUpload(data);
                 fileObj.generateFilesHtml(data);
                 $('#file').modal('show');
             }
 
-            if (col.field === 'real_time') {
+            if (col.field === 'real_time' && !readOnly) {
                 const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
                 if (!datepicker) {
                     datepicker = $('.datepicker-here').datepicker({
@@ -93,11 +94,20 @@ $(document).ready(() => {
                 }
                 const value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
                 if (value) {
-                    datepicker.selectDate(value);
+                    datepicker.selectDate(new Date(value));
                 } else {
                     datepicker.clear();
                 }
                 datepicker.show();
+                if (hitinfo.cellRect.y + pos.y + 272 > $('.main-panel').height()) {
+                    datepicker.update({
+                        position: 'top left',
+                    })
+                } else {
+                    datepicker.update({
+                        position: 'bottom left',
+                    })
+                }
                 $('#datepickers-container').css('top', hitinfo.cellRect.y + pos.y).css('left', hitinfo.cellRect.x + pos.x);
             }
         }
@@ -189,6 +199,13 @@ $(document).ready(() => {
                 fileObj.generateFilesHtml(select);
                 SpreadJsObj.reLoadNodesData(bonusSheet, [select]);
             });
+        },
+        canUpload(data) {
+            if (data.sid === stageId) {
+                $('#upload-file-panel').show();
+            } else {
+                $('#upload-file-panel').hide();
+            }
         }
     };
 
@@ -545,4 +562,4 @@ $(document).ready(() => {
     });
     $('#upload-file').change(fileObj.uploadFile);
     $('body').on('click', '.delete-att', fileObj.deleteFile);
-});
+});

+ 95 - 0
app/public/js/shares/cs_tools.js

@@ -0,0 +1,95 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+(function($){
+    /**
+     *
+     * @param setting
+     * {
+     *      tabSelector: 'a[content=#error-list]',
+     *      selector: '#error-list',
+     *      relaSpread: ledgerSpread,
+     *      storeKey: 'ledger-error-' + tenderId,
+     * }
+     * @returns {{spread: *}}
+     */
+    $.cs_errorList = function (setting) {
+        if (!setting.selector || !setting.relaSpread) return;
+        if (!setting.spreadSetting) {
+            setting.spreadSetting = {
+                cols: [
+                    {title: '行号', field: 'serialNo', width: 80, formatter: '@'},
+                    {title: '清单编号', field: 'b_code', width: 150, formatter: '@'},
+                    {title: '清单名称', field: 'name', width: 230, formatter: '@'},
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                selectedBackColor: '#fffacd',
+                readOnly: true,
+            };
+        }
+
+        const resultId = setting.id + '-spread';
+        const obj = $(setting.selector);
+        obj.html(
+            '                        <div id="' + resultId + '" class="sjs-sh">\n' +
+            '                        </div>'
+        );
+        autoFlashHeight();
+
+        const spread = SpreadJsObj.createNewSpread($('#' + resultId)[0]);
+        const sheet = spread.getActiveSheet();
+        SpreadJsObj.initSheet(sheet, setting.spreadSetting);
+        SpreadJsObj.forbiddenSpreadContextMenu('#' + resultId, spread);
+
+        spread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) {
+            const sheet = info.sheet;
+            const data = sheet.zh_data;
+            if (!data) { return }
+
+            const curBills = data[info.row];
+            if (!curBills) { return }
+
+            SpreadJsObj.locateTreeNode(setting.relaSpread.getActiveSheet(), curBills.ledger_id, true);
+            console.log(curBills);
+            if (setting.afterLocated) {
+                setting.afterLocated();
+            }
+        });
+
+        const loadErrorData = function (data, his = false) {
+            SpreadJsObj.loadSheetData(sheet, SpreadJsObj.DataType.Data, data);
+            if (!his && setting.storeKey) {
+                setLocalCache(setting.storeKey, JSON.stringify(data));
+            }
+            $(setting.tabSelector).show();
+        };
+        if (setting.storeKey) {
+            const storeStr = getLocalCache(setting.storeKey);
+            const storeData = storeStr ? JSON.parse(storeStr) : [];
+            if (storeData.length > 0) loadErrorData(storeData, true);
+        }
+        const showErrorList = function () {
+            const tab = $(setting.tabSelector), tabPanel = $(tab.attr('content'));
+            $('a', '#side-menu').removeClass('active');
+            tab.addClass('active');
+            $('.tab-content .tab-pane').removeClass('active');
+            tabPanel.addClass('active');
+            setting.showSideTools(true);
+            spread.refresh();
+            if (setting.afterShow) setting.afterShow();
+        };
+        return {spread: spread, loadErrorData: loadErrorData, show: showErrorList};
+    };
+})(jQuery);

+ 6 - 6
app/public/js/tender_list.js

@@ -273,7 +273,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td class="in-' + node.level + '">');
+    html.push('<td style="width: 45%" 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> ', node.name);
     } else {
@@ -285,9 +285,9 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     }
     html.push('</td>');
     // 创建人
-    html.push('<td class="text-center">', node.user_name ? node.user_name : '', '</td>');
+    html.push('<td style="width: 10%" class="text-center">', node.user_name ? node.user_name : '', '</td>');
     // 创建时间
-    html.push('<td>', node.create_time ? moment(node.create_time).format('YYYY-MM-DD HH:mm:ss') : '', '</td>');
+    html.push('<td style="width: 15%">', node.create_time ? moment(node.create_time).format('YYYY-MM-DD HH:mm:ss') : '', '</td>');
     // 计量模式
     // html.push('<td>');
     // if (node.measure_type) {
@@ -295,13 +295,13 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     // }
     // html.push('</td>');
     // 计量期数
-    html.push('<td  class="text-center">');
+    html.push('<td style="width: 10%" class="text-center">');
     if (!node.cid) {
         html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
     }
     html.push('</td>');
     // 审批状态
-    html.push('<td>');
+    html.push('<td style="width: 20%">');
     html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
     html.push(node.status_users ? '(' + node.status_users + ')' : '');
     html.push('</td>');
@@ -318,7 +318,7 @@ function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
-        html.push('<thead>', '<tr>');
+        html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
         html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
         html.push('<th class="text-center" style="width: 10%">', '创建人', '</th>');
         html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');

+ 13 - 13
app/public/js/tender_list_info.js

@@ -296,7 +296,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td class="in-' + node.level + '">');
+    html.push('<td style="width: 23%" 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> ', node.name);
     } else {
@@ -308,51 +308,51 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     }
     html.push('</td>');
     // 计量模式
-    html.push('<td class="text-center">');
+    html.push('<td style="width: 7%" class="text-center">');
     if (node.measure_type) {
         html.push(node.measure_type === measureType.tz.value ? '0号台账' : '工程量清单');
     }
     html.push('</td>');
     // 计量期数
-    html.push('<td class="text-center">');
+    html.push('<td style="width: 7%" class="text-center">');
     if (!node.cid) {
         html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
     }
     html.push('</td>');
     // 审批状态
-    html.push('<td>');
+    html.push('<td style="width: 7%">');
     html.push(node.lastStage ? auditConst.stage.statusString[node.lastStage.status] : auditConst.ledger.statusString[node.ledger_status]);
     html.push('</td>');
     // 0号台账合同
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.total_price);
     html.push('</td>');
     // 本期完成
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.gather_tp);
     html.push('</td>');
     // 截止本期合同
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.end_contract_tp);
     html.push('</td>');
     // 截止本期变更
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.end_qc_tp);
     html.push('</td>');
     // 截止本期完成
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.end_gather_tp);
     html.push('</td>');
     // 截止上期完成
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.pre_gather_tp);
     html.push('</td>');
     // 本期应付
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.yf_tp);
     html.push('</td>');
     // 截止本期应付
-    html.push('<td class="text-right">');
+    html.push('<td style="width: 7%" class="text-right">');
     html.push(node.end_yf_tp);
     html.push('</td>');
     html.push('</tr>');
@@ -368,7 +368,7 @@ function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
-        html.push('<thead>', '<tr>');
+        html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
         html.push('<th class="text-center" style="width: 23%">', '标段名称', '</th>');
         html.push('<th class="text-center" style="width: 7%">', '计量模式', '</th>');
         html.push('<th class="text-center" style="width: 7%">', '计量期数', '</th>');

+ 6 - 6
app/public/js/tender_list_manage.js

@@ -260,7 +260,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     const html = [];
     html.push('<tr pid="' + pid + '">');
     // 名称
-    html.push('<td class="in-' + node.level + '">');
+    html.push('<td style="width: 45%" 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> ', node.name);
     } else {
@@ -271,17 +271,17 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
     }
     html.push('</td>');
     // 创建人
-    html.push('<td class="text-center">', node.user_name, '</td>');
+    html.push('<td style="width: 10%" class="text-center">', node.user_name, '</td>');
     // 创建时间
-    html.push('<td>', node.create_time ? moment(node.create_time).format('YYYY-MM-DD HH:mm:ss') : '', '</td>');
+    html.push('<td style="width: 15%">', node.create_time ? moment(node.create_time).format('YYYY-MM-DD HH:mm:ss') : '', '</td>');
     // 完成期数
-    html.push('<td class="text-center">');
+    html.push('<td style="width: 10%" class="text-center">');
     if (!node.cid) {
         html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '第0期');
     }
     html.push('</td>');
     // 管理
-    html.push('<td tid="' + node.id + '">');
+    html.push('<td style="width: 20%" tid="' + node.id + '">');
     if (!node.cid) {
         html.push('<a href="javascript: void(0)" name="edit" class="btn btn-outline-primary btn-sm">编辑</a>');
         if (node.lastStage === null || node.lastStage === undefined) {
@@ -302,7 +302,7 @@ function recursiveGetTenderNodeHtml (node, arr, pid) {
 function getTenderTreeHeaderHtml() {
     const html = [];
     html.push('<table class="table table-hover table-bordered">');
-    html.push('<thead>', '<tr>');
+    html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
     html.push('<th class="text-center" style="width: 45%">', '标段名称', tenderListOrder.getOrderButton('name'), '</th>');
     html.push('<th class="text-center" style="width: 10%">', '创建人', '</th>');
     html.push('<th class="text-center" style="width: 15%">', '创建时间', tenderListOrder.getOrderButton('create_time'), '</th>');

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

@@ -351,7 +351,7 @@ function getTenderTreeHtml () {
     if (tenderTree.length > 0) {
         const html = [];
         html.push('<table class="table table-hover table-bordered">');
-        html.push('<thead>', '<tr>');
+        html.push('<thead style="position: fixed;left:56px;top: 34px;">', '<tr>');
         html.push('<th style="width: 35%" class="text-center">', '标段名称', '</th>');
         html.push('<th style="width: 10%" class="text-center">', '计量期数', '</th>');
         html.push('<th style="width: 10%" class="text-center">', '总价 <i class="fa fa-question-circle text-primary"  data-placement="bottom" data-toggle="tooltip" data-original-title="0号台账+截止本期数量变更"></i>', '</th>');

+ 8 - 7
app/public/js/tender_showhide.js

@@ -30,7 +30,7 @@ function removeValueToCate(cate) {
     return newCate;
 }
 
-function localHideList() {
+function localHideList(wap = false) {
     const pro_cate = getLocalCache('pro_'+ pid + '_category_list');
     const cate = JSON.stringify(removeValueToCate(category));
     if (pro_cate && cate === pro_cate) {
@@ -49,16 +49,17 @@ function localHideList() {
         removeLocalCache(uphlname);
         setLocalCache('pro_'+ pid + '_category_list', cate);
     }
-    setTopTr();
+    if (!wap)
+        setTopTr();
 }
 $(window).resize(setTopTr);
 // 设置表头固定并动态调整宽度高度
 function setTopTr() {
-    // for(let item = 0; item < $(".c-body table>thead>tr>th").length; item ++) {
-    //     $(".c-body table>thead>tr>th").eq(item).outerWidth($(".c-body table>tbody>tr:first").children('td').eq(item).outerWidth());
-    // }
-    //$('.c-body table').css('margin-top', $(".c-body table>thead").height() - 4);
-    $('.c-body table').css('margin-top', -2);
+    for(let item = 0; item < $(".c-body table>thead>tr>th").length; item ++) {
+        $(".c-body table>thead>tr>th").eq(item).outerWidth($(".c-body table>tbody>tr:first").children('td').eq(item).outerWidth());
+    }
+    $('.c-body table').css('margin-top', $(".c-body table>thead").height() - 4);
+    // $('.c-body table').css('margin-top', -2);
 }
 
 function doTrStatus(node, status, all = '') {

+ 97 - 0
app/public/js/wap/global.js

@@ -0,0 +1,97 @@
+/**
+ * 提示框
+ *
+ * @param string message
+ * @param string type
+ * @param string icon
+ * @return void
+ */
+function toast(message, type, icon) {
+    var toast = $(".toast");
+    toast.addClass(type);
+    toast.children('.message').html(message);
+    var iconClass = 'fa-' + icon;
+    toast.children('.icon').addClass(iconClass);
+    toast.fadeIn(500);
+
+    setTimeout(function() {
+        toast.fadeOut('fast');
+        toast.children('.message').text('');
+        toast.children('.icon').removeClass(iconClass);
+    }, 3000);
+}
+
+/**
+ * 获取url中参数
+ * @param variable
+ * @returns {*}
+ */
+function getQueryVariable(variable) {
+    var query = window.location.search.substring(1);
+    var vars = query.split("&");
+    for (var i=0;i<vars.length;i++) {
+        var pair = vars[i].split("=");
+        if(pair[0] == variable){return pair[1];}
+    }
+    return(false);
+}
+
+const zeroRange = 0.00000001;
+function checkZero(value) {
+    return value === null || value === undefined || (_.isNumber(value) && Math.abs(value) < zeroRange);
+}
+function checkFieldChange(o, n) {
+    return o == n || ((!o || o === '') && (n === ''));
+}
+
+/**
+ * 设置本地缓存
+ *
+ * @param {String} key
+ * @param {String|Number} value
+ * @return {void}
+ */
+function setLocalCache(key, value) {
+    const storage = window.localStorage;
+    if (!storage || key === '' || value === '') {
+        return;
+    }
+
+    storage.setItem(key, value);
+}
+
+/**
+ * 获取本地缓存
+ *
+ * @param {String} key
+ * @return {String}
+ */
+function getLocalCache(key) {
+    const storage = window.localStorage;
+    if (!storage || key === '') {
+        return null;
+    }
+
+    return storage.getItem(key);
+}
+
+/**
+ * 移除本地缓存
+ * @param {String} key
+ * @returns {Boolean}
+ */
+function removeLocalCache(key) {
+    const storage = window.localStorage;
+    if (!storage || key === '') {
+        return null;
+    }
+    return storage.removeItem(key);
+}
+
+function trimInvalidChar(str) {
+    return $.trim(str).replace(/\n/g, '').replace(/\r/g, '').replace(/\t/g, '');
+}
+
+function cleanSymbols(str) {
+    return $.trim(str).replace(/\\/g, '').replace(/\'/g, '').replace(/\"/g, '').replace(/\</g, '').replace(/\|/g, '');
+}

+ 245 - 0
app/public/js/wap/list.js

@@ -0,0 +1,245 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/10/11
+ * @version
+ */
+
+const EmptyTenderHtml = [
+    '',
+];
+const tenderTree = [];
+let parentId = 0;
+
+// 查询方法
+function findNode (key, value, arr) {
+    for (const a of arr) {
+        if (a[key] && a[key] === value) {
+            return a;
+        }
+    }
+}
+function getPId(level) {
+    if (level !== 1) {
+        const p = findNode('level', level - 1, levelNodes);
+        if (p) {
+            return p.lid
+        } else {
+            return 1;
+        }
+    } else {
+        return 2;
+    }
+}
+// 分类数据排序
+function sortCategory() {
+    category.sort(function (a, b) {
+        return a.level ? (b.level ? a.level - b.level : -1) : a.id - b.id;
+    });
+}
+function calculateParent(node) {
+    if (node.children && node.cid) {
+        node.end_qc_tp = 0;
+        node.pre_gather_tp = 0;
+        node.gather_tp = 0;
+        node.sum_tp = 0;
+        node.lastStage = 0;
+        for (const c of node.children) {
+            calculateParent(c);
+            node.end_qc_tp = ZhCalc.add(node.end_qc_tp, c.end_qc_tp);
+            node.pre_gather_tp = ZhCalc.add(node.pre_gather_tp, c.pre_gather_tp);
+            node.gather_tp = ZhCalc.add(node.gather_tp, c.gather_tp);
+            node.sum_tp = ZhCalc.add(node.sum_tp, c.sum_tp);
+            node.lastStage = c.cid
+                ? Math.max(node.lastStage, c.lastStage)
+                : (c.lastStage ? Math.max(node.lastStage, c.lastStage.order) : node.lastStage);
+        }
+    }
+}
+// 初始化TenderTree数据
+function initTenderTree () {
+    const levelCategory = category.filter(function (c) {
+        return c.level && c.level > 0;
+    });
+    function findCategoryNode(cid, value, array) {
+        for (const a of array) {
+            if (a.cid === cid && a.vid === value) {
+                return a;
+            }
+        }
+    }
+    function getCategoryNode(category, value, parent, i = null) {
+        const array = parent ?  parent.children : tenderTree;
+        let cate = findCategoryNode(category.id, value, array);
+        if (!cate) {
+            const cateValue = findNode('id', value, category.value);
+            if (!cateValue) return null;
+            cate = {
+                cid: category.id,
+                vid: value,
+                name: cateValue.value,
+                children: [],
+                level: i ? i : category.level,
+                sort_id: ++parentId,
+            };
+            array.push(cate);
+        }
+        return cate;
+    }
+    function loadTenderCategory (tender) {
+        let tenderCategory = null;
+        for (const [index, lc] of levelCategory.entries()) {
+            const tenderCate = findNode('cid', lc.id, tender.category);
+            if (tenderCate) {
+                tenderCategory = getCategoryNode(lc, tenderCate.value, tenderCategory);
+            } else {
+                if (index === 0 && tender.category) {
+                    for (const [i,c] of tender.category.entries()) {
+                        const cate = findNode('id', c.cid, category);
+                        tenderCategory = getCategoryNode(cate, c.value, tenderCategory, i+1);
+                    }
+                }
+                return tenderCategory;
+            }
+        }
+        return tenderCategory;
+    }
+    function calculateTender(tender) {
+        if (tender.lastStage) {
+            tender.end_qc_tp = ZhCalc.add(tender.lastStage.pre_qc_tp, tender.lastStage.qc_tp);
+            tender.pre_gather_tp = ZhCalc.add(tender.lastStage.pre_contract_tp, tender.lastStage.pre_qc_tp);
+            tender.gather_tp = ZhCalc.add(tender.lastStage.contract_tp, tender.lastStage.qc_tp);
+            tender.sum_tp = ZhCalc.add(tender.total_price, tender.end_qc_tp);
+        } else {
+            tender.sum_tp = tender.total_price;
+        }
+    }
+    tenderTree.splice(0, tenderTree.length);
+    for (const t of tenders) {
+        calculateTender(t);
+        t.valid = true;
+        delete t.level;
+        if (t.category && levelCategory.length > 0) {
+            const parent = loadTenderCategory(t);
+            if (parent) {
+                t.level = parent.level + 1;
+                parent.children.push(t);
+            } else {
+                tenderTree.push(t);
+            }
+        } else {
+            tenderTree.push(t);
+        }
+    }
+    for (const t of tenderTree) {
+        calculateParent(t);
+    }
+}
+function getProgressHtml(total, pre, cur) {
+    if (total !== 0) {
+        let preP = ZhCalc.mul(ZhCalc.div(pre, total, 2), 100, 0);
+        let curP = ZhCalc.mul(ZhCalc.div(cur, total, 2), 100, 0);
+        let other = Math.max(ZhCalc.sub(ZhCalc.sub(total, pre), cur), 0);
+        let otherP = Math.max(100 - preP - curP, 0);
+        const html = '<div class="progress">' +
+            '<div class="progress-bar bg-success" style="width: ' + preP + '%;" data-placement="bottom" data-toggle="tooltip" data-original-title="截止上期完成:¥' + pre + '">' + preP + '%</div>' +
+            '<div class="progress-bar bg-info" style="width: ' + curP + '%;" data-placement="bottom" data-toggle="tooltip" data-original-title="本期完成:¥' + cur + '">' + curP + '%</div>' +
+            '<div class="progress-bar bg-gray" style="width: ' + otherP + '%;" data-placement="bottom" data-toggle="tooltip" data-original-title="未完成:¥' + other + '">' + otherP + '%</div>' +
+            '</div>';
+        return html;
+    } else {
+        return '';
+    }
+}
+function recursiveGetTenderNodeHtml (node, arr, pid) {
+    const html = [];
+    html.push('<tr pid="' + pid + '">');
+    // 名称
+    if (node.cid) {
+        html.push('<td class="in-' + node.level + '">');
+        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> ', node.name);
+        html.push('</td><td></td><td></td>');
+    } else {
+        html.push('<td colspan="3" class="in-' + node.level + '">');
+        html.push('<div class="row">');
+        html.push('<span class="col-8">');
+        html.push('<span class="text-muted mr-2">');
+        html.push(arr.indexOf(node) === arr.length - 1 ? '└' : '├');
+        html.push('</span>');
+        html.push('<a href="javascript: void(0)" id="' + node.id + '">', node.name, '</a></span>');
+        html.push('<span class="col-auto ml-auto"></span>');
+        html.push('</div>');
+        html.push('<div class="d-flex justify-content-between"><span>');
+        // 计量期数
+        if (!node.cid) {
+            html.push(node.lastStage ? '第' + node.lastStage.order + '期' : '台账');
+        }
+        html.push('</span><span>');
+        // 累计合同计量
+        const sum = node.lastStage ? ZhCalc.add(node.total_price, node.lastStage.end_qc_tp) : node.total_price;
+        html.push(node.sum_tp ? node.sum_tp : '');
+        html.push('</span></div>');
+        // 截止本期累计完成/本期完成/未完成
+        if (node.lastStage) {
+            html.push(getProgressHtml(node.sum_tp, node.pre_gather_tp, node.gather_tp));
+        } else {
+            html.push('');
+        }
+        html.push('</td>');
+    }
+    html.push('</tr>');
+    if (node.children) {
+        for (const c of node.children) {
+            html.push(recursiveGetTenderNodeHtml(c, node.children, node.sort_id));
+        }
+    }
+    return html.join('');
+}
+// 根据TenderTree数据获取Html代码
+function getTenderTreeHtml () {
+    const html = [];
+    html.push('<table class="table">');
+    html.push('<thead>', '<tr>');
+    html.push('<th>名称</th>');
+    html.push('<th width="120">计量期数</th>');
+    html.push('<th>总价</th>');
+    html.push('</tr>', '</thead>');
+    if (tenderTree.length > 0) {
+        parentId = 0;
+        for (const t of tenderTree) {
+            html.push(recursiveGetTenderNodeHtml(t, tenderTree, ''));
+        }
+    } else {
+        html.push(EmptyTenderHtml.join(''));
+    }
+    html.push('</table>');
+    return html.join('');
+}
+function bindTenderUrl() {
+    $('.c-body').on('click', 'a', function () {
+        const tenderId = parseInt($(this).attr('id'));
+        const tender = _.find(tenders, function (t) {
+            return t.id === tenderId;
+        });
+        if (tender.measure_type) {
+            window.location.href = '/wap/tender/' + tenderId;
+        } else {
+            // for (const a of $('a', '#jlms')) {
+            //     a.href = '/wap/tender/' + tenderId + '/type?type=' + $(a).attr('mst');
+            // }
+            // $('#jlms').modal('show');
+        }
+    });
+}
+
+$(document).ready(() => {
+    // 初始化标段树结构
+    sortCategory();
+    initTenderTree();
+    $('.c-body').html(getTenderTreeHtml());
+    bindTenderUrl();
+    localHideList(true);
+});

+ 57 - 0
app/public/js/wap/login.js

@@ -0,0 +1,57 @@
+$(document).ready(function() {
+    const lSPName = getLocalCache('project_name');
+    const lSPCode = getLocalCache('project_code');
+    if (lSPName !== null && $('#project').attr('readonly') === undefined) {
+        $('#project_name').text(lSPName);
+        $('#project').val(lSPCode);
+        $('#forget-project').val(lSPCode);
+        $('#account').focus();
+    }
+
+    if ($('#project').attr('readonly') !== undefined) {
+        setLocalCache('project_name', $('#project_name').text());
+        setLocalCache('project_code', $('#project').val());
+        $('#account').focus();
+    }
+
+    $('#project').blur(function () {
+        if ($(this).val() == '') {
+            $('#project_name').text('');
+            $('#forget-project').val('');
+            removeLocalCache('project_code');
+            removeLocalCache('project_name');
+        } else {
+            const pcode = getLocalCache('project_code');
+            if ($(this).val() !== pcode) {
+                const pc = $(this).val();
+                $.ajax({
+                    type: 'get',
+                    url: '/project/name',
+                    data: { code: pc },
+                    dataType: 'json',
+                    success: function (result) {
+                        setLocalCache('project_code', pc);
+                        if (result.err === 1) {
+                            $('#project_name').text('');
+                            $('#forget-project').val('');
+                            console.log(result.msg);
+                            toast(result.msg, 'error', 'exclamation-circle');
+                            removeLocalCache('project_name');
+                        } else {
+                            setLocalCache('project_name', result.data);
+                            $('#project_name').text(result.data);
+                            $('#forget-project').val(pc);
+                        }
+                    }
+                })
+            }
+        }
+    });
+
+    $('input').focus(function () {
+        if($(this).hasClass('is-invalid')) {
+            $(this).removeClass('is-invalid');
+            $(this).siblings('div.invalid-feedback').html('');
+        }
+    });
+});

+ 3 - 0
app/public/report/js/rpt_custom.js

@@ -358,6 +358,9 @@ const rptCustomObj = (function () {
             type: gsObj.setting.type,
         };
         const specCol = gsObj.setting.special ? gsObj.setting.special : [];
+        for (const sc of specCol) {
+            sc.sCount = 0;
+        }
         for (const gra of gsObj.grArray) {
             const ra = {tid: gra.tid};
             for (const sc of specCol) {

+ 9 - 0
app/router.js

@@ -312,4 +312,13 @@ module.exports = app => {
     app.post('/gather/tz/load', sessionAuth, 'spssController.loadGatherTz');
     app.get('/gather/stage', sessionAuth, 'spssController.gatherStage');
     app.post('/gather/stage/load', sessionAuth, 'spssController.loadGatherStage');
+
+    // wap页面
+    app.get('/wap', 'wapController.index');
+    app.get('/wap/login', 'wapController.index');
+    app.post('/wap/login', 'wapController.login');
+    app.get('/wap/logout', 'wapController.logout');
+    app.get('/wap/dashboard', sessionAuth, 'wapController.dashboard');
+    app.get('/wap/list', sessionAuth, 'wapController.list');
+    app.get('/wap/tender/:id', sessionAuth, tenderCheck, 'wapController.tender');
 };

+ 13 - 0
app/service/material.js

@@ -230,6 +230,19 @@ module.exports = app => {
             };
             return await this.db.update(this.tableName, updateData);
         }
+
+        /**
+         * 修改增税税率
+         * @param {int} tid 标段id
+         * @param {int} order 调差期数
+         * @returns {Promise<*>}
+         */
+        async getPreTpHs(tid, order) {
+            const sql = 'SELECT SUM(ROUND(`m_tp`*(1+ `rate`/100),2)) AS `pre_tp_hs` FROM ?? WHERE `tid` = ? AND `order` < ?';
+            const sqlParam = [this.tableName, tid, order];
+            const result = await this.db.queryOne(sql, sqlParam);
+            return result.pre_tp_hs;
+        }
     }
 
     return Material;

+ 14 - 11
app/service/report_memory.js

@@ -193,13 +193,12 @@ module.exports = app => {
             }
         }
 
-        async getStageImTzData(tid, sid, fields) {
+        async getStageImTzData(tid, sid, fields, readCache = true) {
             await this.ctx.service.tender.checkTender(tid);
             await this.ctx.service.stage.checkStage(sid);
-            const cache = await this._getReportMemoryCache('mem_stage_im_tz', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
-            if (cache) {
-                // console.log('cache');
-                return cache;
+            if (readCache) {
+                const cache = await this._getReportMemoryCache('mem_stage_im_tz', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
+                if (cache) return cache;
             }
 
             // console.log('build');
@@ -218,11 +217,13 @@ module.exports = app => {
             return this.stageImData.main;
         }
 
-        async getStageImTzBillsData(tid, sid, fields) {
+        async getStageImTzBillsData(tid, sid, fields, readCache = true) {
             await this.ctx.service.tender.checkTender(tid);
             await this.ctx.service.stage.checkStage(sid);
-            const cache = await this._getReportMemoryCache('mem_stage_im_tz_bills', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
-            if (cache) return cache;
+            if (readCache) {
+                const cache = await this._getReportMemoryCache('mem_stage_im_tz_bills', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
+                if (cache) return cache;
+            }
 
             if (!this.stageImData) {
                 this.stageImData = {};
@@ -239,11 +240,13 @@ module.exports = app => {
             return this.stageImData.bills;
         }
 
-        async getStageImZlData(tid, sid, fields) {
+        async getStageImZlData(tid, sid, fields, readCache = true) {
             await this.ctx.service.tender.checkTender(tid);
             await this.ctx.service.stage.checkStage(sid);
-            const cache = await this._getReportMemoryCache('mem_stage_im_zl', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
-            if (cache) return cache;
+            if (readCache) {
+                const cache = await this._getReportMemoryCache('mem_stage_im_zl', tid, sid, this.ctx.stage.cacheTime, stageImVersion);
+                if (cache) return cache;
+            }
 
             this.stageImData = {};
             try {

+ 2 - 1
app/service/rpt_gather_memory.js

@@ -17,6 +17,7 @@ const payConst = require('../const/deal_pay');
 const moment = require('moment');
 
 const indexPre = 'id_';
+const normalPrefix = 't_', sumPrefix = 's_', specPrefix = 'ts_';
 
 const gatherUtils = {
     completeGatherData: function (datas, completeDatas) {
@@ -770,7 +771,7 @@ module.exports = app => {
         async _gatherSpecialDealBills(sTender, sKey) {
             const tender = await this.ctx.service.tender.getCheckTender(sTender.tid);
             await this._gatherDealBills(tender, function (gatherData, sourceData) {
-                const prefix = 'st_' + sKey + '_';
+                const prefix = 'ts_' + sKey + '_';
 
                 gatherData[prefix + 'qty'] = sourceData.quantity;
                 gatherData[prefix + 'tp'] = sourceData.total_price;

+ 14 - 0
app/service/stage.js

@@ -430,6 +430,20 @@ module.exports = app => {
                     }
                 }
                 await transaction.delete(this.ctx.service.stageAtt.tableName, { sid: id });
+                // 其他台账
+                await transaction.delete(this.ctx.service.stageJgcl.tableName, { sid: id });
+                const bonus = await this.ctx.service.stageBonus.getStageData(id);
+                if (bonus && bonus.length > 0) {
+                    for (const b of bonus) {
+                        for (const f of b.proof_file) {
+                            if (fs.existsSync(path.join(this.app.baseDir, f.filepath))) {
+                                await fs.unlinkSync(path.join(this.app.baseDir, f.filepath));
+                            }
+                        }
+                    }
+                }
+                await transaction.delete(this.ctx.service.stageBonus.tableName, { sid: id });
+                await transaction.delete(this.ctx.service.stageOther.tableName, { sid: id });
                 await transaction.commit();
                 return true;
             } catch (err) {

+ 30 - 4
app/service/stage_audit.js

@@ -245,7 +245,8 @@ module.exports = app => {
                         const tenderName = await sms.contentChange(tenderInfo.name);
                         const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
                         const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
-                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。';
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
                 }
@@ -302,8 +303,10 @@ module.exports = app => {
                             const sms = new SMS(this.ctx);
                             const tenderName = await sms.contentChange(tenderInfo.name);
                             const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
+                            const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                            // const result = '';
                             const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
-                            const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。';
+                            const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                             sms.send(smsUser.auth_mobile, content);
                         }
                     }
@@ -513,7 +516,9 @@ module.exports = app => {
                         const tenderName = await sms.contentChange(tenderInfo.name);
                         const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
                         const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
-                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。';
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        // const result = '';
+                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
                 }
@@ -729,7 +734,8 @@ module.exports = app => {
                         const tenderName = await sms.contentChange(tenderInfo.name);
                         const projectName = await sms.contentChange(this.ctx.tender.info.deal_info.buildName);
                         const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
-                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。';
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
                 }
@@ -895,6 +901,26 @@ module.exports = app => {
             const rst = await this.db.query(sql, sqlParam);
             return rst;
         }
+
+
+        /**
+         * 取待审批期列表(wap用)
+         *
+         * @param auditorId
+         * @returns {Promise<*>}
+         */
+        async getAuditStageByWap(auditorId) {
+            const sql = 'SELECT sa.`aid`, sa.`times`, sa.`begin_time`, sa.`end_time`, sa.`tid`, sa.`sid`,' +
+                // '    s.`order` As `sorder`, s.`status` As `sstatus`, s.`s_time`, s.`contract_tp`, s.`qc_tp`, s.`pre_contract_tp`, s.`pre_qc_tp`, s.`yf_tp`, s.`pre_yf_tp`, ' +
+                '    s.*,' +
+                '    t.`name`, t.`project_id`, t.`type`, t.`user_id`,' +
+                '    ti.`deal_info` ' +
+                '  FROM ?? AS sa, ?? AS s, ?? As t, ?? AS ti ' +
+                '  WHERE sa.`aid` = ? and sa.`status` = ?' +
+                '    and sa.`sid` = s.`id` and sa.`tid` = t.`id` and ti.`tid` = t.`id`';
+            const sqlParam = [this.tableName, this.ctx.service.stage.tableName, this.ctx.service.tender.tableName, this.ctx.service.tenderInfo.tableName, auditorId, auditConst.status.checking];
+            return await this.db.query(sql, sqlParam);
+        }
     }
 
     return StageAudit;

+ 0 - 1
app/service/stage_bills.js

@@ -228,7 +228,6 @@ module.exports = app => {
             if (ledgerData.is_tp && insertData.contract_tp !== undefined) {
                 d.contract_tp = this.ctx.helper.round(insertData.contract_tp, info.decimal.tp);
             }
-            console.log(d);
             await transaction.insert(this.tableName, d);
         }
 

+ 1 - 1
app/service/stage_bonus.js

@@ -66,7 +66,7 @@ module.exports = app => {
         }
 
         async getStageDataById(bonusId) {
-            const data = await this.getAllDataByCondition({ where: { sid: this.ctx.stage.id, id: bonusId } });
+            const data = await this.getAllDataByCondition({ where: { id: bonusId } });
             this._parseData(data);
             return data[0];
         }

+ 3 - 1
app/service/stage_pay.js

@@ -99,7 +99,9 @@ module.exports = app => {
                     stagePays.push({
                         tid: p.tid, sid: stage.id, pid: p.id,
                         stimes: stage.times, sorder: 0,
-                        name: pp.name, expr: pp.expr, pause: pp.pause,
+                        name: pp.name,
+                        expr: (p.ptype === payConst.payType.normal || p.ptype === payConst.payType.wc) ? pp.expr : null,
+                        pause: pp.pause,
                         pre_tp: pp.end_tp,
                         pre_used: pp.pre_used || !this.ctx.helper.checkZero(pp.tp),
                         pre_finish: pp.pre_finish || (pp.rprice ? pp.end_tp === pp.rprice : false),

+ 100 - 0
app/view/change/info_modal.ejs

@@ -472,6 +472,56 @@
                             </div>
                         </div>
                         <div class="col-8 modal-height-500" style="overflow: auto">
+                            <% for (const time in auditList4) { %>
+                                <div class="card mt-3">
+                                    <ul class="list-group list-group-flush">
+                                        <% for (const [aindex,al] of auditList4[time].entries()) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <% if (al.usite === 0 && al.status === 2 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90"></i>
+                                                    <% } else if (al.usite === 0 && al.status === 3 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90 text-success"></i>
+                                                    <% } else if (al.status === 1 || al.status === 2) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> "></i>
+                                                    <% } else if (al.status === 3) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-success"></i>
+                                                    <% } else if (al.status === 4) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-danger"></i>
+                                                    <% } else if (al.status === 5 || al.status === 6) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-warning"></i>
+                                                    <% } else if (al.status === 7) { %>
+                                                        <i class="fa fa-chevron-circle-down text-warning"></i>
+                                                    <% } %>
+                                                    <%= al.name %>&nbsp;<small class="text-muted"><%= al.jobs %></small><span class="pull-right"><%= al.usite === 0 ? '原报' : (al.max_sort === al.usite ? '终审' : ctx.helper.transFormToChinese(al.usite) + '审')  %></span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (al.usite === 0 && al.status === 2) { %>
+                                                        <span>重新上报中</span>
+                                                    <% } else if (al.usite === 0 && al.status === 3 && al.times === 1) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>上报</span>
+                                                    <% } else if (al.usite === 0 && al.status === 3 && al.times !== 1) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>重新上报</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 2) { %>
+                                                        <span>审批中</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 3) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批通过</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 4) { %>
+                                                        <span class="text-danger"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批终止</span>
+                                                    <% } else if (al.usite !== 0 && (al.status === 5 || al.status === 6)) { %>
+                                                        <span class="text-warning"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批退回 <% if (al.status === 5) {%><%= auditList4[time][0].name %><% } %></span>
+                                                    <% } else if (al.usite !== 0 && al.status === 7) { %>
+                                                        <span class="text-warning"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>重新审批</span>
+                                                    <% } %>
+                                                    <% if (al.sdesc !== '' && al.sdesc !== null) { %>
+                                                        <p class="card-text"><%- al.sdesc %></p>
+                                                    <% } %>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    </ul>
+                                </div>
+                            <% } %>
                             <div class="card mt-3">
                                 <ul class="list-group list-group-flush">
                                     <% for (const [index,a] of auditList3.entries()) { %>
@@ -569,6 +619,56 @@
                             </div>
                         </div>
                         <div class="col-8 modal-height-500" style="overflow: auto">
+                            <% for (const time in auditList4) { %>
+                                <div class="card mt-3">
+                                    <ul class="list-group list-group-flush">
+                                        <% for (const [aindex,al] of auditList4[time].entries()) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <% if (al.usite === 0 && al.status === 2 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90"></i>
+                                                    <% } else if (al.usite === 0 && al.status === 3 ) { %>
+                                                        <i class="fa fa-play-circle fa-rotate-90 text-success"></i>
+                                                    <% } else if (al.status === 1 || al.status === 2) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> "></i>
+                                                    <% } else if (al.status === 3) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-success"></i>
+                                                    <% } else if (al.status === 4) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-danger"></i>
+                                                    <% } else if (al.status === 5 || al.status === 6) { %>
+                                                        <i class="fa <% if (aindex+1 === auditList4[time].length) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> text-warning"></i>
+                                                    <% } else if (al.status === 7) { %>
+                                                        <i class="fa fa-chevron-circle-down text-warning"></i>
+                                                    <% } %>
+                                                    <%= al.name %>&nbsp;<small class="text-muted"><%= al.jobs %></small><span class="pull-right"><%= al.usite === 0 ? '原报' : (al.max_sort === al.usite ? '终审' : ctx.helper.transFormToChinese(al.usite) + '审')  %></span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (al.usite === 0 && al.status === 2) { %>
+                                                        <span>重新上报中</span>
+                                                    <% } else if (al.usite === 0 && al.status === 3 && al.times === 1) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>上报</span>
+                                                    <% } else if (al.usite === 0 && al.status === 3 && al.times !== 1) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>重新上报</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 2) { %>
+                                                        <span>审批中</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 3) { %>
+                                                        <span class="text-success"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批通过</span>
+                                                    <% } else if (al.usite !== 0 && al.status === 4) { %>
+                                                        <span class="text-danger"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批终止</span>
+                                                    <% } else if (al.usite !== 0 && (al.status === 5 || al.status === 6)) { %>
+                                                        <span class="text-warning"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>审批退回 <% if (al.status === 5) {%><%= auditList4[time][0].name %><% } %></span>
+                                                    <% } else if (al.usite !== 0 && al.status === 7) { %>
+                                                        <span class="text-warning"><% if (al.sin_time !== null) { %><small><%= moment(al.sin_time).format('YYYY-MM-DD') %></small> <% } %>重新审批</span>
+                                                    <% } %>
+                                                    <% if (al.sdesc !== '' && al.sdesc !== null) { %>
+                                                        <p class="card-text"><%- al.sdesc %></p>
+                                                    <% } %>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    </ul>
+                                </div>
+                            <% } %>
                             <div class="card mt-3">
                                 <ul class="list-group list-group-flush">
                                     <% for (const [index,a] of auditList3.entries()) { %>

+ 2 - 1
app/view/layout/layout.ejs

@@ -15,13 +15,14 @@
     <link rel="stylesheet" href="/public/css/ztree/zTreeStyle.css" type="text/css">
     <link rel="stylesheet" href="/public/css/datepicker/datepicker.min.css" type="text/css">
     <link rel="stylesheet" href="/public/css/toastr.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
     <!-- JS. -->
     <% for (const file of jsFiles) { %>
     <script type="text/javascript" src="<%- file %>"></script>
     <% } %>
 </head>
 
-<body oncontextmenu="return false;">
+<body oncontextmenu="return false;"> <!--屏蔽input鼠标右键-->
 <div>
 
 </div>

+ 106 - 0
app/view/ledger/audit_modal.ejs

@@ -27,6 +27,59 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === 0) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- user.name %> <small class="text-muted"><%- user.role %></small><span class="pull-right">原报</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <span class="text-success"><small><%- ah[iA].begin_time.toLocaleDateString() %></small> <% if (auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                </div>
+                                            </li>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <% if (tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked) {%>
                             <div class="card mt-3">
                                 <ul class="list-group list-group-flush">
@@ -114,6 +167,59 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === 0) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- user.name %> <small class="text-muted"><%- user.role %></small><span class="pull-right">原报</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <span class="text-success"><small><%- ah[iA].begin_time.toLocaleDateString() %></small> <% if (auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                </div>
+                                            </li>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <% if (tender.ledger_status === auditConst.status.checking || tender.ledger_status === auditConst.status.checked) {%>
                             <div class="card mt-3">
                                 <ul class="list-group list-group-flush">

+ 0 - 2
app/view/ledger/bwtz.ejs

@@ -19,11 +19,9 @@
                         </div>
                     </div>
                 </div>
-                <% if (ctx.app.config.is_debug) { %>
                 <div class="d-inline-block ml-3">
                     <a id="exportBwtz" class="btn btn-primary btn-sm" href="javascript: void(0)">导出部位台账Excel</a>
                 </div>
-                <% } %>
             </div>
             <div class="ml-auto">
             </div>

+ 5 - 0
app/view/ledger/explode.ejs

@@ -138,6 +138,8 @@
                         <div id="deal-bills-spread" class="sjs-sh-4">
                         </div>
                     </div>
+                    <div id="error-list" class="tab-pane">
+                    </div>
                 </div>
             </div>
         </div>
@@ -156,6 +158,9 @@
                 <li class="nav-item">
                     <a class="nav-link" content="#deal-bills" href="javascript: void(0);">签约清单</a>
                 </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#error-list" id="error-list-tab" href="javascript: void(0);" style="display: none;">错误列表</a>
+                </li>
             </ul>
         </div>
     </div>

+ 4 - 3
app/view/ledger/explode_modal.ejs

@@ -129,10 +129,10 @@
                     </div>
                 </div>
             </div>
-            <form class="modal-footer" method="post" action="/tender/<%- tender.id %>/ledger/audit/start" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" method="post" action="/tender/<%- tender.id %>/ledger/audit/start" name="audit-start">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
-                <button class="btn btn-primary btn-sm" type="submit">确认上报</button>
+                <button class="btn btn-primary btn-sm" type="submit" >确认上报</button>
             </form>
         </div>
     </div>
@@ -257,7 +257,7 @@
                     </div>
                 </div>
             </div>
-            <form class="modal-footer" action="/tender/<%- tender.id %>/ledger/audit/start" method="post" onsubmit="return checkAuditorFrom()">
+            <form class="modal-footer" action="/tender/<%- tender.id %>/ledger/audit/start" method="post" name="audit-start">
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
                 <button type="submit" class="btn btn-primary btn-sm">确认上报</button>
@@ -412,3 +412,4 @@
 <% include ../shares/merge_peg_modal.ejs %>
 <% include ../shares/import_excel_modal.ejs %>
 <% include ../shares/delete_hint_modal.ejs %>
+<% include ../shares/check_data_modal.ejs %>

+ 66 - 54
app/view/login/login.ejs

@@ -5,66 +5,78 @@
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
     <title>用户登录-计量支付</title>
-    <link rel="stylesheet" href="public/css/bootstrap/bootstrap.min.css">
-    <link rel="stylesheet" href="public/css/main.css">
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/main.css">
     <link rel="stylesheet" href="/public/css/toast.css">
-    <link rel="stylesheet" href="public/css/font-awesome/font-awesome.min.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        html{height:100%;}
+    </style>
 </head>
 <body class="login-body">
+<div class="login-bg img-<%- Math.floor(Math.random()*3 + 1); %>"></div>
 <div class="container">
-    <% if (maintainData.status === maintainConst.status.ongoing) { %>
-    <form class="form-signin">
-        <h4 class="text-center mb-3"><i class="fa fa-wrench"></i>系统正在维护</h4>
-        <h4>预计恢复时间<%- (maintainData.duration !== maintainConst.duration.forever ? '为 ' + ctx.helper.dateTran(parseFloat(maintainData.maintain_time) + ctx.helper.timeAdd(maintainData.duration)) : ' 暂未确定') %></h4>
-        <h4>造成不便敬请谅解。</h4>
-    </form>
-    <% } else { %>
-    <!--演示版-->
-    <form class="form-signin" method="post" action="/login">
-        <h4 class="text-center mb-2">纵横云计量</h4>
-        <h5 class="text-center mb-4 text-muted" id="project_name"></h5>
-        <!--<nav class="nav nav-tabs nav-justified mb-3" role="tablist" id="login-tab">-->
-            <!--<a class="nav-item nav-link" data-toggle="tab" data-type="1" href="#preview" role="tab">演示版登录</a>-->
-            <!--<a class="nav-item nav-link active" data-toggle="tab" data-type="2" href="#paid" role="tab">项目版登录</a>-->
-        <!--</nav>-->
-        <div class="tab-content">
-            <!--<div class="tab-pane active" id="preview" role="tabpanel">-->
-                <!--<div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">-->
-                    <!--<input id="username" name="username" class="form-control form-control-sm" placeholder="通行账号 邮箱/手机" value="laiku123@qq.com" autofocus="">-->
-                <!--</div>-->
-                <!--<div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">-->
-                    <!--<input id="password" name="password" class="form-control form-control-sm" placeholder="输入密码" value="19930523" type="password">-->
-                <!--</div>-->
-            <!--</div>-->
-            <div class="tab-pane active" id="paid" role="tabpanel">
-                <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
-                    <input id="project" class="form-control" name="project" placeholder="项目编号" autofocus="" />
-                </div>
-                <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
-                    <input id="account" class="form-control" name="account" placeholder="输入账号" autofocus="" />
-                </div>
-                <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
-                    <input id="project-password" name="project_password" class="form-control" placeholder="输入密码" type="password" />
-                </div>
-            </div>
-            <div class="form-group">
-                <div class="alert alert-danger" <% if(errorMessage === undefined || errorMessage === null) { %>style="display: none"<% } %> role="alert" id="error-msg">
-                    <% if(errorMessage !== undefined && errorMessage !== null) { %><strong>登录失败</strong> <%= errorMessage %><% } %>
-                </div>
-            </div>
+    <div class="row">
+        <div class="col-6">
+            <img src="/public/images/loginlogo.png">
         </div>
-        <div class="form-group">
-            <button class="btn btn-primary btn-block" type="submit">登录</button>
-            <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
-            <input type="hidden" name="type" value="2" />
+        <div class="col-6">
+            <% if (maintainData.status === maintainConst.status.ongoing) { %>
+                <form class="form-signin">
+                    <h4 class="text-center mb-3"><i class="fa fa-wrench"></i>系统正在维护</h4>
+                    <h4>预计恢复时间<%- (maintainData.duration !== maintainConst.duration.forever ? '为 ' + ctx.helper.dateTran(parseFloat(maintainData.maintain_time) + ctx.helper.timeAdd(maintainData.duration)) : ' 暂未确定') %></h4>
+                    <h4>造成不便敬请谅解。</h4>
+                </form>
+            <% } else { %>
+                <!--演示版-->
+                <form class="form-signin" method="post" action="/login">
+                    <h4 class="text-center mb-2">纵横云计量</h4>
+                    <h5 class="text-center mb-4 text-muted" id="project_name"></h5>
+                    <!--<nav class="nav nav-tabs nav-justified mb-3" role="tablist" id="login-tab">-->
+                    <!--<a class="nav-item nav-link" data-toggle="tab" data-type="1" href="#preview" role="tab">演示版登录</a>-->
+                    <!--<a class="nav-item nav-link active" data-toggle="tab" data-type="2" href="#paid" role="tab">项目版登录</a>-->
+                    <!--</nav>-->
+                    <div class="tab-content">
+                        <!--<div class="tab-pane active" id="preview" role="tabpanel">-->
+                        <!--<div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">-->
+                        <!--<input id="username" name="username" class="form-control form-control-sm" placeholder="通行账号 邮箱/手机" value="laiku123@qq.com" autofocus="">-->
+                        <!--</div>-->
+                        <!--<div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">-->
+                        <!--<input id="password" name="password" class="form-control form-control-sm" placeholder="输入密码" value="19930523" type="password">-->
+                        <!--</div>-->
+                        <!--</div>-->
+                        <div class="tab-pane active" id="paid" role="tabpanel">
+                            <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                                <input id="project" class="form-control" name="project" placeholder="项目编号" autofocus="" />
+                            </div>
+                            <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                                <input id="account" class="form-control" name="account" placeholder="输入账号" autofocus="" />
+                            </div>
+                            <div class="form-group <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                                <input id="project-password" name="project_password" class="form-control" placeholder="输入密码" type="password" />
+                            </div>
+                        </div>
+                        <div class="form-group">
+                            <div class="alert alert-danger" <% if(errorMessage === undefined || errorMessage === null) { %>style="display: none"<% } %> role="alert" id="error-msg">
+                                <% if(errorMessage !== undefined && errorMessage !== null) { %><strong>登录失败</strong> <%= errorMessage %><% } %>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <button class="btn btn-primary btn-block" type="submit">登录</button>
+                        <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                        <input type="hidden" name="type" value="2" />
+                    </div>
+                    <div class="pt-1 d-flex justify-content-end">
+                        <a href="#fg-password" data-toggle="modal" data-target="#fg-password"  class="mr-3">忘记密码?</a>
+                    </div>
+                </form>
+            <% } %>
+            <!--项目版-->
         </div>
-        <div class="pt-1 d-flex justify-content-end">
-            <a href="#fg-password" data-toggle="modal" data-target="#fg-password"  class="mr-3">忘记密码?</a>
-        </div>
-    </form>
-    <% } %>
-    <!--项目版-->
-    <div class="text-white fixed-bottom"><p class="text-center mb-1">Copyright © 2018 <a href="https://smartcost.com.cn" target="_blank" class="text-white">珠海纵横创新软件有限公司</a>.All Rights Reserved.<a class="text-white ml-2" href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a></p></div>
+    </div>
+    <div class="text-white fixed-bottom"><p class="text-center mb-1">Copyright © 2019 <a href="https://smartcost.com.cn" target="_blank" class="text-white">珠海纵横创新软件有限公司</a>.All Rights Reserved.<a class="text-white ml-2" href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a></p></div>
 </div>
 <!--忘记项目版密码-->
 <div class="modal fade" id="fg-password" data-backdrop="static">

+ 44 - 32
app/view/login/login_port.ejs

@@ -9,42 +9,54 @@
     <link rel="stylesheet" href="/public/css/main.css">
     <link rel="stylesheet" href="/public/css/toast.css">
     <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        html{height:100%;}
+    </style>
 </head>
 <body class="login-body">
+<div class="login-bg img-<%- Math.floor(Math.random()*3 + 1); %>"></div>
 <div class="container">
-    <% if (maintainData.status === maintainConst.status.ongoing) { %>
-    <form class="form-signin">
-        <h4 class="text-center mb-3"><i class="fa fa-wrench"></i>系统正在维护</h4>
-        <h4>预计恢复时间<%- (maintainData.duration !== maintainConst.duration.forever ? '为 ' + ctx.helper.dateTran(parseFloat(maintainData.maintain_time) + ctx.helper.timeAdd(maintainData.duration)) : ' 暂未确定') %></h4>
-        <h4>造成不便敬请谅解。</h4>
-    </form>
-    <% } else { %>
-    <!--演示版-->
-    <form class="form-signin" method="post" action="/login/port">
-        <h4 class="text-center mb-2">纵横云计量</h4>
-        <h5 class="text-center mb-4 text-muted"><%- projectData ? projectData.name : '' %></h5>
-        <% if (accountData) { %>
-            <h5>您好,<%- accountData.mobile %></h5>
-            <% if (accountData.bind === 0) { %>
-            <!--查询账号存在,进行绑定-->
-            <h5>系统查询以下账号,请确认无误后进行绑定。</h5>
-            <h5>姓名:<span class="text-danger"><%- accountData.name %></span></h5>
-            <h5>账号:<span class="text-danger"><%- accountData.account %></span></h5>
-            <h5>手机:<span class="text-danger"><%- accountData.mobile %></span></h5>
-            <h5>单位:<span class="text-danger"><%- accountData.company %></span></h5>
-            <h5>职称:<span class="text-danger"><%- accountData.role %></span></h5>
-            <div class="form-group mt-4">
-                <input type="hidden" value="<%= ctx.csrf %>" name="_csrf" >
-                <input type="hidden" value="3" name="type" >
-                <input type="hidden" value="<%= projectData.code %>" name="code" >
-                <input type="hidden" value="<%= accountData.id %>" name="accountId" >
-                <button type="submit" class="btn btn-primary btn-block">绑定并登录系统</button>
-            </div>
+    <div class="row">
+        <div class="col-6">
+            <img src="/public/images/loginlogo.png">
+        </div>
+        <div class="col-6">
+            <% if (maintainData.status === maintainConst.status.ongoing) { %>
+                <form class="form-signin">
+                    <h4 class="text-center mb-3"><i class="fa fa-wrench"></i>系统正在维护</h4>
+                    <h4>预计恢复时间<%- (maintainData.duration !== maintainConst.duration.forever ? '为 ' + ctx.helper.dateTran(parseFloat(maintainData.maintain_time) + ctx.helper.timeAdd(maintainData.duration)) : ' 暂未确定') %></h4>
+                    <h4>造成不便敬请谅解。</h4>
+                </form>
+            <% } else { %>
+                <!--演示版-->
+                <form class="form-signin" method="post" action="/login/port">
+                    <h4 class="text-center mb-2">纵横云计量</h4>
+                    <h5 class="text-center mb-4 text-muted"><%- projectData ? projectData.name : '' %></h5>
+                    <% if (accountData) { %>
+                        <h5>您好,<%- accountData.mobile %></h5>
+                        <% if (accountData.bind === 0) { %>
+                            <!--查询账号存在,进行绑定-->
+                            <h5>系统查询以下账号,请确认无误后进行绑定。</h5>
+                            <h5>姓名:<span class="text-danger"><%- accountData.name %></span></h5>
+                            <h5>账号:<span class="text-danger"><%- accountData.account %></span></h5>
+                            <h5>手机:<span class="text-danger"><%- accountData.mobile %></span></h5>
+                            <h5>单位:<span class="text-danger"><%- accountData.company %></span></h5>
+                            <h5>职称:<span class="text-danger"><%- accountData.role %></span></h5>
+                            <div class="form-group mt-4">
+                                <input type="hidden" value="<%= ctx.csrf %>" name="_csrf" >
+                                <input type="hidden" value="3" name="type" >
+                                <input type="hidden" value="<%= projectData.code %>" name="code" >
+                                <input type="hidden" value="<%= accountData.id %>" name="accountId" >
+                                <button type="submit" class="btn btn-primary btn-block">绑定并登录系统</button>
+                            </div>
+                        <% } %>
+                    <% } %>
+                    <h5 class="text-danger"><%- errorMessage %></h5>
+                </form>
             <% } %>
-        <% } %>
-        <h5 class="text-danger"><%- errorMessage %></h5>
-    </form>
-    <% } %>
+        </div>
+    </div>
     <!--项目版-->
     <div class="text-white fixed-bottom"><p class="text-center mb-1">Copyright © 2019 <a href="https://smartcost.com.cn" target="_blank" class="text-white">珠海纵横创新软件有限公司</a>.All Rights Reserved.<a class="text-white ml-2" href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a></p></div>
 </div>

+ 106 - 0
app/view/material/audit_modal.ejs

@@ -82,6 +82,59 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of ctx.material.auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === 0) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.material.user.name %> <small class="text-muted"><%- ctx.material.user.role %></small><span class="pull-right">原报</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <span class="text-success"><small><%- ah[iA].begin_time.toLocaleDateString() %></small> <% if (ctx.material.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                </div>
+                                            </li>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                        <li class="list-group-item">
+                                            <h5 class="card-title">
+                                                <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                            </h5>
+                                            <div class="ml-3">
+                                                <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                    <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                <% } %>
+                                                <p class="card-text"><%- ah[iA].opinion %></p>
+                                            </div>
+                                        </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <div class="card mt-3">
                             <ul class="list-group list-group-flush">
                                 <li class="list-group-item">
@@ -170,6 +223,59 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of ctx.material.auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === 0) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.material.user.name %> <small class="text-muted"><%- ctx.material.user.role %></small><span class="pull-right">原报</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <span class="text-success"><small><%- ah[iA].begin_time.toLocaleDateString() %></small> <% if (ctx.material.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                </div>
+                                            </li>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                        <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                        <li class="list-group-item">
+                                            <h5 class="card-title">
+                                                <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ah[iA].sort === ah[iA].max_sort ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                            </h5>
+                                            <div class="ml-3">
+                                                <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                    <span class="<%- auditConst.statusClass[ah[iA].status] %>"><% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.material.user.name %><% } %></span>
+                                                <% } %>
+                                                <p class="card-text"><%- ah[iA].opinion %></p>
+                                            </div>
+                                        </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <div class="card mt-3">
                             <ul class="list-group list-group-flush">
                                 <li class="list-group-item">

+ 3 - 2
app/view/material/info.ejs

@@ -48,8 +48,8 @@
                         <div class="col-4 p-0">
                             <table class="table table-sm table-bordered">
                                 <tr><th></th><th>本期金额</th><th>截止本期金额</th></tr>
-                                <tr id="tp_set"><td>材料价差费用</td><td><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td></tr>
-                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(ctx.helper.add(material.pre_tp, material.m_tp), 1+material.rate/100), 2) : null %></td></tr>
+                                <tr id="tp_set"><td>材料价差费用</td><td><%= material.m_tp !== null ? ctx.helper.round(material.m_tp, 2) : null %></td><td><%= material.m_tp !== null || material.pre_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td></tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td><td><%= material.m_tp !== null || pre_tp_hs !== null ? ctx.helper.round(ctx.helper.add(pre_tp_hs, ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2)), 2) : null %></td></tr>
                             </table>
                         </div>
                     </div>
@@ -78,5 +78,6 @@
     const materialID = <%- material.id %>;
     let m_tp = <%= material.m_tp !== null ? material.m_tp : 0 %>;
     const pre_tp = <%= material.pre_tp !== null ? material.pre_tp : 0 %>;
+    const pre_tp_hs = <%= pre_tp_hs !== null ? pre_tp_hs : 0 %>;
     const calcBase = JSON.parse('<%- JSON.stringify(calcBase) %>');
 </script>

+ 82 - 0
app/view/revise/info_modal.ejs

@@ -229,6 +229,47 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <li class="list-group-item">
+                                        <h5 class="card-title">
+                                            <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- user.name %> <small class="text-muted"><%- user.role %></small><span class="pull-right">原报</span>
+                                        </h5>
+                                        <div class="ml-3">
+                                            <span class="text-success"><%- ah[0].begin_time.toLocaleDateString() %> <% if (auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                        </div>
+                                    </li>
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- audit.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== audit.status.uncheck) { %>
+                                                        <span class="<%- audit.statusClass[ah[iA].status] %>"><% if (ah[iA].status === audit.status.checked || ah[iA].status === audit.status.checkNo) { %><%- ah[iA].end_time.toLocaleDateString() %> <% } %><%- audit.statusString[ah[iA].status]%><% if (ah[iA].status === audit.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-chevron-circle-down <%- audit.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ctx.helper.transFormToChinese(iA+1) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== audit.status.uncheck) { %>
+                                                        <span class="<%- audit.statusClass[ah[iA].status] %>"><% if (ah[iA].status === audit.status.checked || ah[iA].status === audit.status.checkNo) { %><%- ah[iA].end_time.toLocaleDateString() %> <% } %><%- audit.statusString[ah[iA].status]%><% if (ah[iA].status === audit.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <div class="card mt-3">
                             <ul class="list-group list-group-flush">
                                 <li class="list-group-item">
@@ -309,6 +350,47 @@
                         </div>
                     </div>
                     <div class="col-8 modal-height-500" style="overflow: auto">
+                        <% for (const ah of auditHistory) { %>
+                            <div class="card mt-3">
+                                <ul class="list-group list-group-flush">
+                                    <li class="list-group-item">
+                                        <h5 class="card-title">
+                                            <i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- user.name %> <small class="text-muted"><%- user.role %></small><span class="pull-right">原报</span>
+                                        </h5>
+                                        <div class="ml-3">
+                                            <span class="text-success"><%- ah[0].begin_time.toLocaleDateString() %> <% if (auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                        </div>
+                                    </li>
+                                    <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                        <% if (iA === ah.length - 1) { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-stop-circle <%- audit.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== audit.status.uncheck) { %>
+                                                        <span class="<%- audit.statusClass[ah[iA].status] %>"><% if (ah[iA].status === audit.status.checked || ah[iA].status === audit.status.checkNo) { %><%- ah[iA].end_time.toLocaleDateString() %> <% } %><%- audit.statusString[ah[iA].status]%><% if (ah[iA].status === audit.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } else { %>
+                                            <li class="list-group-item">
+                                                <h5 class="card-title">
+                                                    <i class="fa fa-chevron-circle-down <%- audit.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= ctx.helper.transFormToChinese(iA+1) %>审</span>
+                                                </h5>
+                                                <div class="ml-3">
+                                                    <% if (ah[iA].status !== audit.status.uncheck) { %>
+                                                        <span class="<%- audit.statusClass[ah[iA].status] %>"><% if (ah[iA].status === audit.status.checked || ah[iA].status === audit.status.checkNo) { %><%- ah[iA].end_time.toLocaleDateString() %> <% } %><%- audit.statusString[ah[iA].status]%><% if (ah[iA].status === audit.status.checkNo) { %> <%- user.name %><% } %></span>
+                                                    <% } %>
+                                                    <p class="card-text"><%- ah[iA].opinion %></p>
+                                                </div>
+                                            </li>
+                                        <% } %>
+                                    <% } %>
+                                </ul>
+                            </div>
+                        <% } %>
                         <div class="card mt-3">
                             <ul class="list-group list-group-flush">
                                 <li class="list-group-item">

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

@@ -169,7 +169,7 @@
                 <input type="hidden" name="_csrf" value="<%= ctx.csrf %>">
                 <input type="hidden" name="id" id="account_id" value="">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
-                <button type="button" class="btn btn-sm btn-sm btn-primary">确定解绑</button>
+                <button type="submit" class="btn btn-sm btn-sm btn-primary">确定解绑</button>
             </form>
         </div>
     </div>

+ 86 - 0
app/view/shares/check_data_modal.ejs

@@ -0,0 +1,86 @@
+<!--上报审批 自检-->
+<div class="modal fade" id="check" 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">
+                <h5>数据计算中,完成后会自动进入审批流程设置。</h5>
+                <div class="progress">
+                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+<!--上报审批 自检 计算错误-->
+<div class="modal fade" id="check-error-hint" 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="alert alert-danger" role="alert">
+                    部分清单存在问题,请前往错误列表进行查看,并进行修改。
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-primary" data-dismiss="modal" id="show-error-list">查看错误列表</button>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    /**
+     *
+     * @param setting
+     * {
+     *      loadUrl,
+     *      loadData,
+     *      checkFun,
+     *      errorList
+     * }
+     */
+    const DataChecker = function (setting) {
+        $('#show-error-list').click(function () {
+            $('#check').modal('hide');
+            setting.errorList.show();
+        });
+        const loadCheckData = function () {
+            const promise = new Promise(function (resolve, reject) {
+                postData(setting.loadUrl, setting.loadData, function (result) {
+                    resolve(result);
+                });
+            });
+            return promise;
+        }
+        const progress = function (percent) {
+            $('.progress-bar').attr('aria-valuenow', percent).width(percent + '%').html(percent + '%');
+        }
+        const addProgress = function (percent) {
+            const oldPercent = parseInt($('.progress-bar').attr('aria-valuenow'));
+            progress(oldPercent + percent);
+        }
+        const checkAndPost = async function (postUrl, postForm) {
+            progress(0);
+            $('#check').modal('show');
+            const lastestData = await loadCheckData();
+            progress(50);
+            const result = setting.checkFun(lastestData, addProgress);
+            progress(100);
+            setTimeout(function () {
+                if (result && result.length > 0) {
+                    $('#check-error-hint').modal('show');
+                    setting.errorList.loadErrorData(result);
+                } else {
+                    postDataWithFile(postUrl, postForm, function (data) {
+                        if (data.url) window.location.href = data.url;
+                    });
+                }
+            }, 1000);
+        }
+        return {checkAndPost};
+    }
+</script>

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

@@ -105,6 +105,63 @@
                                 </div>
                             </div>
                             <div class="col-8 modal-height-500" style="overflow: auto">
+                                <% for (const ah of ctx.stage.auditHistory) { %>
+                                    <div class="card mt-3">
+                                        <ul class="list-group list-group-flush">
+                                            <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                                <% if (iA === 0) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.stage.user.name %> <small class="text-muted"><%- ctx.stage.user.role %></small><span class="pull-right">原报</span></h5>
+                                                        <div class="ml-3">
+                                                            <span class="text-success"><%- ah[iA].begin_time.toLocaleDateString() %> <% if (ctx.stage.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                        </div>
+                                                    </li>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else if (iA === ah.length - 1) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } %>
+                                            <% } %>
+                                        </ul>
+                                    </div>
+                                <% } %>
                                 <div class="card mt-3">
                                     <ul class="list-group list-group-flush">
                                         <li class="list-group-item">
@@ -206,6 +263,63 @@
                                 </div>
                             </div>
                             <div class="col-8 modal-height-500" style="overflow: auto">
+                                <% for (const ah of ctx.stage.auditHistory) { %>
+                                    <div class="card mt-3">
+                                        <ul class="list-group list-group-flush">
+                                            <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                                <% if (iA === 0) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.stage.user.name %> <small class="text-muted"><%- ctx.stage.user.role %></small><span class="pull-right">原报</span></h5>
+                                                        <div class="ml-3">
+                                                            <span class="text-success"><%- ah[iA].begin_time.toLocaleDateString() %> <% if (ctx.stage.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                        </div>
+                                                    </li>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else if (iA === ah.length - 1) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } %>
+                                            <% } %>
+                                        </ul>
+                                    </div>
+                                <% } %>
                                 <div class="card mt-3">
                                     <ul class="list-group list-group-flush">
                                         <li class="list-group-item">
@@ -225,7 +339,7 @@
                                                         <span class="text-success"><small><%- auditors[iA].end_time.toLocaleDateString() %></small> 审批通过</span>
                                                         <p class="card-text"><%- auditors[iA].opinion %></p>
                                                     </div>
-                                                <% } else if (auditors[iA].stauts == auditConst.status.checking) { %>
+                                                <% } else if (auditors[iA].status == auditConst.status.checking) { %>
                                                     <span class="pull-right">审批中</span>
                                                     <h5 class="card-title">
                                                         <i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down' : 'fa fa-stop-circle') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small><span class="pull-right"><%= auditors[iA].sort === auditors[iA].max_sort ? '终' : ctx.helper.transFormToChinese(auditors[iA].sort) %>审</span>
@@ -1014,6 +1128,63 @@
                                 </div>
                             </div>
                             <div class="col-8 modal-height-500" style="overflow: auto">
+                                <% for (const ah of ctx.stage.auditHistory) { %>
+                                    <div class="card mt-3">
+                                        <ul class="list-group list-group-flush">
+                                            <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                                <% if (iA === 0) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.stage.user.name %> <small class="text-muted"><%- ctx.stage.user.role %></small><span class="pull-right">原报</span></h5>
+                                                        <div class="ml-3">
+                                                            <span class="text-success"><%- ah[iA].begin_time.toLocaleDateString() %> <% if (ctx.stage.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                        </div>
+                                                    </li>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else if (iA === ah.length - 1) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } %>
+                                            <% } %>
+                                        </ul>
+                                    </div>
+                                <% } %>
                                 <div class="card mt-3">
                                     <ul class="list-group list-group-flush">
                                         <li class="list-group-item">
@@ -1115,6 +1286,63 @@
                                 </div>
                             </div>
                             <div class="col-8 modal-height-500" style="overflow: auto">
+                                <% for (const ah of ctx.stage.auditHistory) { %>
+                                    <div class="card mt-3">
+                                        <ul class="list-group list-group-flush">
+                                            <% for (let iA = 0; iA < ah.length; iA++) { %>
+                                                <% if (iA === 0) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- ctx.stage.user.name %> <small class="text-muted"><%- ctx.stage.user.role %></small><span class="pull-right">原报</span></h5>
+                                                        <div class="ml-3">
+                                                            <span class="text-success"><%- ah[iA].begin_time.toLocaleDateString() %> <% if (ctx.stage.auditHistory.indexOf(ah) > 0) { %>重新<% } %>上报</span>
+                                                        </div>
+                                                    </li>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa <%if (iA === ah.length - 1) { %>fa-stop-circle<% } else { %>fa-chevron-circle-down<% } %> <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else if (iA === ah.length - 1) { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-stop-circle <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right">终审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } else { %>
+                                                    <li class="list-group-item">
+                                                        <h5 class="card-title">
+                                                            <i class="fa fa-chevron-circle-down <%- auditConst.statusClass[ah[iA].status] %>"></i> <%- ah[iA].name %> <small class="text-muted"><%- ah[iA].role %></small><span class="pull-right"><%= parseInt(ah[iA].sort) === parseInt(ah[iA].max_sort) ? '终' : ctx.helper.transFormToChinese(ah[iA].sort) %>审</span>
+                                                        </h5>
+                                                        <div class="ml-3">
+                                                            <% if (ah[iA].status !== auditConst.status.uncheck) { %>
+                                                                <span class="<%- auditConst.statusClass[ah[iA].status] %>">
+                                                                    <% if (ah[iA].status === auditConst.status.checked || ah[iA].status === auditConst.status.checkNo || ah[iA].status === auditConst.status.checkNoPre || ah[iA].status === auditConst.status.checkAgain) { %><small><%- ah[iA].end_time.toLocaleDateString() %></small> <% } %><%- auditConst.statusString[ah[iA].status]%><% if (ah[iA].status === auditConst.status.checkNo) { %> <%- ctx.stage.user.name %><% } %>
+                                                                </span>
+                                                            <% } %>
+                                                            <p class="card-text"><%- ah[iA].opinion %></p>
+                                                        </div>
+                                                    </li>
+                                                <% } %>
+                                            <% } %>
+                                        </ul>
+                                    </div>
+                                <% } %>
                                 <div class="card mt-3">
                                     <ul class="list-group list-group-flush">
                                         <li class="list-group-item">
@@ -1134,7 +1362,7 @@
                                                         <span class="text-success"><small><%- auditors[iA].end_time.toLocaleDateString() %></small> 审批通过</span>
                                                         <p class="card-text"><%- auditors[iA].opinion %></p>
                                                     </div>
-                                                <% } else if (auditors[iA].stauts == auditConst.status.checking) { %>
+                                                <% } else if (auditors[iA].status == auditConst.status.checking) { %>
                                                     <h5 class="card-title">
                                                         <i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down' : 'fa fa-stop-circle') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small><span class="pull-right"><%= auditors[iA].sort === auditors[iA].max_sort ? '终' : ctx.helper.transFormToChinese(auditors[iA].sort) %>审</span>
                                                     </h5>

+ 0 - 2
app/view/stage/bwtz.ejs

@@ -32,11 +32,9 @@
                         </div>
                     </div>
                 </div>
-                <% if (ctx.app.config.is_debug) { %>
                 <div class="d-inline-block ml-3">
                     <a id="exportBwtz" class="btn btn-primary btn-sm" href="javascript: void(0)">导出部位台账Excel</a>
                 </div>
-                <% } %>
             </div>
             <div class="ml-auto">
             </div>

+ 1 - 1
app/view/stage_extra/bonus_modal.ejs

@@ -7,7 +7,7 @@
             </div>
             <div class="modal-body">
                 <% if (!ctx.stage.readOnly) { %>
-                <div class="form-group">
+                <div class="form-group" id="upload-file-panel">
                     <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
                     <input type="file" class="" id="upload-file" multiple onclick="file">
                 </div>

+ 87 - 0
app/view/wap/dashboard.ejs

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <title>待审批-计量支付</title>
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/wap/main.css">
+    <link rel="stylesheet" href="/public/css/toast.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        body {
+            padding: 0;
+        }
+    </style>
+</head>
+<body>
+    <div class="container">
+        <!--顶部-->
+        <nav class="fixed-top bg-dark">
+            <div class="my-2 d-flex justify-content-between">
+                <span class="text-white ml-3">待审批</span>
+                <div class="mr-3">
+                    <div class="dropdown">
+                      <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown">
+                          <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 2 ? ctx.session.sessionUser.name.length - 2 : 0) %>
+                      </button>
+                      <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" href="/wap/logout">退出登录</a>
+                      </div>
+                    </div>
+                </div>
+            </div>
+        </nav>
+        <!--待审批期列表-->
+        <div class="py-6">
+            <% if (auditStages.length !== 0) { %>
+                <% for (const audit of auditStages) { %>
+                <div class="card mb-3">
+                    <div class="card-header">
+                        <%- JSON.parse(audit.deal_info).buildName %>
+                    </div>
+                    <div class="bg-light p-2 px-3"><b><%- audit.name %></b></div>
+                    <div class="card-body">
+                        <div class="d-flex justify-content-between"><span>第<%- audit.order %>期</span><span><%- audit.s_time %></span></div>
+                        <div class="my-2">
+                            <table class="table table-sm table-bordered">
+                                <tr><th>本期合同计量</th><td class="text-right"><%- ctx.helper.formatMoney(audit.contract_tp) %></td></tr>
+                                <tr><th>本期数量变更计量</th><td class="text-right"><%- ctx.helper.formatMoney(audit.qc_tp) %></td></tr>
+                                <tr><th>本期完成计量</th><td class="text-right"><%- ctx.helper.formatMoney(audit.gather_tp) %></td></tr>
+                                <tr><th>截止上期完成计量</th><td class="text-right"><%- ctx.helper.formatMoney(audit.pre_gather_tp) %></td></tr>
+                                <tr><th>截止本期完成计量</th><td class="text-right"><%- ctx.helper.formatMoney(audit.end_gather_tp) %></td></tr>
+                                <tr><th>本期应付</th><td class="text-right"><%- ctx.helper.formatMoney(audit.yf_tp) %></td></tr>
+                            </table>
+                        </div>
+                        <div class="">
+                            <a href="/wap/tender/<%- audit.tid %>#shenpi" class="btn btn-block btn-success">审批</a>
+                        </div>
+                    </div>
+                </div>
+                <% } %>
+            <% } else { %>
+                <h3 class="text-center text-muted">暂无待审批期计量</h3>
+            <% } %>
+        </div>
+        <!--底栏菜单-->
+        <nav class="fixed-bottom navbar-dark bg-light border-top">
+          <ul class="nav nav-fill my-2">
+              <li class="nav-item">
+                <a class="nav-link active" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+              </li>
+              <li class="nav-item">
+                <a class="nav-link text-muted" href="/wap/list"><i class="fa fa-list-ul"></i> 项目</a>
+              </li>
+            </ul>
+        </nav>
+    </div>
+
+    <!-- JS. -->
+    <script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
+    <script src="/public/js/popper/popper.min.js"></script>
+    <script src="/public/js/bootstrap/bootstrap.min.js"></script>
+</body>
+
+</html>

+ 80 - 0
app/view/wap/list.ejs

@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <title>项目-计量支付</title>
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/wap/main.css">
+    <link rel="stylesheet" href="/public/css/toast.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        html{height:100%;}
+        body {
+            padding: 0;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <!--顶部-->
+    <nav class="fixed-top bg-dark">
+        <div class="my-2 d-flex justify-content-between">
+            <span class="text-white ml-3">项目</span>
+            <div class="mr-3">
+                <div class="dropdown">
+                    <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown">
+                        <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 2 ? ctx.session.sessionUser.name.length - 2 : 0) %>
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" href="/wap/logout">退出登录</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </nav>
+    <!--标段列表-->
+    <div class="py-6">
+        <ul class="d-flex justify-content-start list-unstyled">
+            <li class="mr-3"><i class="fa fa-stop text-success"></i>截止上期完成</li>
+            <li class="mr-3"><i class="fa fa-stop text-info"></i>本期完成</li>
+            <li class="mr-3"><i class="fa fa-stop text-muted"></i>未完成</li>
+        </ul>
+        <div class="c-body"></div>
+    </div>
+    <!--底栏菜单-->
+    <nav class="fixed-bottom navbar-dark bg-light border-top">
+        <ul class="nav nav-fill my-2">
+            <li class="nav-item">
+                <a class="nav-link text-muted" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link active " href="/wap/list"><i class="fa fa-list-ul"></i> 项目</a>
+            </li>
+        </ul>
+    </nav>
+</div>
+<script>
+    const tenders = JSON.parse('<%- JSON.stringify(tenderList) %>');
+    const category = JSON.parse('<%- JSON.stringify(categoryData) %>');
+    const uid = '<%- uid %>';
+    const pid = '<%- pid %>';
+    const uphlname = 'user_' + uid + '_pro_' + pid + '_category_wap_hide_list';
+</script>
+<!-- JS. -->
+<script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
+<script src="/public/js/popper/popper.min.js"></script>
+<script src="/public/js/bootstrap/bootstrap.min.js"></script>
+<script src="/public/js/lodash.js"></script>
+<script src="/public/js/lz-string/lz-string.js"></script>
+<script src="/public/js/number-precision.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script src="/public/js/decimal.min.js"></script>
+<script src="/public/js/zh_calc.js"></script>
+<script src="/public/js/shares/tender_list_order.js"></script>
+<script src="/public/js/tender_showhide.js"></script>
+<script src="/public/js/wap/list.js"></script>
+</body>
+</html>

+ 83 - 0
app/view/wap/login.ejs

@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <title>用户登录-计量支付</title>
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/wap/main.css">
+    <link rel="stylesheet" href="/public/css/toast.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        html{height:100%;}
+    </style>
+</head>
+<body class="login-body">
+<div class="container">
+    <!--演示版-->
+    <div class="row">
+        <div class="col-12 text-center">
+            <img src="/public/images/loginlogo.png" class="my-3">
+        </div>
+        <div class="col-12">
+            <% if (maintainData.status === maintainConst.status.ongoing) { %>
+            <form class="card m-3 mt-3">
+                <div class="card-body">
+                    <h4 class="text-center mb-3"><i class="fa fa-wrench"></i>系统正在维护</h4>
+                    <h4>预计恢复时间<%- (maintainData.duration !== maintainConst.duration.forever ? '为 ' + ctx.helper.dateTran(parseFloat(maintainData.maintain_time) + ctx.helper.timeAdd(maintainData.duration)) : ' 暂未确定') %></h4>
+                    <h4>造成不便敬请谅解。</h4>
+                </div>
+            </form>
+            <% } else { %>
+            <form class="card m-3 mt-3" method="post" action="/wap/login">
+                <div class="card-body">
+                    <h5 class="text-center mb-4 text-muted" id="project_name"><%- projectData ? projectData.name : '' %></h5>
+                    <h4 class="text-center mb-4">账号登录</h4>
+                    <div class="form-group mb-3 <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                        <input id="project" class="form-control" name="project" placeholder="项目编号" <% if (projectData) { %>readonly<% } %> value="<%- projectData ? projectData.code : '' %>" autofocus="" />
+                    </div>
+                    <div class="form-group mb-3 <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                        <input id="account" class="form-control" name="account" placeholder="输入账号" autofocus="" />
+                    </div>
+                    <div class="form-group mb-3 <% if (errorMessage !== undefined && errorMessage !== null) { %>has-danger<% } %>">
+                        <input id="project-password" name="project_password" class="form-control" placeholder="输入密码" type="password" />
+                    </div>
+                    <div class="form-group mb-3">
+                        <div class="alert alert-danger" <% if(errorMessage === undefined || errorMessage === null) { %>style="display: none"<% } %> role="alert" id="error-msg">
+                            <% if(errorMessage !== undefined && errorMessage !== null) { %><strong>登录失败</strong> <%= errorMessage %><% } %>
+                        </div>
+                    </div>
+                    <div class="form-group mb-3">
+                        <button class="btn btn-primary btn-block" type="submit">登录</button>
+                        <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                        <input type="hidden" name="type" value="2" />
+                    </div>
+                    <div class="pt-1 d-flex justify-content-end">
+                        <!--<a href="#fg-password" data-toggle="modal" data-target="#fg-password"  class="mr-3">忘记密码?</a>-->
+                    </div>
+                </div>
+            </form>
+            <% } %>
+        </div>
+    </div>
+    <!--项目版-->
+    <div class="text-white fixed-bottom"><p class="text-center mb-1">Copyright © 2019 <a href="https://smartcost.com.cn" target="_blank" class="text-white">珠海纵横创新软件有限公司</a>.All Rights Reserved.<a class="text-white ml-2" href="http://www.miitbeian.gov.cn" target="_blank">粤ICP备14032472号</a></p></div>
+</div>
+<!-- JS. -->
+<div class="toast" style="text-align: center">
+    <i class="icon fa"></i>
+    <span class="message"></span>
+</div>
+<!-- JS. -->
+<script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
+<script src="/public/js/popper/popper.min.js"></script>
+<script src="/public/js/bootstrap/bootstrap.min.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script>
+    const csrf = '<%= ctx.csrf %>'
+</script>
+<script src="/public/js/wap/login.js"></script>
+</body>
+</html>

+ 549 - 0
app/view/wap/tender.ejs

@@ -0,0 +1,549 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
+    <meta http-equiv="x-ua-compatible" content="ie=edge">
+    <title>标段概况-计量支付</title>
+    <link rel="stylesheet" href="/public/css/bootstrap/bootstrap.min.css">
+    <link rel="stylesheet" href="/public/css/wap/main.css">
+    <link rel="stylesheet" href="/public/css/toast.css">
+    <link rel="stylesheet" href="/public/css/font-awesome/font-awesome.min.css">
+    <script src=/public/js/echarts/echarts.min.js></script>
+    <link rel="shortcut icon" href="/public/images/favicon.ico">
+    <style>
+        body {
+            padding: 0;
+        }
+    </style>
+</head>
+<body>
+<div class="container">
+    <!--顶部-->
+    <nav class="fixed-top bg-dark">
+        <div class="my-2 d-flex justify-content-between">
+            <span class="text-white ml-3"><a href="/wap/list" class="mr-2 text-white"><i class="fa fa-chevron-left"></i></a>标段概况</span>
+            <div class="mr-3">
+                <div class="dropdown">
+                    <button class="btn btn-sm btn-light dropdown-toggle" type="button" data-toggle="dropdown">
+                        <%- ctx.session.sessionUser.name.substr(ctx.session.sessionUser.name.length > 2 ? ctx.session.sessionUser.name.length - 2 : 0) %>
+                    </button>
+                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                        <a class="dropdown-item" href="/wap/logout">退出登录</a>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </nav>
+    <!--标段概况-->
+    <div class="py-6">
+        <!--标签-->
+        <ul class="nav nav-tabs nav-fill">
+            <li class="nav-item">
+                <a class="nav-link active" data-toggle="tab" href="#gaikuang" role="tab">概况</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" data-toggle="tab" href="#jlqi" role="tab">计量期</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link" data-toggle="tab" href="#shenpi" role="tab">期审批</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active" id="gaikuang">
+                <!--图表-->
+                <div class="card mb-3 mr-1 mt-3">
+                    <div class="card-body">
+                        <h5 class="card-title">月进度表</h5>
+                        <div id="chartContainer3" style="height: 300px; width: 100%;">
+                        </div>
+                    </div>
+                </div>
+                <div class="card mb-3 mr-1">
+                    <div class="card-body">
+                        <h5 class="card-title">期进度表</h5>
+                        <div id="chartContainer4" style="height: 300px; width: 100%;">
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="tab-pane" id="jlqi">
+                <!--期列表-->
+                <dl class="mb-2 mt-3">
+                    <% for (const s of stages) { %>
+                    <dt class="bg-light p-2 d-flex justify-content-between"><span>第<%- s.order %>期</span>
+                        <span class="<%- auditConst.auditStringClass[s.status] %>">
+                            <% if (s.curAuditor && s.status !== auditConst.status.checked) { %>
+                                <%- s.curAuditor.name %><%if (s.curAuditor.role !== '' && s.curAuditor.role !== null) { %>-<%- s.curAuditor.role %><% } %>
+                            <% } %>
+                                <%- s.status === auditConst.status.checked ? '审批完成' : auditConst.auditProgress[s.status] %>
+                        </span>
+                    </dt>
+                    <dd>
+                        <table class="table table-hover">
+                            <tbody><tr>
+                                <td>
+                                    <p class="mb-0">本期合同计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.contract_tp ? s.contract_tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">本期数量变更计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.qc_tp ? s.qc_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <p class="mb-0">本期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.tp ? s.tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">截止上期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.pre_tp ? s.pre_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <p class="mb-0">截止本期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.end_tp ? s.end_tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">本期应付</p>
+                                    <b>¥<%- ctx.helper.formatMoney((s.yf_tp ? s.yf_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            </tbody></table>
+                    </dd>
+                    <% } %>
+                </dl>
+            </div>
+            <div class="tab-pane" id="shenpi">
+                <!--最新期-->
+                <% let audit = 0; %>
+                <% if (stages.length > 0 && stage) { %>
+                <dl class="mb-2 mt-3">
+                    <dt class="bg-light p-2 d-flex justify-content-between"><span>第<%- stage.order %>期</span>
+                        <span class="<%- auditConst.auditStringClass[stage.status] %>">
+                            <% if (stage.curAuditor && stage.status !== auditConst.status.checked) { %>
+                                <%- stage.curAuditor.name %><%if (stage.curAuditor.role !== '' && stage.curAuditor.role !== null) { %>-<%- stage.curAuditor.role %><% } %>
+                            <% } %>
+                            <%- stage.status === auditConst.status.checked ? '审批完成' : auditConst.auditProgress[stage.status] %>
+                        </span>
+                    </dt>
+                    <dd>
+                        <table class="table table-hover">
+                            <tbody><tr>
+                                <td>
+                                    <p class="mb-0">本期合同计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.contract_tp ? stage.contract_tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">本期数量变更计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.qc_tp ? stage.qc_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <p class="mb-0">本期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.tp ? stage.tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">截止上期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.pre_tp ? stage.pre_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td>
+                                    <p class="mb-0">截止本期完成计量</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.end_tp ? stage.end_tp : 0)) %></b>
+                                </td>
+                                <td>
+                                    <p class="mb-0">本期应付</p>
+                                    <b>¥<%- ctx.helper.formatMoney((stage.yf_tp ? stage.yf_tp : 0)) %></b>
+                                </td>
+                            </tr>
+                            </tbody></table>
+                    </dd>
+                </dl>
+                <!--审批流程-->
+                <div class="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <li class="list-group-item">
+                            <% if (stage.status === auditConst.status.uncheck) { %>
+                            <span class="pull-right"> 上报中</span>
+                            <% } else { %>
+                            <span class="text-success pull-right"><small><%- stage.auditors[0].begin_time.toLocaleDateString() %></small> 上报</span>
+                            <% } %>
+                            <h5 class="card-title"><i class="fa fa-play-circle fa-rotate-90 text-success"></i> <%- stage.user.name %><small class="text-muted"><%- stage.user.role %></small></h5>
+                        </li>
+                        <% for (let iA = 0; iA < stage.auditors.length; iA++) { %>
+                            <% const auditors = stage.auditors; %>
+                        <li class="list-group-item">
+                            <% if (auditors[iA].status === auditConst.status.checked) { %>
+                            <span class="text-success pull-right"><small><%- auditors[iA].end_time.toLocaleDateString() %></small> 审批通过</span>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down text-success' : 'fa fa-stop-circle text-success') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                            <p class="card-text"><%- auditors[iA].opinion %></p>
+                            <% } else if (auditors[iA].status == auditConst.status.checking) { %>
+                            <span class="pull-right">审批中</span>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down' : 'fa fa-stop-circle') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                                <% if (auditors[iA].aid === ctx.session.sessionUser.accountId) { %>
+                                <% audit = auditors[iA]; %>
+                                <div class="form-group">
+                                    <div class="text-center">
+                                        <button class="btn btn-success" data-toggle="modal" data-target="#sp-done" >审批通过</button>
+                                        <button class="btn btn-warning" data-toggle="modal" data-target="#sp-back" >审批退回</button>
+                                    </div>
+                                </div>
+                                <% } %>
+                            <% } else if (auditors[iA].status === auditConst.status.checkNoPre) { %>
+                            <% const auditorIndex = stage.auditors2.findIndex(function (item) { return item.aid === auditors[iA].aid }) %>
+                            <span class="text-warning pull-right"><small><%- auditors[iA].end_time.toLocaleDateString() %></small>审批退回 <%- stage.auditors2[auditorIndex-1].name %></span>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down text-warning' : 'fa fa-stop-circle text-warning') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                            <p class="card-text"><%- auditors[iA].opinion %></p>
+                            <% } else if (auditors[iA].status === auditConst.status.checkNo) { %>
+                            <span class="text-warning pull-right"><small><%- auditors[iA].end_time.toLocaleDateString() %></small>审批退回</span>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down text-warning' : 'fa fa-stop-circle text-warning') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                            <p class="card-text"><%- auditors[iA].opinion %></p>
+                            <% } else if (auditors[iA].status === auditConst.status.checkAgain) { %>
+                            <span class="text-warning pull-right"><small><%- auditors[iA].end_time.toLocaleDateString() %></small>重新审批</span>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down text-warning' : 'fa fa-stop-circle text-warning') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                            <p class="card-text"><%- auditors[iA].opinion %></p>
+                            <% } else { %>
+                            <h5 class="card-title"><i class="<%- (iA < auditors.length - 1 ? 'fa fa-chevron-circle-down' : 'fa fa-stop-circle') %>"></i> <%- auditors[iA].name %> <small class="text-muted"><%- auditors[iA].role %></small></h5>
+                            <% } %>
+                        </li>
+                        <% } %>
+                    </ul>
+                </div>
+                <% } %>
+            </div>
+        </div>
+    </div>
+    <!--底栏菜单-->
+    <nav class="fixed-bottom navbar-dark bg-light border-top">
+        <ul class="nav nav-fill my-2">
+            <li class="nav-item">
+                <a class="nav-link text-muted" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link active " href="/wap/list"><i class="fa fa-list-ul"></i> 项目</a>
+            </li>
+        </ul>
+    </nav>
+</div>
+<% if (audit !== 0) { %>
+<!--审批通过弹窗-->
+<div class="modal" tabindex="-1" role="dialog" id="sp-done">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="/tender/<%- stage.tid %>/measure/stage/<%- stage.order %>/audit/check" method="post" onsubmit="return auditCheck(0);">
+            <div class="modal-header">
+                <h5 class="modal-title">审批通过</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>审批意见</label>
+                    <textarea class="form-control" rows="8" name="opinion">同意</textarea>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                <input type="hidden" name="checkType" value="<%= auditConst.status.checked %>" />
+                <button type="submit" class="btn btn-success">审批通过</button>
+            </div>
+        </form>
+    </div>
+</div>
+<!--审批退回弹窗-->
+<div class="modal" tabindex="-1" role="dialog" id="sp-back">
+    < class="modal-dialog" role="document">
+        <form class="modal-content" action="/tender/<%- stage.tid %>/measure/stage/<%- stage.order %>/audit/check" method="post" onsubmit="return auditCheck(1);">
+            <div class="modal-header">
+                <h5 class="modal-title">审批通过</h5>
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                    <span aria-hidden="true">&times;</span>
+                </button>
+            </div>
+            <div class="modal-body">
+                <div class="form-group">
+                    <label>审批意见</label>
+                    <textarea class="form-control" rows="8" name="opinion">不同意</textarea>
+                </div>
+                <div class="alert alert-warning">
+                    <div class="custom-control custom-radio custom-control-inline">
+                        <input type="radio" id="inlineRadio1" class="custom-control-input" value="<%- auditConst.status.checkNo %>" <% if (audit.order === 1 || audit.aid === stage.auditors[0].aid) { %>checked<% } %>>
+                        <label class="custom-control-label" for="customRadioInline1">退回上报 <%- stage.user.name %></label>
+                    </div>
+                    <% if (audit.order > 1 && audit.aid !== stage.auditors[0].aid) { %>
+                    <% const auditorIndex = stage.auditors2.findIndex(function (item) { return item.aid === audit.aid }) %>
+                    <div class="custom-control custom-radio custom-control-inline">
+                        <input class="custom-control-input" type="radio" name="checkType" id="customRadioInline2" value="<%- auditConst.status.checkNoPre %>" checked>
+                        <label class="custom-control-label" for="customRadioInline2">退回上一审批人 <%- stage.auditors2[auditorIndex-1].name %></label>
+                    </div>
+                    <% } %>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                <input type="hidden" name="_csrf" value="<%= ctx.csrf %>" />
+                <button type="submit" class="btn btn-warning">确认退回</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% } %>
+<!-- JS. -->
+<script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
+<script src="/public/js/popper/popper.min.js"></script>
+<script src="/public/js/bootstrap/bootstrap.min.js"></script>
+<!--<script src="js/global.js"></script>-->
+<script type="text/javascript">
+    //4 标段期数计量进度//
+    var myChart = echarts.init(document.getElementById('chartContainer4'));
+    var option = {
+        color: ['#e9af68','#57b7b6','#e4575a','#959eac','#6699FF',
+            '#d38b70','#8fb7cf','#cd5c5c','#ffa500','#40e0d0',
+            '#1e90ff','#ff6347','#7b68ee','#00fa9a','#ffd700',
+            '#5c616b','#ff6666','#3cb371','#b8860b','#30e0e0'],
+        title : {
+            text: ''
+        },
+        tooltip : {
+            trigger: 'axis'
+        },
+        calculable : true,
+        legend: {
+            data:['本期合同计量','本期数量变更计量','截止上期累计完成','本期完成计量','完成度']
+        },
+        dataZoom: [
+            {show: true,start: 0, end: 100}
+        ],
+        xAxis : [
+            {
+                type : 'category',
+                splitLine : {show : true},
+                data : [
+                    <% for (const s of stagesEcharts) {%>
+                    '第<%- s.order %>期',
+                    <% } %>
+                ]
+            }
+        ],
+        yAxis : [
+            {
+                type : 'value',
+                name : '金额',
+                position:'left',
+                axisLabel : {
+                    formatter: '{value} 元'
+                },
+                splitArea : {show : true},
+                splitLine : {show : true},
+            },
+            {
+                type : 'value',
+                name:'完成度',
+                axisLabel : {
+                    formatter: '{value} %'
+                },
+                position: 'right',
+                splitArea : {show : false},
+                splitLine : {show : false},
+            }
+        ],
+        series : [
+            {
+                name:'本期合同计量',
+                type:'bar',
+                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                stack: '合同',
+                data:[
+                    <% for (const s of stagesEcharts) {%>
+                    <%- s.contract_tp %>,
+                    <% } %>
+                ]
+            },
+            {
+                name:'本期数量变更计量',
+                type:'bar',
+                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                stack: '变更',
+                data:[
+                    <% for (const s of stagesEcharts) {%>
+                    <%- s.qc_tp %>,
+                    <% } %>
+                ]
+            },
+            {
+                name:'截止上期累计完成',
+                type:'bar',
+                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                stack: '完成',
+                data:[
+                    <% for (const s of stagesEcharts) {%>
+                    <%- ctx.helper.add(s.pre_contract_tp, s.pre_qc_tp) %>,
+                    <% } %>
+                ]
+            },
+            {
+                name:'本期完成计量',
+                type:'bar',
+                tooltip : {trigger: 'item',formatter: "{b}  <br/>{a}:{c}元"},
+                stack: '完成',
+                data:[
+                    <% for (const s of stagesEcharts) {%>
+                    <%- ctx.helper.add(s.contract_tp, s.qc_tp) %>,
+                    <% } %>
+                ]
+            },
+            {
+                name:'完成度',
+                type:'line',
+                tooltip : {trigger: 'axis',formatter: "{b}占总标段<br/>{a}:{c} %"},
+                yAxisIndex: 1,
+                data:[
+                    <% for (const s of stagesEcharts) {%>
+                    <%- ctx.helper.mul(ctx.helper.div(ctx.helper.add(s.contract_tp, s.qc_tp), tender.sum, 2), 100) %>,
+                    <% } %>
+                ]
+            },
+        ]
+    };
+
+    // 为echarts对象加载数据
+    myChart.setOption(option);
+    //4 标段期数计量进度//
+    //3 标段月进度//
+    // 基于准备好的dom,初始化echarts图表
+    var myChart = echarts.init(document.getElementById('chartContainer3'));
+    var option = {
+        color:["#e9af68","#57b7b6"],
+        title : {
+            text: ''
+        },
+        tooltip : {
+            trigger: 'axis',
+            formatter: function (params, ticket, callback) {
+                let sHint = '';
+                for (const param of params) {
+                    if (sHint !== '') {
+                        sHint += '<br>';
+                    }
+                    if (sHint === '' && param.name !== '') {
+                        sHint = param.name + '<br>';
+                    }
+                    sHint += '<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:' + param.color +'"></span>';
+                    if (param.data) {
+                        sHint += param.seriesName + ': ' + param.data + ' %';
+                    } else {
+                        sHint += param.seriesName + ': -';
+                    }
+                }
+                return sHint;
+            },
+        },
+        legend: {
+            data:['截止本月完成','本月完成']
+        },
+        toolbox: {
+            show : true,
+            feature : {
+                magicType : {show: true, type: ['line', 'bar']}
+            }
+        },
+        dataZoom : {
+            show : true,
+            start : 50,
+            end : 100
+        },
+        xAxis : [
+            {
+                type : 'category',
+                boundaryGap : true,
+                data : [
+                    <% for (const mp of monthProgress) { %>
+                    '<%- mp.month %>',
+                    <% } %>
+                ]
+            }
+        ],
+        yAxis : [
+            {
+                type : 'value',
+                axisLabel : {
+                    formatter: '{value} %'
+                },
+                splitArea : {show : true}
+            }
+        ],
+        series : [
+            {
+                name:'截止本月完成',
+                type:'line',
+                itemStyle: {
+                    normal: {
+                        lineStyle: {
+                            shadowColor : 'rgba(0,0,0,0.4)',
+                            shadowBlur: 5,
+                            shadowOffsetX: 3,
+                            shadowOffsetY: 3
+                        }
+                    }
+                },
+                data:[
+                    <% for (const mp of monthProgress) { %>
+                    <%- mp.end_ratio %>,
+                    <% } %>
+                ]
+            },
+            {
+                name:'本月完成',
+                type:'line',
+                itemStyle: {
+                    normal: {
+                        lineStyle: {
+                            shadowColor : 'rgba(0,0,0,0.4)',
+                            shadowBlur: 5,
+                            shadowOffsetX: 3,
+                            shadowOffsetY: 3
+                        }
+                    }
+                },
+                data:[
+                    <% for (const mp of monthProgress) { %>
+                    <%- mp.ratio %>,
+                    <% } %>
+                ]
+            }
+        ]
+    };
+    // 为echarts对象加载数据
+    myChart.setOption(option);
+    //3 标段月进度//
+</script>
+<!--sjs-->
+<script>
+    $(document).ready(function () {
+        if (window.location.hash && window.location.hash === '#shenpi') {
+            $('#gaikuang').removeClass('active');
+            $('#jlqi').removeClass('active');
+            $('.nav-item a[href="#gaikuang"]').removeClass('active');
+            $('.nav-item a[href="#jlqi"]').removeClass('active');
+
+            $('#shenpi').addClass('active');
+            $('.nav-item a[href="#shenpi"]').addClass('active');
+        }
+    })
+
+    // texterea换行
+    function auditCheck(i) {
+        const opinion = $('textarea[name="opinion"]').eq(i).val().replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' ');
+        $('textarea[name="opinion"]').eq(i).val(opinion);
+        return true;
+    }
+</script>
+</body>
+
+</html>

+ 14 - 0
builder_report_index_define.js

@@ -760,6 +760,8 @@ const gather_stage_bills = {
 
         { name: '(特殊3-需替换key3)台账-数量', field: 'ts_key3_qty', type: dataType.currency },
         { name: '(特殊3-需替换key3)台账-金额', field: 'ts_key3_tp', type: dataType.currency },
+
+        { name: '交叉排序', field: 'cross_index', type: dataType.int },
     ],
 };
 const gather_tender_info = {
@@ -817,6 +819,8 @@ const gather_stage_pay = {
         { name: '(合计)本期-金额', field: 's_tp', type: dataType.currency },
         { name: '(合计)截止上期-金额', field: 's_pre_tp', type: dataType.currency },
         { name: '(合计)截止本期-金额', field: 's_end_tp', type: dataType.currency },
+
+        { name: '交叉排序', field: 'cross_index', type: dataType.int },
     ],
 };
 const gather_deal_bills = {
@@ -841,6 +845,16 @@ const gather_deal_bills = {
         { name: '(特殊1)金额', field: 'ts_key1_tp', type: dataType.currency },
         { name: '(特殊2)金额', field: 'ts_key2_tp', type: dataType.currency },
         { name: '(特殊3)金额', field: 'ts_key3_tp', type: dataType.currency },
+
+        { name: '(标段)数量', field: 't_qty', type: dataType.currency },
+
+        { name: '(合计)数量', field: 's_qty', type: dataType.currency },
+
+        { name: '(特殊1)数量', field: 'ts_key1_qty', type: dataType.currency },
+        { name: '(特殊2)数量', field: 'ts_key2_qty', type: dataType.currency },
+        { name: '(特殊3)数量', field: 'ts_key3_qty', type: dataType.currency },
+
+        { name: '交叉排序', field: 'cross_index', type: dataType.int },
     ],
 };
 

+ 2 - 0
config/web.js

@@ -136,6 +136,7 @@ const JsFiles = {
                     "/public/js/math.min.js",
                     "/public/js/file-saver/FileSaver.js",
                     "/public/js/shares/export_excel.js",
+                    "/public/js/shares/cs_tools.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",
@@ -156,6 +157,7 @@ const JsFiles = {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/shares/cs_tools.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 41
sql/update.sql


+ 9 - 1
sql/update20200325.sql

@@ -11,4 +11,12 @@ INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`,
 INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES ('62', '升级说明', 'version', '', '31', '', NULL, '1');
 INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES ('63', '添加版本', 'version', 'add', '62', '', NULL, '1');
 INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES ('64', '修改版本', 'version', 'add', '62', '', NULL, '1');
-INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES ('65', '删除版本', 'version', 'delete', '62', '', NULL, '1');
+INSERT INTO `calculation`.`zh_permission` (`id`, `name`, `controller`, `action`, `pid`, `icon_class`, `create_time`, `isshow`) VALUES ('65', '删除版本', 'version', 'delete', '62', '', NULL, '1');
+
+UPDATE zh_role_rpt_rel AS role SET role.sid = source.sid
+  FROM (
+     SELECT s.tid AS tid, s.id AS sid, s.`order` AS sorder
+       FROM (SELECT MAX(`order`) As `order`, tid FROM zh_stage GROUP BY tid) AS ls
+         LEFT JOIN zh_stage AS s ON s.tid = ls.tid AND ls.`order` = s.`order`
+  ) As source
+  WHERE role.tid = source.tid And ISNULL(role.sid);

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 44 - 0
sql/update20200511.sql


+ 3 - 0
sql/update20200513.sql

@@ -0,0 +1,3 @@
+ALTER TABLE `zh_tender` CHANGE `name` `name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '名称';
+
+-- 2020-05-13 更新uat/prod

+ 108 - 20
test/app/service/report_memory_temp.test.js

@@ -13,6 +13,82 @@ const path = require('path');
 let savePath;
 const mockData = {};
 
+const testSetting1 = {
+    postData: {
+        account: 'fuqingqing',
+        project: 'P0505',
+        project_password: '123456',
+    },
+    define: {
+        enable: true,
+        setting: JSON.stringify({
+            title: '请选择汇总的标段',
+            type: 'final',
+        })
+    },
+    select: {
+        tenders: [{tid: 2283}, {tid: 2281}],
+        type: 'final',
+    }
+};
+const testSetting2 = {
+    postData: {
+        account: 'zengpeiwen',
+        project: 'P1201',
+        project_password: '123456',
+    },
+    define: {
+        enable: true,
+        setting: JSON.stringify({
+            title: '请选择汇总的标段',
+            type: 'final',
+        })
+    },
+    select: {
+        tenders: [{tid: 2046}, {tid: 1942}, {tid: 2217}],
+        type: 'final',
+    }
+};
+const testSetting3 = {
+    postData: {
+        account: 'zengpeiwen',
+        project: 'P1201',
+        project_password: '123456',
+    },
+    define: {
+        enable: true,
+        setting: JSON.stringify({
+            "title": "请选择汇总的标段",
+            "type": "final",
+            "nameColWidth": 180,
+            "special": [
+                {"title": "估算", "key": "gu", "width": 60},
+                {"title": "概算", "key": "gai", "width": 60},
+                {"title": "预算", "key": "yu", "width": 60}
+            ]
+        })
+    },
+    select: JSON.parse('{"tenders":[{"tid":1942},{"tid":2217},{"tid":2046},{"tid":2172,"gu":true},{"tid":2324,"gai":true},{"tid":2326,"yu":true}],"type":"final"}')
+};
+const testSetting4 = {
+    postData: {
+        account: 'zengpeiwen',
+        project: 'P1201',
+        project_password: '123456',
+    },
+    tenderId: 2436,
+    sorder: 1,
+};
+const testSetting5 = {
+    postData: {
+        account: 'fuqingqing',
+        project: 'P0505',
+        project_password: '123456',
+    },
+    tenderId: 2142,
+    sorder: 4,
+};
+
 const reportDataAnalysis = require('../../../app/lib/rpt_data_analysis');
 
 describe('test/app/service/report_memory.test.js', () => {
@@ -60,31 +136,43 @@ describe('test/app/service/report_memory.test.js', () => {
         yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_gather_stage_pay, "", "\t"), path.join(savePath, 'mem_gather_stage_pay_converse.json'));
     });
     // 汇总签约清单
-    it('test getGatherStagePay', function* () {
+    it('test getGatherDealBills', function* () {
         const ctx = app.mockContext();
-        const postData = {
-            account: 'zengpeiwen',
-            project: 'P1201',
-            project_password: '123456',
-        };
+        const testSetting = testSetting3;
         ctx.session = {};
-        const loginResult = yield ctx.service.projectAccount.accountLogin(postData, 2);
+        const loginResult = yield ctx.service.projectAccount.accountLogin(testSetting.postData, 2);
         assert(loginResult);
 
-        const select = {
-            tenders: [{tid: 2046}, {tid: 1942}, {tid: 2217}],
-            type: 'final',
-        };
-        const define = {
-            enable: true,
-            setting: JSON.stringify({
-                title: '请选择汇总的标段',
-                type: 'final',
-            })
-        };
-
         const data = {};
-        data.mem_gather_deal_bills = yield ctx.service.rptGatherMemory.getGatherDealBills([], define, select);
+        data.mem_gather_deal_bills = yield ctx.service.rptGatherMemory.getGatherDealBills([], testSetting.define, testSetting.select);
         yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_gather_deal_bills, "", "\t"), path.join(savePath, 'mem_gather_deal_bills.json'));
     });
+    it('test getStageImTzData && getStageImTzBillsData', function* () {
+        const ctx = app.mockContext();
+        const testSetting = testSetting4;
+        ctx.session = {};
+        const loginResult = yield ctx.service.projectAccount.accountLogin(testSetting.postData, 2);
+        assert(loginResult);
+
+        const data = {};
+        const stage = yield ctx.service.stage.getDataByCondition({tid: testSetting.tenderId, order: testSetting.sorder});
+        data.mem_stage_im_tz = yield ctx.service.reportMemory.getStageImTzData(stage.tid, stage.id, [], false);
+        yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_im_tz, "", "\t"), path.join(savePath, 'mem_stage_im_tz.json'));
+        console.log(ctx.helper._.map(data.mem_stage_im_tz, 'calc_memo'));
+        data.mem_stage_im_tz_bills = yield ctx.service.reportMemory.getStageImTzBillsData(stage.tid, stage.id, [], false);
+        yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_im_tz_bills, "", "\t"), path.join(savePath, 'mem_stage_im_tz_bills.json'));
+    });
+    it('test getStageImZlData', function* () {
+        const ctx = app.mockContext();
+        const testSetting = testSetting5;
+        ctx.session = {};
+        const loginResult = yield ctx.service.projectAccount.accountLogin(testSetting.postData, 2);
+        assert(loginResult);
+
+        const data = {};
+        const stage = yield ctx.service.stage.getDataByCondition({tid: testSetting.tenderId, order: testSetting.sorder});
+        data.mem_stage_im_zl = yield ctx.service.reportMemory.getStageImZlData(stage.tid, stage.id, []);
+        yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_im_zl, "", "\t"), path.join(savePath, 'mem_stage_im_zl.json'));
+        console.log(ctx.helper._.map(data.mem_stage_im_zl, 'calc_memo'));
+    });
 });