Bläddra i källkod

1. 期计量,变更令页面
2. 台账分解,经济指标不计算

MaiXinRong 6 år sedan
förälder
incheckning
82126b8875

+ 51 - 5
app/controller/stage_controller.js

@@ -10,6 +10,7 @@
 
 const moment = require('moment');
 const auditConst = require('../const/audit').stage;
+const changeAudit = require('../const/audit').flow;
 const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const payConst = require('../const/deal_pay.js');
@@ -578,6 +579,22 @@ module.exports = app => {
             }
         }
 
+        // 变更令相关
+        /**
+         * 查询变更令 明细数据(包括附件、变更清单、累计使用情况、本期使用情况)
+         * @param tid
+         * @param sid
+         * @param cid
+         * @returns {Promise<{}>}
+         * @private
+         */
+        async _getChangeDetailData(tid, sid, cid) {
+            const data = {};
+            data.attachments = await this.ctx.service.changeAtt.getChangeAttachment(cid);
+            data.addUsedBills = await this.ctx.service.stageChange.getUsedData(tid, cid);
+            data.curUsedBills = await this.ctx.service.stageChange.getStageUsedData(sid, cid);
+            return data;
+        }
         /**
          * 变更令 (Get)
          * @param ctx
@@ -587,13 +604,24 @@ module.exports = app => {
             try {
                 await this._getStage(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
+                // renderData.changes = await ctx.service.change.getAllDataByCondition({
+                //     where: {tid: ctx.tender.id, status: changeAudit.status.checked},
+                // });
+                renderData.ledger = await ctx.service.ledger.getDataByTenderId(ctx.tender.id, -1);
+                renderData.changes = await ctx.service.change.getChangeAndUsedInfo(ctx.tender.id);
+                if (renderData.changes.length > 0) {
+                    const change = renderData.changes[0];
+                    change.detail = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, change.cid);
+                }
+                renderData.usedChangesId = await ctx.service.stageChange.getStageUsedChangeId(ctx.stage.id);
+                renderData.changeConst = changeConst;
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.change);
                 await this.layout('stage/change.ejs', renderData);
             } catch (err) {
                 this.log(err);
                 ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage');
             }
         }
-
         /**
          * 查询可用变更令 (Ajax-Post)
          * @param ctx
@@ -616,7 +644,6 @@ module.exports = app => {
                 ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
-
         /**
          * 调用变更令 (Ajax-Post)
          * @param ctx
@@ -641,6 +668,25 @@ module.exports = app => {
                 ctx.body = {err: 1, msg: err.toString(), data: null};
             }
         }
+        /**
+         * 查询变更令 明细数据(包括附件、变更清单、累计使用情况、本期使用情况) (Ajax-Post)
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async changeDetail(ctx) {
+            try {
+                await this._getStage(ctx);
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.cid) {
+                    throw '查询数据错误';
+                }
+                const detailData = await this._getChangeDetailData(ctx.tender.id, ctx.stage.id, data.cid);
+                ctx.body = {err: 0, msg: '', data: detailData};
+            } catch(err) {
+                this.log(err);
+                ctx.body = {err: 1, msg: err.toString(), data: null};
+            }
+        }
 
 
         // 审批相关
@@ -775,6 +821,7 @@ module.exports = app => {
             }
         }
 
+        // 清单汇总相关
         _getGatherSpreadSetting() {
             const _ = this.app._;
             function removeFieldCols(setting, cols) {
@@ -804,9 +851,8 @@ module.exports = app => {
             }
             return [gcl, leafXmj];
         }
-
         /**
-         * 清单汇总
+         * 清单汇总 页面 (Get)
          * @param ctx
          * @returns {Promise<void>}
          */
@@ -833,7 +879,7 @@ module.exports = app => {
         }
 
         /**
-         * 审核比较
+         * 审核比较 页面 (Get)
          * @param ctx
          * @returns {Promise<void>}
          */

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

