Ver código fonte

一些bug修复和形象进度内容

laiguoran 4 anos atrás
pai
commit
73f56c7f97

+ 33 - 0
app/controller/schedule_controller.js

@@ -179,6 +179,39 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        /**
+         * 计量进度工程量模式计算方式提交(Ajax)
+         *
+         * @param ctx
+         * @return {Promise<void>}
+         */
+        async saveStageGcl(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = {
+                    err: 0,
+                    msg: '',
+                    data: {},
+                };
+                switch (data.type) {
+                    case 'add_stage':
+                        responseData.data = await ctx.service.scheduleMonth.addStageUsed(data.postData);
+                        break;
+                    case 'del_stage':
+                        responseData.data = await ctx.service.scheduleMonth.delStageUsed(data.postData);
+                        break;
+                    case 'ledger_edit':
+                        responseData.data = await ctx.service.scheduleLedgerMonth.saveSj(data.postData);
+                        break;
+                    default: throw '参数有误';
+                }
+                ctx.body = responseData;
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
     }
 
     return ScheduleController;

+ 54 - 63
app/public/js/schedule_plan.js

@@ -53,7 +53,7 @@ $(function () {
     const monthsCols = [];
     if(scheduleMonth.length > 0) {
         for (const sm of scheduleMonth) {
-            const readOnly = sm.sj_gcl !== null || sm.sj_tp !== null;
+            const readOnly = sm.stage_used !== 0;
             const yearmonth = sm.yearmonth.split('-')[0] + '年' + parseInt(sm.yearmonth.split('-')[1]) + '月';
             const cols = {title: yearmonth + '|计划工程量', colSpan: '2|1', rowSpan: '1|1', field: sm.yearmonth+'_gcl', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.gcl'};
             const cols2 = {title: '|计划金额(万元)', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_tp', hAlign: 2, width: 90, type: 'Number', readOnly: readOnly ? readOnly : 'readOnly.tp'};
@@ -256,84 +256,75 @@ $(function () {
             const hint = {
                 cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
                 numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
-                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
             };
             const range = info.cellRange;
-            const sortData = info.sheet.zh_data || [];
-            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+            if (range.rowCount > 1 || range.colCount > 1) {
                 toastMessageUniq(hint.cellError);
-                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
-                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetHeader(ledgerSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
                 return;
             }
-            if (sortData.length > 0 && range.col + range.colCount > 4 + months.length) {
-                toastMessageUniq(hint.cellError);
-                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
-                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+            const select = SpreadJsObj.getSelectObject(info.sheet);
+            const col = info.sheet.zh_setting.cols[range.col];
+            const validText = is_numeric(info.pasteData.text) ? parseFloat(info.pasteData.text) : (info.pasteData.text ? trimInvalidChar(info.pasteData.text) : null);
+            const orgValue = select[col.field];
+            if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
                 return;
             }
-            const data = [];
-            for (let iRow = 0; iRow < range.rowCount; iRow++) {
-                let bPaste = true;
-                const curRow = range.row + iRow;
-                const materialMonthData = sortData[curRow];
-                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;
-
-                    let validText = info.sheet.getText(curRow, curCol);
-                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
-                    const orgValue = sortData[curRow][colSetting.field];
-                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
-                        sameCol++;
-                        if (range.colCount === sameCol)  {
-                            bPaste = false;
-                        }
-                        continue;
-                    }
-                    const num = parseFloat(validText);
-                    if (isNaN(validText)) {
-                        toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
-                        bPaste = false;
-                        continue;
-                    }
-                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
-                        toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
-                        bPaste = false;
-                        continue;
-                    }
-                    materialMonthData[colSetting.field] = validText;
-                    sortData[curRow][colSetting.field] = validText;
+            if (isNaN(validText)) {
+                toastr.error('不能粘贴其它非数字类型字符');
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            const yearmonth = col.field.split('_')[0];
+            const mode = col.field.split('_')[1];
+            let plan_gcl = 0;
+            let plan_tp = 0;
+            // 判断输入位数,提示
+            if (mode === 'tp') {
+                const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.tp) +'})?$');
+                if (validText !== null && (!reg.test(validText))) {
+                    toastr.error('粘贴的金额小数位数不能大于' + tenderInfo.decimal.tp + '位');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    return;
                 }
-                if (bPaste) {
-                    data.push(materialMonthData);
-                } else {
-                    SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                plan_gcl = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.div(validText, select.dgn_price), tenderInfo.decimal.up) : 0;
+                plan_tp = validText;
+            } else {
+                const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
+                if (validText !== null && (!reg.test(validText))) {
+                    toastr.error('粘贴的工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                    return;
                 }
+                plan_gcl = validText;
+                plan_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
             }
-            if (data.length === 0) {
+            select[col.field] = validText;
+            const updateData = {
+                lid: select.ledger_id,
+                yearmonth,
+                plan_gcl,
+                plan_tp,
+            };
+            postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
+                if (mode === 'tp') {
+                    select[yearmonth + '_gcl'] = plan_gcl;
+                } else {
+                    select[yearmonth + '_tp'] = plan_tp;
+                }
                 SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-                return;
-            }
-            // // 更新至服务器
-            // postData(window.location.pathname + '/month/save', { type:'paste', updateData: data }, function (result) {
-            //     SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
-            //     materialBillsData = result.materialBillsData;
-            //     SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
-            //     m_tp = result.m_tp;
-            //     resetTpTable();
-            // }, function () {
-            //     SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
-            //     return;
-            // });
+            },function () {
+                select[col.field] = orgValue;
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+            })
         },
     };
 
     ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
     SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
+    ledgerSpread.bind(spreadNS.Events.ClipboardPasted, ledgerSpreadObj.clipboardPasted);
 
     // 进度计算方式选择
     $('.mode-select').on('click', function () {

+ 405 - 0
app/public/js/schedule_stage_gcl.js

@@ -0,0 +1,405 @@
+/**
+ * 进度台账相关js
+ *
+ * @author Ellisran
+ * @date 2020/11/6
+ * @version
+ */
+function getTenderId() {
+    return window.location.pathname.split('/')[2];
+}
+$(function () {
+    autoFlashHeight();
+    if (monthList.length === 0 && selectedLedgerList.length !== 0) {
+        $('#second').modal('show');
+    }
+    // 初始化台账
+    const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
+    const treeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        fullPath: 'full_path',
+        //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
+        // markFoldKey: 'bills-fold',
+        // markFoldSubKey: window.location.pathname.split('/')[2],
+    };
+    const ledgerTree = createNewPathTree('filter', treeSetting);
+
+    const static_cols = [
+        {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
+        {title: '经济指标', colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+        {title: '总设计|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+    ];
+
+    const ledgerSpreadSetting = {
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        // readOnly: true,
+        localCache: {
+            key: 'ledger-bills',
+            colWidth: true,
+        }
+    };
+    const monthsCols = [];
+    if(scheduleMonth.length > 0) {
+        for (const sm of scheduleMonth) {
+            if (sm.stage_used === 1) {
+                const yearmonth = sm.yearmonth.split('-')[0] + '年' + parseInt(sm.yearmonth.split('-')[1]) + '月';
+                const cols = {title: yearmonth + '|计划工程量', colSpan: '4|1', rowSpan: '1|1', field: sm.yearmonth+'_plan_gcl', hAlign: 2, width: 90, type: 'Number', readOnly: true};
+                const cols2 = {title: '|计划金额(万元)', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_plan_tp', hAlign: 2, width: 90, type: 'Number', readOnly: true};
+                const cols3 = {title: '|计量工程量', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_sj_gcl', hAlign: 2, width: 90, type: 'Number', readOnly: 'readOnly.gcl'};
+                const cols4 = {title: '|计量金额(万元)', colSpan: '|1', rowSpan: '|1', field: sm.yearmonth+'_sj_tp', hAlign: 2, width: 90, type: 'Number', readOnly : true};
+                monthsCols.push(cols);
+                monthsCols.push(cols2);
+                monthsCols.push(cols3);
+                monthsCols.push(cols4);
+            }
+        }
+    }
+    const spreadHeaderCols = static_cols.concat(monthsCols);
+    ledgerSpreadSetting.cols = spreadHeaderCols;
+
+    const ledgerCol = {
+        readOnly: {
+            gcl: function (data) {
+                return !data.is_leaf;
+            },
+        },
+    };
+
+    sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    SpreadJsObj.initSpreadSettingEvents(ledgerSpreadSetting, ledgerCol);
+    SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
+    SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
+
+    postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
+        // let treeData = [];
+        // for(const sl of selectedLedgerList) {
+        //     const one = _.find(data, { 'ledger_id' : sl });
+        //     treeData.push(one);
+        // }
+        // treeData = setLeafData(treeData);
+        // console.log(treeData);
+        // let treeData = data;
+        const calcList = ['total_price'];
+        const showList = ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price'];
+        for (const m of scheduleMonth) {
+            if (m.stage_used === 1) {
+                showList.push(m.yearmonth + '_plan_tp');
+                showList.push(m.yearmonth + '_plan_gcl');
+                showList.push(m.yearmonth + '_sj_tp');
+                showList.push(m.yearmonth + '_sj_gcl');
+            }
+            // calcList.push(m + '_tp');
+            // calcList.push(m + '_gcl');
+        }
+        const baseLedgerTree = createNewPathTree('base', {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+            calcFields: calcList,
+            calcFun: function (node) {
+                node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            }
+        });
+        const newLedgerList = setMonthToLedger(data.bills, data.slm);
+        baseLedgerTree.loadDatas(newLedgerList);
+        treeCalc.calculateAll(baseLedgerTree);
+        console.log(baseLedgerTree);
+        for (const d of baseLedgerTree.nodes) {
+            if (!d.b_code) {
+                const one = _.find(selectedLedgerList, function (item) {
+                    return item === d.ledger_id;
+                });
+                if(one) {
+                    ledgerTree.addData(d, showList);
+                }
+            }
+        }
+        console.log(ledgerTree);
+        ledgerTree.sortTreeNode(true);
+        // console.log(ledgerTree);
+        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
+    }, null, true);
+
+    const ledgerSpreadObj = {
+        refreshTree: function (sheet, data) {
+            SpreadJsObj.massOperationSheet(sheet, function () {
+                const tree = sheet.zh_tree;
+                // 处理删除
+                if (data.delete) {
+                    data.delete.sort(function (x, y) {
+                        return y.deleteIndex - x.deleteIndex;
+                    });
+                    for (const d of data.delete) {
+                        sheet.deleteRows(d.deleteIndex, 1);
+                    }
+                }
+                // 处理新增
+                if (data.create) {
+                    const newNodes = data.create;
+                    if (newNodes) {
+                        newNodes.sort(function (a, b) {
+                            return a.index - b.index;
+                        });
+
+                        for (const node of newNodes) {
+                            sheet.addRows(node.index, 1);
+                            SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
+                        }
+                    }
+                }
+                // 处理更新
+                if (data.update) {
+                    const rows = [];
+                    for (const u of data.update) {
+                        rows.push(tree.nodes.indexOf(u));
+                    }
+                    SpreadJsObj.reLoadRowsData(sheet, rows);
+                }
+                // 处理展开
+                if (data.expand) {
+                    const expanded = [];
+                    for (const e of data.expand) {
+                        if (expanded.indexOf(e) === -1) {
+                            const posterity = tree.getPosterity(e);
+                            for (const p of posterity) {
+                                sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
+                                expanded.push(p);
+                            }
+                        }
+                    }
+                }
+            });
+        },
+        editEnded: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                const orgValue = select[col.field];
+                if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                if (isNaN(validText)) {
+                    toastr.error('不能输入其它非数字类型字符');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const yearmonth = col.field.split('_')[0];
+                // 判断输入位数,提示
+                const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
+                if (validText !== null && (!reg.test(validText))) {
+                    toastr.error('输入工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const sj_gcl = validText;
+                const sj_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
+                select[col.field] = validText;
+                const updateData = {
+                    lid: select.ledger_id,
+                    yearmonth,
+                    sj_gcl,
+                    sj_tp,
+                };
+                console.log(updateData);
+                postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
+                    select[yearmonth + '_sj_tp'] = sj_tp;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                },function () {
+                    select[col.field] = orgValue;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                })
+            }
+        },
+        deletePress: function (sheet) {
+            return;
+        },
+        clipboardPasted(e, info) {
+            const hint = {
+                cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+            };
+            const range = info.cellRange;
+            if (range.rowCount > 1 || range.colCount > 1) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(ledgerSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(ledgerSpread.getActiveSheet());
+                return;
+            }
+            const select = SpreadJsObj.getSelectObject(info.sheet);
+            const col = info.sheet.zh_setting.cols[range.col];
+            const validText = is_numeric(info.pasteData.text) ? parseFloat(info.pasteData.text) : (info.pasteData.text ? trimInvalidChar(info.pasteData.text) : null);
+            const orgValue = select[col.field];
+            if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (isNaN(validText)) {
+                toastr.error('不能粘贴其它非数字类型字符');
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            const yearmonth = col.field.split('_')[0];
+            // 判断输入位数,提示
+            const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
+            if (validText !== null && (!reg.test(validText))) {
+                toastr.error('粘贴的工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            const sj_gcl = validText;
+            const sj_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
+            select[col.field] = validText;
+            const updateData = {
+                lid: select.ledger_id,
+                yearmonth,
+                sj_gcl,
+                sj_tp,
+            };
+            console.log(updateData);
+            postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
+                select[yearmonth + '_sj_tp'] = sj_tp;
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+            },function () {
+                select[col.field] = orgValue;
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+            })
+        },
+    };
+
+    ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
+    SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
+    ledgerSpread.bind(spreadNS.Events.ClipboardPasted, ledgerSpreadObj.clipboardPasted);
+
+    // 月份添加
+    $('#add-month').click(function () {
+        const id = parseInt($('#month-select').val());
+        if(id === 0) {
+            toastr.error('请选择计划进度月');
+            return;
+        }
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'add_stage', postData: { id: id }}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('创建成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+        })
+    });
+
+    $('#month-table input[type="checkbox"]').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push('「' + $(this).parents('td').siblings('td').text() + '」');
+        });
+        if(selectedMonth.length > 0) {
+            $('#del-month-list').text(selectedMonth.join(''));
+            $('#del-month-list').parent().show();
+            $('#del-month').removeAttr('disabled');
+        } else {
+            $('#del-month-list').parent().hide();
+            $('#del-month').attr('disabled', true);
+        }
+    });
+
+    $('#del-month').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push($(this).parents('td').siblings().text());
+        });
+        if (selectedMonth.length === 0) {
+            toastr.error('请选择删除的计划周期');
+            return;
+        }
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'del_stage', postData: selectedMonth}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('删除成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+
+        })
+    });
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            ledgerSpread.refresh();
+            autoFlashHeight();
+        }
+    });
+
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            ledgerSpread.refresh();
+            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+            $(".tab-content").height(bcontent-30);
+            // posSpread.refresh();
+        }
+    });
+});
+function setLeafData(tree) {
+    const newtree = [];
+    for (const t of tree) {
+        const child = _.find(tree, { 'ledger_pid': t.ledger_id });
+        if (!child && !t.is_leaf) {
+            t.is_leaf = true;
+        }
+        newtree.push(t);
+    }
+    return newtree;
+}
+
+function setMonthToLedger(ledgerList, slm) {
+    if (slm.length > 0) {
+        for(const s of slm) {
+            const index = _.findIndex(ledgerList, { 'ledger_id': s.lid });
+            const canCalc = _.find(scheduleMonth, { 'yearmonth': s.yearmonth, 'stage_used': 1});
+            if (index && index !== -1 && canCalc) {
+                ledgerList[index][s.yearmonth + '_plan_tp'] = s.plan_tp;
+                ledgerList[index][s.yearmonth + '_plan_gcl'] = s.plan_gcl;
+                ledgerList[index][s.yearmonth + '_sj_tp'] = s.sj_tp;
+                ledgerList[index][s.yearmonth + '_sj_gcl'] = s.sj_gcl;
+            }
+        }
+    }
+    return ledgerList;
+}
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};

