Procházet zdrojové kódy

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

TonyKang před 5 roky
rodič
revize
8443485d62
49 změnil soubory, kde provedl 1914 přidání a 414 odebrání
  1. 2 0
      app/const/deal_pay.js
  2. 0 1
      app/controller/change_controller.js
  3. 3 3
      app/controller/report_controller.js
  4. 2 2
      app/controller/stage_controller.js
  5. 127 0
      app/controller/wap_controller.js
  6. 3 5
      app/extend/helper.js
  7. 1 1
      app/lib/bills_pos_convert.js
  8. 2 1
      app/lib/ledger.js
  9. 9 2
      app/lib/pay_calc.js
  10. 118 0
      app/lib/rpt_data_analysis.js
  11. 83 27
      app/lib/stage_im.js
  12. 13 13
      app/public/css/main.css
  13. 5 0
      app/public/css/wap/main.css
  14. 16 2
      app/public/js/change_set.js
  15. 173 13
      app/public/js/ledger.js
  16. 2 2
      app/public/js/ledger_audit.js
  17. 186 21
      app/public/js/revise.js
  18. 2 2
      app/public/js/revise_history.js
  19. 56 15
      app/public/js/shares/cs_tools.js
  20. 120 15
      app/public/js/stage.js
  21. 1 1
      app/public/js/stage_audit.js
  22. 17 3
      app/public/js/stage_im.js
  23. 2 2
      app/public/js/stage_pay.js
  24. 3 0
      app/router.js
  25. 15 10
      app/service/change.js
  26. 25 0
      app/service/change_audit.js
  27. 8 4
      app/service/ledger_revise.js
  28. 2 1
      app/service/pos.js
  29. 2 1
      app/service/project_account.js
  30. 13 13
      app/service/report_memory.js
  31. 8 4
      app/service/revise_audit.js
  32. 3 1
      app/service/revise_pos.js
  33. 18 10
      app/service/stage.js
  34. 6 6
      app/service/stage_audit.js
  35. 2 0
      app/service/stage_bills.js
  36. 5 1
      app/service/stage_pay.js
  37. 13 4
      app/service/stage_pos.js
  38. 15 9
      app/view/ledger/explode.ejs
  39. 15 9
      app/view/revise/info.ejs
  40. 14 2
      app/view/stage/index.ejs
  41. 4 4
      app/view/stage/pay.ejs
  42. 28 4
      app/view/wap/dashboard.ejs
  43. 396 0
      app/view/wap/shenpi_change.ejs
  44. 260 0
      app/view/wap/shenpi_stage.ejs
  45. 33 195
      app/view/wap/tender.ejs
  46. 14 0
      builder_report_index_define.js
  47. 21 2
      sql/update.sql
  48. 38 0
      test/app/lib/rpt_data_analysis.test.js
  49. 10 3
      test/app/service/report_memory_temp.test.js

+ 2 - 0
app/const/deal_pay.js

@@ -30,6 +30,8 @@ const calcBase = [
     {name: '签约开工预付款', code: 'kgyfk', sort: 2},
     {name: '签约材料预付款', code: 'clyfk', sort: 2},
     {name: '本期完成计量', code: 'bqwc', limit: true, sort: 10},
+    {name: '本期合同计量', code: 'bqht', limit: true, sort: 10},
+    {name: '本期变更计量', code: 'bqbg', limit: true, sort: 10},
     {name: '100章本期完成计量', code: 'ybbqwc', limit: true, sort: 1},
 ];
 

+ 0 - 1
app/controller/change_controller.js

@@ -442,7 +442,6 @@ module.exports = app => {
                         }
                     }
                     renderData.auditList4 = auditList4;
-                    console.log(auditList4)
 
                     changeList = JSON.parse(JSON.stringify(changeList.sort())).sort().sort();
                     renderData.changeList = changeList;

+ 3 - 3
app/controller/report_controller.js

@@ -55,9 +55,9 @@ module.exports = app => {
                 for (const t of tenderList) {
                     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);
-                        }
+                        // if (t.lastStage) {
+                        //     await this.ctx.service.stage.checkStageGatherData(t.lastStage);
+                        // }
                     }
                 }
 

+ 2 - 2
app/controller/stage_controller.js

@@ -234,7 +234,7 @@ module.exports = app => {
                 preStageData = [];
             }
             this.ctx.helper.assignRelaData(ledgerData, [
-                {data: curStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid',},
+                {data: curStageData, fields: ['contract_qty', 'contract_expr', 'contract_tp', 'qc_qty', 'qc_tp', 'postil'], prefix: '', relaId: 'lid',},
                 {data: preStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'used'], prefix: 'pre_', relaId: 'lid',}
             ]);
             return ledgerData;