@@ -33,7 +33,7 @@ $(document).ready(function() {
         treeSetting.calcFields = ['deal_tp', 'total_price'];
     }
     treeSetting.calcFun = function (node) {
-        if (checkZero(node.dgn_qty1)) {
+        if (!checkZero(node.dgn_qty1)) {
             node.dgn_price = _.round(node.total_price/node.dgn_qty1, 6);
         } else {
             node.dgn_price = null;

+ 22 - 4
app/public/js/path_tree.js

@@ -341,16 +341,34 @@ const createNewPathTree = function (type, setting) {
             });
             return children;
         };
+
+        /**
+         * 递归方式 查询node的已下载的全部后代 (兼容full_path不存在的情况)
+         * @param node
+         * @returns {*}
+         * @private
+         */
+        _recursiveGetPosterity (node) {
+            let posterity = node.children;
+            for (const c of node.children) {
+                posterity = posterity.concat(this._recursiveGetPosterity(c));
+            }
+            return posterity;
+        };
         /**
          * 查询node的已下载的全部后代
          * @param {Object} node
          * @returns {Array}
          */
         getPosterity (node) {
-            const reg = new RegExp('^' + node.full_path + '.');
-            return this.datas.filter(function (x) {
-                return reg.test(x.full_path);
-            })
+            if (node.full_path !== '') {
+                const reg = new RegExp('^' + node.full_path + '.');
+                return this.datas.filter(function (x) {
+                    return reg.test(x.full_path);
+                });
+            } else {
+                return this._recursiveGetPosterity(node);
+            }
         };
         /**
          * 查询node是否是父节点的最后一个子节点

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

@@ -306,7 +306,7 @@ $(document).ready(() => {
                 const html = [];
                 for (const a of change.attachments) {
                     html.push('<tr>');
-                    html.push('<td>', a.filename + a.fileext, '</td>');
+                    html.push('<td>', '<a href="/change/download/file/' + a.id + '">', a.filename + a.fileext, '</a>', '</td>');
                     html.push('<td>', a.u_name, '</td>');
                     html.push('</tr>');
                 }

+ 303 - 0
app/public/js/stage_change.js

@@ -0,0 +1,303 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2019/5/24
+ * @version
+ */
+class ChangeAnalysis {
+
+    constructor (ledgerData) {
+        this.ledgerTreeSetting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+        };
+        this.ledgerTree = createNewPathTree('ledger', this.ledgerTreeSetting);
+        this.ledgerTree.loadDatas(ledgerData);
+    }
+
+    getLeafXmj (node) {
+        let n = node;
+        while (n) {
+            if (this.ledgerTree.isLeafXmj(n)) {
+                return n;
+            }
+            n = this.ledgerTree.getParent(n);
+        }
+        return null;
+    }
+
+    analyze (change) {
+        change.filterBills = false;
+        change.attachments = change.detail.attachments;
+        change.bills = change.detail.addUsedBills;
+        for (const b of change.bills) {
+            b.qty = _.toNumber(b.samount);
+            b.valid_qty = _.round(_.subtract(b.qty - b.used_qty), 6);
+            b.pos = _.filter(change.detail.curUsedBills, {cbid: b.id});
+            b.cur_qty = 0;
+            for (const p of b.pos) {
+                // 查询最底层项目节
+                const node = this.ledgerTree.getItems(p.ledger_id);
+                const leafXmj = this.getLeafXmj(node);
+                if (leafXmj) {
+                    p.leaf_xmj_code = leafXmj.code;
+                    p.leaf_xmj_name = leafXmj.name;
+                }
+                b.cur_qty = _.round(_.add(b.cur_qty, p.qty), 6);
+            }
+
+        }
+    }
+}
+
+$(document).ready(() => {
+    autoFlashHeight();
+    // ------------- begin 初始化界面
+    // 初始化变更令spread
+    const changeSpreadSetting = {
+        cols: [
+            {title: '变更令号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+            {title: '变更名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 350, formatter: '@', readOnly: true},
+            {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number', readOnly: true},
+            {title: '批复文号', colSpan: '1', rowSpan: '1', field: 'w_code', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+            {title: '已执行(%)', colSpan: '1', rowSpan: '1', field: 'used_pt', hAlign: 2, width: 100, type: 'Number', readOnly: true},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [40],
+        defaultRowHeight: 21,
+    };
+    const changeSpread = SpreadJsObj.createNewSpread($('#bgl-spread')[0]);
+    SpreadJsObj.initSheet(changeSpread.getActiveSheet(), changeSpreadSetting);
+    // 初始化变更清单spread
+    const billsSpreadSetting = {
+        cols: [
+            {title: '本期使用', colSpan: '1', rowSpan: '1', field: '', hAlign: 1, width: 40, formatter: '@', readOnly: true, cellType: 'image', img: function (data) {
+                if (!checkZero(data.cur_qty)) {
+                    return $('#icon-ok')[0];
+                } else {
+                    return null;
+                }
+            }},
+            {title: '清单编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 150, type: 'Number', readOnly: true},
+            {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
+            {title: '单价', colSpan: '1', rowSpan: '1', field: 'up', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '数量', colSpan: '1', rowSpan: '1', field: 'qty', hAlign: 2, width: 60, formatter: '@', readOnly: true},
+            {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 60, formatter: '@', readOnly: true},
+            {title: '已变更', colSpan: '1', rowSpan: '1', field: 'used_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '未变更', colSpan: '1', rowSpan: '1', field: 'valid_qty', hAlign: 2, width: 60, formatter: '@', readOnly: true},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [40],
+        defaultRowHeight: 21,
+    };
+    const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
+    SpreadJsObj.initSheet(billsSpread.getActiveSheet(), billsSpreadSetting);
+    // 初始化相关台账spread
+    const posSpreadSetting = {
+        cols: [
+            {title: '相关台账|项目节编号', colSpan: '5|1', rowSpan: '1|1', field: 'l_code', hAlign: 0, width: 120, formatter: '@', readOnly: true},
+            {title: '|名称', colSpan: '|1', rowSpan: '1', field: 'l_name', hAlign: 0, width: 150, formatter: '@', readOnly: true},
+            {title: '|部位明细', colSpan: '|1', rowSpan: '1', field: 'p_name', hAlign: 0, width: 150, type: 'Number', readOnly: true},
+            {title: '|0号台帐数量', colSpan: '|1', rowSpan: '1', field: 'l_qty', hAlign: 2, width: 60, formatter: '@', readOnly: true},
+            {title: '|本期变更数量', colSpan: '|1', rowSpan: '1', field: 'qty', hAlign: 2, width: 65, type: 'Number', readOnly: true},
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 40],
+        defaultRowHeight: 21,
+    };
+    const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
+    SpreadJsObj.initSheet(posSpread.getActiveSheet(), posSpreadSetting);
+    // 上下窗口resizer
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            changeSpread.refresh();
+            let bcontent = $(".bcontent-wrap").length > 0 ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent-40);
+            if ($('#qingdan').hasClass('active')) {
+                billsSpread.refresh();
+                posSpread.refresh();
+            }
+        }
+    });
+    // 左右窗口resizer
+    $.divResizer({
+        select: '#bills-resize',
+        callback: function () {
+            billsSpread.refresh();
+            posSpread.refresh();
+        }
+    });
+    // ------------end 初始化界面
+
+    // ------------begin 预处理数据
+    const analysis = new ChangeAnalysis(ledger);
+    if (changes.length > 0) {
+        analysis.analyze(changes[0]);
+    }
+    // ------------end 预处理数据
+
+    // ------------begin spread界面操作方法
+    const posSpreadObj = {
+        loadCurPosData () {
+            const bills = SpreadJsObj.getSelectObject(billsSpread.getActiveSheet());
+            if (bills) {
+                console.log(bills.pos);
+                SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), SpreadJsObj.DataType.Data, bills.pos);
+            } else {
+                SpreadJsObj.loadSheetData(posSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
+            }
+        }
+    };
+    const billsSpreadObj = {
+        loadCurBillsData () {
+            const change = SpreadJsObj.getSelectObject(changeSpread.getActiveSheet());
+            if (change) {
+                SpreadJsObj.loadSheetData(billsSpread.getActiveSheet(), SpreadJsObj.DataType.Data, change.bills);
+            } else {
+                SpreadJsObj.loadSheetData(billsSpread.getActiveSheet(), SpreadJsObj.DataType.Data, []);
+            }
+        },
+        selectionChanged: function () {
+            posSpreadObj.loadCurPosData();
+        },
+        filterUsedBills: function (isFilter) {
+            const bills = SpreadJsObj.getSortData(billsSpread.getActiveSheet());
+            for (const b of bills) {
+                b.visible = !isFilter || !checkZero(b.cur_qty);
+            }
+            SpreadJsObj.refreshTreeRowVisible(billsSpread.getActiveSheet());
+        }
+    };
+    billsSpread.bind(spreadNS.Events.SelectionChanged, billsSpreadObj.selectionChanged);
+    let curChangeId;
+    const changeSpreadObj = {
+        loadChangeDetailData() {
+            const change = SpreadJsObj.getSelectObject(changeSpread.getActiveSheet());
+            billsSpreadObj.loadCurBillsData();
+            posSpreadObj.loadCurPosData();
+            if (change.filterBills !== $('#used-bills')[0].checked) {
+                change.fitlerBills = $('#used-bills')[0].checked;
+                billsSpreadObj.filterUsedBills(change.fitlerBills);
+            }
+            // 加载变更信息和附件
+            const obj = $('.tab-content');
+            if (change) {
+                const inputs = $('input[type!=checkbox]', obj);
+                for (const i of inputs) {
+                    const field = $(i).attr('name');
+                    const text = (field && change[field]) ? change[field] : '';
+                    $(i).val(text);
+                }
+                const textareas = $('textarea', obj);
+                for (const ta of textareas) {
+                    const field = $(ta).attr('name');
+                    const text = (field && change[field]) ? change[field] : '';
+                    ta.innerText = text;
+                }
+                const html = [];
+                for (const a of change.attachments) {
+                    html.push('<tr>');
+                    html.push('<td>', '<a href="/change/download/file/' + a.id + '">', a.filename + a.fileext, '</a>', '</td>');
+                    html.push('<td>', a.u_name, '</td>');
+                    html.push('</tr>');
+                }
+                // 变更类型
+                const cType = change.type.split(',');
+                $('input[name="type"]').prop("checked", false);
+                for (const c of cType) {
+                    $('input[name="type"][value='+ c +']').prop("checked", true);
+                }
+                // 变更类别
+                $('select[name=class]').val(change.class);
+                // 变更性质
+                $('select[name=quality]').val(change.quality);
+                // 变更单位
+                $('select[name=company]').html('<option>' + change.company ? change.company : '' + '</option>');
+                // 费用承担方
+                $('input[name=charge][value=' + change.charge + ']').prop('checked', true);
+                // 附件
+                $('#attachment').html(html.join(''));
+            } else {
+                const inputs = $('input', obj);
+                for (const i of inputs) {
+                    $(i).val('');
+                }
+                const textareas = $('textarea', obj);
+                for (const ta of textareas) {
+                    ta.innerText = '';
+                }
+                $('#attachment').html('');
+            }
+        },
+        selectionChanged: function () {
+            const change = SpreadJsObj.getSelectObject(changeSpread.getActiveSheet());
+            if (change.cid === curChangeId) { return; }
+            curChangeId = change.cid;
+            if (change.detail) {
+                changeSpreadObj.loadChangeDetailData();
+            } else {
+                postData(window.location.pathname + '/detail', {cid: change.cid}, function (result) {
+                    change.detail = result;
+                    analysis.analyze(change);
+                    changeSpreadObj.loadChangeDetailData();
+                });
+            }
+        },
+        filterUsedChange: function (isFilter) {
+            for (const c of changes) {
+                c.visible = !isFilter || usedChangesId.indexOf(c.cid) >= 0;
+            }
+            SpreadJsObj.refreshTreeRowVisible(changeSpread.getActiveSheet());
+        }
+    };
+    changeSpread.bind(spreadNS.Events.SelectionChanged, changeSpreadObj.selectionChanged);
+    // ------------end spread界面操作方法
+
+
+    // ------------begin 加载数据至界面
+    SpreadJsObj.loadSheetData(changeSpread.getActiveSheet(), SpreadJsObj.DataType.Data, changes);
+    changeSpreadObj.loadChangeDetailData();
+    if (usedChangesId.length > 0) {
+        changeSpreadObj.filterUsedChange(true);
+    } else {
+        $('#used-change')[0].checked = false;
+    }
+    // ------------end 加载数据至界面
+
+    // ------------begin dom事件定义
+    // 本期已用变更
+    $('#used-change').click(function () {
+        if (usedChangesId.length === 0 && this.checked) {
+            toast('本期无已用变更令', 'hint');
+            this.checked = false;
+        }
+        changeSpreadObj.filterUsedChange(this.checked);
+    });
+    // 切换标签页
+    $('a[data-toggle="tab"]').on('show.bs.tab', function (e) {
+        if (e.target.href.indexOf('#qingdan') !== -1) {
+            $('#qd-filter').show();
+        } else {
+            $('#qd-filter').hide();
+        }
+    });
+    // 只显示本期使用 变更清单
+    $('#used-bills').click(function () {
+        const change = SpreadJsObj.getSelectObject(changeSpread.getActiveSheet());
+        change.filterBills = this.checked;
+        billsSpreadObj.filterUsedBills(this.checked);
+    });
+    // ------------end dom事件定义
+});

+ 1 - 0
app/router.js

@@ -143,6 +143,7 @@ module.exports = app => {
     app.post('/tender/:id/measure/stage/:order/pay/save', sessionAuth, tenderCheck, 'stageController.savePayData');
     // 变更令
     app.get('/tender/:id/measure/stage/:order/change', sessionAuth, tenderCheck, 'stageController.change');
+    app.post('/tender/:id/measure/stage/:order/change/detail', sessionAuth, tenderCheck, 'stageController.changeDetail');
     // 审批
     app.post('/tender/:id/measure/stage/:order/audit/add', sessionAuth, tenderCheck, 'stageController.addAudit');
     app.post('/tender/:id/measure/stage/:order/audit/delete', sessionAuth, tenderCheck, 'stageController.deleteAudit');

+ 27 - 0
app/service/change.js

@@ -715,6 +715,33 @@ module.exports = app => {
         }
 
         /**
+         * 查询变更令 + 变更令执行
+         * @param tid
+         * @returns {Promise<void>}
+         */
+        async getChangeAndUsedInfo(tid) {
+            const sql = 'SELECT C.*, Sum(U.utp) As used_tp, Round(Sum(U.utp) / C.total_price * 100, 2) As used_pt' +
+                '  FROM ' + this.tableName + ' As C' +
+                '  LEFT JOIN (SELECT sc.tid, sc.cid, sc.cbid, Round(SUM(sc.qty) * cb.unit_price, ?) As utp' +
+                '    FROM ' + this.ctx.service.stageChange.tableName + ' As sc' +
+                '    INNER JOIN (' +
+                '      SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `cbid`, `sid` ' +
+                '        FROM ' + this.ctx.service.stageChange.tableName +
+                '        WHERE tid = ?' +
+                '        GROUP By `lid`, `pid`, `cbid`, `sid`' +
+                '    ) As m' +
+                '    ON sc.stimes = m.stimes And sc.sorder = m.sorder And sc.`cbid` = m.`cbid` AND sc.`sid` = m.`sid`' +
+                '    LEFT JOIN ' + this.ctx.service.changeAuditList.tableName + ' As cb ON sc.cbid = cb.id' +
+                '    GROUP By sc.`cbid`' +
+                '  ) As U ON C.cid = U.cid' +
+                '  WHERE C.tid = ?' +
+                '  GROUP By C.cid' +
+                '  ORDER By in_time';
+            const sqlParam = [this.ctx.tender.info.decimal.tp, tid, tid];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        /**
          * 查询可用的变更令
          * @param { string } cid - 查询的清单
          * @return {Promise<*>} - 可用的变更令列表

+ 15 - 0
app/service/change_att.js

@@ -38,6 +38,21 @@ module.exports = app => {
             const result = await this.db.insert(this.tableName, data);
             return result;
         }
+
+        /**
+         * 获取 变更令 所有附件
+         * @param {uuid} cid - 变更令id
+         * @returns {Promise<void>}
+         */
+        async getChangeAttachment(cid) {
+            const sql = 'SELECT ca.*, pa.name As u_name, pa.role As u_role ' +
+                '  FROM ?? As ca ' +
+                '  Left Join ?? As pa ' +
+                '  On ca.uid = pa.id ' +
+                '  Where ca.cid = ?';
+            const sqlParam = [this.tableName, this.ctx.service.projectAccount.tableName, cid];
+            return await this.db.query(sql, sqlParam);
+        }
     }
 
     return ChangeAtt;

+ 68 - 0
app/service/stage_change.js

@@ -38,6 +38,7 @@ module.exports = app => {
                 '  INNER JOIN ( ' +
                 '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid` From ' + this.tableName +
                 '      WHERE tid = ? And sid = ? And lid = ? And pid = ?' +
+                '      GROUP By `lid`, `pid`' +
                 '  ) As m ' +
                 '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid';
             const sqlParam = [tid, sid, lid, pid ? pid : -1];
@@ -203,6 +204,73 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 获取 变更令 - 变更清单 使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @returns {Promise<void>}
+         */
+        async getUsedData(tid, cid) {
+            const sql = 'SELECT c.lid, c.pid, SUM(c.qty) as used_qty,' +
+                        '    cb.tid, cb.cid, cb.id, cb.code, cb.name, cb.unit, cb.unit_price, cb.detail, cb.samount' +
+                        '  FROM ' + this.ctx.service.changeAuditList.tableName + ' As cb' +
+                        '  LEFT JOIN ' + this.tableName + ' As c ON cb.id = c.cbid ' +
+                        '  LEFT JOIN (' +
+                        '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `cbid`, `sid`, `cid` ' +
+                        '      FROM ' + this.tableName +
+                        '      WHERE tid = ? AND cid = ?' +
+                        '      GROUP By `lid`, `pid`, `cbid`, `sid`' +
+                        '  ) As m' +
+                        '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.`cbid` = m.`cbid` AND c.`sid` = m.`sid` And c.`cid` = m.`cid`' +
+                        '  WHERE cb.cid = ?' +
+                        '  GROUP By c.`cbid`';
+            const sqlParam = [tid, cid, cid];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        /**
+         * 获取 变更令 - 变更清单 当期使用情况
+         * @param {Number} sid - 查询期id
+         * @param {uuid} cid - 变更令id
+         * @returns {Promise<*>}
+         */
+        async getStageUsedData(sid, cid) {
+            const sql = 'SELECT c.*, ' +
+                        '    l.ledger_id As `ledger_id`, l.b_code As `l_code`, l.name As `l_name`, l.unit As `l_unit`, l.unit_price As `l_up`,' +
+                        '    l.deal_qty As `l_deal_qty`, l.deal_tp As `l_deal_tp`, l.quantity As `l_qty`, l.total_price As `l_tp`, ' +
+                        '    l.drawing_code As `l_drawing_code`, ' +
+                        '    p.name As `p_name`, p.drawing_code As `p_drawing_code`, p.`quantity` As `p_qty`' +
+                        '  FROM ' + this.tableName + ' As c ' +
+                        '  INNER JOIN ( ' +
+                        '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `cbid` From ' + this.tableName +
+                        '      WHERE sid = ? And cid = ?' +
+                        '      GROUP By `lid`, `pid`, `cbid`' +
+                        '  ) As m ' +
+                        '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.cbid = m.cbid' +
+                        '  LEFT JOIN ' + this.ctx.service.ledger.tableName + ' As l ON c.lid = l.id' +
+                        '  LEFT JOIN ' + this.ctx.service.pos.tableName + ' As p ON c.pid = p.id';
+            const sqlParam = [sid, cid];
+            return await this.db.query(sql, sqlParam);
+        }
+
+        /**
+         * 获取 本期 使用的变更令
+         * @param sid
+         * @returns {Promise<void>}
+         */
+        async getStageUsedChangeId(sid) {
+            const sql = 'SELECT c.`cid` FROM ' + this.tableName + ' As c' +
+                        '  INNER JOIN (' +
+                        '    SELECT MAX(`stimes`) As `stimes`, MAX(`sorder`) As `sorder`, `lid`, `pid`, `cbid` From ' + this.tableName +
+                        '      WHERE sid = ? And not ISNULL(qty)' +
+                        '      GROUP By `lid`, `pid`, `cbid`' +
+                        '  ) As m' +
+                        '  ON c.stimes = m.stimes And c.sorder = m.sorder And c.lid = m.lid And c.pid = m.pid And c.cbid = m.cbid' +
+                        '  GROUP BY c.`cid`';
+            const sqlParam = [sid];
+            const result = await this.db.query(sql, sqlParam);
+            return this._.map(result, 'cid');
+        }
     }
 
     return StageChange;

+ 77 - 106
app/view/stage/change.ejs

@@ -3,37 +3,29 @@
     <div class="panel-title">
         <div class="title-main d-flex justify-content-between">
             <div>
-                <div class="d-inline-block ml-3">
-                    <div class="form-check-inline">
-                        <label class="form-check-label">
-                            <input class="form-check-input" type="checkbox">
-                            本期已用变更
-                        </label>
+                <a class="btn btn-sm btn-light">
+                    <div class="custom-control custom-checkbox">
+                        <input type="checkbox" class="custom-control-input" id="used-change" checked="">
+                        <label class="custom-control-label text-primary" for="used-change">本期已用变更</label>
                     </div>
-                </div>
+                </a>
             </div>
-            <div>
-                <!--按钮-->
+            <div class="ml-auto">
             </div>
         </div>
     </div>
     <div class="content-wrap">
         <div class="c-header p-0">
         </div>
-        <div class="row w-100 sub-content">
+        <div class="c-body">
             <!--左栏-->
-            <div class="c-body col-6">
+            <div class="w-100 sub-content">
                 <!--上部分-->
-                <div class="sjs-height-1">
-                    <table class="table table-bordered table-sm">
-                        <tr><th>变更令号</th><th>变更名称</th><th>金额</th><th>批复文号</th><th>已执行(%)</th></tr>
-                        <tr><td>LZTJ-1标项目部发变更(2015)001号 </td><td>XX变更1</td><td>140343</td><td>纵路投[2019]126号</td><td></td></tr>
-                        <tr><td>LZTJ-1标项目部发变更(2015)002号 </td><td>XX变更2</td><td></td><td>纵路投[2019]126号</td><td></td></tr>
-                        <tr><td>LZTJ-1标项目部发变更(2015)003号 </td><td>XX变更3</td><td></td><td>纵路投[2019]126号</td><td></td></tr>
-                    </table>
+                <div class="sjs-height-1" id="bgl-spread">
                 </div>
                 <!--下部分-->
-                <div class="bcontent-wrap">
+                <div class="bcontent-wrap" id="bottom-view">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#bgl-spread" div2="#bottom-view" store-id="stage-change" min="150"></div>
                     <div class="bc-bar mb-1">
                         <ul class="nav nav-tabs">
                             <li class="nav-item">
@@ -45,108 +37,122 @@
                             <li class="nav-item">
                                 <a class="nav-link" data-toggle="tab" href="#fujian" role="tab">附件</a>
                             </li>
+                            <li>
+                                <div id="qd-filter">
+                                    <a class="btn btn-sm btn-light">
+                                        <div class="custom-control custom-checkbox">
+                                            <input type="checkbox" class="custom-control-input" id="used-bills">
+                                            <label class="custom-control-label text-primary" for="used-bills">只显示本期使用</label>
+                                        </div>
+                                    </a>
+                                </div>
+                            </li>
                         </ul>
                     </div>
                     <div class="tab-content">
                         <div class="tab-pane active" id="qingdan">
-                            <div class="sp-wrap" style="overflow: auto">
-                                <table class="table table-sm table-bordered">
-                                    <tr><th>本期使用</th><th>清单编号</th><th>名称</th><th>单位</th><th>单价</th><th>数量</th><th>金额</th><th>已变更</th><th>未变更</th></tr>
-                                    <tr><td></td><td>203-1-a</td><td>挖土方</td><td>m3</td><td>7.53</td><td>6467.48</td><td>48700</td><td>1000</td></tr>
-                                    <tr><td><i class="fa fa-check text-success"></i></td><td>403-1-a</td><td>I级钢筋(包括基础、承台、支撑梁等)</td><td></td><td></td><td></td><td></td><td></td><td>1000</td></tr>
-                                    <tr><td></td><td>403-2</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
-                                </table>
+                            <div class="row">
+                                <div class="col-6" id="left-view">
+                                    <div class="sp-wrap" id="bills-spread">
+                                    </div>
+                                </div>
+                                <div class="col-6" id="right-view">
+                                    <div id="bills-resize" class="resize-x" r-Type="width" div1="#left-spread" div2="#right-view" store-id="stage-change-bills" min="150"></div>
+                                    <div class="tab-content">
+                                        <div class="tab-pane active" id="tz">
+                                            <div class="sp-wrap" id="pos-spread">
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
                             </div>
                         </div>
-                        <div class="tab-pane" id="detail">
+                        <div class="tab-pane col-12" id="detail">
                             <div class="sp-wrap" style="overflow: auto">
                                 <div class="form-group">
                                     <label>变更类型</label>
                                     <div class="checkbox">
+                                        <% for (const t in changeConst.type) { %>
+                                        <% const cType = changeConst.type[t] %>
                                         <label class="checkbox-inline">
-                                            <input id="inlineCheckbox1" value="option1" checked="" disabled="" type="checkbox">A.位置
-                                        </label>
-                                        <!-- <label class="checkbox-inline">
-                                          <input id="inlineCheckbox2" value="option2" type="checkbox">B.数量
-                                        </label>
-                                        <label class="checkbox-inline">
-                                          <input id="inlineCheckbox3" value="option3" type="checkbox">C.结构
-                                        </label>
-                                        <label class="checkbox-inline">
-                                          <input id="inlineCheckbox3" value="option3" checked="" type="checkbox">D.新增
-                                        </label>
-                                        <label class="checkbox-inline">
-                                          <input id="inlineCheckbox3" value="option3" type="checkbox">E.取消
+                                            <input value="<%- cType.value %>" name="type" type="checkbox" disabled><%- cType.name %>
                                         </label>
-                                        <label class="checkbox-inline">
-                                          <input id="inlineCheckbox3" value="option3" type="checkbox">F.纠错
-                                        </label> -->
+                                        <% } %>
                                     </div>
                                 </div>
                                 <div class="form-group">
                                     <label>变更类别 </label>
-                                    <select class="form-control form-control-sm" disabled=""><option>A类变更</option><option>B类变更</option><option>C类变更</option><option>D类变更</option><option>E类变更</option></select>
+                                    <select class="form-control form-control-sm" name="class" disabled>
+                                        <% for (const c in changeConst.class) { %>
+                                        <% const cClass = changeConst.class[c] %>
+                                        <option value="<%- cClass.value %>"><%- cClass.name %></option>
+                                        <% } %>
+                                    </select>
                                 </div>
                                 <div class="form-group">
                                     <label>变更性质 </label>
-                                    <select class="form-control form-control-sm" disabled=""><option>一般设计变更</option><option>较大设计变更</option><option>重大设计变更</option></select>
+                                    <select class="form-control form-control-sm" name="quality" disabled>
+                                        <% for (const q in changeConst.quality) { %>
+                                        <% const cQuality = changeConst.quality[q] %>
+                                        <option value="<%- cQuality.value %>"><%- cQuality.name %></option>
+                                        <% } %>
+                                    </select>
                                 </div>
                                 <div class="form-group">
                                     <label>桩号</label>
-                                    <input class="form-control form-control-sm" value="K0+532" readonly="" type="text">
+                                    <input class="form-control form-control-sm" name="peg" value="K0+532" readonly="" type="text">
                                 </div>
                                 <div class="form-group">
                                     <label>原设计图名称</label>
-                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                    <input class="form-control form-control-sm" name="org_name" placeholder="" readonly="" type="text">
                                 </div>
                                 <div class="form-group">
                                     <label>原图号</label>
-                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                    <input class="form-control form-control-sm" name="org_code" placeholder="" readonly="" type="text">
                                 </div>
                                 <div class="form-group">
                                     <label>变更设计图名称</label>
-                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                    <input class="form-control form-control-sm" name="new_name" placeholder="" readonly="" type="text">
                                 </div>
                                 <div class="form-group">
                                     <label>变更图号</label>
-                                    <input class="form-control form-control-sm" placeholder="" readonly="" type="text">
+                                    <input class="form-control form-control-sm" name="new_code" placeholder="" readonly="" type="text">
                                 </div>
                                 <div class="form-group">
                                     <label>工程变更理由及内容</label>
-                                    <textarea class="form-control form-control-sm" rows="6" readonly="">由于K0+532涵洞基底土质天然含水率为25%、收费站出口右侧加宽段K0+120-K0+190段基底土质天然含水率为28.8%。含水率较大形成过湿土,地基松软,无法满足承载力要求。经处项目办、设计、监理、施工单位四方勘察,采用抛石挤淤、砂砾换填以达到地基承载力。
-                          </textarea>
+                                    <textarea class="form-control form-control-sm" name="content" rows="6" readonly=""></textarea>
                                 </div>
                                 <div class="form-group">
                                     <label>工程变更合同依据</label>
-                                    <textarea class="form-control form-control-sm" rows="6" readonly=""></textarea>
+                                    <textarea class="form-control form-control-sm" name="basis" rows="6" readonly=""></textarea>
                                 </div>
                                 <div class="form-group">
                                     <label>变更提出单位</label>
-                                    <!-- <a href="#editcompany" style="float:right;" class="" data-toggle="modal">编辑</a> -->
-                                    <select class="form-control form-control-sm" disabled=""><option>AA公司</option><option>BB公司</option></select>
+                                    <select class="form-control form-control-sm" name="company" disabled>
+                                    </select>
                                 </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">
-                                            <input id="inlineCheckbox1" value="option1" checked="" disabled="" type="radio"> 业主
+                                            <input value="<%- cCharge.value %>" name="charge" type="radio" disabled> <%- cCharge.name %>
                                         </label>
-                                        <!-- <label class="radio-inline">
-                                          <input id="inlineCheckbox2" value="option2" type="radio"> 承包人
-                                        </label> -->
+                                        <% } %>
                                     </div>
                                 </div>
                                 <div class="form-group">
                                     <label>备注</label>
-                                    <textarea class="form-control form-control-sm" rows="3" readonly=""></textarea>
+                                    <textarea name="memo" class="form-control form-control-sm" rows="3" readonly=""></textarea>
                                 </div>
                             </div>
                         </div>
-                        <div class="tab-pane" id="fujian">
+                        <div class="tab-pane col-12" id="fujian">
                             <div class="sp-wrap" style="overflow: auto">
                                 <table class="table table-bordered">
-                                    <tbody><tr><th>名称</th><th>上传者</th></tr>
-                                    <tr><td><a href="">合同段工程变更现场办公会议纪要的通知.rar</a></td><th>张三</th></tr>
+                                    <thead><tr><th>名称</th><th>上传者</th></tr></thead>
+                                    <tbody id="attachment">
                                     </tbody>
                                 </table>
                             </div>
@@ -154,47 +160,12 @@
                     </div>
                 </div>
             </div>
-            <!--右栏-->
-            <div class="c-body col">
-                <div class="sjs-bar">
-                    <ul class="nav nav-tabs">
-                        <li class="nav-item">
-                            <a class="nav-link active" data-toggle="tab" href="#qingdan" role="tab">本期使用</a>
-                        </li>
-                    </ul>
-                </div>
-                <!--上部-->
-                <div class="tab-content">
-                    <div class="tab-pane active">
-                        <div class="sjs-height-3" style="overflow: auto">
-                            <table class="table table-sm table-bordered">
-                                <tr><th>清单编号</th><th>名称</th><th>单位</th><th>单价</th><th>本期已变更</th></tr>
-                                <tr><td>403-1-a</td><td>I级钢筋(包括基础、承台、支撑梁等)</td><td>kg</td><td>5.18</td><td>400</td></tr>
-                            </table>
-                        </div>
-                        <div class="sjs-bottom">
-                            <ul class="nav nav-tabs">
-                                <li class="nav-item">
-                                    <a class="nav-link active" data-toggle="tab" href="#tz" role="tab" aria-selected="false">相关台帐</a>
-                                </li>
-                            </ul>
-                            <div class="tab-content">
-                                <div class="tab-pane active" id="tz">
-                                    <div class="sjs-bottom-2">
-                                        <!--分项清单-->
-                                        <table class="table table-bordered table-sm">
-                                            <tr><th>项目节编号</th><th>名称</th><th>部位明细</th><th>0号台帐数量</th><th>本期变更数量</th></tr>
-                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台1#桩</td><td>126</td><td>100</td></tr>
-                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>0#桥台2#桩</td><td>126</td><td>100</td></tr>
-                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>8#桥台1#桩</td><td>126</td><td>100</td></tr>
-                                            <tr><td>1-4-5-1-1-1-1</td><td>桥台桩基础</td><td>8#桥台2#桩</td><td>126</td><td>100</td></tr>
-                                        </table>
-                                    </div>
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
         </div>
-    </div>
+    </div>
+</div>
+<img src="/public/images/icon-ok.png" id="icon-ok" />
+<script>
+    const changes = JSON.parse('<%- JSON.stringify(changes) %>');
+    const usedChangesId = JSON.parse('<%- JSON.stringify(usedChangesId) %>')
+    const ledger = JSON.parse('<%- JSON.stringify(ledger) %>');
+</script>

+ 12 - 0
config/web.js

@@ -146,6 +146,18 @@ const JsFiles = {
                 ],
                 mergeFile: 'stage_pay',
             },
+            change: {
+                files: [
+                    "/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js",
+                ],
+                mergeFiles: [
+                    "/public/js/div_resizer.js",
+                    "/public/js/spreadjs_rela/spreadjs_zh.js",
+                    "/public/js/path_tree.js",
+                    "/public/js/stage_change.js",
+                ],
+                mergeFile: 'stage_change',
+            },
             gather: {
                 files: [
                     "/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js",