+ 409 - 0
app/public/js/schedule_stage_tp.js

@@ -0,0 +1,409 @@
+/**
+ * 进度台账相关js
+ *
+ * @author Ellisran
+ * @date 2020/11/6
+ * @version
+ */
+function getTenderId() {
+    return window.location.pathname.split('/')[2];
+}
+$(function () {
+    autoFlashHeight();
+    if (monthList.length === 0 && selectedLedgerList.length !== 0) {
+        $('#second').modal('show');
+    }
+    // 初始化台账
+    const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
+    const treeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        fullPath: 'full_path',
+        //treeCacheKey: 'ledger_bills_fold' + '_' + getTenderId(),
+        // markFoldKey: 'bills-fold',
+        // markFoldSubKey: window.location.pathname.split('/')[2],
+    };
+    const ledgerTree = createNewPathTree('filter', treeSetting);
+
+    const static_cols = [
+        {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
+        {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
+        {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', readOnly: true},
+        {title: '经济指标', colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+        {title: '总设计|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '自开工至本月计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '截止本期完成计量|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本年计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本年累计完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本月计划完成|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '本期完成计量|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '下月计划|工程量', colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+        {title: '|金额(万元)', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 70, type: 'Number', readOnly: true},
+    ];
+
+    const ledgerSpreadSetting = {
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        // readOnly: true,
+        localCache: {
+            key: 'ledger-bills',
+            colWidth: true,
+        }
+    };
+    ledgerSpreadSetting.cols = static_cols;
+
+    sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
+    SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
+    SpreadJsObj.selChangedRefreshBackColor(ledgerSpread.getActiveSheet());
+
+    postData('/tender/' + getTenderId() + '/schedule/ledger/load', {}, function (data) {
+        // let treeData = [];
+        // for(const sl of selectedLedgerList) {
+        //     const one = _.find(data, { 'ledger_id' : sl });
+        //     treeData.push(one);
+        // }
+        // treeData = setLeafData(treeData);
+        // console.log(treeData);
+        // let treeData = data;
+        const calcList = ['total_price'];
+        const showList = ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'dgn_price', 'quantity', 'total_price'];
+        for (const m of scheduleMonth) {
+            if (m.stage_used === 1) {
+                showList.push(m.yearmonth + '_plan_tp');
+                showList.push(m.yearmonth + '_plan_gcl');
+                showList.push(m.yearmonth + '_sj_tp');
+                showList.push(m.yearmonth + '_sj_gcl');
+            }
+            // calcList.push(m + '_tp');
+            // calcList.push(m + '_gcl');
+        }
+        const baseLedgerTree = createNewPathTree('base', {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+            calcFields: calcList,
+            calcFun: function (node) {
+                node.dgn_price = ZhCalc.round(ZhCalc.div(node.total_price, node.dgn_qty1), 2);
+            }
+        });
+        const newLedgerList = setMonthToLedger(data.bills, data.slm);
+        baseLedgerTree.loadDatas(newLedgerList);
+        treeCalc.calculateAll(baseLedgerTree);
+        console.log(baseLedgerTree);
+        for (const d of baseLedgerTree.nodes) {
+            if (!d.b_code) {
+                const one = _.find(selectedLedgerList, function (item) {
+                    return item === d.ledger_id;
+                });
+                if(one) {
+                    ledgerTree.addData(d, showList);
+                }
+            }
+        }
+        console.log(ledgerTree);
+        ledgerTree.sortTreeNode(true);
+        // console.log(ledgerTree);
+        SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), SpreadJsObj.DataType.Tree, ledgerTree);
+    }, null, true);
+
+    const ledgerSpreadObj = {
+        refreshTree: function (sheet, data) {
+            SpreadJsObj.massOperationSheet(sheet, function () {
+                const tree = sheet.zh_tree;
+                // 处理删除
+                if (data.delete) {
+                    data.delete.sort(function (x, y) {
+                        return y.deleteIndex - x.deleteIndex;
+                    });
+                    for (const d of data.delete) {
+                        sheet.deleteRows(d.deleteIndex, 1);
+                    }
+                }
+                // 处理新增
+                if (data.create) {
+                    const newNodes = data.create;
+                    if (newNodes) {
+                        newNodes.sort(function (a, b) {
+                            return a.index - b.index;
+                        });
+
+                        for (const node of newNodes) {
+                            sheet.addRows(node.index, 1);
+                            SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
+                        }
+                    }
+                }
+                // 处理更新
+                if (data.update) {
+                    const rows = [];
+                    for (const u of data.update) {
+                        rows.push(tree.nodes.indexOf(u));
+                    }
+                    SpreadJsObj.reLoadRowsData(sheet, rows);
+                }
+                // 处理展开
+                if (data.expand) {
+                    const expanded = [];
+                    for (const e of data.expand) {
+                        if (expanded.indexOf(e) === -1) {
+                            const posterity = tree.getPosterity(e);
+                            for (const p of posterity) {
+                                sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
+                                expanded.push(p);
+                            }
+                        }
+                    }
+                }
+            });
+        },
+        editEnded: function (e, info) {
+            if (info.sheet.zh_setting) {
+                const select = SpreadJsObj.getSelectObject(info.sheet);
+                const col = info.sheet.zh_setting.cols[info.col];
+                const validText = is_numeric(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
+                const orgValue = select[col.field];
+                if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                if (isNaN(validText)) {
+                    toastr.error('不能输入其它非数字类型字符');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const yearmonth = col.field.split('_')[0];
+                // 判断输入位数,提示
+                const reg = new RegExp('^([-]?)\\d+(\\.\\d{0,'+ parseInt(tenderInfo.decimal.up) +'})?$');
+                if (validText !== null && (!reg.test(validText))) {
+                    toastr.error('输入工程量小数位数不能大于' + tenderInfo.decimal.up + '位');
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                    return;
+                }
+                const sj_gcl = validText;
+                const sj_tp = select.dgn_price && select.dgn_price !== 0 ? ZhCalc.round(ZhCalc.mul(validText, select.dgn_price), tenderInfo.decimal.tp) : 0;
+                select[col.field] = validText;
+                const updateData = {
+                    lid: select.ledger_id,
+                    yearmonth,
+                    sj_gcl,
+                    sj_tp,
+                };
+                console.log(updateData);
+                postData(window.location.pathname + '/save', {type: 'ledger_edit', postData: updateData}, function (result) {
+                    select[yearmonth + '_sj_tp'] = sj_tp;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                },function () {
+                    select[col.field] = orgValue;
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                })
+            }
+        },
+        deletePress: function (sheet) {
+            return;
+        },
+        clipboardPasted(e, info) {
+            const hint = {
+                cellError: {type: 'error', msg: '粘贴内容超出了表格范围'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+            };
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            if (sortData.length > 0 && range.col + range.colCount > 4 + months.length) {
+                toastMessageUniq(hint.cellError);
+                SpreadJsObj.reLoadSheetHeader(materialMonthSpread.getActiveSheet());
+                SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+                return;
+            }
+            const data = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                const materialMonthData = sortData[curRow];
+                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;
+
+                    let validText = info.sheet.getText(curRow, curCol);
+                    validText = is_numeric(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    const orgValue = sortData[curRow][colSetting.field];
+                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                        sameCol++;
+                        if (range.colCount === sameCol)  {
+                            bPaste = false;
+                        }
+                        continue;
+                    }
+                    const num = parseFloat(validText);
+                    if (isNaN(validText)) {
+                        toastMessageUniq(getPasteHint(hint.numberExpr, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
+                        toastMessageUniq(getPasteHint(hint.numberCan, hintRow));
+                        bPaste = false;
+                        continue;
+                    }
+                    materialMonthData[colSetting.field] = validText;
+                    sortData[curRow][colSetting.field] = validText;
+                }
+                if (bPaste) {
+                    data.push(materialMonthData);
+                } else {
+                    SpreadJsObj.reLoadRowData(info.sheet, curRow);
+                }
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            // // 更新至服务器
+            // postData(window.location.pathname + '/month/save', { type:'paste', updateData: data }, function (result) {
+            //     SpreadJsObj.reLoadSheetData(materialMonthSpread.getActiveSheet());
+            //     materialBillsData = result.materialBillsData;
+            //     SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+            //     m_tp = result.m_tp;
+            //     resetTpTable();
+            // }, function () {
+            //     SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+            //     return;
+            // });
+        },
+    };
+
+    ledgerSpread.bind(spreadNS.Events.EditEnded, ledgerSpreadObj.editEnded);
+    SpreadJsObj.addDeleteBind(ledgerSpread, ledgerSpreadObj.deletePress);
+
+    // 月份添加
+    $('#add-month').click(function () {
+        const id = parseInt($('#month-select').val());
+        if(id === 0) {
+            toastr.error('请选择计划进度月');
+            return;
+        }
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'add_stage', postData: { id: id }}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('创建成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+        })
+    });
+
+    $('#month-table input[type="checkbox"]').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push('「' + $(this).parents('td').siblings('td').text() + '」');
+        });
+        if(selectedMonth.length > 0) {
+            $('#del-month-list').text(selectedMonth.join(''));
+            $('#del-month-list').parent().show();
+            $('#del-month').removeAttr('disabled');
+        } else {
+            $('#del-month-list').parent().hide();
+            $('#del-month').attr('disabled', true);
+        }
+    });
+
+    $('#del-month').click(function () {
+        const selectedMonth = [];
+        $('#month-table input:checkbox:checked').each(function () {
+            selectedMonth.push($(this).parents('td').siblings().text());
+        });
+        if (selectedMonth.length === 0) {
+            toastr.error('请选择删除的计划周期');
+            return;
+        }
+        const _self = $(this);
+        postData(window.location.pathname + '/save', {type: 'del_stage', postData: selectedMonth}, function (result) {
+            _self.addClass('disabled').attr('disabled', true);
+            toastr.success('删除成功');
+            setTimeout(function () {
+                window.location.reload();
+            }, 500)
+
+        })
+    });
+
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            ledgerSpread.refresh();
+            autoFlashHeight();
+        }
+    });
+});
+function setLeafData(tree) {
+    const newtree = [];
+    for (const t of tree) {
+        const child = _.find(tree, { 'ledger_pid': t.ledger_id });
+        if (!child && !t.is_leaf) {
+            t.is_leaf = true;
+        }
+        newtree.push(t);
+    }
+    return newtree;
+}
+
+function setMonthToLedger(ledgerList, slm) {
+    if (slm.length > 0) {
+        for(const s of slm) {
+            const index = _.findIndex(ledgerList, { 'ledger_id': s.lid });
+            const canCalc = _.find(scheduleMonth, { 'yearmonth': s.yearmonth, 'stage_used': 1});
+            if (index && index !== -1 && canCalc) {
+                ledgerList[index][s.yearmonth + '_plan_tp'] = s.plan_tp;
+                ledgerList[index][s.yearmonth + '_plan_gcl'] = s.plan_gcl;
+                ledgerList[index][s.yearmonth + '_sj_tp'] = s.sj_tp;
+                ledgerList[index][s.yearmonth + '_sj_gcl'] = s.sj_gcl;
+            }
+        }
+    }
+    return ledgerList;
+}
+const is_numeric = (value) => {
+    if (typeof(value) === 'object') {
+        return false;
+    } else {
+        return !Number.isNaN(Number(value)) && value.toString().trim() !== '';
+    }
+};