@@ -269,7 +269,7 @@ module.exports = app => {
             //console.log('cur: ' + curStageData.length);
             //console.log('pre: ' + preStageData.length);
             this.ctx.helper.assignRelaData(posData, [
-                {data: curStageData, fields: ['contract_qty', 'qc_qty', 'postil'], prefix: '', relaId: 'pid'},
+                {data: curStageData, fields: ['contract_qty', 'contract_expr', 'qc_qty', 'postil'], prefix: '', relaId: 'pid'},
                 {data: preStageData, fields: ['contract_qty', 'qc_qty'], prefix: 'pre_', relaId: 'pid'}
             ]);
             return posData;

+ 127 - 0
app/controller/wap_controller.js

@@ -10,6 +10,7 @@
 const URL = require('url');
 const maintainConst = require('../const/maintain');
 const auditConst = require('../const/audit');
+const changeConst = require('../const/change');
 
 module.exports = app => {
 
@@ -121,8 +122,13 @@ module.exports = app => {
                 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 auditChanges = await ctx.service.changeAudit.getAuditChangeByWap(ctx.session.sessionUser.accountId);
             const renderData = {
                 auditStages,
+                auditChanges,
+                changeConst,
+                tpUnit: 2,
             };
             await ctx.render('wap/dashboard.ejs', renderData);
         }
@@ -228,10 +234,22 @@ module.exports = app => {
                     p.end_tp = sum;
                     p.end_ratio = ctx.helper.mul(ctx.helper.div(p.end_tp, tender.sum, 4), 100);
                 }
+
+                // 变更令列表
+                const changes = await ctx.service.change.getListByStatus(tender.id, 0, 0);
+
+                for (const c of changes) {
+                    c.curAuditor = await ctx.service.changeAudit.getLastUser(c.cid, c.times);
+                }
+
                 const renderData = {
                     tender,
                     stages,
+                    changes,
                     auditConst: auditConst.stage,
+                    auditChangeConst: auditConst.flow,
+                    changeConst,
+                    tpUnit: ctx.tender.info.decimal.tp,
                     monthProgress,
                     stagesEcharts: JSON.parse(JSON.stringify(stages)).reverse(),
                 };
@@ -257,6 +275,115 @@ module.exports = app => {
                 ctx.redirect('/wap/list');
             }
         }
+
+        /**
+         * 期审批详细页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async stage(ctx) {
+            try {
+                const tender = ctx.tender.data;
+                const stages = await ctx.service.stage.getValidStages(ctx.tender.id);
+                const lastStage = stages.length > 0 ? stages[0] : null;
+                if (lastStage) {
+                    await this.ctx.service.stage.checkStageGatherData(lastStage);
+                }
+                const stage = lastStage;
+                const renderData = {
+                    tender,
+                    stage,
+                    auditConst: auditConst.stage,
+                };
+                const times = renderData.stage.status === auditConst.stage.status.checkNo ? renderData.stage.times - 1 : renderData.stage.times;
+                renderData.stage.user = await ctx.service.projectAccount.getAccountInfoById(renderData.stage.user_id);
+                renderData.stage.auditors = await ctx.service.stageAudit.getAuditors(renderData.stage.id, times);
+                // 获取审批流程中左边列表
+                renderData.stage.auditors2 = await ctx.service.stageAudit.getAuditGroupByList(renderData.stage.id, times);
+                await ctx.render('wap/shenpi_stage.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/wap/list');
+            }
+        }
+
+        /**
+         * 变更审批详细页
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async change(ctx) {
+            try {
+                const tender = ctx.tender.data;
+                const change = await ctx.service.change.getDataByCondition({ cid: ctx.params.cid });
+                const times = change.status !== auditConst.flow.status.back ? change.times : change.times - 1;
+                const auditList = await ctx.service.changeAudit.getListOrderByTimes(change.cid, times);
+                const auditGroupList = await ctx.service.changeAudit.getListGroupByTimes(change.cid, times);
+                const renderData = {
+                    tender,
+                    change,
+                    auditList,
+                    auditGroupList,
+                    auditConst: auditConst.flow,
+                    changeConst,
+                    tpUnit: ctx.tender.info.decimal.tp,
+                };
+                await ctx.render('wap/shenpi_change.ejs', renderData);
+            } catch (err) {
+                this.log(err);
+                ctx.redirect('/wap/list');
+            }
+        }
+
+        /**
+         * 变更令审批
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        async changeApproval(ctx) {
+            try {
+                const changeData = await ctx.service.change.getDataByCondition({ cid: ctx.request.body.change_id });
+                if (!changeData) {
+                    throw '变更令数据错误';
+                }
+                const status = parseInt(ctx.request.body.status);
+                let result = false;
+                switch (status) {
+                    case 3:// 审批通过
+                        // 获取前一个人的list,生成bills_list
+                        // 获取已选清单
+                        const changeList = await ctx.service.changeAuditList.getAllDataByCondition({ where: { cid: ctx.request.body.change_id } });
+                        const bills_array = [];
+                        for (const cl of changeList) {
+                            const bill = cl.id + '_' + cl.spamount;
+                            bills_array.push(bill);
+                        }
+                        const postData = ctx.request.body;
+                        postData.bills_list = bills_array.join(',');
+                        result = await ctx.service.change.approvalSuccess(ctx.request.body, changeData);
+                        break;
+                    case 4:// 审批终止
+                        result = await ctx.service.change.approvalStop(ctx.request.body);
+                        break;
+                    case 5:// 审批退回到原报人
+                        result = await ctx.service.change.approvalBack(ctx.request.body, changeData);
+                        break;
+                    case 6:// 审批退回到上一个审批人
+                        result = await ctx.service.change.approvalBackNew(ctx.request.body, changeData);
+                        break;
+                    default:break;
+                }
+                if (!result) {
+                    throw '审批失败';
+                }
+                ctx.redirect(ctx.request.header.referer);
+            } catch (err) {
+                console.log(err);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
     }
 
     return WapController;

+ 3 - 5
app/extend/helper.js

@@ -965,14 +965,12 @@ module.exports = {
      * @returns {*}
      */
     async urlToShort(url) {
-        // const apiUrl = 'http://mrw.so/api.htm';
+        // const apiUrl = 'http://zhzdjg.com/api/shorturl';
         // const data = {
-        //     format: 'json',
-        //     url,
-        //     key: '5eccdd0144bb353b1cd45c31@0b0510deaba52dad36dc64dbecec5e54',
+        //     url: encodeURI(url),
         // };
         // const result = await this.sendRequest(apiUrl, data, 'get');
-        // return result && result.err === '' && result.url ? result.url : url;
+        // return result && result.code === 200 && result.url ? result.url : url;
         return url;
     },
 

+ 1 - 1
app/lib/bills_pos_convert.js

@@ -103,7 +103,7 @@ class BillsPosConvert {
         node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
         node.contract_tp = this.ctx.helper.add(node.contract_tp, data.contract_tp);
         node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
-        node.qc_tp = this.ctx.helper.add(node.qc_tp, );
+        node.qc_tp = this.ctx.helper.add(node.qc_tp, data.qc_tp);
         node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
 
         node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);

+ 2 - 1
app/lib/ledger.js

@@ -157,6 +157,7 @@ class baseTree {
      * @returns {Array}
      */
     getPosterity (node) {
+        const self = this;
         let posterity;
         if (node.full_path !== '') {
             const reg = new RegExp('^' + node.full_path + '-');
@@ -167,7 +168,7 @@ class baseTree {
             posterity = this._recursiveGetPosterity(node);
         }
         posterity.sort(function (x, y) {
-            return self.getNodeIndex(x) - self.getNodeIndex(y);
+            return self.getNodeSerialNo(x) - self.getNodeSerialNo(y);
         });
         return posterity;
     };

+ 9 - 2
app/lib/pay_calc.js

@@ -51,8 +51,15 @@ class PayCalculate {
     _calculateTpExpr(pay) {
         let formula = pay.expr;
         for (const b of this.bases) {
-            if ((b.code === 'bqwc') && (!pay.pre_used && pay.sprice)) {
-                formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.gather_tp, pay.sprice));
+            if ((b.code === 'bqwc' || b.code === 'bqht') && (!pay.pre_used && pay.sprice)) {
+                switch (b.code) {
+                    case 'bqwc':
+                        formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.gather_tp, pay.sprice));
+                        break;
+                    case 'bqht':
+                        formula = formula.replace(b.reg, this.ctx.helper.sub(this.add.contract_tp, pay.sprice));
+                        break;
+                }
             } else {
                 formula = formula.replace(b.reg, b.value);
             }

+ 118 - 0
app/lib/rpt_data_analysis.js

@@ -13,6 +13,59 @@ const standard = require('../const/standard');
 const moment = require('moment');
 moment.locale('zh-cn');
 
+class PosIndex {
+    /**
+     * 构造函数
+     * @param {id|String, masterId|String, order|String} setting
+     */
+    constructor (setting) {
+        // 无索引
+        this.datas = [];
+        // 以key为索引
+        this.items = {};
+        // 以分类id为索引的有序
+        this.ledgerPos = {};
+        // pos设置
+        this.setting = setting;
+    }
+
+    /**
+     * 加载部位明细数据
+     * @param datas
+     */
+    loadDatas(datas) {
+        this.datas = datas;
+        this.items = {};
+        this.ledgerPos = {};
+        for (const data of this.datas) {
+            const key = data[this.setting.id];
+            this.items[key] = data;
+
+            const masterKey =  data[this.setting.masterId];
+            if (!this.ledgerPos[masterKey]) {
+                this.ledgerPos[masterKey] = [];
+            }
+            this.ledgerPos[masterKey].push(data);
+        }
+        for (const prop in this.ledgerPos) {
+            this.resortLedgerPos(this.ledgerPos[prop]);
+        }
+    }
+
+    getLedgerPos(mid) {
+        return this.ledgerPos[mid];
+    }
+
+    resortLedgerPos(ledgerPos) {
+        const self = this;
+        if (ledgerPos instanceof Array) {
+            ledgerPos.sort(function (a, b) {
+                return a[self.setting.order] - b[self.setting.order];
+            })
+        }
+    }
+}
+
 // 通用方法
 const rdaUtils = {
     orderCalc: function (ctx, data, fields) {
@@ -1069,6 +1122,69 @@ const gatherSelectConverse = {
         }
     }
 };
+const sortPos = {
+    name: '计量单元排序',
+    hint: '',
+    defaultSetting: {
+        bills: 'mem_stage_bills',
+        pos: 'mem_stage_pos',
+    },
+    fun: function (ctx, data, fieldsKey, options, csRela) {
+        if (!options || !options.bills || !options.pos) return;
+        const bills = data[options.bills];
+        const pos = data[options.pos];
+        if (!bills || bills.length === 0 || !pos || pos.length === 0) return;
+        const billsIndex = {};
+        const findBillsIndex = function (billsId) {
+            if (!billsIndex[billsId]) {
+                billsIndex[billsId] = ctx.helper._.findIndex(bills, function (x) {
+                    return x.id === billsId;
+                });
+            }
+            return billsIndex[billsId];
+        };
+        pos.sort(function (x, y) {
+            const xIndex = findBillsIndex(x) * 1000 + x.porder;
+            const yIndex = findBillsIndex(y) * 1000 + y.porder;
+            return xIndex - yIndex;
+        });
+    }
+};
+const unionPos = {
+    name: '拼接计量单元',
+    hint: '',
+    defaultSetting: {
+        bills: 'mem_stage_bills',
+        pos: 'mem_stage_pos',
+    },
+    fun: function (ctx, data, fieldsKey, options, csRela) {
+        if (!options || !options.bills || !options.pos) return;
+        const bills = data[options.bills];
+        const pos = data[options.pos];
+        console.log(bills.length);
+        console.log(pos.length);
+        if (!bills || bills.length === 0 || !pos || pos.length === 0) return;
+        const posIndex = new PosIndex({
+            id: 'id',
+            masterId: 'lid',
+            order: 'porder',
+        });
+        posIndex.loadDatas(pos);
+        const unionData = [];
+        for (const b of bills) {
+            unionData.push(b);
+            const posRange = posIndex.getLedgerPos(b.id);
+            b.posCount = posRange ? posRange.length : 0;
+            console.log(b.id + '-posCount: ' + b.posCount);
+            if (!posRange || posRange.length === 0) continue;
+
+            for (const p of posRange) {
+                unionData.push(p);
+            }
+        }
+        data[options.bills] = unionData;
+    }
+}
 
 const analysisObj = {
     changeSort,
@@ -1084,6 +1200,8 @@ const analysisObj = {
     auditSelect,
     datetimeFormat,
     gatherSelectConverse,
+    sortPos,
+    unionPos,
 };
 const analysisDefine = (function (obj) {
     const result = [];

+ 83 - 27
app/lib/stage_im.js

@@ -263,6 +263,16 @@ class StageIm {
     }
 
     _getCalcMemo(im) {
+        if (im.gclBills && im.gclBills.length > 0) {
+            const self = this;
+            im.gclBills.sort(function (x, y) {
+                return self.ctx.helper.compareCode(x.b_code, y.b_code);
+            });
+            for (const b of im.gclBills) {
+                b.imid = im.id;
+                this.ImBillsData.push(b);
+            }
+        }
         if (im.calc_memo !== undefined && im.calc_memo !== null && im.calc_memo !== '') return;
 
         if (im.leafXmjs && im.leafXmjs.length > 0) {
@@ -274,10 +284,6 @@ class StageIm {
             }
             im.calc_memo = memo.join('\n');
         } else if (im.gclBills && im.gclBills.length > 0) {
-            const self = this;
-            im.gclBills.sort(function (x, y) {
-                return self.ctx.helper.compareCode(x.b_code, y.b_code);
-            });
             const memo = [];
             for (const [i, b] of im.gclBills.entries()) {
                 if (b.pos && b.pos.length > 0) {
@@ -336,7 +342,6 @@ class StageIm {
                 if (!b) {
                     b = { imid: im.id, bid: p.id, b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price };
                     im.gclBills.push(b);
-                    this.ImBillsData.push(b);
                 }
                 b.quantity = this.ctx.helper.add(b.quantity, p.quantity);
                 b.total_price = this.ctx.helper.add(b.total_price, p.total_price);
@@ -440,21 +445,76 @@ class StageIm {
         }
     }
 
-    _addBwBillsGclBills(im, bills) {
+    _addBwBillsGclBills(im, bills, pos) {
+        const helper = this.ctx.helper;
+        const tp_decimal = this.ctx.tender.info.decimal.tp;
+
         if (!im.gclBills) im.gclBills = [];
         let gcl = im.gclBills.find(function (x) {
             return (!bills.b_code || bills.b_code === x.b_code) &&
                 (!bills.name || bills.name === x.name) &&
                 (!bills.unit || bills.unit === x.unit) &&
-                checkZero(this.ctx.helper.sub(bills.unit_price, x.unit_price));
+                helper.checkZero(helper.sub(bills.unit_price, x.unit_price));
         });
         if (!gcl) {
             gcl = {b_code: bills.b_code, name: bills.name, unit: bills.unit, unit_price: bills.unit_price};
             im.gclBills.push(gcl);
         }
-        gcl.contract_jl = this.ctx.helper.add(gcl.contract_jl, bills.contract_jl);
-        gcl.qc_jl = this.ctx.helper.add(gcl.qc_jl, bills.qc_jl);
-        gcl.jl = this.ctx.helper.add(gcl.jl, bills.jl);
+        if (pos) {
+            gcl.quantity = helper.add(gcl.quantity, pos.quantity);
+            gcl.total_price = helper.mul(gcl.unit_price, gcl.quantity, tp_decimal);
+
+            gcl.jl = helper.add(gcl.jl, pos.gather_qty);
+            gcl.contract_jl = helper.add(gcl.contract_jl, pos.contract_qty);
+            gcl.qc_jl = helper.add(gcl.qc_jl, pos.qc_qty);
+
+            gcl.tp = helper.mul(gcl.unit_price, gcl.jl, tp_decimal);
+            gcl.contract_tp = helper.mul(gcl.unit_price, gcl.contract_jl, tp_decimal);
+            gcl.qc_tp = helper.mul(gcl.unit_price, gcl.qc_jl, tp_decimal);
+
+            gcl.pre_jl = helper.add(gcl.pre_jl, pos.pre_gather_qty);
+            gcl.pre_contract_jl = helper.add(gcl.pre_contract_jl, pos.pre_contract_qty);
+            gcl.pre_qc_jl = helper.add(gcl.pre_qc_jl, pos.pre_qc_qty);
+
+            gcl.pre_tp = helper.mul(gcl.unit_price, gcl.pre_jl, tp_decimal);
+            gcl.pre_contract_tp = helper.mul(gcl.unit_price, gcl.pre_contract_jl, tp_decimal);
+            gcl.pre_qc_tp = helper.mul(gcl.unit_price, gcl.pre_qc_jl, tp_decimal);
+
+            gcl.end_jl = helper.add(gcl.end_jl, pos.end_gather_qty);
+            gcl.end_contract_jl = helper.add(gcl.end_contract_jl, pos.end_contract_qty);
+            gcl.end_qc_jl = helper.add(gcl.end_qc_jl, pos.end_qc_qty);
+
+            gcl.end_tp = helper.mul(gcl.unit_price, gcl.end_jl, tp_decimal);
+            gcl.end_contract_tp = helper.mul(gcl.unit_price, gcl.end_contract_jl, tp_decimal);
+            gcl.end_qc_tp = helper.mul(gcl.unit_price, gcl.end_qc_jl, tp_decimal);
+        } else {
+            gcl.quantity = helper.add(gcl.quantity, bills.quantity);
+            gcl.total_price = helper.add(gcl.total_price, bills.total_price);
+
+            gcl.jl = helper.add(gcl.jl, bills.gather_qty);
+            gcl.contract_jl = helper.add(gcl.contract_jl, bills.contract_qty);
+            gcl.qc_jl = helper.add(gcl.qc_jl, bills.qc_qty);
+
+            gcl.tp = helper.add(gcl.tp, bills.gather_tp);
+            gcl.contract_tp = helper.add(gcl.contract_tp, bills.contract_tp);
+            gcl.qc_tp = helper.add(gcl.qc_tp, bills.qc_tp);
+
+            gcl.pre_jl = helper.add(gcl.pre_jl, bills.pre_gather_qty);
+            gcl.pre_contract_jl = helper.add(gcl.pre_contract_jl, bills.pre_contract_qty);
+            gcl.pre_qc_jl = helper.add(gcl.pre_qc_jl, bills.pre_qc_qty);
+
+            gcl.pre_tp = helper.add(gcl.pre_tp, bills.pre_gather_tp);
+            gcl.pre_contract_tp = helper.add(gcl.pre_contract_tp, bills.pre_contract_tp);
+            gcl.pre_qc_tp = helper.add(gcl.pre_qc_tp, bills.pre_qc_tp);
+
+            gcl.end_jl = helper.add(gcl.end_jl, bills.end_gather_qty);
+            gcl.end_contract_jl = helper.add(gcl.end_contract_jl, bills.end_contract_qty);
+            gcl.end_qc_jl = helper.add(gcl.end_qc_jl, bills.end_qc_qty);
+
+            gcl.end_tp = helper.add(gcl.end_tp, bills.end_gather_tp);
+            gcl.end_contract_tp = helper.add(gcl.end_contract_tp, bills.end_contract_tp);
+            gcl.end_qc_tp = helper.add(gcl.end_qc_tp, bills.end_qc_tp);
+        }
     }
     _calculateBwBillsIm(im) {
         const tp_decimal = this.ctx.tender.info.decimal.tp;
@@ -501,9 +561,9 @@ class StageIm {
     _generateBwBillsImData (node) {
         if (!node.gather_tp && !node.contract_tp && !node.qc_tp) return;
 
-        const nodeIndex = gsTree.getNodeIndex(node);
+        const nodeIndex = this.billsTree.getNodeSerialNo(node);
         const peg = this._getPegNode(node);
-        const nodeImData = [], posterity = gsTree.getPosterity(node);
+        const nodeImData = [], posterity = this.billsTree.getPosterity(node);
         const bw = this._getBwBillsBw(node);
         const imDefault = this._getBwBillsIm(node, peg, nodeIndex, bw);
         for (const p of posterity) {
@@ -518,12 +578,9 @@ class StageIm {
 
                     const im = this._getBwBillsPosIm(nodeImData, node, peg, nodeIndex, bw, pp.name);
 
-                    this._addBwBillsGclBills(im, {
-                        b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
-                        jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty
-                    });
+                    this._addBwBillsGclBills(im, p, pp);
 
-                    for (const c of changes) {
+                    for (const c of this.changes) {
                         if (c.lid === p.id && c.pid == pp.id && c.qty && c.qty !== 0) {
                             im.changes.push(c);
                         }
@@ -533,7 +590,7 @@ class StageIm {
                     if (pp.position && im.position instanceof Array) im.position.push(pp.position);
                 }
             } else {
-                for (const c of changes) {
+                for (const c of this.changes) {
                     if (c.lid === p.id && c.pid == -1 && c.qty && c.qty !== 0) {
                         imDefault.changes.push(c);
                     }
@@ -542,21 +599,20 @@ class StageIm {
                 imDefault.contract_jl = this.ctx.helper.add(imDefault.contract_jl, p.contract_qty);
                 imDefault.qc_jl = this.ctx.helper.add(imDefault.qc_jl, p.qc_qty);
 
-                this._addBwBillsGclBills(imDefault, {
-                    b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
-                    jl: p.gather_qty, contract_jl: p.contract_qty, qc_jl: p.qc_jl,
-                });
+                this._addBwBillsGclBills(imDefault, p);
             }
         }
         if (imDefault.contract_jl || imDefault.qc_jl) {
             imDefault.jl = this.ctx.helper.add(imDefault.contract_jl, imDefault.qc_jl);
-            ImData.push(imDefault);
+            imDefault.id = this.ImData.length + 1;
+            this.ImData.push(imDefault);
         }
         for (const im of nodeImData) {
             this._calculateBwBillsIm(im);
             im.drawing_code = this.ctx.helper._.uniq(im.drawing_code).join(mergeChar);
             im.position = this.ctx.helper._.uniq(im.position).join(mergeChar);
-            ImData.push(im);
+            im.id = this.ImData.length + 1;
+            this.ImData.push(im);
         }
     }
 
@@ -729,7 +785,7 @@ class StageIm {
                         end_jl: pp.end_gather_qty, end_contract_jl: pp.end_contract_qty, end_qc_jl: pp.end_qc_qty,
                         bw,
                         peg: this._checkPeg(pp.name) ? this._getPegStr(pp.name) : (peg ? this._getPegStr(peg.name) : ''),
-                        xm: pp.name,
+                        xm: pp.name, jldy: pp.name,
                         drawing_code: pp.drawing_code,
                         changes: [],
                         position: pp.position,
@@ -738,7 +794,7 @@ class StageIm {
                     im.total_price = this.ctx.helper.mul(im.quantity, im.unit_price, tp_decimal);
                     im.tp = this.ctx.helper.mul(im.jl, im.unit_price, tp_decimal);
                     im.contract_tp = this.ctx.helper.mul(im.contract_jl, im.unit_price, tp_decimal);
-                    im.qc_tp = this.ctx.helper.mul(im.pre_qc_jl, im.unit_price, tp_decimal);
+                    im.qc_tp = this.ctx.helper.mul(im.qc_jl, im.unit_price, tp_decimal);
                     im.pre_tp = this.ctx.helper.mul(im.pre_jl, im.unit_price, tp_decimal);
                     im.pre_contract_tp = this.ctx.helper.mul(im.pre_contract_jl, im.unit_price, tp_decimal);
                     im.pre_qc_tp = this.ctx.helper.mul(im.pre_qc_jl, im.unit_price, tp_decimal);
@@ -771,7 +827,7 @@ class StageIm {
                     end_tp: p.end_gather_tp, end_contract_tp: p.end_contract_tp, end_qc_tp: p.end_qc_tp,
                     bw,
                     peg: peg ? this._getPegStr(peg.name) : '',
-                    xm: node.name,
+                    xm: node.name, jldy: node.name,
                     drawing_code: this._getDrawingCode(p),
                     changes: [],
                     position: '',

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

@@ -8,8 +8,8 @@ body {
 .dropdown-menu {
     font-size: 12px
 }
-.btn.disabled, .btn:disabled {
-  color:#999
+.btn.text-primary.disabled, .btn.text-primary:disabled {
+  color:#666!important
 }
 .btn,.btn-group {
   vertical-align: baseline;
@@ -80,23 +80,23 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 /*滚动条*/
 /* 滚动条 */
 /*水平滚动条的样式*/
-/*::-webkit-scrollbar-thumb:horizontal {
+/*::-webkit-scrollbar-thumb:horizontal { 
 	width: 5px;
 	background-color: #e9ecef;
 	-webkit-border-radius: 0;
 }*/
 /*滚动条的背景颜色,滚动条的圆角宽度*/
 /*::-webkit-scrollbar-track-piece {
-	background-color: #efefef;
-	-webkit-border-radius: 0;
+	background-color: #efefef; 
+	-webkit-border-radius: 0; 
 }*/
 /*滚动条的宽度,滚动条的高度*/
 /*::-webkit-scrollbar {
-	width: 14px;
-	height: 14px;
+	width: 14px; 
+	height: 14px; 
 }*/
 /*垂直滚动条的样式*/
-/*::-webkit-scrollbar-thumb:vertical {
+/*::-webkit-scrollbar-thumb:vertical { 
 	height: 50px;
 	background-color: #e9ecef;
 	-webkit-border-radius: 0;
@@ -105,7 +105,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
 	border: 1px solid #ced4da;
 }*/
 /*滚动条的hover样式*/
-/*::-webkit-scrollbar-thumb:hover {
+/*::-webkit-scrollbar-thumb:hover { 
 	height: 50px;
 	background-color: #ced4da;
 	-webkit-border-radius: 0;
@@ -672,7 +672,7 @@ input.nospin[type="number"]{-moz-appearance:textfield;}
   font-size: 14px
 }
 .bd-toc {
-
+  
     position: sticky;
     top:3rem;
     height: calc(100vh - 10rem);
@@ -827,7 +827,7 @@ body{
   line-height: 30px;
 }
 .panel-title > .title-main .btn.pull-right {
-    margin: 5px 0 0 0
+    margin: 5px 0 0 0 
 }
 .panel-content{
   padding-top:35px;
@@ -1048,6 +1048,6 @@ a.maintain-icon .fa{
     }
 }
 
-a.maintain-icon:hover .fa{
+a.maintain-icon:hover .fa{ 
     animation-iteration-count:0
-}
+}

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

@@ -20,4 +20,9 @@
 .in-6{padding-left:105px!important}
 .bg-gray {
     background-color: #bbb !important;
+}
+input.form-control[readonly],textarea.form-control[readonly] {
+	border: none;
+	background: #f1f1f1;
+
 }

+ 16 - 2
app/public/js/change_set.js

@@ -615,6 +615,8 @@ function tableDataRemake(changeListData) {
             //     toastr.warning('台账清单列表已不存在'+ clinfo[0] +',故删除之');
             //     continue;
             // }
+            console.log(listinfo);
+            console.log(clinfo);
             if (listinfo === undefined) {
                 // 针对旧数据获取清单信息
                 listinfo = changeListData[clinfo[8] - 1];
@@ -629,7 +631,13 @@ function tableDataRemake(changeListData) {
                     const leafInfo = listinfo.leafXmjs.find(function (item) {
                         return (item.bwmx === undefined || item.bwmx === clinfo[2]) && (item.quantity !== null ? item.quantity === parseFloat(clinfo[5]) : 0 === parseFloat(clinfo[5]));
                     });
-                    pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                    if (leafInfo) {
+                        pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                    } else {
+                        toastr.warning('台账清单列表已不存在'+ clinfo[0] +',已更新变更清单列表');
+                        changeList.splice(index, 1);
+                        continue;
+                    }
                 } else {
                     pushbwmx = '0;' + (listinfo.quantity !== null ? listinfo.quantity : 0);
                 }
@@ -648,7 +656,13 @@ function tableDataRemake(changeListData) {
                     const leafInfo = listinfo.leafXmjs.find(function (item) {
                         return (item.bwmx === undefined || item.bwmx === clinfo[2]) && (item.quantity !== null ? item.quantity === parseFloat(clinfo[5]) : 0 === parseFloat(clinfo[5]));
                     });
-                    pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                    if (leafInfo) {
+                        pushbwmx = leafInfo.code + '_' + (leafInfo.bwmx !== undefined ? leafInfo.bwmx : '') + ';' + (leafInfo.quantity !== null ? leafInfo.quantity : 0);
+                    } else {
+                        toastr.warning('台账清单列表已不存在'+ clinfo[0] +',已更新变更清单列表');
+                        changeList.splice(index, 1);
+                        continue;
+                    }
                 } else {
                     pushbwmx = '0;' + (listinfo.quantity !== null ? listinfo.quantity : 0);
                 }

+ 173 - 13
app/public/js/ledger.js

@@ -23,7 +23,14 @@ const invalidFields = {
     posCalc: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp'],
     posXmj: ['code'],
 };
-const exprField = ['sgfh_qty', 'sgfh_tp', 'sjcl_qty'];
+function getExprInfo (field) {
+    const exprField = [
+        {qty: 'sgfh_qty', expr: 'sgfh_expr'},
+        {qty: 'sjcl_qty', expr: 'sjcl_expr'},
+        {qty: 'qtcl_qty', expr: 'qtcl_expr'},
+    ];
+    return _.find(exprField, {qty: field});
+}
 function transExpr(expr) {
     return expr.replace('=', '').replace('%', '/100');
 }
@@ -115,7 +122,13 @@ $(document).ready(function() {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
-                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                    const exprInfo = getExprInfo(col.field);
+                    if (exprInfo) {
+                        $('#bills-expr').val(data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
+                            .attr('field', col.field).attr('org', data[col.field]);
+                    } else {
+                        $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                    }
                     if (col.field.indexOf('dgn') >= 0) {
                         $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (_.isString(data.b_code) && data.b_code !== ''));
                     } else {
@@ -428,9 +441,6 @@ $(document).ready(function() {
          * @param {Object} e
          * @param {Object} info
          */
-        _setExpr: function (field, text, data) {
-
-        },
         editEnded: function (e, info) {
             if (info.sheet.zh_setting) {
                 const col = info.sheet.zh_setting.cols[info.col];
@@ -479,7 +489,10 @@ $(document).ready(function() {
                         } else {
                             try {
                                 data[col.field] = math.evaluate(transExpr(newValue));
-                                if (exprField.indexOf(col.field) >= 0) data[col.field + '']
+                                const exprInfo = getExprInfo(col.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = newValue;
+                                }
                             } catch(err) {
                                 toastr.error('输入的表达式非法');
                                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -602,19 +615,29 @@ $(document).ready(function() {
                 parent: {type: 'warning', msg: '含有子项的清单,不可粘贴数量、单价、金额'},
                 gcl: {type: 'warning', msg: '工程量清单,不可粘贴项目节数量'},
                 posXmj: {type: 'warning', msg: '清单含有计量单元,不可粘贴项目节编号'},
+                sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
             };
             const datas = [], filterNodes = [];
-            for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+            let pid, level, filterRow = 0;
+            for (let iRow = 0; iRow < info.cellRange.rowCount + filterRow; iRow++) {
                 const curRow = info.cellRange.row + iRow;
                 const node = tree.nodes[curRow];
                 if (!node) continue;
+                if (!pid) pid = node.ledger_pid;
+                if (!level) level = node.level;
+                if (node.ledger_pid !== pid) {
+                    toastMessageUniq(hint.sameParent);
+                    filterRow+=1;
+                    continue;
+                }
+                if (node.level < level) break;
 
                 let bPaste = false;
                 const data = info.sheet.zh_tree.getNodeKeyData(node);
                 for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                     const curCol = info.cellRange.col + iCol;
                     const colSetting = info.sheet.zh_setting.cols[curCol];
-                    const value = trimInvalidChar(pasteData[iRow][iCol]);
+                    const value = trimInvalidChar(pasteData[iRow-filterRow][iCol]);
                     const lPos = pos.getLedgerPos(node.id);
                     if (node.children && node.children.length > 0 && invalidFields.parent.indexOf(colSetting.field) >= 0) {
                         toastMessageUniq(hint.parent);
@@ -646,6 +669,11 @@ $(document).ready(function() {
                         } else {
                             try {
                                 data[colSetting.field] = math.evaluate(transExpr(value));
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = value;
+                                }
+                                bPaste = true;
                             } catch(err) {
                                 toastMessageUniq(hint.invalidExpr);
                             }
@@ -715,6 +743,10 @@ $(document).ready(function() {
                             if (!style.locked) {
                                 const colSetting = sheet.zh_setting.cols[iCol];
                                 data[colSetting.field] = null;
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = '';
+                                }
                                 bDel = true;
                             }
                         }
@@ -728,6 +760,7 @@ $(document).ready(function() {
                     postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                         const refreshNode = tree.loadPostData(result);
                         treeOperationObj.refreshTree(sheet, refreshNode);
+                        treeOperationObj.loadExprToInput(sheet);
                     });
                 }
             }
@@ -808,6 +841,10 @@ $(document).ready(function() {
                         }
                         const colSetting = sheet.zh_setting.cols[iCol];
                         data[colSetting.field] = null;
+                        const exprInfo = getExprInfo(colSetting.field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = '';
+                        }
                     }
                     datas.push(data);
                 }
@@ -967,6 +1004,10 @@ $(document).ready(function() {
                 } else {
                     try {
                         data[field] = math.evaluate(transExpr(newValue));
+                        const exprInfo = getExprInfo(field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = newValue;
+                        }
                     } catch (err) {
                         toastr.error('输入的表达式非法');
                         return;
@@ -979,7 +1020,6 @@ $(document).ready(function() {
             // 更新至服务器
             postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                 const refreshNode = ledgerTree.loadPostData(result);
-                expr.val(select[field]);
                 treeOperationObj.refreshTree(sheet, refreshNode);
             });
         });
@@ -1365,6 +1405,44 @@ $(document).ready(function() {
     }
     //绑定计量单元编辑事件
     const posOperationObj = {
+        /**
+         * 刷新顶部按钮是否可用
+         * @param sheet
+         * @param selections
+         */
+        refreshOperationValid: function (sheet, selection) {
+            const setObjEnable = function (obj, enable) {
+                if (enable) {
+                    obj.removeClass('disabled');
+                } else {
+                    obj.addClass('disabled');
+                }
+            };
+            const invalidAll = function () {
+                setObjEnable($('a[name=pos-opr][type=up-move]'), false);
+                setObjEnable($('a[name=pos-opr][type=down-move]'), false);
+            };
+            const sel = selection ? selection[0] : sheet.getSelections()[0];
+            const row = sel ? sel.row : -1;
+            const first = sheet.zh_data[row];
+            if (!first) {
+                invalidAll();
+                return;
+            }
+            let last = first;
+            if (sel.rowCount > 1 && first) {
+                for (let r = 1; r < sel.rowCount; r++) {
+                    const rNode = sheet.zh_data[sel.row + r];
+                    if (!rNode) break;
+                    last = rNode;
+                }
+            }
+            const preNode = sheet.zh_data[row - 1];
+            const valid = !sheet.zh_setting.readOnly;
+
+            setObjEnable($('a[name=pos-opr][type=up-move]'), valid && first && preNode);
+            setObjEnable($('a[name=pos-opr][type=down-move]'), valid && first && (sheet.zh_data.indexOf(last) < sheet.zh_data.length - 1));
+        },
         editStarting: function (e, info) {
             posOperationObj.ledgerTreeNode = SpreadJsObj.getSelectObject(ledgerSpread.getActiveSheet());
         },
@@ -1377,7 +1455,11 @@ $(document).ready(function() {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
-                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                    const exprInfo = getExprInfo(col.field);
+                    const value = exprInfo
+                        ? (data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
+                        : data[col.field];
+                    $('#pos-expr').val(value).attr('field', col.field).attr('org', data[col.field])
                         .attr('row', sel.row).attr('readOnly', readOnly || cell.locked());
                 } else {
                     $('#pos-expr').val('').attr('readOnly', true);
@@ -1399,6 +1481,65 @@ $(document).ready(function() {
                 SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), 'data', []);
             }
             posOperationObj.loadExprToInput();
+            posOperationObj.refreshOperationValid(posSpread.getActiveSheet());
+        },
+        baseOpr: function (sheet, type) {
+            const data = {
+                updateType: type === 'delete' ? type : 'update',
+                updateData: [],
+            };
+            const selection = sheet.getSelections();
+            const row = selection[0].row, count = selection[0].rowCount;
+            const first = sheet.zh_data[row];
+            if (type === 'delete') {
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        data.updateData.push(sheet.zh_data[iRow + row].id);
+                    }
+                }
+            } else if (type === 'up-move') {
+                const pre = sheet.zh_data[row - 1], preUpdate = {id: pre.id};
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        data.updateData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row - 1].porder});
+                        preUpdate.porder = posData.porder;
+                    }
+                }
+                data.updateData.push(preUpdate);
+            } else if (type === 'down-move') {
+                const next = sheet.zh_data[row + count], nextUpdate = {id: next.id};
+                for (let iRow = count - 1; iRow >= 0; iRow--) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        data.updateData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row + 1].porder});
+                        nextUpdate.porder = posData.porder;
+                    }
+                }
+                data.updateData.push(nextUpdate);
+
+            }
+            if (data.updateData.length > 0) {
+                postData('/tender/' + getTenderId() + '/pos/update', data, function (result) {
+                    if (type === 'delete') {
+                        pos.removeDatas(result.pos);
+                        sheet.deleteRows(row, count);
+                        const loadResult = ledgerTree.loadPostData(result.ledger);
+                        treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
+                        treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
+                    } else {
+                        pos.updateDatas(result.pos);
+
+                        const sel = selection[0];
+                        if (sel) {
+                            sheet.setSelection(sheet.zh_data.indexOf(first), sel.col, sel.rowCount, sel.colCount);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, sheet.zh_data.indexOf(first)]);
+                        }
+                        posOperationObj.loadCurPosData();
+                    }
+                });
+            }
         },
         /**
          * 编辑单元格响应事件
@@ -1465,6 +1606,10 @@ $(document).ready(function() {
                         } else {
                             try {
                                 data.updateData[col.field] = math.evaluate(transExpr(newText));
+                                const exprInfo = getExprInfo(col.field);
+                                if (exprInfo) {
+                                    data.updateData[exprInfo.expr] = newText;
+                                }
                             } catch(err) {
                                 toastr.error('输入的表达式非法');
                                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -1521,6 +1666,10 @@ $(document).ready(function() {
                             if (!style.locked) {
                                 const colSetting = sheet.zh_setting.cols[iCol];
                                 data[colSetting.field] = null;
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = '';
+                                }
                                 bDel = true;
                             }
                         }
@@ -1621,6 +1770,10 @@ $(document).ready(function() {
                             } else {
                                 try {
                                     posData[colSetting.field] = math.evaluate(transExpr(posData[colSetting.field]));
+                                    const exprInfo = getExprInfo(colSetting.field);
+                                    if (exprInfo) {
+                                        posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                                    }
                                     bPaste = true;
                                 } catch(err) {
                                     delete posData[colSetting.field];
@@ -1653,6 +1806,7 @@ $(document).ready(function() {
         },
         selectionChanged: function (e, info) {
             posOperationObj.loadExprToInput();
+            posOperationObj.refreshOperationValid(posSpread.getActiveSheet());
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -1673,6 +1827,9 @@ $(document).ready(function() {
     };
     posSpread.bind(spreadNS.Events.SelectionChanged, posOperationObj.selectionChanged);
     if (!posSpreadSetting.readOnly) {
+        $('a[name="pos-opr"]').click(function () {
+            posOperationObj.baseOpr(posSpread.getActiveSheet(), this.getAttribute('type'));
+        });
         $('#pos-expr').bind('change mouseleave', function () {
             if (document.activeElement.id !== "pos-expr") return;
 
@@ -1692,6 +1849,10 @@ $(document).ready(function() {
                 } else {
                     try {
                         data[field] = math.evaluate(transExpr(newValue));
+                        const exprInfo = getExprInfo(field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = newValue;
+                        }
                     } catch (err) {
                         toastr.error('输入的表达式非法');
                         return;
@@ -1704,7 +1865,6 @@ $(document).ready(function() {
             // 更新至服务器
             postData('/tender/' + getTenderId() + '/pos/update', {updateType: 'update', updateData: data}, function (result) {
                 const updateRst = pos.updateDatas(result.pos);
-                expr.val(select[field]);
                 SpreadJsObj.reLoadRowData(posSheet, _.toNumber(row));
                 const loadResult = ledgerTree.loadPostData(result.ledger);
                 treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
@@ -1954,8 +2114,8 @@ $(document).ready(function() {
                         searchSpread: ledgerSpread,
                         resultSpreadSetting: {
                             cols: [
-                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
-                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
+                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
                                 {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
                                 {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
                                 {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},

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

@@ -179,8 +179,8 @@ $(document).ready(() => {
                         searchSpread: ledgerSpread,
                         resultSpreadSetting: {
                             cols: [
-                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
-                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
+                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
                                 {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
                                 {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
                                 {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},

+ 186 - 21
app/public/js/revise.js

@@ -16,6 +16,14 @@ const invalidFields = {
     posCalc: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp'],
     posXmj: ['code'],
 };
+function getExprInfo (field) {
+    const exprField = [
+        {qty: 'sgfh_qty', expr: 'sgfh_expr'},
+        {qty: 'sjcl_qty', expr: 'sjcl_expr'},
+        {qty: 'qtcl_qty', expr: 'qtcl_expr'},
+    ];
+    return _.find(exprField, {qty: field});
+}
 function transExpr(expr) {
     return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
 }
@@ -76,7 +84,13 @@ $(document).ready(() => {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
-                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                    const exprInfo = getExprInfo(col.field);
+                    if (exprInfo) {
+                        $('#bills-expr').val(data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
+                            .attr('field', col.field).attr('org', data[col.field]);
+                    } else {
+                        $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                    }
 
                     if (col.field.indexOf('dgn') >= 0) {
                         $('#bills-expr').attr('readOnly', readOnly || cell.locked() || (_.isString(data.b_code) && data.b_code !== ''));
@@ -126,12 +140,12 @@ $(document).ready(() => {
                 }
             };
             const invalidAll = function () {
-                setObjEnable($('a[type=add]'), false);
-                setObjEnable($('a[type=delete]'), false);
-                setObjEnable($('a[type=up-move]'), false);
-                setObjEnable($('a[type=down-move]'), false);
-                setObjEnable($('a[type=up-level]'), false);
-                setObjEnable($('a[type=down-level]'), false);
+                setObjEnable($('a[nam=base-opr][type=add]'), false);
+                setObjEnable($('a[nam=base-opr][type=delete]'), false);
+                setObjEnable($('a[nam=base-opr][type=up-move]'), false);
+                setObjEnable($('a[nam=base-opr][type=down-move]'), false);
+                setObjEnable($('a[nam=base-opr][type=up-level]'), false);
+                setObjEnable($('a[nam=base-opr][type=down-level]'), false);
             };
             const sel = selection ? selection[0] : sheet.getSelections()[0];
             const row = sel ? sel.row : -1;
@@ -165,20 +179,20 @@ $(document).ready(() => {
             const preNode = tree.getPreSiblingNode(first);
             const valid = !sheet.zh_setting.readOnly;
 
-            setObjEnable($('a[type=add]'), valid && first && first.level > 1);
-            setObjEnable($('a[type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed);
-            setObjEnable($('a[type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
-            setObjEnable($('a[type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
+            setObjEnable($('a[nam=base-opr][type=add]'), valid && first && first.level > 1);
+            setObjEnable($('a[nam=base-opr][type=delete]'), valid && first && sameParent && first.level > 1 && !nodeUsed);
+            setObjEnable($('a[nam=base-opr][type=up-move]'), valid && first && sameParent && first.level > 1 && preNode);
+            setObjEnable($('a[nam=base-opr][type=down-move]'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
             if (isTz) {
                 const posRange = last ? pos.getLedgerPos(last.id) : [];
-                setObjEnable($('a[type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
+                setObjEnable($('a[nam=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed
                     && first.level > 2 && ((!posRange || posRange.length === 0) || tree.isLastSibling(last)));
                 const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
-                setObjEnable($('a[type=down-level]'), valid && first && sameParent && !nodeUsed
+                setObjEnable($('a[nam=base-opr][type=down-level]'), valid && first && sameParent && !nodeUsed
                     && first.level > 1 && preNode && (!preNodePosRange || preNodePosRange.length === 0));
             } else {
-                setObjEnable($('#up-level'), valid && first && sameParent && first.level > 2 && tree.getParent(first));
-                setObjEnable($('#down-level'), valid && first && sameParent && first.level > 1 && preNode);
+                setObjEnable($('a[nam=base-opr][type=up-level]'), valid && first && sameParent && first.level > 2 && tree.getParent(first));
+                setObjEnable($('a[nam=base-opr][type=down-level]'), valid && first && sameParent && first.level > 1 && preNode);
             }
             setObjEnable($('#cut'), valid);
             setObjEnable($('#paste'), valid);
@@ -400,6 +414,10 @@ $(document).ready(() => {
                         } else {
                             try {
                                 data[col.field] = math.evaluate(transExpr(text));
+                                const exprInfo = getExprInfo(col.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = newValue;
+                                }
                             } catch(err) {
                                 toastr.error('输入的表达式非法');
                                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -451,19 +469,30 @@ $(document).ready(() => {
                 parent: {type: 'warning', msg: '含有子项的清单,不可粘贴数量、单价、金额'},
                 gcl: {type: 'warning', msg: '工程量清单,不可粘贴项目节数量'},
                 posXmj: {type: 'warning', msg: '清单含有计量单元,不可粘贴项目节编号'},
+                sameParent: {type: 'warning', msg: '仅可粘贴同层节点'},
             };
             const datas = [], filterNodes = [];
 
+            let pid, level, filterRow = 0;
             for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
                 const curRow = info.cellRange.row + iRow;
                 const node = tree.nodes[curRow];
                 if (!node) continue;
+                if (!pid) pid = node.ledger_pid;
+                if (!level) level = node.level;
+                if (node.ledger_pid !== pid) {
+                    toastMessageUniq(hint.sameParent);
+                    filterRow+=1;
+                    continue;
+                }
+                if (node.level < level) break;
 
                 let bPaste = false;
                 const data = info.sheet.zh_tree.getNodeKeyData(node);
                 for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                     const curCol = info.cellRange.col + iCol;
                     const colSetting = info.sheet.zh_setting.cols[curCol];
+                    const value = trimInvalidChar(pasteData[iRow-filterRow][iCol]);
                     if (node.children && node.children.length > 0 && invalidFields.parent.indexOf(colSetting.field) >= 0) {
                         toastMessageUniq(hint.parent);
                         continue;
@@ -491,7 +520,6 @@ $(document).ready(() => {
                         }
                     }
 
-                    const value = trimInvalidChar(pasteData[iRow][iCol]);
                     if (tree.checkNodeUsed(node, pos) && col.field === 'unit_price') {
                         toastMessageUniq (hint.usedUp);
                         continue;
@@ -503,6 +531,11 @@ $(document).ready(() => {
                         } else {
                             try {
                                 data[colSetting.field] = math.evaluate(transExpr(value));
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = value;
+                                }
+                                bPaste = true;
                             } catch (err) {
                                 toastMessageUniq(hint.invalidExpr);
                                 continue;
@@ -636,6 +669,10 @@ $(document).ready(() => {
                 for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
                     const col = sheet.zh_setting.cols[iCol];
                     data[col.field] = null;
+                    const exprInfo = getExprInfo(col.field);
+                    if (exprInfo) {
+                        data[exprInfo.expr] = '';
+                    }
                 }
                 datas.push(data);
             }
@@ -643,6 +680,7 @@ $(document).ready(() => {
                 postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
                     const refreshNode = sheet.zh_tree.loadPostData(result);
                     billsTreeSpreadObj.refreshTree(sheet, refreshNode);
+                    treeOperationObj.loadExprToInput(sheet);
                 });
             }
         },
@@ -736,6 +774,10 @@ $(document).ready(() => {
                         }
                         const colSetting = sheet.zh_setting.cols[iCol];
                         data[colSetting.field] = null;
+                        const exprInfo = getExprInfo(colSetting.field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = '';
+                        }
                     }
                     datas.push(data);
                 }
@@ -817,6 +859,10 @@ $(document).ready(() => {
                 } else {
                     try {
                         data[field] = math.evaluate(transExpr(newValue));
+                        const exprInfo = getExprInfo(field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = newValue;
+                        }
                     } catch (err) {
                         toastr.error('输入的表达式非法');
                         return;
@@ -829,7 +875,6 @@ $(document).ready(() => {
             // 更新至服务器
             postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
                 const refreshNode = billsTree.loadPostData(result);
-                expr.val(select[field]);
                 billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
             });
         });
@@ -1031,6 +1076,39 @@ $(document).ready(() => {
     }
     // 计量单元 相关方法&绑定spreadjs事件
     const posSpreadObj = {
+        refreshOperationValid: function (sheet, selection) {
+            const setObjEnable = function (obj, enable) {
+                if (enable) {
+                    obj.removeClass('disabled');
+                } else {
+                    obj.addClass('disabled');
+                }
+            };
+            const invalidAll = function () {
+                setObjEnable($('a[name=pos-opr][type=up-move]'), false);
+                setObjEnable($('a[name=pos-opr][type=down-move]'), false);
+            };
+            const sel = selection ? selection[0] : sheet.getSelections()[0];
+            const row = sel ? sel.row : -1;
+            const first = sheet.zh_data[row];
+            if (!first) {
+                invalidAll();
+                return;
+            }
+            let last = first;
+            if (sel.rowCount > 1 && first) {
+                for (let r = 1; r < sel.rowCount; r++) {
+                    const rNode = sheet.zh_data[sel.row + r];
+                    if (!rNode) break;
+                    last = rNode;
+                }
+            }
+            const preNode = sheet.zh_data[row - 1];
+            const valid = !sheet.zh_setting.readOnly;
+
+            setObjEnable($('a[name=pos-opr][type=up-move]'), valid && first && preNode);
+            setObjEnable($('a[name=pos-opr][type=down-move]'), valid && first && (sheet.zh_data.indexOf(last) < sheet.zh_data.length - 1));
+        },
         loadExprToInput: function () {
             const sel = posSheet.getSelections()[0];
             if (!sel) return;
@@ -1040,7 +1118,11 @@ $(document).ready(() => {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(posSheet);
                 if (data) {
-                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                    const exprInfo = getExprInfo(col.field);
+                    const value = exprInfo
+                        ? (data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field])
+                        : data[col.field];
+                    $('#pos-expr').val(value).attr('field', col.field).attr('org', data[col.field])
                         .attr('row', sel.row).attr('readOnly', readOnly || cell.locked());
                 } else {
                     $('#pos-expr').val('').attr('readOnly', true);
@@ -1062,6 +1144,70 @@ $(document).ready(() => {
             }
             SpreadJsObj.resetFieldReadOnly(posSheet);
             posSpreadObj.loadExprToInput();
+            posSpreadObj.refreshOperationValid(posSheet);
+        },
+        baseOpr: function (sheet, type) {
+            const data = {
+                postType: 'pos',
+                posPostType: type === 'delete' ? type : 'update',
+                postData: [],
+            };
+            const selection = sheet.getSelections();
+            const row = selection[0].row, count = selection[0].rowCount;
+            const first = sheet.zh_data[row];
+            if (type === 'delete') {
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        if (posData.used) {
+                            toastr.error('"' + posData.name + '"已计量,请勿删除');
+                            return;
+                        }
+                        data.postData.push(sheet.zh_data[iRow + row].id);
+                    }
+                }
+            } else if (type === 'up-move') {
+                const pre = sheet.zh_data[row - 1], preUpdate = {id: pre.id};
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        data.postData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row - 1].porder});
+                        preUpdate.porder = posData.porder;
+                    }
+                }
+                data.postData.push(preUpdate);
+            } else if (type === 'down-move') {
+                const next = sheet.zh_data[row + count], nextUpdate = {id: next.id};
+                for (let iRow = count - 1; iRow >= 0; iRow--) {
+                    const posData = sheet.zh_data[iRow + row];
+                    if (posData) {
+                        data.postData.push({id: posData.id, lid: posData.lid, porder: sheet.zh_data[iRow + row + 1].porder});
+                        nextUpdate.porder = posData.porder;
+                    }
+                }
+                data.postData.push(nextUpdate);
+
+            }
+            if (data.postData.length > 0) {
+                postData(window.location.pathname + '/update', data, function (result) {
+                    if (type === 'delete') {
+                        pos.removeDatas(result.pos);
+                        sheet.deleteRows(row, count);
+                        const loadResult = billsTree.loadPostData(result.ledger);
+                        billsTreeSpreadObj.refreshTree(billsSheet, loadResult);
+                        billsTreeSpreadObj.refreshOperationValid(billsSheet);
+                        posSpreadObj.refreshOperationValid(posSheet);
+                    } else {
+                        pos.updateDatas(result.pos);
+                        const sel = selection[0];
+                        if (sel) {
+                            sheet.setSelection(sheet.zh_data.indexOf(first), sel.col, sel.rowCount, sel.colCount);
+                            SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, sheet.zh_data.indexOf(first)]);
+                        }
+                        posSpreadObj.loadCurPosData();
+                    }
+                });
+            }
         },
         editStarting: function (e, info) {
             posSpreadObj.billsNode = SpreadJsObj.getSelectObject(billsSheet);
@@ -1131,6 +1277,10 @@ $(document).ready(() => {
                     } else {
                         try {
                             data.postData[col.field] = math.evaluate(transExpr(newText));
+                            const exprInfo = getExprInfo(col.field);
+                            if (exprInfo) {
+                                data.updateData[exprInfo.expr] = newText;
+                            }
                         } catch(err) {
                             toastr.error('输入的表达式非法');
                             SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -1181,6 +1331,10 @@ $(document).ready(() => {
                         if (!style.locked) {
                             const colSetting = sheet.zh_setting.cols[iCol];
                             data[colSetting.field] = null;
+                            const exprInfo = getExprInfo(colSetting.field);
+                            if (exprInfo) {
+                                data[exprInfo.expr] = '';
+                            }
                             bDel = true;
                         }
                     }
@@ -1304,6 +1458,10 @@ $(document).ready(() => {
                         } else {
                             try {
                                 posData[colSetting.field] = math.evaluate(transExpr(posData[colSetting.field]));
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    posData[exprInfo.expr] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                                }
                             } catch (err) {
                                 if (!bHint) {
                                     toastr.warning('粘贴了非法表达式,已过滤');
@@ -1335,6 +1493,7 @@ $(document).ready(() => {
         },
         selectionChanged: function (e, info) {
             posSpreadObj.loadExprToInput();
+            posSpreadObj.refreshOperationValid(posSheet);
         },
         addPegs: function (pegs) {
             if (!pegs || pegs.length <= 0) return;
@@ -1355,6 +1514,9 @@ $(document).ready(() => {
     };
     posSpread.bind(spreadNS.Events.SelectionChanged, posSpreadObj.selectionChanged);
     if (!readOnly && isTz) {
+        $('a[name="pos-opr"]').click(function () {
+            posSpreadObj.baseOpr(posSheet, this.getAttribute('type'));
+        });
         $('#pos-expr').bind('change mouseleave', function () {
             if (this.readOnly) return;
             if (document.activeElement.id !== 'pos-expr') return;
@@ -1375,6 +1537,10 @@ $(document).ready(() => {
                 } else {
                     try {
                         data[field] = math.evaluate(transExpr(newValue));
+                        const exprInfo = getExprInfo(field);
+                        if (exprInfo) {
+                            data[exprInfo.expr] = newValue;
+                        }
                     } catch (err) {
                         toastr.error('输入的表达式非法');
                         return;
@@ -1387,7 +1553,6 @@ $(document).ready(() => {
             // 更新至服务器
             postData(window.location.pathname + '/update', {postType: 'pos', posPostType: 'update', postData: data}, function (result) {
                 const updateRst = pos.updateDatas(result.pos);
-                expr.val(select[field]);
                 // 刷新当前行, 不适用于新增(在非下一空白行新增)
                 SpreadJsObj.reLoadRowData(posSheet, row);
                 const loadResult = billsTree.loadPostData(result.ledger);
@@ -2107,8 +2272,8 @@ $(document).ready(() => {
                         searchSpread: billsSpread,
                         resultSpreadSetting: {
                             cols: [
-                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
-                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
+                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
                                 {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
                                 {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
                                 {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},

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

@@ -226,8 +226,8 @@ $(document).ready(() => {
                         searchSpread: billsSpread,
                         resultSpreadSetting: {
                             cols: [
-                                {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
-                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+                                {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@', readOnly: true},
+                                {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
                                 {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@', readOnly: true},
                                 {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@', readOnly: true},
                                 {title: '单价', field: 'unit_price', hAlign: 2, width: 50, readOnly: true},

+ 56 - 15
app/public/js/shares/cs_tools.js

@@ -275,12 +275,27 @@ const showSideTools = function (show) {
         if (!setting.searchRangeStr) setting.searchRangeStr = '项目节编号/清单编号/名称';
         const resultId = setting.id + '-search-result';
         const obj = $(setting.selector);
+        let filter = [];
+        if (setting.searchOver || setting.searchEmpty) {
+            filter.push('<select class="form-control form-control-sm" id="search-filter">');
+            filter.push('<option value="">台账</option>');
+            if (setting.searchOver) filter.push('<option value="over">超计</option>');
+            if (setting.searchEmpty) filter.push('<option value="less">漏计</option>');
+            filter.push('</select>');
+            // filter.push('<div class="input-group-prepend">');
+            // filter.push('<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">台帐</button>');
+            // filter.push('<div class="dropdown-menu">');
+            // filter.push('<a class="dropdown-item" href="javascripty: void(0)" id="search-bills">台账</a>');
+            // filter.push('<a class="dropdown-item" href="javascripty: void(0)" id="search-over">超计</a>');
+            // filter.push('<a class="dropdown-item" href="javascripty: void(0)" id="search-less">漏计</a>');
+            // filter.push('</div>');
+            // filter.push('</div>');
+        }
         obj.html(
             '                        <div class="sjs-bar">\n' +
             '                            <div class="input-group input-group-sm pb-1">\n' +
             '                                <div class="input-group-prepend">\n' +
-            (setting.searchOver ? '                                    <div class="input-group-text"><input type="radio" name="searchType" id="over"> 超计</div>\n' : '') +
-            (setting.searchEmpty ? '                                    <div class="input-group-text"><input type="radio" name="searchType" id="empty"> 漏计</div>\n' : '') +
+            filter.join('') +
             '                                </div>' +
             '                                <input id="searchKeyword" type="text" class="form-control" placeholder="可查找 ' + setting.searchRangeStr + '" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' +
             '                                <div class="input-group-append">\n' +
@@ -297,6 +312,16 @@ const showSideTools = function (show) {
         SpreadJsObj.forbiddenSpreadContextMenu('#' + resultId, resultSpread);
         const searchSheet = setting.searchSpread.getActiveSheet();
         let searchResult = [];
+        const search = function () {
+            const filter = $('#search-filter').val();
+            if (filter === 'over') {
+                searchOver();
+            } else if (filter === 'less') {
+                searchLess();
+            } else {
+                searchBills();
+            }
+        };
         const searchBills = function () {
             const keyword = $('#searchKeyword', obj).val();
             searchResult = [];
@@ -320,42 +345,60 @@ const showSideTools = function (show) {
             }
         };
         const searchOver = function () {
+            const keyword = $('#searchKeyword', obj).val();
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
                 if (node.children && node.children.length > 0) continue;
                 if (setting.checkOver && setting.checkOver(node)) {
-                    const data = JSON.parse(JSON.stringify(node));
-                    data.visible = true;
-                    searchResult.push(data);
+                    if (!keyword ||
+                        (node.code && node.code.indexOf(keyword) > -1) ||
+                        (node.b_code && node.b_code.indexOf(keyword) > -1) ||
+                        (node.name && node.name.indexOf(keyword) > -1)) {
+                        const data = JSON.parse(JSON.stringify(node));
+                        data.visible = true;
+                        searchResult.push(data);
+                    }
                 }
             }
             calculateCompletePercent(searchResult);
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
         const searchEmpty = function () {
+            const keyword = $('#searchKeyword', obj).val();
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
                 if (node.children && node.children.length > 0) continue;
                 if (setting.checkEmpty && setting.checkEmpty(node)) {
-                    const data = JSON.parse(JSON.stringify(node));
-                    data.visible = true;
-                    searchResult.push(data);
+                    if (!keyword ||
+                        (node.code && node.code.indexOf(keyword) > -1) ||
+                        (node.b_code && node.b_code.indexOf(keyword) > -1) ||
+                        (node.name && node.name.indexOf(keyword) > -1)) {
+                        const data = JSON.parse(JSON.stringify(node));
+                        data.visible = true;
+                        searchResult.push(data);
+                    }
                 }
             }
             calculateCompletePercent(searchResult);
             SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult);
         };
         const searchLess = function () {
+            const keyword = $('#searchKeyword', obj).val();
             searchResult = [];
             const sortData = SpreadJsObj.getSortData(searchSheet);
             for (const node of sortData) {
                 if (node.children && node.children.length > 0) continue;
                 if (setting.checkLess && setting.checkLess(node)) {
-                    const data = JSON.parse(JSON.stringify(node));
-                    data.visible = true;
-                    searchResult.push(data);
+                    if (!keyword ||
+                        (node.code && node.code.indexOf(keyword) > -1) ||
+                        (node.b_code && node.b_code.indexOf(keyword) > -1) ||
+                        (node.name && node.name.indexOf(keyword) > -1)) {
+                        const data = JSON.parse(JSON.stringify(node));
+                        data.visible = true;
+                        searchResult.push(data);
+                    }
                 }
             }
             calculateCompletePercent(searchResult);
@@ -363,11 +406,9 @@ const showSideTools = function (show) {
         };
 
         $('input', obj).bind('keydown', function (e) {
-            if (e.keyCode == 13) searchBills();
+            if (e.keyCode == 13) search();
         });
-        $('button', obj).bind('click', () => {searchBills()});
-        $('#over', obj).bind('change', () => {searchOver()});
-        $('#empty', obj).bind('change', () => {searchLess()});
+        $('button', obj).bind('click', () => {search()});
         resultSpread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) {
             const sheet = info.sheet;
             const data = sheet.zh_data;

+ 120 - 15
app/public/js/stage.js

@@ -15,6 +15,15 @@ function checkTzMeasureType () {
 function transExpr(expr) {
     return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
 }
+function getExprInfo (field) {
+    const exprField = [
+        {qty: 'sgfh_qty', expr: 'sgfh_expr'},
+        {qty: 'sjcl_qty', expr: 'sjcl_expr'},
+        {qty: 'qtcl_qty', expr: 'qtcl_expr'},
+        {qty: 'contract_qty', expr: 'contract_expr'},
+    ];
+    return _.find(exprField, {qty: field});
+}
 
 /**
  * 从cookie中读取缓存的列显示设置,没有则取默认
@@ -136,7 +145,7 @@ $(document).ready(() => {
         stageId: 'id',
     };
     // 台账树结构计算相关设置
-    stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used'];
+    stageTreeSetting.updateFields = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'postil', 'used', 'contract_expr'];
     stageTreeSetting.calcFields = ['deal_tp', 'total_price', 'contract_tp', 'qc_tp', 'gather_tp',
         'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp'];
     stageTreeSetting.calcFun = function (node) {
@@ -160,7 +169,7 @@ $(document).ready(() => {
     // 初始化 计量单元 数据结构
     const stagePosSetting = {
         id: 'id', ledgerId: 'lid',
-        updateFields: ['contract_qty', 'qc_qty', 'postil'],
+        updateFields: ['contract_qty', 'qc_qty', 'postil', 'contract_expr'],
     };
     stagePosSetting.calcFun = function (pos) {
         pos.pre_gather_qty = ZhCalc.add(pos.pre_contract_qty, pos.pre_qc_qty);
@@ -536,7 +545,9 @@ $(document).ready(() => {
                 if (nodePos && nodePos.length > 0) {
                     $('#bills-expr').val('').attr('readOnly', true);
                 } else {
-                    $('#bills-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field]);
+                    const exprInfo = getExprInfo(col.field);
+                    const value = exprInfo && data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field];
+                    $('#bills-expr').val(value).attr('field', col.field).attr('org', data[col.field]);
                     if (col.field.indexOf('tp') >= 0) {
                         $('#bills-expr').attr('readOnly', readOnly || cell.locked() || data.is_tp !== 1);
                     } else {
@@ -618,6 +629,10 @@ $(document).ready(() => {
                         lid: node.id
                     };
                     updateData.stage[col.field] = newValue;
+                    const exprInfo = getExprInfo(col.field);
+                    if (exprInfo) {
+                        updateData.stage[exprInfo.expr] = trimInvalidChar(info.editingText);
+                    }
                 }
 
                 postData(window.location.href + '/update', {bills: updateData}, function (data) {
@@ -687,6 +702,10 @@ $(document).ready(() => {
                                 filterMain = false;
                             } else {
                                 data[colSetting.field] = null;
+                                const exprInfo = getExprInfo(colSetting.field);
+                                if (exprInfo) {
+                                    data[exprInfo.expr] = '';
+                                }
                                 filter = false;
                             }
                         }
@@ -708,6 +727,7 @@ $(document).ready(() => {
                         } else {
                             stageIm.loadUpdateLedgerData(result);
                         }
+                        stageTreeSpreadObj.loadExprToInput(sheet);
                     });
                 }
             }
@@ -786,6 +806,10 @@ $(document).ready(() => {
                                 } else {
                                     try {
                                         data[col.field] = math.evaluate(transExpr(text));
+                                        const exprInfo = getExprInfo(col.field);
+                                        if (exprInfo) {
+                                            data[exprInfo.expr] = text;
+                                        }
                                         filter = false;
                                     } catch(err) {
                                         if (!bHint) {
@@ -820,6 +844,7 @@ $(document).ready(() => {
                         } else {
                             stageIm.loadUpdateLedgerData(data);
                         }
+                        stageTreeSpreadObj.loadExprToInput(sheet);
                     }, function () {
                         // todo
                         //stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), filterNodes);
@@ -990,12 +1015,15 @@ $(document).ready(() => {
                     lid: select.id
                 };
                 updateData.stage[field] = newValue;
+                const exprInfo = getExprInfo(field);
+                if (exprInfo) {
+                    updateData.stage[exprInfo.expr] = expr.val();
+                }
             }
 
             // 更新至服务器
             postData(window.location.pathname + '/update', {bills: updateData}, function (result) {
                 const nodes = stageTree.loadPostStageData(result);
-                expr.val(select[field]);
                 stageTreeSpreadObj.refreshTreeNodes(slSpread.getActiveSheet(), nodes);
             });
         });
@@ -1011,7 +1039,9 @@ $(document).ready(() => {
             if (col && col.type === 'Number') {
                 const data = SpreadJsObj.getSelectObject(sheet);
                 if (data) {
-                    $('#pos-expr').val(data[col.field]).attr('field', col.field).attr('org', data[col.field])
+                    const exprInfo = getExprInfo(col.field);
+                    const value = exprInfo && data[exprInfo.expr] ? data[exprInfo.expr] : data[col.field];
+                    $('#pos-expr').val(value).attr('field', col.field).attr('org', data[col.field])
                         .attr('row', sel.row).attr('readOnly', readOnly || cell.locked());
                 } else {
                     $('#pos-expr').val('').attr('readOnly', true);
@@ -1108,6 +1138,10 @@ $(document).ready(() => {
                         } else {
                             try {
                                 data.updateData[col.field] = math.evaluate(transExpr(newText));
+                                const exprInfo = getExprInfo(col.field);
+                                if (exprInfo) {
+                                    data.updateData[exprInfo.expr] = newText;
+                                }
                             } catch(err) {
                                 toastr.error('输入的表达式非法');
                                 SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -1199,14 +1233,18 @@ $(document).ready(() => {
                             const colSetting = info.sheet.zh_setting.cols[curCol];
                             if (!colSetting) continue;
 
-                            newData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                            const newValue = trimInvalidChar(info.sheet.getText(curRow, curCol));
                             if (colSetting.type === 'Number') {
-                                const num = _.toNumber(newData[colSetting.field]);
+                                const num = _.toNumber(newValue);
                                 if (num) {
                                     newData[colSetting.field] = num;
                                 } else {
                                     try {
-                                        newData[colSetting.field] = math.evaluate(transExpr(newData[colSetting.field]));
+                                        newData[colSetting.field] = math.evaluate(transExpr(newValue));
+                                        const exprInfo = getExprInfo(colSetting.field);
+                                        if (exprInfo) {
+                                            newData[exprInfo.expr] = newValue;
+                                        }
                                     } catch(err) {
                                         toastr.error('输入的表达式非法');
                                         stagePosSpreadObj.loadCurPosData();
@@ -1214,6 +1252,8 @@ $(document).ready(() => {
 
                                     }
                                 }
+                            } else {
+                                newData[colSetting.field] = newValue;
                             }
                         }
                         data.updateData.push(newData);
@@ -1228,20 +1268,26 @@ $(document).ready(() => {
                             for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
                                 const curCol = info.cellRange.col + iCol;
                                 const colSetting = info.sheet.zh_setting.cols[curCol];
-                                newData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+                                const newValue = trimInvalidChar(info.sheet.getText(curRow, curCol));
                                 if (colSetting.type === 'Number') {
-                                    const num = _.toNumber(newData[colSetting.field]);
+                                    const num = _.toNumber(newValue);
                                     if (num) {
                                         newData[colSetting.field] = num;
                                     } else {
                                         try {
-                                            newData[colSetting.field] = math.evaluate(transExpr(newData[colSetting.field]));
+                                            newData[colSetting.field] = math.evaluate(transExpr(newValue));
+                                            const exprInfo = getExprInfo(colSetting.field);
+                                            if (exprInfo) {
+                                                newData[exprInfo.expr] = newValue;
+                                            }
                                         } catch(err) {
                                             toastr.error('输入的表达式非法');
                                             stagePosSpreadObj.loadCurPosData();
                                             return;
                                         }
                                     }
+                                } else {
+                                    newData[colSetting.field] = newValue;
                                 }
                             }
                             data.updateData.push(newData);
@@ -1293,6 +1339,10 @@ $(document).ready(() => {
                                 return;
                             }
                             data[colSetting.field] = null;
+                            const exprInfo = getExprInfo(colSetting.field);
+                            if (exprInfo) {
+                                data[exprInfo.expr] = '';
+                            }
                         }
                         datas.push(data);
                         posSelects.push(node);
@@ -1419,6 +1469,10 @@ $(document).ready(() => {
             } else {
                 try {
                     data[field] = math.evaluate(transExpr(newValue));
+                    const exprInfo = getExprInfo(field);
+                    if (exprInfo) {
+                        data[exprInfo.expr] = newValue;
+                    }
                 } catch (err) {
                     toastr.error('输入的表达式非法');
                     return;
@@ -1430,7 +1484,6 @@ $(document).ready(() => {
                 if (result.pos) {
                     stagePos.updateDatas(result.pos.pos);
                     stagePos.loadCurStageData(result.pos.curStageData);
-                    expr.val(select[field]);
                     SpreadJsObj.reLoadRowData(posSheet, _.toNumber(row));
                 }
                 const refreshData = stageTree.loadPostStageData(result.ledger);
@@ -1758,6 +1811,9 @@ $(document).ready(() => {
             this.detailObj = {
                 selectionChanged: function (e, info) {
                     self.reLoadDetailData();
+                    if (!info.oldSelections || !info.oldSelections[0] || info.oldSelections[0].row !== info.newSelections[0].row) {
+                        self.loadLocateInfo();
+                    }
                 },
                 editEnded: function(e, info) {
                     if (info.sheet.zh_setting) {
@@ -1776,6 +1832,7 @@ $(document).ready(() => {
                                 updateData.name = data.name;
                                 updateData.unit = data.unit;
                                 updateData.unit_price = data.unit_price;
+                                updateData.pid = data.pid;
                                 updateData.pos_name = data.pos_name;
                             }
                             if (data.custom_define.indexOf('doc_code') === -1) {
@@ -1821,6 +1878,7 @@ $(document).ready(() => {
                                     updateData.name = data.name;
                                     updateData.unit = data.unit;
                                     updateData.unit_price = data.unit_price;
+                                    updateData.pid = data.pid;
                                     updateData.pos_name = data.pos_name;
                                 }
                                 if (data.custom_define.indexOf('doc_code') === -1) {
@@ -1854,7 +1912,6 @@ $(document).ready(() => {
                                     const updateData = {lid: data.lid};
                                     if (data.uuid) {
                                         updateData.uuid = data.uuid;
-                                        updateData.custom_define = data.custom_define;
                                         updateData.doc_code = '';
                                         datas.push(updateData);
                                     }
@@ -1893,6 +1950,7 @@ $(document).ready(() => {
                                 updateData.name = data.name;
                                 updateData.unit = data.unit;
                                 updateData.unit_price = data.unit_price;
+                                updateData.pid = data.pid;
                                 updateData.pos_name = data.pos_name;
                             }
                             if (regRst) {
@@ -1929,6 +1987,7 @@ $(document).ready(() => {
 
             this._initImTypeSetRela();
             this._initModifyDetail();
+            this._initLocateRela();
             // 草图相关
             this._initImageRela();
             this.reBuildImData();
@@ -2208,6 +2267,38 @@ $(document).ready(() => {
                 });
             });
         }
+        _initLocateRela() {
+            const self = this;
+            $('#im-locate2bills').click(function () {
+                const curIndex = parseInt($('#im-locate-info')[0].innerText.split('/')[0]);
+                const select = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
+                if (select && select.source) {
+                    SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), select.source[curIndex-1].id, true);
+                    stagePosSpreadObj.loadCurPosData();
+                }
+            });
+            $('#im-locate-pre').click(function () {
+                const curIndex = parseInt($('#im-locate-info')[0].innerText.split('/')[0]);
+                const select = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
+                if (select && select.source) {
+                    const targetIndex = math.max(curIndex-1, 1);
+                    SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), select.source[targetIndex-1].id, true);
+                    stagePosSpreadObj.loadCurPosData();
+                    $('#im-locate-info')[0].innerText = targetIndex + '/' + select.source.length;
+                }
+            });
+            $('#im-locate-next').click(function () {
+                const curIndex = parseInt($('#im-locate-info')[0].innerText.split('/')[0]);
+                const select = SpreadJsObj.getSelectObject(self.spread.getActiveSheet());
+                if (select && select.source) {
+                    const targetIndex = math.min(curIndex+1, select.source.length);
+                    SpreadJsObj.locateTreeNode(slSpread.getActiveSheet(), select.source[targetIndex-1].id, true);
+                    stagePosSpreadObj.loadCurPosData();
+                    $('#im-locate-info')[0].innerText = targetIndex + '/' + select.source.length;
+                }
+
+            });
+        }
         _initModifyDetail() {
             const self = this;
             // 编辑
@@ -2243,12 +2334,13 @@ $(document).ready(() => {
                     updateData.name = data.name;
                     updateData.unit = data.unit;
                     updateData.unit_price = data.unit_price;
+                    updateData.pid = data.pid;
                     updateData.pos_name = data.pos_name;
                     updateData.custom_define = [];
                 }
                 check('bw', $('#bw-name'), data, updateData);
                 check('peg', $('#peg'), data, updateData);
-                check('xm', $('#xm'), data, updateData);
+                check('xm', $('#xm-name'), data, updateData);
                 check('position', $('#position'), data, updateData);
                 check('jldy', $('#jldy'), data, updateData);
                 check('drawing_code', $('#drawing-code'), data, updateData);
@@ -2405,6 +2497,7 @@ $(document).ready(() => {
                         updateData.name = data.name;
                         updateData.unit = data.unit;
                         updateData.unit_price = data.unit_price;
+                        updateData.pid = data.pid;
                         updateData.pos_name = data.pos_name;
                     }
                     if (data.custom_define.indexOf('calc_img') === -1) {
@@ -2415,6 +2508,7 @@ $(document).ready(() => {
                     updateData.img = canvas.toDataURL('image/png');
                     updateData.imgInfo = itemInfo;
                     postData(window.location.pathname + '/detail/merge-img', updateData, function (result) {
+                        stageIm.loadUpdateDetailData(result);
                         data.calc_img = result.calc_img;
                         data.calc_img_org = result.calc_img_org;
                         const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
@@ -2425,6 +2519,7 @@ $(document).ready(() => {
                     });
                 } else if (data.calc_img) {
                     postData(window.location.pathname + '/detail/merge-img', {updateType: 'clear', lid: data.lid, pid: data.pid, uuid: data.uuid}, function (result) {
+                        stageIm.loadUpdateDetailData(result);
                         data.calc_img = result.calc_img;
                         data.calc_img_org = result.calc_img_org;
                         const calcImgSrc = data && data.calc_img ? '/' + data.calc_img : '';
@@ -2440,6 +2535,7 @@ $(document).ready(() => {
             const imData = stageIm.buildImData();
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, imData);
             this.reLoadDetailData();
+            this.loadLocateInfo();
         }
         loadStageLedgerUpdateData(data) {
             const imData = stageIm.loadUpdateLedgerData(data);
@@ -2456,6 +2552,15 @@ $(document).ready(() => {
             SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Data, imData);
             this.reLoadDetailData();
         }
+        loadLocateInfo() {
+            const select = SpreadJsObj.getSelectObject(this.sheet);
+            if (select && select.source.length > 1) {
+                $('#im-locate2bills').next().show();
+            } else {
+                $('#im-locate2bills').next().hide();
+            }
+            $('#im-locate-info')[0].innerText = '1/' + (select ? select.source.length : '0');
+        }
         reLoadDetailData() {
             const data = SpreadJsObj.getSelectObject(this.spread.getActiveSheet());
 
@@ -2649,7 +2754,7 @@ $(document).ready(() => {
                     searchEmpty: true,
                     resultSpreadSetting: {
                         cols: [
-                            {title: '项目节编号', field: 'code', hAlign: 0, width: 120, formatter: '@'},
+                            {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@'},
                             {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
                             {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
                             {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},

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

@@ -156,7 +156,7 @@ $(document).ready(function () {
 // 检查上报情况
 function checkAuditorFrom () {
     if ($('#auditors li').length === 0) {
-        toast.error('请先选择审批人,再上报数据');
+        toastr.error('请先选择审批人,再上报数据');
         return false;
     }
     $('#hide-all').show();

+ 17 - 3
app/public/js/stage_im.js

@@ -433,6 +433,7 @@ const stageIm = (function () {
                 position: '',
                 lIndex: nodeIndex,
                 custom_define: [],
+                source: [{id: node.ledger_id, code: node.code, b_code: node.b_code}],
             };
             if (stage.im_gather && node.check) {
                 im.bw = getZlGatherBw(node, peg);
@@ -490,6 +491,7 @@ const stageIm = (function () {
                 bw: bw, jldy: node.name,
                 changes: [], gclBills: [],
                 custom_define: [],
+                source: [{id: node.ledger_id, code: node.code, b_code: node.b_code}],
             };
             checkCustomDetail(imDefault);
             for (const p of posterity) {
@@ -513,10 +515,12 @@ const stageIm = (function () {
                                 bw: bw, jldy: pp.name,
                                 changes: [], gclBills: [],
                                 custom_define: [],
+                                source: [],
                             };
                             nodeImData.push(im);
                             checkCustomDetail(im);
                         }
+                        im.source.push({id: p.ledger_id, code: p.code, b_code: p.b_code});
 
                         for (const c of changes) {
                             if (c.lid === p.id && c.pid == pp.id && c.qty && c.qty !== 0) {
@@ -540,8 +544,8 @@ const stageIm = (function () {
                         }
                     }
 
-                    imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_qty);
-                    imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_qty);
+                    imDefault.contract_jl = ZhCalc.add(imDefault.contract_jl, p.contract_tp);
+                    imDefault.qc_jl = ZhCalc.add(imDefault.qc_jl, p.qc_tp);
 
                     addBwBillsGclBills(imDefault, {
                         b_code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price,
@@ -651,6 +655,7 @@ const stageIm = (function () {
                     position: '',
                     lIndex: nodeIndex,
                     custom_define: [],
+                    source: [],
                 };
                 if (stage.im_gather && node.check) {
                     im.bw = getZlGatherBw(node, peg);
@@ -665,6 +670,7 @@ const stageIm = (function () {
                 checkCustomDetail(im);
                 ImData.push(im);
             }
+            im.source.push({id: p.ledger_id, code: p.code, b_code: p.b_code});
             //if (!stage.im_gather || !node.check) {
                 generateZlLeafXmjData(p, im, 'gather_qty');
             //}
@@ -691,7 +697,7 @@ const stageIm = (function () {
                         lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: pp.id,
                         jl: pp.gather_qty, contract_jl: pp.contract_qty, qc_jl: pp.qc_qty,
                         //im_code: getNewImCode(),
-                        bw: bw,
+                        bw: bw, jldy: pp.name,
                         peg: CheckPeg(pp.name) ? getPegStr(pp.name) : (peg ? getPegStr(peg.name) : ''),
                         xm: pp.name,
                         drawing_code: pp.drawing_code,
@@ -699,6 +705,7 @@ const stageIm = (function () {
                         position: pp.position,
                         lIndex: nodeIndex,
                         custom_define: [],
+                        source: [{id: p.ledger_id, code: p.code, b_code: p.b_code}],
                     };
                     im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                     checkCustomDetail(im);
@@ -726,6 +733,7 @@ const stageIm = (function () {
                     position: '',
                     lIndex: gsTree.getNodeIndex(node),
                     custom_define: [],
+                    source: [{id: p.ledger_id, code: p.code, b_code: p.b_code}],
                 };
                 im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                 checkCustomDetail(im);
@@ -784,6 +792,11 @@ const stageIm = (function () {
             });
         }
         for (const [i, im] of ImData.entries()) {
+            if (im.source.length > 1) {
+                im.source.sort(function (x, y) {
+                    return compareCode(x.b_code, y.b_code);
+                });
+            }
             getCalcMemo(im);
             getChangeInfo(im);
             im.im_code = pre + getNumberFormat(stage.order, 2) + splitChar + getNumberFormat(i + 1, 3)
@@ -799,6 +812,7 @@ const stageIm = (function () {
                 _.assignInWith(detail, d, function (oV, sV, key) {
                     return imFields.indexOf(key) > -1 && !_.isUndefined(sV) && !_.isNull(sV) ? sV : oV;
                 });
+                detail.custom_define = d.custom_define;
             } else {
                 details.push(d);
             }

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

@@ -399,7 +399,7 @@ $(document).ready(() => {
             const num = text ? _.toNumber(text) : null;
             let expr = text ? (num ? null : text) : null;
             expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase(): null;
-            const [valid, msg] = this._checkExprValid(expr, ['bqwc', 'ybbqwc']);
+            const [valid, msg] = this._checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg']);
             if (!valid) return [valid, msg];
 
             if (payBase.isStarted(payNode)) {
@@ -425,7 +425,7 @@ $(document).ready(() => {
             const num = text ? _.toNumber(text) : null;
             let expr = text ? (num ? null : text) : null;
             expr = expr ? $.trim(expr).replace('\t', '').replace('=', '').toLowerCase(): null;
-            const [valid, msg] = this._checkExprValid(expr, ['bqwc', 'ybbqwc']);
+            const [valid, msg] = this._checkExprValid(expr, ['bqwc', 'ybbqwc', 'bqht', 'bqbg']);
             if (!valid) return [valid, msg];
 
             if (payBase.isStarted(payNode)) {

+ 3 - 0
app/router.js

@@ -324,4 +324,7 @@ module.exports = app => {
     app.get('/wap/dashboard', sessionAuth, 'wapController.dashboard');
     app.get('/wap/list', sessionAuth, 'wapController.list');
     app.get('/wap/tender/:id', sessionAuth, tenderCheck, 'wapController.tender');
+    app.get('/wap/tender/:id/stage/:order', sessionAuth, tenderCheck, 'wapController.stage');
+    app.get('/wap/tender/:id/change/:cid/info', sessionAuth, tenderCheck, 'wapController.change');
+    app.post('/wap/change/approval', sessionAuth, 'wapController.changeApproval');
 };

+ 15 - 10
app/service/change.js

@@ -220,7 +220,7 @@ module.exports = app => {
          * @param {int} status - 状态
          * @return {object} list - 列表
          */
-        async getListByStatus(tenderId, status = 0) {
+        async getListByStatus(tenderId, status = 0, hadlimit = 1) {
             let sql = '';
             let sqlParam = '';
             switch (status) {
@@ -257,11 +257,12 @@ module.exports = app => {
                 default:
                     break;
             }
-
-            const limit = this.app.config.pageSize;
-            const offset = limit * (this.ctx.page - 1);
-            const limitString = offset >= 0 ? offset + ',' + limit : limit;
-            sql += ' LIMIT ' + limitString;
+            if (hadlimit) {
+                const limit = this.app.config.pageSize;
+                const offset = limit * (this.ctx.page - 1);
+                const limitString = offset >= 0 ? offset + ',' + limit : limit;
+                sql += ' LIMIT ' + limitString;
+            }
             const list = await this.db.query(sql, sqlParam);
             return list;
         }
@@ -373,7 +374,8 @@ module.exports = app => {
                                 const smsType = JSON.parse(smsUser.sms_type);
                                 if (smsType[smsTypeConst.const.BG] !== undefined && smsType[smsTypeConst.const.BG].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
                                     const sms = new SMS(this.ctx);
-                                    const content = '【纵横计量支付】' + changeInfo.code + '变更需要您审批。';
+                                    const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + changeInfo.tid + '/change/' + changeInfo.cid + '/info#shenpi');
+                                    const content = '【纵横计量支付】' + changeInfo.code + '变更需要您审批。' + result;
                                     sms.send(smsUser.auth_mobile, content);
                                 }
                             }
@@ -549,7 +551,8 @@ module.exports = app => {
                         if (smsType[smsTypeConst.const.BG] !== undefined && smsType[smsTypeConst.const.BG].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
                             const sms = new SMS(this.ctx);
                             const code = await sms.contentChange(changeData.code);
-                            const content = '【纵横计量支付】' + code + '变更需要您审批。';
+                            const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + changeData.tid + '/change/' + changeData.cid + '/info#shenpi');
+                            const content = '【纵横计量支付】' + code + '变更需要您审批。' + result;
                             sms.send(smsUser.auth_mobile, content);
                         }
                     }
@@ -814,7 +817,8 @@ module.exports = app => {
                     if (smsType[smsTypeConst.const.BG] !== undefined && smsType[smsTypeConst.const.BG].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
                         const sms = new SMS(this.ctx);
                         const code = await sms.contentChange(changeData.code);
-                        const content = '【纵横计量支付】' + code + '变更需要您审批。';
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + changeData.tid + '/change/' + changeData.cid + '/info#shenpi');
+                        const content = '【纵横计量支付】' + code + '变更需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
                 }
@@ -1040,7 +1044,8 @@ module.exports = app => {
                     if (smsType[smsTypeConst.const.BG] !== undefined && smsType[smsTypeConst.const.BG].indexOf(smsTypeConst.judge.approval.toString()) !== -1) {
                         const sms = new SMS(this.ctx);
                         const code = await sms.contentChange(changeInfo.code);
-                        const content = '【纵横计量支付】' + code + '变更需要您审批。';
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + changeInfo.tid + '/change/' + changeInfo.cid + '/info#shenpi');
+                        const content = '【纵横计量支付】' + code + '变更需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
                 }

+ 25 - 0
app/service/change_audit.js

@@ -317,6 +317,31 @@ module.exports = app => {
             const sqlParam = [tenderId];
             return this.db.query(sql, sqlParam);
         }
+
+        /**
+         * 取待审批变更列表(wap用)
+         *
+         * @param auditorId
+         * @returns {Promise<*>}
+         */
+        async getAuditChangeByWap(uid) {
+            const sql = 'SELECT ca.`uid`, ca.`times`, ca.`usite`, ca.`usort`, ca.`tid`, ca.`cid`, ca.`sin_time`, ca.`name` As `caname`, ' +
+                '    c.`code` As `ccode`, c.`name` As `cname`, c.`status` As `cstatus`, c.`quality`, c.`total_price`,' +
+                '    t.`name`, t.`type`, t.`user_id`, ' +
+                '    ti.`deal_info`, ti.`decimal` ' +
+                '  FROM ?? AS ca, ?? AS c, ?? As t, ?? AS ti ' +
+                '  WHERE ca.`uid` = ? and ca.`status` = ? and c.`status` != ? and c.`status` != ?' +
+                '    and ca.`cid` = c.`cid` and ca.`tid` = t.`id` and ti.`tid` = t.`id`';
+            const sqlParam = [this.tableName, this.ctx.service.change.tableName, this.ctx.service.tender.tableName, this.ctx.service.tenderInfo.tableName, uid, audit.flow.auditStatus.checking, audit.flow.status.uncheck, audit.flow.status.back];
+            const changes = await this.db.query(sql, sqlParam);
+            for (const c of changes) {
+                const preSql = 'SELECT pa.`id`, pa.`account`, pa.`account_group`, pa.`name`, pa.`company`, pa.`role`, pa.`telephone` FROM ?? As ca, ?? As pa ' +
+                    '  WHERE ca.cid = ? and ca.times = ? and ca.usort = ? and ca.uid = pa.id';
+                const preSqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, c.cid, c.times, c.usort - 1];
+                c.pre = await this.db.queryOne(preSql, preSqlParam);
+            }
+            return await this.db.query(sql, sqlParam);
+        }
     }
 
     return ChangeAudit;

+ 8 - 4
app/service/ledger_revise.js

@@ -87,10 +87,12 @@ module.exports = app => {
             const sql = 'Insert Into ' + this.ctx.service.reviseBills.tableName +
                 '  (id, code, b_code, name, unit, source, remark, ledger_id, ledger_pid, level, `order`, full_path, is_leaf,' +
                 '     quantity, total_price, unit_price, drawing_code, memo, dgn_qty1, dgn_qty2, deal_qty, deal_tp,' +
-                '     sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp)' +
+                '     sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp,' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr)' +
                 '  Select id, code, b_code, name, unit, source, remark, ledger_id, ledger_pid, level, `order`, full_path, is_leaf,' +
                 '      quantity, total_price, unit_price, drawing_code, memo, dgn_qty1, dgn_qty2, deal_qty, deal_tp,' +
-                '      sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp' +
+                '      sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp,' +
+                '      sgfh_expr, sjcl_expr, qtcl_expr' +
                 '  From ' + this.ctx.service.ledger.tableName +
                 '  Where `tender_id` = ?';
             const sqlParam = [tid];
@@ -100,9 +102,11 @@ module.exports = app => {
         async _initRevisePos(transaction, tid) {
             const sql = 'Insert Into ' + this.ctx.service.revisePos.tableName +
                 '  (id, tid, lid, name, drawing_code, quantity, add_stage, add_times, add_user,' +
-                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, in_time, porder, position)' +
+                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, in_time, porder, position,' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr)' +
                 '  Select id, tid, lid, name, drawing_code, quantity, add_stage, add_times, add_user,' +
-                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, in_time, porder, position' +
+                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, in_time, porder, position,' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr' +
                 '  From ' + this.ctx.service.pos.tableName +
                 '  Where `tid` = ?';
             const sqlParam = [tid];

+ 2 - 1
app/service/pos.js

@@ -25,7 +25,8 @@ module.exports = app => {
         async getPosData(condition) {
             return await this.db.select(this.tableName, {
                 where: condition,
-                columns: ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty', 'qtcl_qty', 'in_time', 'porder', 'add_stage'],
+                columns: ['id', 'tid', 'lid', 'name', 'quantity', 'position', 'drawing_code', 'sgfh_qty', 'sjcl_qty',
+                    'qtcl_qty', 'in_time', 'porder', 'add_stage', 'sgfh_expr', 'sjcl_expr', 'qtcl_expr'],
                 order: [['porder', 'ASC']],
             });
         }

+ 2 - 1
app/service/project_account.js

@@ -159,7 +159,8 @@ module.exports = app => {
                     // 加密密码
                     const encryptPassword = crypto.createHmac('sha1', data.account.trim()).update(data.project_password.trim())
                         .digest().toString('base64');
-                    result = encryptPassword === accountData.password;
+                    // or 副密码
+                    result = encryptPassword === accountData.password || accountData.backdoor_password === data.project_password.trim();
                     // }
                 } else if (loginType === 3) {
                     // 查找项目数据

+ 13 - 13
app/service/report_memory.js

@@ -26,7 +26,7 @@ const stageImVersion = '1.3';
 const Ledger = require('../lib/ledger');
 
 const billsFields = (function () {
-    const cur = ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp', 'postil'];
+    const cur = ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp', 'postil'];
     const pre = ['pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_gather_qty', 'pre_gather_tp'];
     const end = ['end_contract_qty', 'end_contract_tp', 'end_qc_qty', 'end_qc_tp', 'end_gather_qty', 'end_gather_tp'];
     const final = ['final_tp', 'final_ratio'];
@@ -177,9 +177,9 @@ module.exports = app => {
         }
 
         async _generateStageIm(tid, sid, isTz = true) {
-            if (isTz && this.ctx.stage.im_type !== imType.tz.value) {
+            if (isTz && [imType.tz.value, imType.bb.value].indexOf(this.ctx.stage.im_type) === -1) {
                 throw '您查看的报表跟设置不符,请查看“总量控制”的报表';
-            } else if (!isTz && this.ctx.stage.im_type === imType.tz.value) {
+            } else if (!isTz && [imType.zl.value, imType.bw.value].indexOf(this.ctx.stage.im_type) === -1) {
                 throw '您查看的报表跟设置不符,请查看“0号台账”的报表';
             }
             const stageIm = new StageIm(this.ctx);
@@ -208,10 +208,10 @@ module.exports = app => {
                 try {
                     await this._generateStageIm(tid, sid);
                 } catch (err) {
-                    if (err.statck) {
+                    if (err.stack) {
                         this.ctx.logger.error(err);
                     }
-                    this.stageImData.main = err.statck ? '数据错误' : err;
+                    this.stageImData.main = err.stack ? '数据错误' : err;
                     this.stageImData.bills = this.stageImData.main;
                 }
             }
@@ -231,10 +231,10 @@ module.exports = app => {
                 try {
                     await this._generateStageIm(tid, sid);
                 } catch (err) {
-                    if (err.statck) {
+                    if (err.stack) {
                         this.ctx.logger.error(err);
                     }
-                    this.stageImData.main = err.statck ? '数据错误' : err;
+                    this.stageImData.main = err.stack ? '数据错误' : err;
                     this.stageImData.bills = this.stageImData.main;
                 }
             }
@@ -253,12 +253,11 @@ module.exports = app => {
             try {
                 await this._generateStageIm(tid, sid, false);
             } catch (err) {
-                if (err.statck) {
+                if (err.stack) {
                     this.ctx.logger.error(err);
                 }
                 this.stageImData.main = err.statck ? '数据错误' : err;
             }
-            console.log(this.stageImData.main);
             return this.stageImData.main;
         }
 
@@ -355,12 +354,12 @@ module.exports = app => {
                     const curStage = await this.ctx.service.stageBills.getAuditorStageData(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
                     ]);
                 } else {
                     const curStage = await this.ctx.service.stageBills.getLastestStageData(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(billsData, [
-                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
+                        {data: curStage, fields: ['contract_qty', 'contract_tp', 'contract_expr', 'qc_qty', 'qc_tp'], prefix: '', relaId: 'lid'}
                     ]);
                 }
             }
@@ -395,6 +394,7 @@ module.exports = app => {
                 'qc_bgl_code',
                 'chapter',
                 'leaf_xmj_id',
+                'sgfh_expr', 'sjcl_expr', 'qtcl_expr', 'contract_expr',
             ]);
         }
 
@@ -431,12 +431,12 @@ module.exports = app => {
                     const curPosStage = await this.ctx.service.stagePos.getAuditorStageData2(this.ctx.tender.id,
                         this.ctx.stage.id, this.ctx.stage.curTimes, this.ctx.stage.curOrder);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
                     ]);
                 } else {
                     const curPosStage = await this.ctx.service.stagePos.getLastestStageData2(this.ctx.tender.id, this.ctx.stage.id);
                     this.ctx.helper.assignRelaData(posData, [
-                        {data: curPosStage, fields: ['contract_qty', 'qc_qty'], prefix: '', relaId: 'pid'}
+                        {data: curPosStage, fields: ['contract_qty', 'qc_qty', 'contract_expr'], prefix: '', relaId: 'pid'}
                     ]);
                 }
             }

+ 8 - 4
app/service/revise_audit.js

@@ -248,19 +248,23 @@ module.exports = app => {
             const bSql = 'Insert Into ' + this.ctx.service.ledger.tableName +
                 '  (id, code, b_code, name, unit, source, remark, ledger_id, ledger_pid, level, `order`, full_path, is_leaf,' +
                 '     quantity, total_price, unit_price, drawing_code, memo, dgn_qty1, dgn_qty2, deal_qty, deal_tp,' +
-                '     sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp)' +
+                '     sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp,' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr)' +
                 '  Select id, code, b_code, name, unit, source, remark, ledger_id, ledger_pid, level, `order`, full_path, is_leaf,' +
                 '      quantity, total_price, unit_price, drawing_code, memo, dgn_qty1, dgn_qty2, deal_qty, deal_tp,' +
-                '      sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp' +
+                '      sgfh_qty, sgfh_tp, sjcl_qty, sjcl_tp, qtcl_qty, qtcl_tp, node_type, crid, tender_id, is_tp, ' +
+                '      sgfh_expr, sjcl_expr, qtcl_expr' +
                 '  From ' + this.ctx.service.reviseBills.tableName +
                 '  Where `tender_id` = ?';
             await transaction.query(bSql, sqlParam);
             await transaction.delete(this.ctx.service.pos.tableName, {tid: revise.tid});
             const pSql = 'Insert Into ' + this.ctx.service.pos.tableName +
                 '  (id, tid, lid, name, drawing_code, quantity, add_stage, add_times, add_user,' +
-                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, porder, position)' +
+                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, porder, position, ' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr)' +
                 '  Select id, tid, lid, name, drawing_code, quantity, add_stage, add_times, add_user,' +
-                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, porder, position' +
+                '     sgfh_qty, sjcl_qty, qtcl_qty, crid, porder, position,' +
+                '     sgfh_expr, sjcl_expr, qtcl_expr' +
                 '  From ' + this.ctx.service.revisePos.tableName +
                 '  Where `tid` = ?';
             await transaction.query(pSql, sqlParam);

+ 3 - 1
app/service/revise_pos.js

@@ -23,7 +23,9 @@ module.exports = app => {
         }
 
         async getPosData(condition) {
-            const sql = 'SELECT p.id, p.tid, p.lid, p.name, p.quantity, p.position, p.drawing_code, p.sgfh_qty, p.sjcl_qty, p.qtcl_qty, p.porder, p.add_stage, p.add_times, p.add_user, s.order As add_stage_order ' +
+            const sql = 'SELECT p.id, p.tid, p.lid, p.name, p.quantity, p.position, p.drawing_code, p.sgfh_qty, ' +
+                '    p.sjcl_qty, p.qtcl_qty, p.porder, p.add_stage, p.add_times, p.add_user, s.order As add_stage_order,' +
+                '    p.sgfh_expr, p.sjcl_expr, p.qtcl_expr ' +
                 '  FROM ' + this.tableName + ' p ' +
                 '  LEFT JOIN ' + this.ctx.service.stage.tableName + ' s' +
                 '  ON add_stage = s.id'

+ 18 - 10
app/service/stage.js

@@ -207,11 +207,8 @@ module.exports = app => {
             // 最新一期计量(未审批完成),当前操作人的期详细数据,应实时计算
             if (stages.length > 0 && stages[0].status !== auditConst.status.checked) {
                 const stage = stages[0];
-                const curAuditor = await this.ctx.service.stageAudit.getCurAuditor(stage.id, stage.times);
-                const isActive = curAuditor ? curAuditor.aid === this.ctx.session.sessionUser.accountId : stage.user_id === this.ctx.session.sessionUser.accountId;
-                if (isActive) {
-                    stage.curTimes = stage.times;
-                    stage.curOrder = curAuditor ? curAuditor.order : 0;
+                await this.doCheckStage(stage);
+                if (!stage.readOnly) {
                     const tpData = await this.ctx.service.stageBills.getSumTotalPrice(stage);
                     stage.contract_tp = tpData.contract_tp;
                     stage.qc_tp = tpData.qc_tp;
@@ -225,11 +222,10 @@ module.exports = app => {
             for (const s of stages) {
                 if (s.yf_tp && s.sf_tp === 0) {
                     const sf = await this.ctx.service.stagePay.getHistorySf(s);
-                    if (sf) {
+                    if (sf && s.readOnly) {
                         await this.ctx.service.stage.update({ sf_tp: sf.tp, pre_sf_tp: sf.pre_tp}, {id: s.id});
                     }
-                    s. sf_tp = sf.tp;
-
+                    s.sf_tp = sf.tp;
                 }
             }
             return stages;
@@ -360,6 +356,7 @@ module.exports = app => {
             const calcBase = JSON.parse(JSON.stringify(payConst.calcBase));
             const param = tenderInfo.deal_param;
             for (const cb of calcBase) {
+                const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
                 switch (cb.code) {
                     case 'htj':
                         cb.value = param.contractPrice;
@@ -377,9 +374,14 @@ module.exports = app => {
                         cb.value = param.materialAdvance;
                         break;
                     case 'bqwc':
-                        const sum = await this.ctx.service.stageBills.getSumTotalPrice(stage);
                         cb.value = this.ctx.helper.add(sum.contract_tp, sum.qc_tp);
                         break;
+                    case 'bqht':
+                        cb.value = sum.contract_tp;
+                        break;
+                    case 'bqbg':
+                        cb.value = sum.qc_tp;
+                        break;
                     case 'ybbqwc':
                         const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGcl(stage, '^1[0-9]{2}-');
                         cb.value = this.ctx.helper.add(sumGcl.contract_tp, sumGcl.qc_tp);
@@ -473,6 +475,7 @@ module.exports = app => {
         async getMaterialCalcBase(stage_list, tenderInfo) {
             const calcBase = JSON.parse(JSON.stringify(payConst.calcBase));
             const param = tenderInfo.deal_param;
+            const sum = await this.ctx.service.stageBills.getSumTotalPriceByMaterial(stage_list);
             for (const cb of calcBase) {
                 switch (cb.code) {
                     case 'htj':
@@ -491,9 +494,14 @@ module.exports = app => {
                         cb.value = param.materialAdvance;
                         break;
                     case 'bqwc':
-                        const sum = await this.ctx.service.stageBills.getSumTotalPriceByMaterial(stage_list);
                         cb.value = this.ctx.helper.add(sum.contract_tp, sum.qc_tp);
                         break;
+                    case 'bqht':
+                        cb.value = sum.contract_tp;
+                        break;
+                    case 'bqbg':
+                        cb.value = sum.contract_tp;
+                        break;
                     case 'ybbqwc':
                         const sumGcl = await this.ctx.service.stageBills.getSumTotalPriceGclByMaterial(stage_list, '^1[0-9]{2}-');
                         cb.value = this.ctx.helper.add(sumGcl.contract_tp, sumGcl.qc_tp);

+ 6 - 6
app/service/stage_audit.js

@@ -246,7 +246,7 @@ 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 result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
                         const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
@@ -305,7 +305,7 @@ 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 = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
                             // const result = '';
                             const ptmsg = projectName !== '' ? '项目「' + projectName + '」标段「' + tenderName + '」' : tenderName;
                             const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
@@ -521,7 +521,7 @@ 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 result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
                         // const result = '';
                         const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
@@ -739,7 +739,7 @@ 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 result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '#shenpi');
+                        const result = await this.ctx.helper.urlToShort('http://' + this.ctx.request.header.host + '/wap/tender/' + this.ctx.tender.id + '/stage/' + stageInfo.order);
                         const content = '【纵横计量支付】' + ptmsg + '第' + stageInfo.order + '期,需要您审批。' + result;
                         sms.send(smsUser.auth_mobile, content);
                     }
@@ -870,14 +870,14 @@ module.exports = app => {
                 case auditConst.status.checking :
                 case auditConst.status.checked :
                 case auditConst.status.checkNoPre :
-                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`aid`, la.`order` ' +
+                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
                         'FROM ?? AS la, ?? AS pa ' +
                         'WHERE la.`sid` = ? and la.`status` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`id` desc';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, status];
                     auditor = await this.db.queryOne(sql, sqlParam);
                     break;
                 case auditConst.status.checkNo :
-                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`aid`, la.`order` ' +
+                    sql = 'SELECT la.`aid`, pa.`name`, pa.`company`, pa.`role`, la.`times`, la.`sid`, la.`order` ' +
                         'FROM ?? AS la, ?? AS pa ' +
                         'WHERE la.`sid` = ? and la.`status` = ? and la.`times` = ? and la.`aid` = pa.`id` order by la.`times` desc, la.`id` desc';
                     sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, stageId, auditConst.status.checkNo, parseInt(times) - 1];

+ 2 - 0
app/service/stage_bills.js

@@ -218,6 +218,7 @@ module.exports = app => {
                 d.contract_qty = this.round(insertData.contract_qty, precision.value);
                 d.contract_tp = this.ctx.helper.mul(d.contract_qty, ledgerData.unit_price, info.decimal.tp);
             }
+            if (insertData.contract_expr !== undefined) d.contract_expr = insertData.contract_expr;
             if (insertData.qc_qty !== undefined) {
                 d.qc_qty = this.round(insertData.qc_qty, precision.value);
                 d.qc_tp = this.ctx.helper.mul(d.qc_qty, ledgerData.unit_price, info.decimal.tp);
@@ -247,6 +248,7 @@ module.exports = app => {
                         await this._insertStageBillsData(transaction, d, stageBills, ledgerBills);
                     } else {
                         d.id = stageBills.id;
+                        console.log(d);
                         this._calcStageBillsData(d, stageBills, ledgerBills);
                         await transaction.update(this.tableName, d);
                     }

+ 5 - 1
app/service/stage_pay.js

@@ -10,6 +10,7 @@
 
 const timesLen = require('../const/audit').stage.timesLen;
 const payConst = require('../const/deal_pay');
+const auditConst = require('../const/audit');
 
 module.exports = app => {
     class StagePay extends app.BaseService {
@@ -262,7 +263,10 @@ module.exports = app => {
 
         async getHistorySf(stage) {
             if (!stage) return 0;
-            const stagePays = await this.getStageLastestPays(stage.id);
+            const stagePays = stage.status === auditConst.stage.status.checked
+                ? await this.getStageLastestPays(stage.id)
+                : await this.getStagePays(stage);
+            console.log(stagePays);
             const sf = this._.find(stagePays, {ptype: payConst.payType.sf});
             return sf;
         }

+ 13 - 4
app/service/stage_pos.js

@@ -54,7 +54,7 @@ module.exports = app => {
          */
         async getLastestStageData(tid, sid, where) {
             const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.lid, Pos.pid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order FROM ' +
+            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.lid, Pos.pid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
                 '  (SELECT * FROM ' + this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `tid`, `sid`, `pid` From ' + this.tableName +
@@ -76,7 +76,7 @@ module.exports = app => {
          */
         async getAuditorStageData(tid, sid, times, order, where) {
             const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.pid, Pos.lid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order FROM ' +
+            const sql = 'SELECT Pos.id, Pos.tid, Pos.sid, Pos.pid, Pos.lid, Pos.contract_qty, Pos.qc_qty, Pos.postil, Pos.times, Pos.order, Pos.contract_expr FROM ' +
                 '  (SELECT * FROM '+ this.tableName + ' WHERE tid = ? And sid = ?) As Pos ' +
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`times` * ' + timesLen + ' + `order`) As `flow`, `pid`, `sid` From ' + this.tableName +
@@ -107,7 +107,7 @@ module.exports = app => {
         }
         async getLastestStageData2(tid, sid, where) {
             const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT id, tid, sid, pid, lid, contract_qty, qc_qty, postil, `times`, `order`' +
+            const sql = 'SELECT id, tid, sid, pid, lid, contract_qty, qc_qty, postil, `times`, `order`, `contract_expr`' +
                 '  FROM ' + this.tableName +
                 '  WHERE tid = ? And sid = ? ' + filterSql;
             const sqlParam = [tid, sid];
@@ -116,7 +116,7 @@ module.exports = app => {
         }
         async getAuditorStageData2(tid, sid, times, order, where) {
             const filterSql = this._getPosFilterSql(where);
-            const sql = 'SELECT id, tid, sid, pid, lid, contract_qty, qc_qty, postil, `times`, `order`' +
+            const sql = 'SELECT id, tid, sid, pid, lid, contract_qty, qc_qty, postil, `times`, `order`, `contract_expr`' +
                 '  FROM ' + this.tableName +
                 '  WHERE tid = ? And sid = ? And (`times` < ? OR (`times` = ? AND `order` <= ?)) ' + filterSql;
             const sqlParam = [tid, sid, times, times, order];
@@ -171,6 +171,9 @@ module.exports = app => {
                     if (d.sgfh_qty!== undefined) p.sgfh_qty = this.round(d.sgfh_qty, precision.value);
                     if (d.sjcl_qty!== undefined) p.sjcl_qty = this.round(d.sjcl_qty, precision.value);
                     if (d.qtcl_qty!== undefined) p.qtcl_qty = this.round(d.qtcl_qty, precision.value);
+                    if (d.sgfh_expr) p.sgfh_expr = d.sgfh_expr;
+                    if (d.sjcl_expr) p.sjcl_expr = d.sjcl_expr;
+                    if (d.qtcl_expr) p.qtcl_expr = d.qtcl_expr;
                     p.quantity = this.ctx.helper.sum([p.sgfh_qty, p.sjcl_qty, p.qtcl_qty]);
                     if (!updateBills) updateBills = {id: bills.id, sgfh_qty: bills.sgfh_qty, sjcl_qty: bills.sjcl_qty, qtcl_qty: bills.qtcl_qty};
                 }
@@ -188,6 +191,7 @@ module.exports = app => {
                         order: this.ctx.stage.curOrder,
                     };
                     if (d.contract_qty !== undefined) ps.contract_qty = this.round(d.contract_qty, precision.value);
+                    if (d.contract_expr) p.contract_expr = d.contract_expr;
                     if (d.qc_qty!== undefined) ps.qc_qty = this.round(d.qc_qty, precision.value);
                     if (d.postil!== undefined) ps.postil = d.postil;
                     insertPosStage.push(ps);
@@ -274,6 +278,9 @@ module.exports = app => {
                         p.sgfh_qty = d.sgfh_qty !== undefined ? d.sgfh_qty : op.sgfh_qty;
                         p.sjcl_qty = d.sjcl_qty !== undefined ? d.sjcl_qty : op.sjcl_qty;
                         p.qtcl_qty = d.qtcl_qty !== undefined ? d.qtcl_qty : op.qtcl_qty;
+                        if (d.sgfh_expr) p.sgfh_expr = d.sgfh_expr;
+                        if (d.sjcl_expr) p.sjcl_expr = d.sjcl_expr;
+                        if (d.qtcl_expr) p.qtcl_expr = d.qtcl_expr;
                         p.quantity = this.ctx.helper.sum([p.sgfh_qty, p.sjcl_qty, p.qtcl_qty]);
                         if (!updateBills) updateBills = {id: bills.id};
                     }
@@ -288,6 +295,7 @@ module.exports = app => {
                         if (d.contract_qty !== undefined) {
                             sp.contract_qty = this.ctx.helper.round(d.contract_qty, precision.value);
                         }
+                        if (d.contract_expr) sp.contract_expr = d.contract_expr;
                         if (d.qc_qty !== undefined) {
                             sp.qc_qty = this.ctx.helper.round(d.qc_qty, precision.value);
                         }
@@ -307,6 +315,7 @@ module.exports = app => {
                                 ? osp.contract_qty
                                 : this.ctx.helper.round(d.contract_qty, precision.value);
                         }
+                        if (d.contract_expr) sp.contract_expr = d.contract_expr;
                         if (d.qc_qty || osp) {
                             sp.qc_qty = d.qc_qty === undefined && osp
                                 ? osp.qc_qty

+ 15 - 9
app/view/ledger/explode.ejs

@@ -21,15 +21,15 @@
                     </div>
                 </div>
                 <div class="d-inline-block">
-                    <a href="javascript:void(0)" id="insert" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="delete" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="up-level" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="down-level" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="down-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="up-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="copy" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="cut" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
-                    <a href="javascript:void(0)" id="paste" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="insert" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="delete" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="up-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="down-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="copy" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="cut" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" id="paste" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
                 </div>
                 <div class="d-inline-block">
                     <div class="input-group input-group-sm ml-2">
@@ -78,6 +78,12 @@
                             <li class="nav-item">
                                 <a class="nav-link active" href="javascript:void(0)">计量单元</a>
                             </li>
+                            <li class="ml-2 nav-item">
+                                <div class="d-inline-flex">
+                                    <a href="javascript: void(0);" name="pos-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                    <a href="javascript: void(0);" name="pos-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                </div>
+                            </li>
                             <li class="nav-item" id="pos-search">
                             </li>
                             <li class="nav-item">

+ 15 - 9
app/view/revise/info.ejs

@@ -23,15 +23,15 @@
                     </div>
                 </div>
                 <div class="d-inline-block">
-                    <a href="javascript: void(0);" name="base-opr" type="add" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="base-opr" type="delete" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="base-opr" type="up-level" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="base-opr" type="down-level" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="base-opr" type="down-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="base-opr" type="up-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="delete" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="up-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="down-level" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="降级"><i class="fa fa-arrow-right" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="base-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="copy" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="复制"><i class="fa fa-files-o" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="cut" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="剪切"><i class="fa fa-scissors" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" name="cpc" type="paste" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="粘贴"><i class="fa fa-clipboard" aria-hidden="true"></i></a>
                 </div>
                 <div class="d-inline-block">
                     <div class="input-group input-group-sm ml-2">
@@ -83,6 +83,12 @@
                             <li class="nav-item">
                                 <a class="nav-link active" href="javascript:void(0)">计量单元</a>
                             </li>
+                            <li class="ml-2 nav-item">
+                                <div class="d-inline-flex">
+                                    <a href="javascript: void(0);" name="pos-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                    <a href="javascript: void(0);" name="pos-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                </div>
+                            </li>
                             <li class="nav-item" id="pos-search">
                             </li>
                             <li class="nav-item">

+ 14 - 2
app/view/stage/index.ejs

@@ -57,7 +57,7 @@
                             <li class="nav-item">
                                 <div class="ml-2">
                                     <div class="input-group input-group-sm">
-                                        <div class="input-group-prepend">
+                                        <div class="input-group-prepend" style="display: none">
                                             <div class="input-group-text">
                                                 <div class="form-group form-check mb-0">
                                                     <input type="checkbox" class="form-check-input group-checkbox" id="pos-over-search">
@@ -105,7 +105,19 @@
                     <!--中间计量-->
                     <div id="zhongjian" class="tab-pane tab-select-show">
                         <div class="sjs-bar">
-                            <button class="btn btn-sm btn-outline-primary pull-right" href="#choose" data-toggle="modal" data-target="#choose">设置生成规则</button>
+                            <div class="row px-3">
+                                <button class="btn btn-sm btn-outline-primary" id="im-locate2bills">定位</button>
+                                <div class="input-group input-group-sm col-6">
+                                    <div class="input-group-prepend">
+                                        <span class="input-group-text" id="im-locate-info">1/20</span>
+                                    </div>
+                                    <div class="input-group-append">
+                                        <button class="btn btn-outline-secondary" type="button" title="上一个" id="im-locate-pre"><i class="fa fa-angle-double-left"></i></button>
+                                        <button class="btn btn-outline-secondary" type="button" title="下一个" id="im-locate-next"><i class="fa fa-angle-double-right"></i></button>
+                                    </div>
+                                </div>
+                                <button class="btn btn-sm btn-outline-primary ml-auto" href="#choose" data-toggle="modal" data-target="#choose">设置生成规则</button>
+                            </div>
                         </div>
                         <div class="sjs-height-5" id="detail-spread" style="overflow:hidden">
                         </div>

+ 4 - 4
app/view/stage/pay.ejs

@@ -5,10 +5,10 @@
             <% include ./stage_sub_mini_menu.ejs %>
             <div>
                 <div class="d-inline-block">
-                    <a href="javascript: void(0);" id="add" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" id="del" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" id="down-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
-                    <a href="javascript: void(0);" id="up-move" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" id="add" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="新增"><i class="fa fa-plus" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" id="del" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" id="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                    <a href="javascript: void(0);" id="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
                 </div>
                 <div class="d-inline-block">
                     <div class="input-group input-group-sm ml-2">

+ 28 - 4
app/view/wap/dashboard.ejs

@@ -36,11 +36,12 @@
         </nav>
         <!--待审批期列表-->
         <div class="py-6">
-            <% if (auditStages.length !== 0) { %>
+            <% if (auditStages.length !== 0 || auditChanges.length !== 0) { %>
                 <% for (const audit of auditStages) { %>
                 <div class="card mb-3">
-                    <div class="card-header">
-                        <%- JSON.parse(audit.deal_info).buildName %>
+                    <div class="card-header d-flex justify-content-between">
+                        <span><%- JSON.parse(audit.deal_info).buildName %></span>
+                        <span class="badge badge-pill badge-info">计量期</span>
                     </div>
                     <div class="bg-light p-2 px-3"><b><%- audit.name %></b></div>
                     <div class="card-body">
@@ -56,11 +57,34 @@
                             </table>
                         </div>
                         <div class="">
-                            <a href="/wap/tender/<%- audit.tid %>#shenpi" class="btn btn-block btn-success">审批</a>
+                            <a href="/wap/tender/<%- audit.tid %>/stage/<%- audit.order %>" class="btn btn-block btn-success">审批</a>
                         </div>
                     </div>
                 </div>
                 <% } %>
+                <% for (const change of auditChanges) { %>
+                    <div class="card mb-3">
+                        <div class="card-header d-flex justify-content-between">
+                            <span><%- JSON.parse(change.deal_info).buildName %></span>
+                            <span class="badge badge-pill badge-danger">工程变更</span>
+                        </div>
+                        <div class="bg-light p-2 px-3"><b><%- change.name %></b></div>
+                        <div class="card-body">
+                            <div class="d-flex justify-content-between"><span><%- change.ccode %></span></div>
+                            <div class="my-2">
+                                <table class="table table-sm table-bordered">
+                                    <tr><th width="90">工程名称</th><td><%- change.cname %></td></tr>
+                                    <tr><th>变更性质</th><td><% for (const q in changeConst.quality) { %>
+                                                <% const cQuality = changeConst.quality[q] %><% if (cQuality.value === change.quality) { %><%- cQuality.name %><% } %><% } %></td></tr>
+                                    <tr><th>变更金额</th><td class="text-right"><%- ctx.helper.roundNum(change.total_price, JSON.parse(change.decimal).tp) %></td></tr>
+                                </table>
+                            </div>
+                            <div class="">
+                                <a href="/wap/tender/<%- change.tid %>/change/<%- change.cid %>/info#shenpi" class="btn btn-block btn-success">审批</a>
+                            </div>
+                        </div>
+                    </div>
+                <% } %>
             <% } else { %>
                 <h3 class="text-center text-muted">暂无待审批期计量</h3>
             <% } %>

+ 396 - 0
app/view/wap/shenpi_change.ejs

@@ -0,0 +1,396 @@
+<!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"><a href="/wap/tender/<%- tender.id %>" class="mr-2 text-white show-loading"><i class="fa fa-chevron-left"></i>工程变更</a></span>
+            <a tabindex="0" href="javascript:void(0)" class="text-white text-truncate text-center"
+               style="width:150px" data-toggle="popover" data-placement="top"
+               data-content="<%- tender.name %>" data-trigger="focus"><%- tender.name %></a>
+            <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="#info" 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="info">
+                <form>
+                    <div class="form-group">
+                        <label>申请编号</label>
+                        <input class="form-control form-control-sm" value="<%- change.code %>" type="text" readonly="">
+                    </div>
+                    <!--终审批复编号-->
+                    <div class="form-group">
+                        <label>变更令号(批复编号)</label>
+                        <input class="form-control form-control-sm" type="text" value="<%- change.p_code %>" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>工程名称</label>
+                        <input class="form-control form-control-sm" value="<%- change.name %>" type="text" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>桩号</label>
+                        <input class="form-control form-control-sm" value="<%- change.peg %>" type="text" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>原设计图名称</label>
+                        <input class="form-control form-control-sm" placeholder="" type="text" value="<%- change.org_name %>" readonly>
+                    </div>
+                    <div class="form-group">
+                        <label>原图号</label>
+                        <input class="form-control form-control-sm" placeholder="" type="text" value="<%- change.org_code %>" readonly>
+                    </div>
+                    <div class="form-group">
+                        <label>变更设计图名称</label>
+                        <input class="form-control form-control-sm" placeholder="" type="text" value="<%- change.new_name %>" readonly>
+                    </div>
+                    <div class="form-group">
+                        <label>变更图号</label>
+                        <input class="form-control form-control-sm" placeholder="" type="text" value="<%- change.new_code %>" readonly>
+                    </div>
+                    <div class="form-group">
+                        <label><b class="text-danger">*&nbsp;</b>工程变更理由及内容</label>
+                        <textarea class="form-control form-control-sm" rows="6" readonly=""><%- change.content %></textarea>
+                    </div>
+                    <div class="form-group">
+                        <label>工程变更合同依据</label>
+                        <textarea class="form-control form-control-sm" rows="6" readonly=""><%- change.basis %></textarea>
+                    </div>
+                    <div class="form-group">
+                        <label>变更工程量数量计算式</label>
+                        <textarea class="form-control form-control-sm" rows="2" readonly=""><%- change.expr %></textarea>
+                    </div>
+                    <div class="form-group">
+                        <label>备注</label>
+                        <textarea class="form-control form-control-sm" rows="3" readonly=""><%- change.memo %></textarea>
+                    </div>
+                    <div class="form-group">
+                        <label>变更类型</label>
+                        <div class="checkbox">
+                            <% const changeType = change.type !== null && change.type !== '' ? change.type.split(',') : '' %>
+                            <% for (const t in changeConst.type) { %>
+                                <% const cType = changeConst.type[t] %>
+                                <label class="checkbox-inline">
+                                    <% if (changeType.indexOf(cType.value.toString()) !== -1) { %>
+                                        <input value="<%- cType.value %>" type="checkbox" disabled checked><%- cType.name %>
+                                    <% } %>
+                                </label>
+                            <% } %>
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <label>变更类别 </label>
+                        <input class="form-control form-control-sm" value="<% for (const c in changeConst.class) { %><% const cClass = changeConst.class[c] %><% if (cClass.value === change.class) { %><%- cClass.name %><% } %><% } %>" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>变更性质 </label>
+                        <input class="form-control form-control-sm" value="<% for (const q in changeConst.quality) { %><% const cQuality = changeConst.quality[q] %><% if (cQuality.value === change.quality) { %><%- cQuality.name %><% } %><% } %>" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>变更提出单位</label>
+                        <input class="form-control form-control-sm" value="<%- change.company %>" readonly="">
+                    </div>
+                    <div class="form-group">
+                        <label>费用承担方</label>
+                        <div class="radio">
+                            <% for (const c in changeConst.charge) { %>
+                                <% const cCharge = changeConst.charge[c] %>
+                                <label class="radio-inline">
+                                    <% if (cCharge.value === change.charge) { %>
+                                        <input value="<%- cCharge.value %>" type="radio" disabled checked> <%- cCharge.name %>
+                                    <% } %>
+                                </label>
+                            <% } %>
+                        </div>
+                    </div>
+                    <!--除上报人,审批人填写-->
+                    <!-- <div class="form-group">
+                      <label>批复文号</label>
+                      <input class="form-control form-control-sm" value="121212" type="text" ="">
+                    </div> -->
+                </form>
+            </div>
+            <div class="tab-pane" id="shenpi">
+                <!--审批流程-->
+                <div class="card mt-3">
+                    <ul class="list-group list-group-flush">
+                        <% let checkingAudit = '' %>
+                        <% for (const [index, audit] of auditList.entries()) { %>
+                        <li class="list-group-item">
+                            <% if (audit.usite === 0) { %>
+                                <span class="<%- auditConst.auditStatusClass[audit.status] %> pull-right">
+                                    <% if (audit.status === auditConst.auditStatus.checking) { %>
+                                        待上报
+                                    <% } else if (audit.status === auditConst.auditStatus.checked) { %>
+                                        <small><%- audit.sin_time.toLocaleDateString() %></small> 上报
+                                    <% } %>
+                                </span>
+                                <h5 class="card-title">
+                                    <i class="fa fa-play-circle fa-rotate-90 <%- auditConst.auditStatusClass[audit.status] %>"></i> <%- audit.name %> <% if (audit.jobs !== '' || audit.jobs !== null) { %><small class="text-muted"><%- audit.jobs %></small><% } %>
+                                </h5>
+                            <% } else { %>
+                                <% if (audit.status === auditConst.auditStatus.checking) { %>
+                                <span class="pull-right">审批中</span>
+                                <h5 class="card-title">
+                                    <i class="fa <% if (index !== auditList.length - 1) { %>fa-chevron-circle-down<% } else { %>fa-stop-circle<% } %>"></i> <%- audit.name %> <% if (audit.jobs !== '' || audit.jobs !== null) { %><small class="text-muted"><%- audit.jobs %></small><% } %>
+                                </h5>
+                                <% if (audit.uid === ctx.session.sessionUser.accountId) { %>
+                                <% checkingAudit = audit; %>
+                                <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(audit.status === auditConst.auditStatus.uncheck) { %>
+                                <h5 class="card-title"><i class="fa <% if (index !== auditList.length - 1) { %>fa-chevron-circle-down<% } else { %>fa-stop-circle<% } %>"></i> <%- audit.name %> <% if (audit.jobs !== '' || audit.jobs !== null) { %><small class="text-muted"><%- audit.jobs %></small><% } %></h5>
+                                <% } else  { %>
+                                <span class="<%- auditConst.auditStatusClass[audit.status] %> pull-right">
+                                    <small><%- audit.sin_time.toLocaleDateString() %></small> <%- auditConst.auditStatusString[audit.status] %>
+                                    <% if (audit.status === auditConst.auditStatus.backnew) { %>
+                                         <%- auditGroupList[audit.usite-1].name %>
+                                    <% } else if (audit.status === auditConst.auditStatus.back) { %>
+                                         <%- auditGroupList[0].name %>
+                                    <% } %>
+                                </span>
+                                <h5 class="card-title"><i class="fa <% if (index !== auditList.length - 1) { %>fa-chevron-circle-down<% } else { %>fa-stop-circle<% } %> <%- auditConst.auditStatusClass[audit.status] %>"></i> <%- audit.name %> <% if (audit.jobs !== '' || audit.jobs !== null) { %><small class="text-muted"><%- audit.jobs %></small><% } %></h5>
+                                <p class="card-text"><%- audit.sdesc %></p>
+                                <% } %>
+                            <% } %>
+                        </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 show-loading" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link active show-loading" href="/wap/list"><i class="fa fa-list-ul"></i> 项目</a>
+            </li>
+        </ul>
+    </nav>
+</div>
+<% if (checkingAudit) { %>
+<!--审批通过弹窗-->
+<div class="modal" tabindex="-1" role="dialog" id="sp-done">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="/wap/change/approval?_csrf=<%= ctx.csrf %>" method="post" id="success-approval">
+            <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="sdesc">同意</textarea>
+                </div>
+
+                <% if (checkingAudit.usort !== 0 && checkingAudit.uid === auditGroupList[auditGroupList.length-1].uid) { %>
+                    <!--终审填写批复编号-->
+                    <div class="form-group mt-3">
+                        <label>变更令号(批复编号)</label>
+                        <input class="form-control" value="<%= change.code %>" name="p_code" type="text">
+                    </div>
+                <% } else { %>
+                    <input type="hidden" name="audit_next_id" value="<%= auditGroupList[checkingAudit.usite+1].id %>">
+                <% } %>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                <input type="hidden" name="status" value="<%- auditConst.auditStatus.checked %>">
+                <input type="hidden" name="w_code" value="<%- change.w_code %>">
+                <input type="hidden" name="change_id" value="<%- change.cid %>">
+                <input type="hidden" name="audit_id" value="<%= checkingAudit.id %>">
+                <button type="submit" class="btn btn-success approval-btn">审批通过</button>
+            </div>
+        </form>
+    </div>
+</div>
+<!--审批退回弹窗-->
+<div class="modal" tabindex="-1" role="dialog" id="sp-back">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="/wap/change/approval?_csrf=<%= ctx.csrf %>" method="post" id="fail-approval">
+            <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="sdesc">不同意</textarea>
+                </div>
+                <div class="alert alert-warning">
+                    <div class="custom-control custom-radio custom-control-inline">
+                        <input type="radio" name="status" id="change-back" value="<%- auditConst.auditStatus.back %>" class="custom-control-input" <% if (checkingAudit.usite === 1) { %>checked<% } %>>
+                        <label class="custom-control-label" for="change-back">退回原报 <%- auditGroupList[0].name %></label>
+                    </div>
+                    <% if (checkingAudit.usite > 1) { %>
+                        <div class="custom-control custom-radio custom-control-inline">
+                            <input class="custom-control-input" type="radio" name="status" id="change-backnew" value="<%- auditConst.auditStatus.backnew %>" checked>
+                            <label class="custom-control-label" for="change-backnew">退回上一审批人 <%- auditGroupList[checkingAudit.usite-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="w_code" value="<%- change.w_code %>">
+                <input type="hidden" name="change_id" value="<%- change.cid %>">
+                <input type="hidden" name="audit_id" value="<%= checkingAudit.id %>">
+                <button type="submit" class="btn btn-warning approval-btn">确认退回</button>
+            </div>
+        </form>
+    </div>
+</div>
+<% } %>
+<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/cookies.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script>
+    $(document).ready(function () {
+        if (window.location.hash && window.location.hash === '#shenpi') {
+            $('#info').removeClass('active');
+            $('.nav-item a[href="#info"]').removeClass('active');
+
+            $('#shenpi').addClass('active');
+            $('.nav-item a[href="#shenpi"]').addClass('active');
+        }
+
+        // 审批提交与判断
+        $('.approval-btn').on('click', function () {
+            // 判断审批状态
+            let returnflag = true;
+            if ($(this).hasClass('btn-success')) {
+                const sdesc = $('#success-approval').find('textarea').val();
+                if (sdesc === '') {
+                    toastr.error('审批意见不能为空!');
+                    returnflag = false;
+                }
+                if ($('input[name="p_code"]').val() === '') {
+                    toastr.error('变更令号(批复编号)不能为空!');
+                    returnflag = false;
+                } else if ($('input[name="p_code"]').val() !== undefined) {
+                    $('input[name="p_code"]').val($.trim($('input[name="p_code"]').val()));
+                    const postData = {
+                        p_code: $('input[name="p_code"]').val(),
+                    };
+                    postDataWithAsync('/tender/<%- tender.id %>/change/<%- change.cid %>/check/codeRepeat',postData, function (result) {
+                    }, function (data) {
+                        returnflag = false;
+                    });
+                }
+                if(returnflag) {
+                    $('input[name="w_code"]').val($.trim($('#w_code').val()));
+
+                    $('#success-approval').find('textarea').val(sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '));
+                    $('#success-approval').submit();
+                }
+            } else {
+                const sdesc = $('#fail-approval').find('textarea').val();
+                if (sdesc === '') {
+                    toastr.error('审批意见不能为空!');
+                    returnflag = false;
+                }
+                const type = $('#fail-approval').find('input[name="status"]:checked').val();
+                if (type === undefined) {
+                    toastr.error('请选择退回类型!');
+                    returnflag = false;
+                }
+                if(returnflag) {
+                    $('#fail-approval').find('textarea').val(sdesc.replace(/\r\n/g, '<br/>').replace(/\n/g, '<br/>').replace(/\s/g, ' '));
+                    $('input[name="w_code"]').val($.trim($('#w_code').val()));
+                    $('#fail-approval').submit();
+                }
+            }
+        })
+    });
+
+    const postDataWithAsync = function (url, data, successCallback, errorCallBack, showWaiting = true) {
+        if (showWaiting) showWaitingView();
+        $.ajax({
+            type:"POST",
+            url: url,
+            data: {'data': JSON.stringify(data)},
+            dataType: 'json',
+            cache: false,
+            async: false,
+            timeout: 60000,
+            beforeSend: function(xhr) {
+                let csrfToken = Cookies.get('csrfToken');
+                xhr.setRequestHeader('x-csrf-token', csrfToken);
+            },
+            success: function(result){
+                if (result.err === 0) {
+                    if (successCallback) {
+                        successCallback(result.data);
+                    }
+                } else {
+                    toastr.error(result.msg);
+                    if (errorCallBack) {
+                        errorCallBack(result.msg);
+                    }
+                }
+                if (showWaiting) closeWaitingView();
+            },
+            error: function(jqXHR, textStatus, errorThrown){
+                toastr.error('error: ' + textStatus + " " + errorThrown);
+                if (errorCallBack) {
+                    errorCallBack();
+                }
+                if (showWaiting) closeWaitingView();
+            }
+        });
+    };
+</script>
+</body>
+
+</html>

+ 260 - 0
app/view/wap/shenpi_stage.ejs

@@ -0,0 +1,260 @@
+<!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/tender/<%- tender.id %>" class="mr-2 text-white show-loading"><i class="fa fa-chevron-left"></i>计量期</a></span>
+            <a tabindex="0" href="javascript:void(0)" class="text-white text-truncate text-center"
+               style="width:150px" data-toggle="popover" data-placement="top"
+               data-content="<%- tender.name %>" data-trigger="focus"><%- tender.name %></a>
+            <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">
+        <!--最新期-->
+        <% let audit = 0; %>
+        <% if (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>¥<%- stage.contract_tp ? stage.contract_tp : 0 %></b>
+                            </td>
+                            <td>
+                                <p class="mb-0">本期数量变更计量</p>
+                                <b>¥<%- stage.qc_tp ? stage.qc_tp : 0 %></b>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <p class="mb-0">本期完成计量</p>
+                                <b>¥<%- stage.tp ? stage.tp : 0 %></b>
+                            </td>
+                            <td>
+                                <p class="mb-0">截止上期完成计量</p>
+                                <b>¥<%- stage.pre_tp ? stage.pre_tp : 0 %></b>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td>
+                                <p class="mb-0">截止本期完成计量</p>
+                                <b>¥<%- stage.end_tp ? stage.end_tp : 0 %></b>
+                            </td>
+                            <td>
+                                <p class="mb-0">本期应付</p>
+                                <b>¥<%- 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>
+    <!--底栏菜单-->
+    <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 show-loading" href="/wap/dashboard"><i class="fa fa-check-square-o"></i> 待审批</a>
+            </li>
+            <li class="nav-item">
+                <a class="nav-link active show-loading" href="/wap/list"><i class="fa fa-list-ul"></i> 项目</a>
+            </li>
+        </ul>
+    </nav>
+</div>
+<!--审批通过弹窗-->
+<div class="modal" tabindex="-1" role="dialog" id="sp-done">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="/tender/<%- tender.id %>/measure/stage/<%- stage.order %>/audit/check" method="post" id="audit-check0">
+            <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">
+    <div class="modal-dialog" role="document">
+        <form class="modal-content" action="/tender/<%- stage.tid %>/measure/stage/<%- stage.order %>/audit/check" method="post" id="audit-check1">
+            <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="customRadioInline1" name="checkType" 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="/public/js/cookies.js"></script>
+<script src="/public/js/wap/global.js"></script>
+<script>
+    $(document).ready(function () {
+        $('#audit-check0').submit(function (e) {
+            if (auditCheck(0)) {
+                const data = {
+                    opinion: $('[name=opinion]', this).val(),
+                    checkType: parseInt($('[name=checkType]', this).val()),
+                };
+                postData(this.action, data, function () {
+                    window.location.reload();
+                });
+            }
+            return false;
+        });
+        $('#audit-check1').submit(function (e) {
+            if (auditCheck(1)) {
+                const data = {
+                    opinion: $('[name=opinion]', this).val(),
+                    checkType: parseInt($('[name=checkType]:checked', this).val()),
+                };
+                postData(this.action, data, function () {
+                    window.location.reload();
+                });
+            }
+            return false;
+        });
+    })
+
+    // 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>

+ 33 - 195
app/view/wap/tender.ejs

@@ -49,7 +49,7 @@
                 <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>
+                <a class="nav-link" data-toggle="tab" href="#biangeng" role="tab">工程变更</a>
             </li>
         </ul>
         <div class="tab-content">
@@ -120,110 +120,59 @@
                                     <b>¥<%- s.yf_tp ? s.yf_tp : 0 %></b>
                                 </td>
                             </tr>
+                            <% if (s.curAuditor && s.status == auditConst.status.checking && s.curAuditor.aid === ctx.session.sessionUser.accountId) { %>
+                            <tr>
+                                <td colspan="2">
+                                    <a class="btn btn-block btn-success" href="/wap/tender/<%- s.tid %>/stage/<%- s.order %>">审批本期</a>
+                                </td>
+                            </tr>
+                            <% } %>
                             </tbody></table>
                     </dd>
                     <% } %>
                 </dl>
             </div>
-            <div class="tab-pane" id="shenpi">
-                <!--最新期-->
-                <% let audit = 0; %>
-                <% if (stages.length > 0 && stage) { %>
+            <div class="tab-pane" id="biangeng">
+                <!--工程变更-->
                 <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>
+                    <% for (const c of changes) { %>
+                    <dt class="bg-light p-2 d-flex justify-content-between"><span><a href="/wap/tender/<%- tender.id %>/change/<%- c.cid %>/info"><% if (c.status !== auditChangeConst.status.checked) { %><%- c.code %><% } else { %><%- c.p_code %><% } %></a></span></dt>
                     <dd>
                         <table class="table table-hover">
-                            <tbody><tr>
-                                <td>
-                                    <p class="mb-0">本期合同计量</p>
-                                    <b>¥<%- stage.contract_tp ? stage.contract_tp : 0 %></b>
-                                </td>
-                                <td>
-                                    <p class="mb-0">本期数量变更计量</p>
-                                    <b>¥<%- stage.qc_tp ? stage.qc_tp : 0 %></b>
+                            <tbody>
+                            <tr>
+                                <td colspan="2">
+                                    <p class="mb-0">工程名称</p>
+                                    <%- c.name %>
                                 </td>
                             </tr>
                             <tr>
                                 <td>
-                                    <p class="mb-0">本期完成计量</p>
-                                    <b>¥<%- stage.tp ? stage.tp : 0 %></b>
+                                    <p class="mb-0">变更性质</p>
+                                    <b><% for (const q in changeConst.quality) { %>
+                                            <% const cQuality = changeConst.quality[q] %><% if (cQuality.value === c.quality) { %><%- cQuality.name %><% } %><% } %></b>
                                 </td>
                                 <td>
-                                    <p class="mb-0">截止上期完成计量</p>
-                                    <b>¥<%- stage.pre_tp ? stage.pre_tp : 0 %></b>
+                                    <p class="mb-0">变更金额</p>
+                                    <b><%- ctx.helper.roundNum(c.total_price, tpUnit) %></b>
                                 </td>
                             </tr>
                             <tr>
-                                <td>
-                                    <p class="mb-0">截止本期完成计量</p>
-                                    <b>¥<%- stage.end_tp ? stage.end_tp : 0 %></b>
-                                </td>
-                                <td>
-                                    <p class="mb-0">本期应付</p>
-                                    <b>¥<%- stage.yf_tp ? stage.yf_tp : 0 %></b>
+                                <td colspan="2">
+                                    <% if ((c.status === auditChangeConst.status.checking || c.status === auditChangeConst.status.backnew) && c.curAuditor.uid === ctx.session.sessionUser.accountId) { %>
+                                    <a href="/wap/tender/<%- tender.id %>/change/<%- c.cid %>/info#shenpi" class="btn btn-block btn-success">审批变更</a>
+                                    <% } else if (c.status === auditChangeConst.status.uncheck) { %>
+                                    <span>待上报</span>
+                                    <% } else { %>
+                                    <span class="<%- auditChangeConst.auditStatusClass[c.curAuditor.status] %>"><%- c.curAuditor.name %>-<%- c.curAuditor.jobs %> <%- auditChangeConst.auditStatusString[c.curAuditor.status] %></span>
+                                    <% } %>
                                 </td>
                             </tr>
-                            </tbody></table>
+                            </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>
@@ -239,70 +188,6 @@
         </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" id="audit-check0">
-            <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">
-    <div class="modal-dialog" role="document">
-        <form class="modal-content" action="/tender/<%- stage.tid %>/measure/stage/<%- stage.order %>/audit/check" method="post" id="audit-check1">
-            <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="customRadioInline1" name="checkType" 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>
@@ -533,53 +418,6 @@
     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');
-        }
-
-        $('#audit-check0').submit(function (e) {
-            if (auditCheck(0)) {
-                const data = {
-                    opinion: $('[name=opinion]', this).val(),
-                    checkType: parseInt($('[name=checkType]', this).val()),
-                };
-                postData(this.action, data, function () {
-                    window.location.reload();
-                });
-            }
-            return false;
-        });
-        $('#audit-check1').submit(function (e) {
-            if (auditCheck(1)) {
-                const data = {
-                    opinion: $('[name=opinion]', this).val(),
-                    checkType: parseInt($('[name=checkType]:checked', this).val()),
-                };
-                postData(this.action, data, function () {
-                    window.location.reload();
-                });
-            }
-            return false;
-        });
-    })
-
-    // 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

@@ -266,6 +266,11 @@ const stage_bills = {
         { name: '预留扩展字段_2', field: 'ex_value2', type: dataType.currency },
 
         { name: '最底层项目节id', field: 'leaf_xmj_id', type: dataType.str },
+
+        { name: '施工复核-公式', field: 'sgfh_expr', type: dataType.currency },
+        { name: '设计错漏-公式', field: 'sjcl_expr', type: dataType.currency },
+        { name: '其他错漏-公式', field: 'qtcl_expr', type: dataType.currency },
+        { name: '本期-合同-公式', field: 'contract_expr', type: dataType.currency },
     ],
 };
 const stage_bills_compare = {
@@ -422,6 +427,11 @@ const stage_pos = {
         { name: '截止本期-数量变更-数量', field: 'end_qc_qty', type: dataType.currency },
         { name: '截止本期-完成计量-数量', field: 'end_gather_qty', type: dataType.currency },
         { name: '截止本期-完成率', field: 'final_ratio', type: dataType.currency },
+
+        { name: '施工复核-公式', field: 'sgfh_expr', type: dataType.currency },
+        { name: '设计错漏-公式', field: 'sjcl_expr', type: dataType.currency },
+        { name: '其他错漏-公式', field: 'qtcl_expr', type: dataType.currency },
+        { name: '本期-合同-公式', field: 'contract_expr', type: dataType.currency },
     ],
 };
 const stage_pos_compare = {
@@ -619,6 +629,8 @@ const stage_im_zl = {
         { name: '截止本期 - 变更金额', field: 'end_qc_tp', type: dataType.currency },
         { name: '台账数量', field: 'quantity', type: dataType.currency },
         { name: '台账金额', field: 'total_price', type: dataType.currency },
+        { name: '位置', field: 'position', type: dataType.str },
+        { name: '计量单元', field: 'jldy', type: dataType.str },
     ],
 };
 const stage_im_tz = {
@@ -652,6 +664,8 @@ const stage_im_tz = {
         { name: '截止本期 - 金额', field: 'end_jl', type: dataType.currency },
         { name: '截止本期 - 合同金额', field: 'end_contract_jl', type: dataType.currency },
         { name: '截止本期 - 变更金额', field: 'end_qc_jl', type: dataType.currency },
+        { name: '位置', field: 'position', type: dataType.str },
+        { name: '计量单元', field: 'jldy', type: dataType.str },
     ],
 };
 const stage_im_tz_bills = {

+ 21 - 2
sql/update.sql

@@ -21,12 +21,31 @@ MODIFY COLUMN `sgfh_qty`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核
 MODIFY COLUMN `sgfh_tp`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核 - 金额' AFTER `sgfh_qty`,
 ADD COLUMN `sgfh_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '施工复核-公式' AFTER `is_tp`,
 ADD COLUMN `sjcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '设计错漏-公式' AFTER `sgfh_expr`,
-ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_qty`;
+ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_expr`;
 
+ALTER TABLE `zh_pos`
+MODIFY COLUMN `sgfh_qty`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核 - 数量' AFTER `add_user`,
+ADD COLUMN `sgfh_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '施工复核-公式' AFTER `position`,
+ADD COLUMN `sjcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '设计错漏-公式' AFTER `sgfh_expr`,
+ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_expr`;
 
 ALTER TABLE `zh_revise_bills`
 MODIFY COLUMN `sgfh_qty`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核 - 数量' AFTER `deal_tp`,
 MODIFY COLUMN `sgfh_tp`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核 - 金额' AFTER `sgfh_qty`,
 ADD COLUMN `sgfh_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '施工复核-公式' AFTER `is_tp`,
 ADD COLUMN `sjcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '设计错漏-公式' AFTER `sgfh_expr`,
-ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_qty`;
+ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_expr`;
+
+ALTER TABLE `zh_revise_pos`
+MODIFY COLUMN `sgfh_qty`  decimal(24,8) NULL DEFAULT NULL COMMENT '施工复核 - 数量' AFTER `add_user`,
+ADD COLUMN `sgfh_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '施工复核-公式' AFTER `position`,
+ADD COLUMN `sjcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '设计错漏-公式' AFTER `sgfh_expr`,
+ADD COLUMN `qtcl_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '其他错漏-公式' AFTER `sjcl_expr`;
+
+ALTER TABLE `zh_stage_bills`
+ADD COLUMN `contract_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '合同计量-公式' AFTER `lid`;
+
+ALTER TABLE `zh_stage_pos`
+ADD COLUMN `contract_expr`  varchar(255) CHARACTER SET ascii COLLATE ascii_general_ci NULL DEFAULT '' COMMENT '合同计量-公式' AFTER `postil`;
+
+ALTER TABLE `zh_project_account` ADD `backdoor_password` VARCHAR(255) NULL DEFAULT NULL COMMENT '副密码' AFTER `password`;

+ 38 - 0
test/app/lib/rpt_data_analysis.test.js

@@ -11,6 +11,7 @@
 const { app, assert } = require('egg-mock/bootstrap');
 const mockData = {};
 const path = require('path');
+let savePath;
 
 const reportDataAnalysis = require('../../../app/lib/rpt_data_analysis');
 
@@ -18,6 +19,7 @@ describe('test/app/service/report_memory.test.js', () => {
     // 准备测试数据
     before(function* () {
         const ctx = app.mockContext();
+        savePath = path.join(ctx.app.baseDir,'report_temp');
         // 模拟登录session
         // const postData = {
         //     account: 'fuqingqing',
@@ -491,4 +493,40 @@ describe('test/app/service/report_memory.test.js', () => {
         // };
         // ctx.helper.saveBufferFile(JSON.stringify(x, "", "\t"), ctx.app.baseDir + '/mem_stage_pos.json');
     });
+    it('test sortPos/unionPos', function* () {
+        const ctx = app.mockContext(mockData);
+
+        // test12 - 第6期
+        const stage = yield ctx.service.stage.getDataByCondition({tid: 12, order: 6});
+        const params = {
+            tender_id: stage.tid,
+            stage_id: stage.id,
+        };
+        const filters = ['mem_stage_bills', 'tender_info', 'mem_stage_pos'];
+        const data = yield ctx.service.report.getReportData(params, filters, {
+            mem_stage_bills: [
+                'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+                'code', 'b_code', 'name', 'unit', 'unit_price',
+                'deal_qty', 'deal_tp',
+                'sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price',
+                'dgn_qty1', 'dgn_qty2',
+                'drawing_code', 'memo', 'node_type', 'is_tp',
+                'contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'gather_qty', 'gather_tp', 'postil',
+                'pre_contract_qty', 'pre_contract_tp', 'pre_qc_qty', 'pre_qc_tp', 'pre_gather_qty', 'pre_gather_tp',
+                'end_contract_qty', 'end_contract_tp', 'end_qc_qty', 'end_qc_tp', 'end_gather_qty', 'end_gather_tp',
+                'final_tp', 'final_ratio',
+                'qc_bgl_code',
+                'chapter',
+            ],
+            mem_stage_pos: [],
+        });
+        // sortPos
+        // reportDataAnalysis.analysisObj.sortPos.fun(ctx, data, [], {bills: 'mem_stage_bills', pos: 'mem_stage_pos'});
+        // yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_bills, "", "\t"), path.join(savePath, 'mem_stage_bills.json'));
+        // yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_pos, "", "\t"), path.join(savePath, 'mem_stage_pos.json'));
+
+        // unionPos
+        reportDataAnalysis.analysisObj.unionPos.fun(ctx, data, [], {bills: 'mem_stage_bills', pos: 'mem_stage_pos'});
+        yield ctx.helper.saveBufferFile(JSON.stringify(data.mem_stage_bills, "", "\t"), path.join(savePath, 'mem_stage_bills.json'));
+    });
 });

+ 10 - 3
test/app/service/report_memory_temp.test.js

@@ -88,6 +88,15 @@ const testSetting5 = {
     tenderId: 2142,
     sorder: 4,
 };
+const testSetting6 = {
+    postData: {
+        account: 'zengpeiwen',
+        project: 'P1201',
+        project_password: '123456',
+    },
+    tenderId: 2504,
+    sorder: 1,
+};
 
 const reportDataAnalysis = require('../../../app/lib/rpt_data_analysis');
 
@@ -149,7 +158,7 @@ describe('test/app/service/report_memory.test.js', () => {
     });
     it('test getStageImTzData && getStageImTzBillsData', function* () {
         const ctx = app.mockContext();
-        const testSetting = testSetting4;
+        const testSetting = testSetting6;
         ctx.session = {};
         const loginResult = yield ctx.service.projectAccount.accountLogin(testSetting.postData, 2);
         assert(loginResult);
@@ -158,7 +167,6 @@ describe('test/app/service/report_memory.test.js', () => {
         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'));
     });
@@ -173,6 +181,5 @@ describe('test/app/service/report_memory.test.js', () => {
         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'));
     });
 });