Преглед изворни кода

申请清单功能,新增方案复制申请的清单

laiguoran пре 3 година
родитељ
комит
08ec826b0f

+ 67 - 2
app/controller/change_controller.js

@@ -2415,8 +2415,11 @@ module.exports = app => {
             const orders = ctx.query.order ? ctx.query.order : 0;
             const changes = await ctx.service.changeApply.getListByStatus(tender.id, status, 1, sorts, orders);
             const total = await ctx.service.changeApply.getCountByStatus(tender.id, status);
+            let page_total = 0;
+            const tp = await ctx.service.changeApply.getTp(tender.id, status);
             for (const c of changes) {
                 c.curAuditor = await ctx.service.changeApplyAudit.getAuditorByStatus(c.id, c.status, c.times);
+                page_total = ctx.helper.add(page_total, c.total_price);
             }
             const tender_userInfo = await ctx.service.projectAccount.getDataById(ctx.tender.data.user_id);
             // 分页相关
@@ -2491,6 +2494,8 @@ module.exports = app => {
                 changeConst,
                 changeProjectList,
                 pcLists,
+                tp,
+                page_total,
                 tenderMenu: this.menu.tenderMenu,
                 preUrl: '/tender/' + tender.id,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.apply),
@@ -2601,17 +2606,25 @@ module.exports = app => {
             try {
                 const whiteList = this.ctx.app.config.multipart.whitelist;
                 const tender = await ctx.service.tender.getDataById(ctx.tender.id);
+                await this._getChangeApplyAuditViewData(ctx);
+                // 获取清单列表
+                const changeList = await ctx.service.changeApplyList.getList(ctx.change.id);
                 // 获取附件列表
                 const fileList = await ctx.service.changeApplyAtt.getAllChangeApplyAtt(ctx.tender.id, ctx.change.id);
-                await this._getChangeApplyAuditViewData(ctx);
                 const renderData = {
                     tender,
                     change: ctx.change,
+                    listRule: ctx.change.list_rule ? JSON.parse(ctx.change.list_rule) : { source: 1, rule: ['unit', 'unit_price'] },
+                    changeList,
                     changeConst,
                     auditConst: audit.changeApply,
                     fileList,
                     whiteList,
-                    returnUrl: this.app._.includes(ctx.request.headers.referer, '/tender/' + ctx.tender.id + '/change/apply') ? ctx.request.headers.referer : null,
+                    tpUnit: ctx.tender.info.decimal.tp,
+                    upUnit: ctx.tender.info.decimal.up,
+                    changeUnits: changeConst.units,
+                    precision: ctx.tender.info.precision,
+                    returnUrl: this.app._.includes(ctx.request.headers.referer, '/tender/' + ctx.tender.id + '/change/apply') && !this.app._.includes(ctx.request.headers.referer, '/tender/' + ctx.tender.id + '/change/apply/' + ctx.change.id + '/information') ? ctx.request.headers.referer : null,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.change.apply_information),
                     preUrl: '/tender/' + ctx.tender.id + '/change/apply/' + ctx.change.id + '/information',
                 };
@@ -2944,6 +2957,58 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 变更清单 - 操作 (Ajax)
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveApplyListsData(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                switch (data.type) {
+                    case 'add':
+                        responseData.data = await ctx.service.changeApplyList.add(data.updateData);
+                        break;
+                    case 'batchadd':
+                        responseData.data = await ctx.service.changeApplyList.batchAdd(data);
+                        break;
+                    case 'del':
+                        await ctx.service.changeApplyList.del(data.ids);
+                        // 取所有工料表
+                        responseData.data = await ctx.service.changeApplyList.getList(ctx.change.id);
+                        break;
+                    case 'update':
+                        // if (data.updateData.code === '' || data.updateData.code === null) {
+                        //     throw '请先输入编号';
+                        // }
+                        await ctx.service.changeApplyList.save(data.updateData);
+                        break;
+                    case 'paste':
+                        await ctx.service.changeApplyList.saveDatas(data.updateData);
+                        // 取所有工料表
+                        responseData.data = await ctx.service.changeApplyList.getList(ctx.change.id);
+                        break;
+                    case 'list_rule':
+                        const result = await ctx.service.changeApply.saveInfo(ctx.change.id, { name: 'list_rule', val: data.postData });
+                        if (!result) {
+                            throw '修改失败';
+                        }
+                        break;
+                    default: throw '参数有误';
+                }
+
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         // 变更方案
         async _filterChangesPlan(ctx, status = 0) {
             const tenderId = ctx.params.id;

+ 8 - 0
app/public/js/change_apply_audit.js

@@ -197,6 +197,14 @@ function checkAuditorFrom () {
         toastr.error('变更原因不能为空');
         flag = true;
     }
+    // 判断变更清单是否有空值
+    const nullCodeList = _.filter(changeList, function(item) {
+        return item.code === null || item.code === '';
+    });
+    if (nullCodeList.length > 0) {
+        toastr.error('变更清单都需要设置清单编号才能上报');
+        flag = true;
+    }
     if (flag) {
         return false;
     }

+ 410 - 1
app/public/js/change_apply_information.js

@@ -187,7 +187,396 @@ $(document).ready(() => {
         } else {
             $(this).val(change[val_name]);
         }
-    })
+    });
+
+
+    const changeSpread = SpreadJsObj.createNewSpread($('#apply-spread')[0]);
+    const changeSpreadSheet = changeSpread.getActiveSheet();
+    const style1 = new GC.Spread.Sheets.Style();
+    style1.locked = true;
+
+    const changeSpreadSetting = {
+        cols: [
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 110, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 130, formatter: '@', readOnly: 'readOnly.isEdit'},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'unit', comboItems: changeUnits, comboEdit: true},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.unit_price'},
+            {title: '原设计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'oamount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.oamount'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'oa_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.oa_tp'},
+            {title: '申请变更增(+)减(-)|数量', colSpan: '2|1', rowSpan: '1|1', field: 'camount', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit', getValue: 'getValue.camount'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'ca_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true, getValue: 'getValue.ca_tp'},
+        ],
+        emptyRows: !readOnly ? 3 : 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: readOnly,
+        localCache: {
+            key: 'changes-apply-list-spread',
+            colWidth: true,
+        }
+    };
+
+    console.log(changeList);
+
+    const changeCol = {
+        getValue: {
+            unit_price: function(data) {
+                return ZhCalc.round(data.unit_price, unitPriceUnit);
+            },
+            oa_tp: function (data) {
+                return ZhCalc.round(ZhCalc.mul(data.unit_price, data.oamount), totalPriceUnit);
+            },
+            ca_tp: function (data) {
+                return ZhCalc.round(ZhCalc.mul(data.unit_price, data.camount), totalPriceUnit);
+            },
+            oamount: function (data) {
+                return ZhCalc.round(data.oamount, findDecimal(data.unit));
+            },
+            camount: function (data) {
+                return ZhCalc.round(data.camount, findDecimal(data.unit));
+            },
+        },
+        readOnly: {
+            isEdit: function (data) {
+                return readOnly;
+            },
+        },
+    };
+    const changeSpreadObj = {
+        makeSjsFooter: function() {
+            // 增加汇总行并设为锁定禁止编辑状态
+            changeSpreadSheet.addRows(changeSpreadSheet.getRowCount(), 1);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 0, '合计');
+            changeSpreadSheet.setStyle(changeSpreadSheet.getRowCount() - 1, -1, style1);
+            changeSpreadObj.countSum();
+        },
+        countSum: function() {
+            const rowCount = changeSpreadSheet.getRowCount();
+            let oSum = 0,
+                cSum = 0;
+            for (let i = 0; i < rowCount - 1; i++) {
+                oSum = ZhCalc.add(oSum, changeSpreadSheet.getValue(i, 5));
+                cSum = ZhCalc.add(cSum, changeSpreadSheet.getValue(i, 7));
+            }
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 5, oSum !== 0 ? oSum : null);
+            changeSpreadSheet.setValue(changeSpreadSheet.getRowCount() - 1, 7, cSum !== 0 ? cSum : null);
+        },
+        deletePress: function (sheet) {
+            return;
+        },
+        valueChanged: function (e, info) {
+            // 防止ctrl+z撤销数据
+            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+        }
+    };
+    if (!readOnly) {
+        changeSpreadObj.del = function (sheet) {
+            const selection = sheet.getSelections();
+            const row = selection[0].row, count = selection[0].rowCount;
+            const sortData = sheet.zh_data;
+            const ids = [];
+            for (let iRow = 0; iRow < count; iRow++) {
+                if (sortData[iRow + row]) {
+                    ids.push(sortData[iRow + row].id);
+                }
+            }
+            if (ids.length > 0) {
+                postData(preUrl + '/list/save', {type: 'del', ids}, function (result) {
+                    changeList = result;
+                    SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
+                    changeSpreadObj.makeSjsFooter();
+                });
+            }
+        };
+        changeSpreadObj.editEnded = function (e, info) {
+            if (info.sheet.zh_setting) {
+                const type = SpreadJsObj.getSelectObject(info.sheet) ? 'update' : 'add';
+                const select = type === 'update' ? SpreadJsObj.getSelectObject(info.sheet) : {unit: ''};
+                const col = info.sheet.zh_setting.cols[info.col];
+                // 未改变值则不提交
+                let validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : '');
+                const orgValue = type === 'update' ? select[col.field] : '';
+                if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                if (col.field === 'oa_tp' || col.field === 'ca_tp') {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                // 判断部分值是否输入的是数字判断和数据计算
+                if (col.type === 'Number') {
+                    if (isNaN(validText)) {
+                        toastr.error('不能输入其它非数字类型字符');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    if (col.field === 'unit_price') {
+                        validText = ZhCalc.round(validText, unitPriceUnit);
+                    } else {
+                        validText = ZhCalc.round(validText, findDecimal(select.unit)) || 0;
+                    }
+                }
+                if (col.field === 'unit') {
+                    select.camount = ZhCalc.round(select.camount, findDecimal(validText)) || 0;
+                    select.oamount = ZhCalc.round(select.oamount, findDecimal(validText)) || 0;
+                }
+                select[col.field] = validText;
+
+                console.log(select, type);
+                delete select.waitingLoading;
+
+                // 更新至服务器
+                postData(preUrl + '/list/save', { type, updateData: select }, function (result) {
+                    if(type === 'update') {
+                        changeList.splice(info.row, 1, select);
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        changeSpreadObj.countSum();
+                    } else {
+                        changeList.push(result);
+                        changeSpreadSheet.addRows(changeList.length - 1, 1);
+                        SpreadJsObj.reLoadRowData(changeSpreadSheet, changeList.length - 1);
+                        changeSpreadSheet.setStyle(changeSpreadSheet.getRowCount() - 1, -1, style1);
+                        changeSpreadSheet.setSelection(changeList.length - 1, 0, 1, 1);
+                    }
+                }, function () {
+                    select[col.field] = orgValue;
+                    if(col.field === 'camount') {
+                        select.spamount = orgValue;
+                    }
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            }
+        };
+        changeSpreadObj.clipboardPasted  = function(e, info, cellRange) {
+            if (info.sheet.getColumnCount() > info.sheet.zh_setting.cols.length) {
+                info.sheet.setColumnCount(info.sheet.zh_setting.cols.length);
+            }
+            const hint = {
+                cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+            };
+            if (info.sheet.zh_setting) {
+                const sortData = info.sheet.zh_data || [];
+                const range = info.cellRange;
+                const data = [];
+                for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                    let bPaste = true;
+                    const curRow = range.row + iRow;
+                    // const materialData = JSON.parse(JSON.stringify(sortData[curRow]));
+                    const cLData = curRow >= sortData.length ? {unit: ''} : {id: sortData[curRow].id};
+                    const hintRow = range.rowCount > 1 ? curRow : '';
+                    let sameCol = 0;
+                    for (let iCol = 0; iCol < range.colCount; iCol++) {
+                        const curCol = range.col + iCol;
+                        const colSetting = info.sheet.zh_setting.cols[curCol];
+                        if (!colSetting) continue;
+                        // cLData[colSetting.field] = trimInvalidChar(info.sheet.getText(curRow, curCol));
+
+                        let validText = info.sheet.getText(curRow, curCol);
+                        validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : '');
+                        const orgValue = curRow >= sortData.length ? '' : sortData[curRow][colSetting.field];
+                        if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                            sameCol++;
+                            if (range.colCount === sameCol) {
+                                bPaste = false;
+                            }
+                            continue;
+                        }
+
+                        if (colSetting.type === 'Number') {
+                            if (isNaN(validText)) {
+                                // toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                                toastMessageUniq(hint.numberExpr);
+                                bPaste = false;
+                                continue;
+                            }
+                            if (colSetting.field === 'unit_price') {
+                                validText = ZhCalc.round(validText, unitPriceUnit);
+                            } else {
+                                validText = ZhCalc.round(validText, findDecimal(cLData.unit)) || 0;
+                            }
+                        }
+                        let unitdecimal = validText;
+                        if (colSetting.field === 'unit') {
+                            //粘贴内容要为下拉列表里所有的单位,不然为空
+                            if (changeUnits.indexOf(validText) === -1) {
+                                unitdecimal = '';
+                                // validText = null;
+                            }
+                            cLData.camount = curRow >= sortData.length ? 0 : ZhCalc.round(sortData[curRow].camount, findDecimal(unitdecimal)) || 0;
+                            cLData.oamount = curRow >= sortData.length ? 0 : ZhCalc.round(sortData[curRow].oamount, findDecimal(unitdecimal)) || 0;
+                        }
+                        cLData[colSetting.field] = validText;
+
+                    }
+                    if (bPaste) {
+                        delete cLData.oa_tp;
+                        delete cLData.ca_tp;
+                        data.push(cLData);
+                        // rowData.push(curRow);
+                    } else {
+                        SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                    }
+                }
+                if (data.length === 0) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    return;
+                }
+                console.log(data);
+                // 更新至服务器
+                postData(preUrl + '/list/save', { type:'paste', updateData: data }, function (result) {
+                    changeList = result;
+                    SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
+                    changeSpreadObj.makeSjsFooter();
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    return;
+                });
+            }
+        };
+
+        changeSpreadObj.updateOamount = function () {
+            const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
+            console.log(changeSpreadSheet);
+            const index = changeList.indexOf(select);
+            if (index > -1) {
+                const dataSource = listRule.source === 1 ? gclGatherData : dealBillList;
+                const source = _.find(dataSource, function (item) {
+                    if (item.b_code === select.code && item.name === select.name) {
+                        if (listRule.rule.length > 0) {
+                            for(const r of listRule.rule) {
+                                if (item[r] !== select[r]) {
+                                    return false;
+                                }
+                            }
+                        }
+                        return true;
+                    }
+                    return false;
+                });
+                if (source && source.quantity !== select.oamount) {
+                    select.oamount = source.quantity;
+                    delete select.waitingLoading;
+                    console.log(select);
+                    // 更新至服务器
+                    postData(preUrl + '/list/save', { type:'update', updateData: select }, function (result) {
+                        changeList.splice(index, 1, select);
+                        SpreadJsObj.reLoadRowData(changeSpreadSheet, index);
+                        changeSpreadObj.countSum();
+                    }, function () {
+                        SpreadJsObj.reLoadRowData(changeSpreadSheet, index);
+                    });
+                }
+            }
+        };
+        // changeSpread.bind(spreadNS.Events.CellChanged, changeSpreadObj.cellChanged);
+        changeSpread.bind(spreadNS.Events.EditEnded, changeSpreadObj.editEnded);
+        changeSpread.bind(spreadNS.Events.ClipboardPasted, changeSpreadObj.clipboardPasted);
+        changeSpread.bind(spreadNS.Events.ValueChanged, changeSpreadObj.valueChanged);
+        SpreadJsObj.addDeleteBind(changeSpread, changeSpreadObj.deletePress);
+
+        // 右键菜单
+        $.contextMenu({
+            selector: '#apply-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, changeSpread);
+                return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+            },
+            items: {
+                'updateOamount': {
+                    name: '原设计数量读取',
+                    icon: '',
+                    callback: function (key, opt) {
+                        changeSpreadObj.updateOamount(changeSpreadSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
+                        const sel = changeSpreadSheet.getSelections()[0];
+                        // console.log(select, sel);
+                        if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1) {
+                            return false;
+                        } else {
+                            return true;
+                        }
+                    }
+                },
+                sprDel: '------------',
+                'delete': {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        changeSpreadObj.del(changeSpreadSheet);
+                    },
+                    disabled: function (key, opt) {
+                        // const select = SpreadJsObj.getSelectObject(changeSpreadSheet);
+                        if (changeSpreadSheet.zh_data) {
+                            const selection = changeSpreadSheet.getSelections();
+                            return changeSpreadSheet.zh_data.length < selection[0].row + selection[0].rowCount;
+                        } else {
+                            return true;
+                        }
+                        // const sel = changeSpreadSheet.getSelections()[0];
+                        // // console.log(select, sel);
+                        // if (!readOnly && select && sel.row !== changeSpreadSheet.getRowCount() - 1) {
+                        //     return false;
+                        // } else {
+                        //     return true;
+                        // }
+                    }
+                },
+            }
+        });
+
+        $('#shuliangguize').on('show.bs.modal', function () {
+            $('#shuliangguize input[name="data_source"][value="'+ listRule.source +'"]').prop('checked', true);
+            $('#shuliangguize input[name="data_rule"]').prop('checked', false);
+            for(const r of listRule.rule) {
+                $('#shuliangguize input[name="data_rule"][value="' + r +'"]').prop('checked', true);
+            }
+        });
+
+        // 设置原设计数量读取
+        $('#setListRule').click(function () {
+            const rule = [];
+            $('#shuliangguize input[name="data_rule"]:checked').each(function () {
+                rule.push($(this).val());
+            });
+            const newListRule = {
+                source: parseInt($('#shuliangguize input[name="data_source"]:checked').val()),
+                rule,
+            };
+            postData(preUrl + '/list/save', { type: 'list_rule', postData: JSON.stringify(newListRule) }, function (result) {
+                listRule = newListRule;
+                $('#shuliangguize').modal('hide');
+            });
+        })
+    }
+    let changeListData;
+    let gclGatherData;
+    let dealBillList;
+    const billUrl = window.location.pathname.split('/').slice(0, 4).join('/');
+    postData(billUrl + '/defaultBills', {}, function (result) {
+        gclGatherModel.loadLedgerData(result.bills);
+        gclGatherModel.loadPosData(result.pos);
+
+        gclGatherData = gclGatherModel.gatherGclData();
+        gclGatherData = _.filter(gclGatherData, function (item) {
+            return item.leafXmjs && item.leafXmjs.length !== 0;
+        });
+        // 数组去重
+        dealBillList = result.dealBills;
+        changeListData = gclGatherData;
+        console.log(changeListData, dealBillList);
+
+        SpreadJsObj.initSpreadSettingEvents(changeSpreadSetting, changeCol);
+        SpreadJsObj.initSheet(changeSpreadSheet, changeSpreadSetting);
+        SpreadJsObj.loadSheetData(changeSpreadSheet, SpreadJsObj.DataType.Data, changeList);
+        changeSpreadObj.makeSjsFooter();
+    });
 });
 
 /**
@@ -211,3 +600,23 @@ function validateFiles(files) {
         return true
     })
 }
+
+function findDecimal(unit) {
+    let value = precision.other.value;
+    const changeUnits = precision;
+    for (const d in changeUnits) {
+        if (changeUnits[d].unit !== undefined && changeUnits[d].unit === unit) {
+            value = changeUnits[d].value;
+            break;
+        }
+    }
+    return value;
+}
+
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};

+ 0 - 8
app/public/js/change_plan_information.js

@@ -825,11 +825,3 @@ const is_numeric = (value) => {
         return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
     }
 };
-
-function getPasteHint (str, row = '') {
-    let returnObj = str;
-    if (row) {
-        returnObj.msg = '清单第' + (row+1) + '行' + str.msg;
-    }
-    return returnObj;
-}

+ 1 - 0
app/router.js

@@ -487,6 +487,7 @@ module.exports = app => {
     app.post('/tender/:id/change/apply/:caid/information/audit/start', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.startApplyAudit');
     app.post('/tender/:id/change/apply/:caid/information/audit/check', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.checkApplyAudit');
     app.get('/tender/:id/change/apply/:caid/information/notice', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.applyInformationNotice');
+    app.post('/tender/:id/change/apply/:caid/information/list/save', sessionAuth, tenderCheck, uncheckTenderCheck, changeApplyCheck, 'changeController.saveApplyListsData');
     // 变更方案
     app.get('/tender/:id/change/plan', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.plan');
     app.get('/tender/:id/change/plan/status/:status', sessionAuth, tenderCheck, uncheckTenderCheck, 'changeController.planStatus');

+ 72 - 0
app/service/change_apply.js

@@ -258,6 +258,78 @@ module.exports = app => {
         }
 
         /**
+         * 获取变更方案金额
+         * @param {int} tenderId - 标段id
+         * @param {int} status - 状态
+         * @return {void}
+         */
+        async getTp(tenderId, status) {
+            if (this.ctx.tender.isTourist && status === 0) {
+                const sql5 = 'SELECT SUM(cast (total_price as decimal(18,6))) AS total_price FROM ?? WHERE tid = ?';
+                const sqlParam5 = [this.tableName, tenderId];
+                const result5 = await this.db.query(sql5, sqlParam5);
+                return result5[0].total_price ? result5[0].total_price : 0;
+            }
+            switch (status) {
+                case 0: // 包含你的所有变更令
+                    const sql =
+                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE a.tid = ? AND ' +
+                        '(a.uid = ? OR (a.status != ? AND a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid)) OR a.status = ?)';
+                    const sqlParam = [
+                        this.tableName,
+                        tenderId,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.status.uncheck,
+                        this.ctx.service.changeApplyAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        audit.status.checked,
+                    ];
+                    const result = await this.db.query(sql, sqlParam);
+                    return result[0].total_price ? result[0].total_price : 0;
+                case 1: // 待处理(你的)
+                    // return await this.ctx.service.changeAudit.count({
+                    //     tid: tenderId,
+                    //     uid: this.ctx.session.sessionUser.accountId,
+                    //     status: 2,
+                    // });
+                    const sql6 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? as a WHERE a.tid = ? AND (a.id in(SELECT b.caid FROM ?? as b WHERE b.tid = ? AND b.aid = ? AND b.status = ?) OR (a.uid = ? AND (a.status = ? OR a.status = ?)))';
+                    const sqlParam6 = [this.tableName, tenderId, this.ctx.service.changeApplyAudit.tableName, tenderId, this.ctx.session.sessionUser.accountId, audit.status.checking, this.ctx.session.sessionUser.accountId, audit.status.uncheck, audit.status.checkNo];
+                    const result6 = await this.db.query(sql6, sqlParam6);
+                    return result6[0].total_price ? result6[0].total_price : 0;
+                case 5: // 待上报(所有的)PS:取未上报,退回的变更立项
+                    const sql2 =
+                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
+                        // 'a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid) ' +
+                        'a.uid = ? AND a.tid = ? AND (a.status = ? OR a.status = ?)';
+                    const sqlParam2 = [
+                        this.tableName,
+                        // this.ctx.service.changePlanAudit.tableName,
+                        this.ctx.session.sessionUser.accountId,
+                        tenderId,
+                        audit.status.uncheck,
+                        audit.status.checkNo,
+                    ];
+                    const result2 = await this.db.query(sql2, sqlParam2);
+                    return result2[0].total_price ? result2[0].total_price : 0;
+                case 2: // 进行中(所有的)
+                case 4: // 终止(所有的)
+                    const sql3 =
+                        'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? AS a WHERE ' +
+                        'a.status = ? AND a.tid = ? AND (a.uid = ? OR a.id IN (SELECT b.caid FROM ?? AS b WHERE b.aid = ? AND a.times = b.times GROUP BY b.caid))';
+                    const sqlParam3 = [this.tableName, status, tenderId, this.ctx.session.sessionUser.accountId, this.ctx.service.changeApplyAudit.tableName, this.ctx.session.sessionUser.accountId];
+                    const result3 = await this.db.query(sql3, sqlParam3);
+                    return result3[0].total_price ? result3[0].total_price : 0;
+                case 3: // 已完成(所有的)
+                    const sql4 = 'SELECT SUM(cast (a.total_price as decimal(18,6))) AS total_price FROM ?? WHERE status = ? AND tid = ?';
+                    const sqlParam4 = [this.tableName, status, tenderId];
+                    const result4 = await this.db.query(sql4, sqlParam4);
+                    return result4[0].total_price ? result4[0].total_price : 0;
+                default:
+                    break;
+            }
+        }
+
+        /**
          * 保存变更信息
          * @param {int} postData - 表单提交的数据
          * @param {int} tenderId - 标段id

+ 202 - 0
app/service/change_apply_list.js

@@ -0,0 +1,202 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/8/14
+ * @version
+ */
+
+const audit = require('../const/audit');
+
+module.exports = app => {
+    class ChangeApplyList extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'change_apply_list';
+        }
+
+        /**
+         * 取出变更令清单列表,并按台账清单在前,空白清单在后排序
+         * @return {void}
+         */
+        async getList(caid, transaction = false) {
+            const sql = 'SELECT * FROM ?? WHERE `caid` = ? ORDER BY `id` asc';
+            const sqlParam = [this.tableName, caid];
+            return transaction ? await transaction.query(sql, sqlParam) : await this.db.query(sql, sqlParam);
+        }
+
+        /**
+         * 添加空白变更清单
+         * @return {void}
+         */
+        async add(data) {
+            if (!this.ctx.tender || !this.ctx.change) {
+                throw '数据错误';
+            }
+            const insertData = {
+                tid: this.ctx.tender.id,
+                caid: this.ctx.change.id,
+            };
+            const newData = this._.assign(insertData, data);
+            // 新增工料
+            const result = await this.db.insert(this.tableName, newData);
+            if (result.affectedRows === 0) {
+                throw '新增空白清单数据失败';
+            }
+            return await this.getDataById(result.insertId);
+        }
+
+        /**
+         * 批量添加空白变更清单
+         * @return {void}
+         */
+        async batchAdd(data) {
+            if (!this.ctx.tender || !this.ctx.change) {
+                throw '数据错误';
+            }
+            const num = data.num ? parseInt(data.num) : 0;
+            if (num < 1 || num > 100) {
+                throw '批量添加的空白清单数目不能小于1或大于100';
+            }
+            const insertArray = [];
+            for (let i = 0; i < num; i++) {
+                const insertData = {
+                    tid: this.ctx.tender.id,
+                    caid: this.ctx.change.id,
+                };
+                insertArray.push(insertData);
+            }
+            // 新增工料
+            const result = await this.db.insert(this.tableName, insertArray);
+            if (result.affectedRows !== num) {
+                throw '批量添加空白清单数据失败';
+            }
+            // 获取刚批量添加的所有list
+            for (let j = 0; j < num; j++) {
+                insertArray[j].id = result.insertId + j;
+            }
+            return insertArray;
+        }
+
+        /**
+         * 删除变更清单
+         * @param {int} id 清单id
+         * @return {void}
+         */
+        async del(ids) {
+            if (!this.ctx.tender || !this.ctx.change) {
+                throw '数据错误';
+            }
+            const transaction = await this.db.beginTransaction();
+            try {
+                // 判断是否可删
+                await transaction.delete(this.tableName, { id: ids });
+                // 重新算变更令总额
+                await this.calcCamountSum(transaction);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改变更清单
+         * @param {Object} data 工料内容
+         * @param {int} order 期数
+         * @return {void}
+         */
+        async save(data, order) {
+            if (!this.ctx.tender || !this.ctx.change) {
+                throw '数据错误';
+            }
+
+            const transaction = await this.db.beginTransaction();
+            try {
+                // const mb_id = data.mb_id;
+                // delete data.mb_id;
+                await transaction.update(this.tableName, data);
+                // await this.calcQuantityByML(transaction, mb_id);
+                await this.calcCamountSum(transaction);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        /**
+         * 修改变更清单 复制粘贴
+         * @param {Object} datas 修改内容
+         * @return {void}
+         */
+        async saveDatas(datas) {
+            if (!this.ctx.tender || !this.ctx.change) {
+                throw '数据错误';
+            }
+            // 判断是否可修改
+            // 判断t_type是否为费用
+            const transaction = await this.db.beginTransaction();
+            try {
+                const insertArray = [];
+                const updateArray = [];
+                for (const d of datas) {
+                    if (d.id) {
+                        updateArray.push(d);
+                    } else {
+                        d.tid = this.ctx.tender.id;
+                        d.caid = this.ctx.change.id;
+                        insertArray.push(d);
+                    }
+                }
+                if (insertArray.length > 0) await transaction.insert(this.tableName, insertArray);
+                if (updateArray.length > 0) await transaction.updateRows(this.tableName, updateArray);
+                await this.calcCamountSum(transaction);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async calcCamountSum(transaction, updateTpDecimal = false) {
+            // const sql = 'SELECT SUM(ROUND(`camount`*`unit_price`, )) as total_price FROM ?? WHERE cid = ?';
+            // const sqlParam = [this.tableName, this.change.cid];
+            // const tp = await transaction.queryOne(sql, sqlParam);
+            // 防止小数位不精确,采用取值计算
+            const sql = 'SELECT unit_price, camount FROM ?? WHERE caid = ?';
+            const sqlParam = [this.tableName, this.ctx.change.id];
+            const changeList = await transaction.query(sql, sqlParam);
+            let total_price = 0;
+            const tp_decimal = this.ctx.tender.info.decimal.tp;
+            for (const cl of changeList) {
+                total_price = this.ctx.helper.accAdd(total_price, this.ctx.helper.mul(cl.unit_price, cl.camount, tp_decimal));
+            }
+            const updateData = {
+                total_price,
+            };
+            // if (updateTpDecimal) {
+            //     updateData.tp_decimal = tp_decimal;
+            // }
+            const options = {
+                where: {
+                    id: this.ctx.change.id,
+                },
+            };
+            await transaction.update(this.ctx.service.changeApply.tableName, updateData, options);
+        }
+    }
+
+    return ChangeApplyList;
+};

+ 23 - 1
app/service/change_plan.js

@@ -55,10 +55,12 @@ module.exports = app => {
                     name,
                     quality: changeConst.quality.common.name,
                 };
+                let caid = null;
                 if (apply_code) {
                     const applyInfo = await this.ctx.service.changeApply.getDataByCondition({ tid: tenderId, code: apply_code });
                     console.log(applyInfo);
                     if (applyInfo) {
+                        caid = applyInfo.id;
                         change.org_name = applyInfo.org_name;
                         change.peg = applyInfo.peg;
                         change.new_code = applyInfo.new_code;
@@ -68,8 +70,9 @@ module.exports = app => {
                         change.quality = applyInfo.quality;
                         change.reason = applyInfo.reason;
                         change.content = applyInfo.content;
+                        change.total_price = applyInfo.total_price;
+                        change.list_rule = applyInfo.list_rule;
                     }
-                    // 清单也要同步
                 }
                 const operate = await this.transaction.insert(this.tableName, change);
 
@@ -77,6 +80,25 @@ module.exports = app => {
                     throw '新建变更令数据失败';
                 }
                 change.id = operate.insertId;
+                // 清单也要同步
+                if (caid) {
+                    const changeList = await this.ctx.service.changeApplyList.getList(caid);
+                    const insertArray = [];
+                    for (const c of changeList) {
+                        insertArray.push({
+                            tid: this.ctx.tender.id,
+                            cpid: operate.insertId,
+                            code: c.code,
+                            name: c.name,
+                            unit: c.unit,
+                            unit_price: c.unit_price,
+                            oamount: c.oamount,
+                            camount: c.camount,
+                            spamount: c.camount,
+                        });
+                    }
+                    if (insertArray.length > 0) await this.transaction.insert(this.ctx.service.changePlanList.tableName, insertArray);
+                }
                 // 先找出标段最近存在的变更令审批人的变更令info
                 const preChangeInfo = await this.getHaveAuditLastInfo(tenderId);
                 if (preChangeInfo) {

+ 5 - 2
app/view/change/apply.ejs

@@ -51,6 +51,9 @@
                         </div>
                     </div>
                 </div>
+                <div class="d-inline-block">
+                    <span class="ml-3">本页小计:<%- page_total %>元</span><span class="ml-3">合计:<%- tp %>元</span>
+                </div>
             </div>
             <% if (tender.user_id === uid) { %>
             <div class="ml-auto">
@@ -67,14 +70,14 @@
                     <thead>
                     <tr>
                         <th width="13%" id="sort_change">变更申请编号</th><th width="15%">变更工程名称</th>
-                        <th width="10%">创建时间</th><th width="15%">变更立项书</th><th width="13%">变更通知书</th>
+                        <th width="10%">变更金额</th><th width="15%">变更立项书</th><th width="13%">变更通知书</th>
                         <th width="7%">通知书发出人</th><th width="7%">审批状态</th><th width="13%">审批进度</th><th width="5%">操作</th>
                     </tr>
                     </thead>
                     <tbody id="changeList">
                     <% for (const c of changes) { %>
                         <tr><td><a href="/tender/<%- tender.id %>/change/apply/<%- c.id %>/information"><%- c.code %></a></td><td><%- c.name %></td>
-                            <td><%- ctx.helper.formatFullDate(c.in_time) %></td>
+                            <td style="text-align: right"><%- c.total_price %></td>
                             <td><%- c.project_code %></td>
                             <td><% if (c.notice_code) { %><a href="/tender/<%- tender.id %>/change/apply/<%- c.id %>/information/notice"><%- c.notice_code %></a><% } %></td>
                             <td><%- c.account_name %></td>

+ 117 - 91
app/view/change/apply_information.ejs

@@ -10,6 +10,11 @@
                 <div class="d-inline-block" id="change-apply-code">
                     <%- change.code %>
                 </div>
+                <% if (!change.readOnly) { %>
+                    <div class="d-inline-block">
+                        <a href="#shuliangguize" data-toggle="modal" data-target="#shuliangguize" class="btn btn-outline-primary btn-sm"><i class="fa fa-cog"></i></a>
+                    </div>
+                <% } %>
             </div>
             <div class="ml-auto" id="sp-btn">
                 <% if (ctx.change.status === auditConst.status.uncheck) { %>
@@ -38,109 +43,121 @@
     </div>
     <div class="content-wrap">
         <div class="c-body">
-            <div class="sjs-height-0">
-                <div class="col-xl-8 mx-auto">
-                    <h4 class="text-center py-2">工程变更申请书</h4>
-                    <table class="table table-bordered" id="apply-table">
-                        <tr>
-                            <th width="120" class="text-center" style="vertical-align: middle">变更申请编号<b class="text-danger">*&nbsp;</b></th>
-                            <td><input class="form-control form-control-sm" value="<%- change.code %>" data-name="code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="120" class="text-center" style="vertical-align: middle">变更工程名称<b class="text-danger">*&nbsp;</b></th>
-                            <td><input class="form-control form-control-sm" value="<%- change.name %>" data-name="name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
-                            <th width="140" class="text-center" style="vertical-align: middle">桩号</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.peg %>" data-name="peg" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">原设计图名称</th>
-                            <td><input class="form-control form-control-sm" value="<%- change.org_name %>" data-name="org_name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
-                            <th width="" class="text-center" style="vertical-align: middle">图号</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.new_code %>" data-name="new_code" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">变更设计名称</th>
-                            <td><input class="form-control form-control-sm" value="<%- change.design_name %>" data-name="design_name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
-                            <th width="" class="text-center" style="vertical-align: middle">变更图号</th>
-                            <td><input class="form-control form-control-sm" value="<%- change.c_new_code %>" data-name="c_new_code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">变更立项编号</th>
-                            <td><input class="form-control form-control-sm" value="<%- change.project_code %>" data-name="project_code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder="自动读取,没有就为空,可编辑"></td>
-                            <th width="" class="text-center" style="vertical-align: middle">原工程造价(元)</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.org_price %>" data-name="org_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">工程变更类别</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                            <th width="" class="text-center" style="vertical-align: middle">变更后工程造价(元)</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.change_price %>" data-name="change_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">工程变更性质</th>
-                            <% if (change.readOnly) { %>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.quality %>" data-name="quality" readonly placeholder=""></td>
-                            <% } else { %>
-                            <td><select class="form-control form-control-sm" name="quality" data-name="quality" <% if (change.readOnly) { %>readonly<% } %>>
-                                <% for (const q in changeConst.quality) { %>
-                                    <% const cQuality = changeConst.quality[q] %>
-                                    <option <% if (cQuality.name === change.quality) { %> selected<% } %>><%- cQuality.name %></option>
+            <div class="sjs-height-0" data-spy="scroll" data-target="#navbar-example">
+                <div class="row mx-0">
+                    <div class="col-xl-2" id="navbar-example">
+                        <ul class="nav flex-column nav-pills nav-stacked nav-padding sticky-top">
+                            <li class="nav-item"><a class="nav-link active" href="#xinxi">基本信息</a></li>
+                            <li class="nav-item"><a class="nav-link" href="#qingdan">变更清单</a></li>
+                            <li class="nav-item"><a class="nav-link" href="#fujian">附件</a></li>
+                        </ul>
+                    </div>
+                    <div class="col-xl-8 mx-auto">
+                        <h4 id="xinxi" class="text-center py-2">工程变更申请书</h4>
+                        <table class="table table-bordered" id="apply-table">
+                            <tr>
+                                <th width="120" class="text-center" style="vertical-align: middle">变更申请编号<b class="text-danger">*&nbsp;</b></th>
+                                <td><input class="form-control form-control-sm" value="<%- change.code %>" data-name="code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="120" class="text-center" style="vertical-align: middle">变更工程名称<b class="text-danger">*&nbsp;</b></th>
+                                <td><input class="form-control form-control-sm" value="<%- change.name %>" data-name="name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
+                                <th width="140" class="text-center" style="vertical-align: middle">桩号</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.peg %>" data-name="peg" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">原设计图名称</th>
+                                <td><input class="form-control form-control-sm" value="<%- change.org_name %>" data-name="org_name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
+                                <th width="" class="text-center" style="vertical-align: middle">图号</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.new_code %>" data-name="new_code" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">变更设计名称</th>
+                                <td><input class="form-control form-control-sm" value="<%- change.design_name %>" data-name="design_name" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
+                                <th width="" class="text-center" style="vertical-align: middle">变更图号</th>
+                                <td><input class="form-control form-control-sm" value="<%- change.c_new_code %>" data-name="c_new_code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">变更立项编号</th>
+                                <td><input class="form-control form-control-sm" value="<%- change.project_code %>" data-name="project_code" <% if (change.readOnly) { %>readonly<% } %> type="text" placeholder="自动读取,没有就为空,可编辑"></td>
+                                <th width="" class="text-center" style="vertical-align: middle">原工程造价(元)</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.org_price %>" data-name="org_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">工程变更类别</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.class %>" data-name="class" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                                <th width="" class="text-center" style="vertical-align: middle">变更后工程造价(元)</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.change_price %>" data-name="change_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">工程变更性质</th>
+                                <% if (change.readOnly) { %>
+                                    <td><input class="form-control form-control-sm" type="text" value="<%- change.quality %>" data-name="quality" readonly placeholder=""></td>
+                                <% } else { %>
+                                    <td><select class="form-control form-control-sm" name="quality" data-name="quality" <% if (change.readOnly) { %>readonly<% } %>>
+                                            <% for (const q in changeConst.quality) { %>
+                                                <% const cQuality = changeConst.quality[q] %>
+                                                <option <% if (cQuality.name === change.quality) { %> selected<% } %>><%- cQuality.name %></option>
+                                            <% } %>
+                                        </select></td>
                                 <% } %>
-                            </select></td>
-                            <% } %>
-                            <th width="" class="text-center" style="vertical-align: middle">工程造价增减(元)</th>
-                            <td><input class="form-control form-control-sm" type="text" value="<%- change.crease_price %>" data-name="crease_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">变更原因<b class="text-danger">*&nbsp;</b></th>
-                            <td colspan="3"><textarea class="form-control form-control-sm" data-name="reason" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.reason %></textarea></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">原设计情况描述</th>
-                            <td colspan="3"><textarea class="form-control form-control-sm" data-name="org_content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.org_content %></textarea></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">现场实际情况描述</th>
-                            <td colspan="3"><textarea class="form-control form-control-sm" data-name="site_content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.site_content %></textarea></td>
-                        </tr>
-                        <tr>
-                            <th width="" class="text-center" style="vertical-align: middle">变更内容</th>
-                            <td colspan="3"><textarea class="form-control form-control-sm" data-name="content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.content %></textarea></td>
-                        </tr>
-                    </table>
-                    <table class="table table-bordered">
-                        <thead>
-                        <tr>
-                            <th></th>
-                            <th>附件</th>
-                            <th>上传者</th>
-                            <th>资料类型</th>
-                            <th>上传时间</th>
-                            <th>操作</th>
-                        </tr>
-                        </thead>
-                        <tbody>
-                        <!--<tr>-->
+                                <th width="" class="text-center" style="vertical-align: middle">工程造价增减(元)</th>
+                                <td><input class="form-control form-control-sm" type="text" value="<%- change.crease_price %>" data-name="crease_price" <% if (change.readOnly) { %>readonly<% } %> placeholder=""></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">变更原因<b class="text-danger">*&nbsp;</b></th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="reason" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.reason %></textarea></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">原设计情况描述</th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="org_content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.org_content %></textarea></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">现场实际情况描述</th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="site_content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.site_content %></textarea></td>
+                            </tr>
+                            <tr>
+                                <th width="" class="text-center" style="vertical-align: middle">变更内容</th>
+                                <td colspan="3"><textarea class="form-control form-control-sm" data-name="content" <% if (change.readOnly) { %>readonly<% } %> rows="3"><%- change.content %></textarea></td>
+                            </tr>
+                        </table>
+                        <h5 id="qingdan">变更清单</h5>
+                        <div style="height: <%= 21*(changeList.length+3) + 100 %>px;min-height: 300px" id="apply-spread"></div>
+                        <h5 id="fujian">附件</h5>
+                        <table class="table table-bordered">
+                            <thead>
+                            <tr>
+                                <th></th>
+                                <th>附件</th>
+                                <th>上传者</th>
+                                <th>资料类型</th>
+                                <th>上传时间</th>
+                                <th>操作</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            <!--<tr>-->
                             <!--<td colspan="5"><button type="button" class="btn btn-primary btn-sm"  data-toggle="modal" data-target="#upload-fj">上传附件</button></td>-->
-                        <!--</tr>-->
-                        <tbody id="file-content">
-                        </tbody>
-                        <!--<tr>-->
+                            <!--</tr>-->
+                            <tbody id="file-content">
+                            </tbody>
+                            <!--<tr>-->
                             <!--<td>1</td>-->
                             <!--<td>XXX设计图纸</td>-->
                             <!--<td>仁温书</td>-->
                             <!--<td>2021-12-09 16:58:47</td>-->
                             <!--<td><a href="#" class="mr-2"><i class="fa fa-download"></i></a><a href="#" class="text-danger"><i class="fa fa-remove"></i></a></td>-->
-                        <!--</tr>-->
-                        <!--<tr>-->
+                            <!--</tr>-->
+                            <!--<tr>-->
                             <!--<td>1</td>-->
                             <!--<td>XXX资料说明</td>-->
                             <!--<td>仁温书</td>-->
                             <!--<td>2021-12-09 16:58:47</td>-->
                             <!--<td><a href="#" class="mr-2"><i class="fa fa-download"></i></a><a href="#" class="text-danger"><i class="fa fa-remove"></i></a></td>-->
-                        <!--</tr>-->
-                        </tbody>
-                    </table>
+                            <!--</tr>-->
+                            </tbody>
+                        </table>
+                    </div>
                 </div>
             </div>
         </div>
@@ -154,4 +171,13 @@
     const whiteList = JSON.parse('<%- JSON.stringify(whiteList) %>');
     const preUrl = '<%- preUrl %>';
     const change = JSON.parse(unescape('<%- escape(JSON.stringify(change)) %>'));
+    let listRule = JSON.parse(unescape('<%- escape(JSON.stringify(listRule)) %>'));
+    const readOnly = <%- change.readOnly %>;
+    const totalPriceUnit = '<%- tpUnit %>';
+    const unitPriceUnit = '<%- upUnit %>';
+    const precision = JSON.parse('<%- JSON.stringify(precision) %>');
+    let changeUnits = JSON.parse('<%- JSON.stringify(changeUnits) %>');
+    changeUnits = _.map(changeUnits, 'unit');
+    changeUnits.push('');
+    let changeList = JSON.parse(unescape('<%- escape(JSON.stringify(changeList)) %>'));
 </script>

+ 50 - 0
app/view/change/apply_information_modal.ejs

@@ -96,6 +96,56 @@
             </div>
         </div>
     </div>
+    <!-- 数量读取规则 -->
+    <div class="modal fade" id="shuliangguize" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">原设计数量读取规则</h5>
+                </div>
+                <div class="modal-body">
+                    <div class="form-group">
+                        <label for="">数据来源</label>
+                        <div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="radio" name="data_source" id="gclGather" value="1" <% if(listRule.source === 1) { %>checked<% } %>>
+                                <label class="form-check-label" for="gclGather">台账数量</label>
+                            </div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="radio" name="data_source" id="dealBills" value="2" <% if(listRule.source === 2) { %>checked<% } %>>
+                                <label class="form-check-label" for="dealBills">签约数量</label>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <label for="">匹配规则</label>
+                        <div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="rule_code" value="code" disabled checked>
+                                <label class="form-check-label" for="rule_code">编号</label>
+                            </div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="rule_code" value="name" disabled checked>
+                                <label class="form-check-label" for="rule_code">名称</label>
+                            </div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" name="data_rule" type="checkbox" id="rule_unit" value="unit" <% if(ctx.helper._.indexOf(listRule.rule, 'unit') !== -1) { %>checked<% } %>>
+                                <label class="form-check-label" for="rule_unit">单位</label>
+                            </div>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" name="data_rule" type="checkbox" id="rule_unitprice" value="unit_price" <% if(ctx.helper._.indexOf(listRule.rule, 'unit_price') !== -1) { %>checked<% } %>>
+                                <label class="form-check-label" for="rule_unitprice">单价</label>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-primary btn-sm" id="setListRule">确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
 <% } %>
 
 <!--审批流程/结果-->

+ 1 - 1
app/view/change/plan_information.ejs

@@ -10,7 +10,7 @@
                 <div class="d-inline-block" id="change-plan-code">
                     <%- change.code %>
                 </div>
-                <% if (change.status === auditConst.status.uncheck || change.status === auditConst.status.checkNo) { %>
+                <% if (!change.readOnly) { %>
                 <div class="d-inline-block">
                     <a href="#shuliangguize" data-toggle="modal" data-target="#shuliangguize" class="btn btn-outline-primary btn-sm"><i class="fa fa-cog"></i></a>
                 </div>

+ 7 - 1
config/web.js

@@ -926,8 +926,14 @@ const JsFiles = {
                 mergeFile: 'change_apply',
             },
             apply_information: {
-                files: ['/public/js/moment/moment.min.js'],
+                files: ['/public/js/moment/moment.min.js', '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js', '/public/js/decimal.min.js'],
                 mergeFiles: [
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/gcl_gather.js',
                     '/public/js/sub_menu.js',
                     '/public/js/change_apply_audit.js',
                     '/public/js/change_apply_information.js',

+ 6 - 1
sql/update.sql

@@ -139,4 +139,9 @@ CREATE TABLE `zh_change_project_xs_audit`  (
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `cpid`(`cpid`) USING BTREE,
   INDEX `tid`(`tid`) USING BTREE
-) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '立项协审人' ROW_FORMAT = Dynamic;
+) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '立项协审人' ROW_FORMAT = Dynamic;
+
+
+ALTER TABLE `zh_change_apply`
+ADD `total_price` DECIMAL(30,8) NULL DEFAULT NULL COMMENT '金额' AFTER `content`,
+ADD `list_rule` VARCHAR(255) NULL DEFAULT NULL AFTER `total_price` COMMENT '原设计数量读取规则';