+ 6 - 5
app/router.js

@@ -112,11 +112,11 @@ module.exports = app => {
     app.post('/tender/:id/save', sessionAuth, tenderCheck, 'tenderController.saveTenderInfo');
     app.post('/tender/rule', sessionAuth, 'tenderController.rule');
     app.post('/tender/:id/rule/first', sessionAuth, tenderCheck, 'tenderController.ruleFirst');
-    app.get('/tender/:id/shenpi', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.shenpiSet');
-    app.post('/tender/:id/shenpi/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.saveTenderInfoShenpi');
-    app.post('/tender/:id/shenpi/audit/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.saveShenpiAudit');
-    app.post('/tender/:id/shenpi/ledger/load', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.loadLedgerData');
-    app.post('/tender/:id/shenpi/save-sign', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.saveCooperateSign');
+    app.get('/tender/:id/shenpi', sessionAuth, tenderCheck, 'tenderController.shenpiSet');
+    app.post('/tender/:id/shenpi/save', sessionAuth, tenderCheck, 'tenderController.saveTenderInfoShenpi');
+    app.post('/tender/:id/shenpi/audit/save', sessionAuth, tenderCheck, 'tenderController.saveShenpiAudit');
+    app.post('/tender/:id/shenpi/ledger/load', sessionAuth, tenderCheck, 'tenderController.loadLedgerData');
+    app.post('/tender/:id/shenpi/save-sign', sessionAuth, tenderCheck, 'tenderController.saveCooperateSign');
     app.post('/tender/:id/copy-setting', sessionAuth, tenderCheck, 'tenderController.copyTender');
 
     // 预付款
@@ -443,6 +443,7 @@ module.exports = app => {
     app.post('/tender/:id/schedule/plan/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.savePlan');
     app.get('/tender/:id/schedule/stage', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageTp');
     app.get('/tender/:id/schedule/stage/gcl', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.stageGcl');
+    app.post('/tender/:id/schedule/stage/gcl/save', sessionAuth, tenderCheck, uncheckTenderCheck, 'scheduleController.saveStageGcl');
 
     // 书签
     app.post('/tender/:id/ledger/tag', sessionAuth, tenderCheck, uncheckTenderCheck, 'tenderController.billsTag');

+ 55 - 1
app/service/schedule_ledger_month.js

@@ -13,7 +13,7 @@ module.exports = app => {
             try {
                 const info = await this.getDataByCondition({ tid: this.ctx.tender.id, lid: data.lid, yearmonth: data.yearmonth });
                 if (info) {
-                    if (data.plan_gcl === null && data.plan_tp === null) {
+                    if (data.plan_gcl === null && data.plan_tp === null && info.sj_gcl === null && info.sj_tp === null) {
                         await transaction.delete(this.tableName, { id: info.id });
                     } else {
                         const updateData = {
@@ -44,6 +44,60 @@ module.exports = app => {
             }
         }
 
+        async saveSj(data) {
+            // 判断是添加,删除,还是修改
+            const transaction = await this.db.beginTransaction();
+            try {
+                const info = await this.getDataByCondition({ tid: this.ctx.tender.id, lid: data.lid, yearmonth: data.yearmonth });
+                if (info) {
+                    if (data.sj_gcl === null && data.sj_tp === null && info.plan_gcl === null && info.plan_tp === null) {
+                        await transaction.delete(this.tableName, { id: info.id });
+                    } else {
+                        const updateData = {
+                            id: info.id,
+                            sj_gcl: data.sj_gcl,
+                            sj_tp: data.sj_tp,
+                        };
+                        await transaction.update(this.tableName, updateData);
+                    }
+                } else {
+                    const insertData = {
+                        tid: this.ctx.tender.id,
+                        lid: data.lid,
+                        yearmonth: data.yearmonth,
+                        sj_gcl: data.sj_gcl,
+                        sj_tp: data.sj_tp,
+                    };
+                    await transaction.insert(this.tableName, insertData);
+                }
+                // 重新计算本月、总 计划金额和计划工程量
+                await this.calcMonthSj(transaction, this.ctx.tender.id, data.yearmonth);
+                await this.ctx.service.scheduleMonth.calcSj(transaction, this.ctx.tender.id);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
+
+        async calcMonthSj(transaction, tid, yearmonth) {
+            const sql = 'SELECT SUM(`sj_gcl`) as total_sj_gcl, SUM(`sj_tp`) as total_sj_tp FROM ?? WHERE tid = ? and yearmonth = ?';
+            const sqlParam = [this.tableName, tid, yearmonth];
+            const result = await transaction.queryOne(sql, sqlParam);
+            const updateData = {
+                sj_gcl: result.total_sj_gcl,
+                sj_tp: result.total_sj_tp,
+            };
+            const option = {
+                where: {
+                    tid,
+                    yearmonth,
+                },
+            };
+            return await transaction.update(this.ctx.service.scheduleMonth.tableName, updateData, option);
+        }
+
         async calcMonthPlan(transaction, tid, yearmonth) {
             const sql = 'SELECT SUM(`plan_gcl`) as total_plan_gcl, SUM(`plan_tp`) as total_plan_tp FROM ?? WHERE tid = ? and yearmonth = ?';
             const sqlParam = [this.tableName, tid, yearmonth];

+ 45 - 0
app/service/schedule_month.js

@@ -62,6 +62,51 @@ module.exports = app => {
             };
             return await transaction.update(this.ctx.service.schedule.tableName, updateData, option);
         }
+
+        async calcSj(transaction, tid) {
+            const sql = 'SELECT SUM(`sj_gcl`) as total_sj_gcl, SUM(`sj_tp`) as total_sj_tp FROM ?? WHERE tid = ?';
+            const sqlParam = [this.tableName, tid];
+            const result = await transaction.queryOne(sql, sqlParam);
+            const updateData = {
+                sj_gcl: result.total_sj_gcl,
+                sj_tp: result.total_sj_tp,
+            };
+            const option = {
+                where: {
+                    tid,
+                },
+            };
+            return await transaction.update(this.ctx.service.schedule.tableName, updateData, option);
+        }
+
+        async addStageUsed(data) {
+            const updateData = {
+                id: data.id,
+                stage_used: 1,
+            };
+            return await this.db.update(this.tableName, updateData);
+        }
+
+        async delStageUsed(data) {
+            const transaction = await this.db.beginTransaction();
+            try {
+                const updateDatas = [];
+                const updateLmDatas = [];
+                for (const m of data) {
+                    updateDatas.push({ row: { stage_used: 0, sj_gcl: null, sj_tp: null }, where: { yearmonth: m, tid: this.ctx.tender.id } });
+                    updateLmDatas.push({ row: { sj_gcl: null, sj_tp: null }, where: { yearmonth: m, tid: this.ctx.tender.id } });
+                }
+                if (updateDatas.length > 0) await transaction.updateRows(this.tableName, updateDatas);
+                if (updateLmDatas.length > 0) await transaction.updateRows(this.ctx.service.scheduleLedgerMonth.tableName, updateLmDatas);
+                // 重新计算总 计划金额和计划工程量
+                await this.calcSj(transaction, this.ctx.tender.id);
+                await transaction.commit();
+                return true;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            }
+        }
     }
     return ScheduleMonth;
 };

+ 1 - 1
app/service/tender_info.js

@@ -70,7 +70,7 @@ module.exports = app => {
                 info = await this.addTenderInfo(tenderId, this.ctx.session.sessionProject.id);
             }
             for (const pi of parseInfo) {
-                info[pi] = !info[pi] || info[pi] === '' ? defaultInfo[pi] : JSON.parse(info[pi]);
+                info[pi] = !info[pi] || info[pi] === '' ? (pi === 'shenpi' ? JSON.parse(JSON.stringify(defaultInfo[pi])) : defaultInfo[pi]) : JSON.parse(info[pi]);
                 this.ctx.helper._.defaults(info[pi], defaultInfo[pi]);
             }
             for (const ai of arrayInfo) {

+ 1 - 1
app/view/schedule/plan_modal.ejs

@@ -33,7 +33,7 @@
                     <% for (const m of scheduleMonth) { %>
                     <tr>
                         <td><%- m.yearmonth %></td>
-                        <td><% if (m.sj_gcl === null && m.sj_tp === null) { %><label></label><input type="checkbox" value="<%- m.id %>"><% } %></td>
+                        <td><% if (m.stage_used === 0) { %><label></label><input type="checkbox" value="<%- m.id %>"><% } %></td>
                     </tr>
                     <% } %>
                 </table>

+ 51 - 3
app/view/schedule/stage_gcl.ejs

@@ -16,14 +16,62 @@
                 </div>
             </div>
             <div class="ml-auto">
-                <a href="#edit-计量" data-toggle="modal" data-target="#edit-plan" class="btn btn-sm btn-outline-primary">管理计量</a>
-                <a href="" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建实际计量</a>
+                <a href="#edit-stage" data-toggle="modal" data-target="#edit-stage" class="btn btn-sm btn-outline-primary">管理计量</a>
+                <a href="#add" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#add">创建实际计量</a>
             </div>
         </div>
     </div>
     <div class="content-wrap">
         <div class="c-body">
-            <div class="sjs-height-0" style="overflow: auto;" id="ledger-spread">
+            <div class="sjs-height-1" style="overflow: auto;" id="ledger-spread">
+            </div>
+            <div class="bcontent-wrap">
+                <div id="main-resize" class="resize-y" id="top-spr" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
+                <div class="bc-bar mb-1">
+                    <ul class="nav nav-tabs">
+                        <li class="nav-item">
+                            <a class="nav-link active" data-toggle="tab" href="#huizong" role="tab">汇总</a>
+                        </li>
+                        <li class="nav-item">
+                            <div class="d-inline-block ml-2">
+                                <div class="dropdown">
+                                    <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        2020年
+                                    </button>
+                                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start">
+                                        <a class="dropdown-item" href="#">2019年</a>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="d-inline-block">
+                                <div class="dropdown">
+                                    <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                                        1月
+                                    </button>
+                                    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton" x-placement="bottom-start">
+                                        <a class="dropdown-item" href="#">2月</a>
+                                        <a class="dropdown-item" href="#">3月</a>
+                                        <a class="dropdown-item" href="#">4月</a>
+                                    </div>
+                                </div>
+                            </div>
+                        </li>
+                    </ul>
+                </div>
+                <div class="tab-content">
+                    <div class="tab-pane active" id="huizong-spread">
+                        <table class="table table-bordered table-sm">
+                            <thead>
+                            <tr class="text-center"><th rowspan="2">项目节编号</th><th rowspan="2">名称</th><th rowspan="2">单位</th><th rowspan="2">经济指标</th><th colspan="2">总设计</th><th colspan="2">自开工至本月计划完成</th><th colspan="2">截止本月完成计量</th><th colspan="2">本年计划完成</th><th colspan="2">本年累计完成</th><th colspan="2">本月计划完成</th><th colspan="2">本月完成计量</th><th colspan="2">下月计划</th></tr>
+                            <tr class="text-center"><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th><th>工程量</th><th>金额(万元)</th></tr>
+                            </thead>
+                            <tr><td>1-2</td><td>路基工程</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
+                            <tr><td>1-2-2</td><td>挖方</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
+                            <tr><td>1-2-2-1</td><td>挖土方</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
+                            <tr><td>1-2-2-1-1</td><td>挖路基土方</td><td></td><td></td><td></td><td></td><td>11111</td><td>11111</td><td>22222</td><td>22222</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>
+                        </table>
+                    </div>
+                </div>
             </div>
         </div>
     </div>

+ 33 - 12
app/view/schedule/stage_gcl_modal.ejs

@@ -8,39 +8,60 @@
             <div class="modal-body">
                 <div class="form-group">
                     <label>计划进度月</label>
-                    <select class="form-control">
-                        <option>2020年1月</option>
-                        <option>2020年2月</option>
-                        <option>2020年3月</option>
+                    <select class="form-control" id="month-select">
+                        <option value="0">选择计量月</option>
+                        <% for (const m of scheduleMonth) { %>
+                            <% if (m.stage_used === 0) { %>
+                            <option value="<%- m.id %>"><%- m.yearmonth.split('-')[0] %>年<%- parseInt(m.yearmonth.split('-')[1]) %>月 %></option>
+                            <% } %>
+                        <% } %>
                     </select>
                 </div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-primary" >确认创建</button>
+                <button type="button" class="btn btn-sm btn-primary" id="add-month">确认创建</button>
             </div>
         </div>
     </div>
 </div>
 <!--管理计量-->
-<div class="modal fade" id="edit-plan" data-backdrop="static">
+<div class="modal fade" id="edit-stage" data-backdrop="static">
     <div class="modal-dialog" role="document">
         <div class="modal-content">
             <div class="modal-header">
                 <h5 class="modal-title">管理计量</h5>
             </div>
             <div class="modal-body">
-                <table class="table table-bordered table-hover">
+                <table class="table table-bordered table-hover" id="month-table">
                     <tr><th>计量月</th><th width="100">删除</th></tr>
-                    <tr><td>2020-1</td><td></td></tr>
-                    <tr><td>2020-2</td><td><label><input type="checkbox"></label></td></tr>
-                    <tr><td>2020-3</td><td><label><input type="checkbox"></label></td></tr>
+                    <% for (const m of scheduleMonth) { %>
+                    <% if (m.stage_used === 1) { %>
+                    <tr><td><%- m.yearmonth %></td><td><label></label><input type="checkbox" value="<%- m.id %>"></td></tr>
+                    <% } %>
+                    <% } %>
                 </table>
-                <div class="alert alert-danger">确认删除「2020-2」「2020-3」计量进度?删除后,数据无法恢复,请谨慎操作。</div>
+                <div class="alert alert-danger" style="display: none">确认删除<span id="del-month-list"></span> 计量进度?删除后,数据无法恢复,请谨慎操作。</div>
             </div>
             <div class="modal-footer">
                 <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
-                <button type="button" class="btn btn-sm btn-danger" >确认删除</button>
+                <button type="button" class="btn btn-sm btn-danger" disabled id="del-month" >确认删除</button>
+            </div>
+        </div>
+    </div>
+</div>
+<!--首次使用提示-->
+<div class="modal fade" id="second" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">计量进度</h5>
+            </div>
+            <div class="modal-body">
+                <h5>使用计量进度需要进行计划进度设置</h5>
+            </div>
+            <div class="modal-footer">
+                <a href="/tender/<%- ctx.tender.id %>/schedule/plan" class="btn btn-sm btn-primary">开始设置</a>
             </div>
         </div>
     </div>

+ 16 - 0
app/view/schedule/stage_tp_modal.ejs

@@ -71,4 +71,20 @@
         </div>
     </div>
 </div>
+<!--首次使用提示-->
+<div class="modal fade" id="second" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">计量进度</h5>
+            </div>
+            <div class="modal-body">
+                <h5>使用计量进度需要进行计划进度设置</h5>
+            </div>
+            <div class="modal-footer">
+                <a href="/tender/<%- ctx.tender.id %>/schedule/plan" class="btn btn-sm btn-primary">开始设置</a>
+            </div>
+        </div>
+    </div>
+</div>
 <% include ./modal.ejs %>

+ 2 - 2
config/web.js

@@ -726,7 +726,7 @@ const JsFiles = {
                     '/public/js/shares/sjs_setting.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
-                    // '/public/js/schedule_stage_tp.js',
+                    '/public/js/schedule_stage_tp.js',
                 ],
                 mergeFile: 'schedule_stage_tp',
             },
@@ -742,7 +742,7 @@ const JsFiles = {
                     '/public/js/shares/sjs_setting.js',
                     '/public/js/zh_calc.js',
                     '/public/js/path_tree.js',
-                    // '/public/js/schedule_stage_gcl.js',
+                    '/public/js/schedule_stage_gcl.js',
                 ],
                 mergeFile: 'schedule_stage_gcl',
             },