瀏覽代碼

1. 其他台账-其他,1.0
2. 奖罚金,时间列调整
3. 甲供材料,截止本期-数量只读

MaiXinRong 5 年之前
父節點
當前提交
5076cde6ab

+ 2 - 2
app/controller/stage_extra_controller.js

@@ -157,7 +157,7 @@ module.exports = app => {
                 ctx.body = {err: 0, msg: '', data: data};
             } catch (error) {
                 ctx.helper.log(error);
-                ctx.body = this.ajaxErrorBody(error, '获取甲供材料数据失败,请刷新');
+                ctx.body = this.ajaxErrorBody(error, '获取数据失败,请刷新');
             }
 
         }
@@ -174,7 +174,7 @@ module.exports = app => {
                 ctx.body = { err: 0, msg: '', data: result };
             } catch (error) {
                 ctx.helper.log(error);
-                ctx.body = this.ajaxErrorBody(error, '提交甲供材料数据失败,请重试');
+                ctx.body = this.ajaxErrorBody(error, '提交数据失败,请重试');
             }
         }
     }

+ 67 - 22
app/public/js/se_bonus.js

@@ -11,30 +11,75 @@
 const isPre = function (data) {
     return data.sid !== stageId;
 };
-const spreadSetting = {
-    cols: [
-        {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@', readOnly: isPre, },
-        {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number', readOnly: isPre, },
-        {title: '时间', colSpan: '1', rowSpan: '1', field: 'real_time', hAlign: 1, width: 150, formatter: 'yyyy-MM-dd', /*cellType: 'datepicker', */readOnly: isPre, },
-        {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: isPre, },
-        {title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof', hAlign: 0, width: 180, formatter: '@', readOnly: isPre, },
-        {
-            title: '计量期', colSpan: '1', rowSpan: '1', field: 'sorder', hAlign: 1, width: 100, formatter: '@',
-            getValue: function (data) {
-                return '第' + data.sorder + '期';
-            }, readOnly: true,
-        },
-        {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 180, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: isPre, }
-    ],
-    emptyRows: 3,
-    headRows: 1,
-    headRowHeight: [32],
-    defaultRowHeight: 21,
-    headerFont: '12px 微软雅黑',
-    font: '12px 微软雅黑',
-};
 $(document).ready(() => {
     autoFlashHeight();
+
+    let datepicker;
+    const spreadSetting = {
+        cols: [
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@', readOnly: isPre, },
+            {title: '金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number', readOnly: isPre, },
+            {
+                title: '时间', colSpan: '1', rowSpan: '1', field: 'real_time', hAlign: 2, width: 150, readOnly: true,
+                formatter: 'yyyy-MM-dd', cellType: 'activeImageBtn', normalImg: '#ellipsis-icon', indent: 5,
+            },
+            {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: isPre, },
+            {title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof', hAlign: 0, width: 180, formatter: '@', readOnly: isPre, },
+            {
+                title: '计量期', colSpan: '1', rowSpan: '1', field: 'sorder', hAlign: 1, width: 100, formatter: '@',
+                getValue: function (data) {
+                    return '第' + data.sorder + '期';
+                }, readOnly: true,
+            },
+            {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 180, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: isPre, }
+        ],
+        emptyRows: 3,
+        headRows: 1,
+        headRowHeight: [32],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        imageClick: function (data, hitinfo) {
+            if (!data) return;
+
+            const setting = hitinfo.sheet.zh_setting;
+            if (!setting) return;
+            const col = setting.cols[hitinfo.col];
+            if (!col || col.field !== 'real_time') return;
+
+            const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
+            if (!datepicker) {
+                datepicker = $('.datepicker-here').datepicker({
+                    language: 'zh',
+                    dateFormat: 'yyyy-MM-dd',
+                    autoClose: true,
+                    onSelect: function (formattedDate, date, inst) {
+                        if (!inst.visible) return;
+                        const sels = hitinfo.sheet.getSelections();
+                        if (!sels || !sels[0]) return;
+                        const node = SpreadJsObj.getSelectObject(hitinfo.sheet);
+                        const uData = { update: {id: node.id, real_time: date} };
+
+                        postData(window.location.pathname + '/update', uData, function (result) {
+                            bonusObj.loadUpdateData(result);
+                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                        }, function () {
+                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                        });
+                    }
+                }).data('datepicker');
+            }
+            const value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
+            if (value) {
+                datepicker.selectDate(value);
+            } else {
+                datepicker.clear();
+            }
+            datepicker.show();
+            $('#datepickers-container').css('top', hitinfo.cellRect.y + pos.y).css('left', hitinfo.cellRect.x + pos.x);
+        }
+    };
+
     const bonusSpread = SpreadJsObj.createNewSpread($('#bonus-spread')[0]);
     const bonusSheet = bonusSpread.getActiveSheet();
     spreadSetting.readOnly = readOnly;

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

@@ -15,11 +15,11 @@ const spreadSetting = {
         {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
         {title: '本期到场|数量',  colSpan: '2|1', rowSpan: '1|1', field: 'arrive_qty', hAlign: 2, width: 60, type: 'Number'},
         {title: '|金额',  colSpan: '|1', rowSpan: '|1', field: 'arrive_tp', hAlign: 2, width: 60, type: 'Number'},
-        {title: '截止本期到场|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_arrive_qty', hAlign: 2, width: 60, type: 'Number'},
+        {title: '截止本期到场|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_arrive_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
         {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_arrive_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
         {title: '本期扣回|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deduct_qty', hAlign: 2, width: 60, type: 'Number'},
         {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deduct_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
-        {title: '截止本期扣回|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_deduct_qty', hAlign: 2, width: 60, type: 'Number'},
+        {title: '截止本期扣回|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_deduct_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
         {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_deduct_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
         {title: '材料来源', colSpan: '1', rowSpan: '2', field: 'source', hAlign: 0, width: 80, formatter: '@'},
         {title: '单据号', colSpan: '1', rowSpan: '2', field: 'bills_code', hAlign: 0, width: 80, formatter: '@'},

+ 424 - 51
app/public/js/se_other.js

@@ -8,50 +8,86 @@
  * @version
  */
 
-const mainSpreadSetting = {
-    cols: [
-        {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 185, formatter: '@'},
-        {title: '金额', colSpan: '1', rowSpan: '1', field: 'e_type', hAlign: 1, width: 80, formatter: '@'},
-        {title: '本期金额', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 100, type: 'Number'},
-        {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number', readOnly: true},
-        {title: '时间', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
-        {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 150, formatter: '@', cellType: 'ellipsisAutoTip'}
-    ],
-    emptyRows: 3,
-    headRows: 1,
-    headRowHeight: [32],
-    defaultRowHeight: 21,
-    headerFont: '12px 微软雅黑',
-    font: '12px 微软雅黑',
-};
-
-const subSpreadSetting = {
-    cols: [
-        {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 185, formatter: '@'},
-        {title: '金额', colSpan: '1', rowSpan: '1', field: 'e_type', hAlign: 1, width: 80, formatter: '@'},
-        {title: '本期金额', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 100, type: 'Number'},
-        {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number', readOnly: true},
-        {title: '时间', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
-        {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 150, formatter: '@', cellType: 'ellipsisAutoTip'}
-    ],
-    emptyRows: 3,
-    headRows: 1,
-    headRowHeight: [32],
-    defaultRowHeight: 21,
-    headerFont: '12px 微软雅黑',
-    font: '12px 微软雅黑',
-};
 
 $(document).ready(() => {
     autoFlashHeight();
+    let datepicker;
 
-    const mainSpread = SpreadJsObj.createNewSpread($('#main-spread')[0]);
-    const mainSheet = mainSpread.getActiveSheet();
-    SpreadJsObj.initSheet(mainSheet, mainSpreadSetting);
+    const otherSpreadSetting = {
+        cols: [
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 235, formatter: '@'},
+            {title: '金额', colSpan: '1', rowSpan: '1', field: 'total_price', hAlign: 2, width: 100, type: 'Number'},
+            {title: '本期金额', colSpan: '1', rowSpan: '1', field: 'tp', hAlign: 2, width: 100, type: 'Number'},
+            {title: '截止本期金额', colSpan: '1', rowSpan: '1', field: 'end_tp', hAlign: 2, width: 100, type: 'Number', readOnly: true},
+            {
+                title: '时间', colSpan: '1', rowSpan: '1', field: 'real_time', hAlign: 2, width: 120, readOnly: true,
+                formatter: 'yyyy-MM-dd', cellType: 'activeImageBtn', normalImg: '#ellipsis-icon', indent: 5,
+            },
+            {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 180, formatter: '@', cellType: 'ellipsisAutoTip'}
+        ],
+        emptyRows: 3,
+        headRows: 1,
+        headRowHeight: [32],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        getColor: function (sheet, data, row, col, defaultColor) {
+            if (data) {
+                if (data.total_price) {
+                    return data.total_price >= 0
+                        ? data.end_tp > data.total_price ? '#f8d7da' : defaultColor
+                        : data.end_tp < data.total_price ? '#f8d7da' : defaultColor;
+                } else {
+                    return defaultColor;
+                }
+            } else {
+                return defaultColor;
+            }
+        },
+        imageClick: function (data, hitinfo) {
+            if (!data) return;
+
+            const setting = hitinfo.sheet.zh_setting;
+            if (!setting) return;
+            const col = setting.cols[hitinfo.col];
+            if (!col || col.field !== 'real_time') return;
+
+            const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
+            if (!datepicker) {
+                datepicker = $('.datepicker-here').datepicker({
+                    language: 'zh',
+                    dateFormat: 'yyyy-MM-dd',
+                    autoClose: true,
+                    onSelect: function (formattedDate, date, inst) {
+                        if (!inst.visible) return;
+                        const sels = hitinfo.sheet.getSelections();
+                        if (!sels || !sels[0]) return;
+                        const node = SpreadJsObj.getSelectObject(hitinfo.sheet);
+                        const uData = { update: {id: node.id, real_time: date} };
+
+                        postData(window.location.pathname + '/update', uData, function (result) {
+                            seOtherObj.loadUpdateData(result);
+                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                        }, function () {
+                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                        });
+                    }
+                }).data('datepicker');
+            }
+            const value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
+            if (value) {
+                datepicker.selectDate(value);
+            } else {
+                datepicker.clear();
+            }
+            datepicker.show();
+            $('#datepickers-container').css('top', hitinfo.cellRect.y + pos.y).css('left', hitinfo.cellRect.x + pos.x);
+        }
+    };
 
-    const subSpread = SpreadJsObj.createNewSpread($('#sub-spread')[0]);
-    const subSheet = subSpread.getActiveSheet();
-    SpreadJsObj.initSheet(subSheet, subSpreadSetting);
+    const otherSpread = SpreadJsObj.createNewSpread($('#other-spread')[0]);
+    const otherSheet = otherSpread.getActiveSheet();
+    SpreadJsObj.initSheet(otherSheet, otherSpreadSetting);
 
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
@@ -67,18 +103,355 @@ $(document).ready(() => {
                 $('#sub-menu').addClass('panel-sidebar');
             }
             autoFlashHeight();
-            mainSpread.refresh();
-            subSpread.refresh();
+            otherSpread.refresh();
         }
-    });    
-    // 上下窗口resizer
-    $.divResizer({
-        select: '#main-resize',
-        callback: function () {
-            mainSpread.refresh();
-            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
-            $(".sp-wrap").height(bcontent-30);
-            subSpread.refresh();
+    });
+
+    class SeOther {
+        constructor () {
+            this.data = [];
         }
-    });  
+        resortData() {
+            this.data.sort(function (a, b) {
+                return a.order - b.order;
+            });
+        }
+        calculateAll() {
+            for (const d of this.data) {
+                d.end_tp = ZhCalc.add(d.pre_tp, d.tp);
+            }
+        }
+        loadDatas(datas) {
+            this.data = datas;
+            this.calculateAll();
+            this.resortData();
+        }
+        loadUpdateData(updateData) {
+            if (updateData.add) {
+                for (const a of updateData.add) {
+                    this.data.push(a);
+                }
+            }
+            if (updateData.update) {
+                for (const u of updateData.update) {
+                    const d = this.data.find(function (x) {
+                        return u.id === x.id;
+                    });
+                    if (d) {
+                        _.assign(d, u);
+                    } else {
+                        this.data.push(d);
+                    }
+                }
+            }
+            if (updateData.del) {
+                _.remove(this.data, function (d) {
+                    return updateData.del.indexOf(d.id) >= 0;
+                });
+            }
+            this.calculateAll();
+            this.resortData();
+        }
+    }
+    const seOtherObj = new SeOther();
+
+    postData(window.location.pathname + '/load', null, function (result) {
+        seOtherObj.loadDatas(result);
+        SpreadJsObj.loadSheetData(otherSheet, SpreadJsObj.DataType.Data, seOtherObj.data);
+    });
+
+    if (!readOnly) {
+        const seOtherOprObj = {
+            /**
+             * 删除按钮响应事件
+             * @param sheet
+             */
+            deletePress: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+
+                for (let iRow = sels[0].row; iRow < sels[0].row + sels[0].rowCount; iRow++) {
+                    let bDel = false;
+                    const node = sortData[iRow];
+                    if (node) {
+                        const data = {id: node.id};
+                        for (let iCol = sels[0].col; iCol < sels[0].col + sels[0].colCount; iCol++) {
+                            const colSetting = sheet.zh_setting.cols[iCol];
+                            if (colSetting.field === 'name') {
+                                toastr.error('名称不能为空,如需删除请使用右键删除');
+                                return;
+                            }
+                            const style = sheet.getStyle(iRow, iCol);
+                            if (!style.locked) {
+                                const colSetting = sheet.zh_setting.cols[iCol];
+                                data[colSetting.field] = null;
+                                bDel = true;
+                            }
+                        }
+                        if (bDel) {
+                            datas.push(data);
+                        }
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {updateType: 'update', updateData: datas}, function (result) {
+                        seOtherObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(otherSheet);
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(otherSheet);
+                    });
+                }
+            },
+            delete: function (sheet) {
+                if (!sheet.zh_setting || readOnly) return;
+
+                const sortData = sheet.zh_data;
+                const datas = [];
+                const sels = sheet.getSelections();
+                if (!sels || !sels[0]) return;
+                const hint = {
+                    isOld: {type: 'warning', msg: '该数据已计量,不可删除'},
+                    invalidDel: {type: 'warning', msg: '该数据不是您新增的,只有原报和新增人可删除'},
+                };
+
+                for (let iRow = sels[0].row, iLen = sels[0].row + sels[0].rowCount; iRow < iLen; iRow++) {
+                    const node = sortData[iRow];
+                    if (node.pre_used) {
+                        toastMessageUniq(hint.isOld);
+                        continue;
+                    } else {
+                        if (node.add_uid !== userID || stageUserId !== userID) {
+                            toastMessageUniq(hint.invalidDel);
+                            continue;
+                        }
+                        datas.push(node.id);
+                    }
+                }
+                if (datas.length > 0) {
+                    postData(window.location.pathname + '/update', {del: datas}, function (result) {
+                        seOtherObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(otherSheet);
+                    }, function () {
+                        SpreadJsObj.reLoadSheetData(otherSheet);
+                    });
+                }
+            },
+            editEnded: function (e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+
+                const node = info.sheet.zh_data[info.row];
+                const col = info.sheet.zh_setting.cols[info.col];
+                const data = {};
+
+                if (node) {
+                    data.update = {};
+                    data.update.id = node.id;
+
+                    const oldValue = node ? node[col.field] : null;
+                    const newValue = trimInvalidChar(info.editingText);
+                    if (oldValue == info.editingText || ((!oldValue || oldValue === '') && (newValue === ''))) {
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.update[col.field] = newValue;
+                } else {
+                    if (col.field !== 'name') {
+                        toastr.warning('请先输入名称');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    data.add = {};
+                    data.add.order = info.row + 1;
+                    data.add.name = trimInvalidChar(info.editingText);
+                }
+
+                postData(window.location.pathname + '/update', data, function (result) {
+                    seOtherObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }, function () {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                });
+            },
+            editStarting(e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_data) {
+                    info.cancel = true;
+                    return;
+                }
+
+                const col = info.sheet.zh_setting.cols[info.col];
+                const node = info.sheet.zh_data[info.row];
+                if (!node) return;
+
+                switch (col.field) {
+                    case 'name':
+                        info.cancel = readOnly || node.pre_used;
+                        break;
+                }
+            },
+            clipboardPasting(e, info) {
+                const setting = info.sheet.zh_setting, sortData = info.sheet.zh_data;
+                info.cancel = true;
+
+                if (!setting || !sortData) return;
+                const pasteData = info.pasteData.html
+                    ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
+                    : (info.pasteData.text === ''
+                        ? SpreadJsObj.Clipboard.getAnalysisPasteText()
+                        : SpreadJsObj.analysisPasteText(info.pasteData.text));
+                const hint = {
+                    name: {type: 'warning', msg: '名称不可为空,已过滤'},
+                    tp: {type: 'warning', msg: '输入的 金额 非法,已过滤'},
+                };
+
+                const uDatas = [], iDatas = [];
+                for (let iRow = 0; iRow < info.cellRange.rowCount; iRow++) {
+                    const curRow = info.cellRange.row + iRow;
+                    const node = sortData[curRow];
+
+                    let bPaste = false;
+                    const data = {};
+                    for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                        const curCol = info.cellRange.col + iCol;
+                        const colSetting = setting.cols[curCol];
+                        const value = trimInvalidChar(pasteData[iRow][iCol]);
+
+                        if (colSetting.field === 'name' && (!value || value === '')) {
+                            toastMessageUniq(hint.name);
+                            break;
+                        }
+                        if (colSetting.type === 'Number') {
+                            const num = _.toNumber(value);
+                            if (num) {
+                                data[colSetting.field] = num;
+                                bPaste = true;
+                            }
+                        } else {
+                            data[colSetting.field] = value;
+                            bPaste = true;
+                        }
+                    }
+                    if (bPaste) {
+                        if (node) {
+                            data.id = node.id;
+                            uDatas.push(data);
+                        } else {
+                            data.order = curRow + 1;
+                            iDatas.push(data);
+                        }
+                    }
+                }
+                const updateData = {};
+                if (uDatas.length > 0) updateData.update = uDatas;
+                if (iDatas.length > 0) updateData.add = iDatas;
+                if (uDatas.length > 0 || iDatas.length > 0) {
+                    postData(window.location.pathname + '/update', updateData, function (result) {
+                        seOtherObj.loadUpdateData(result);
+                        SpreadJsObj.reLoadSheetData(info.sheet);
+                    });
+                } else {
+                    SpreadJsObj.reLoadSheetData(info.sheet);
+                }
+            },
+            upMove: function () {
+                const sels = otherSheet.getSelections(), sortData = otherSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const preNode = sortData[sels[0].row - 1];
+                const data = [
+                    {id: node.id, order: preNode.order},
+                    {id: preNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seOtherObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(otherSheet, [sels[0].row, sels[0].row - 1]);
+                    otherSheet.setSelection(sels[0].row - 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            },
+            downMove: function () {
+                const sels = otherSheet.getSelections(), sortData = otherSheet.zh_data;
+                const node = sortData[sels[0].row];
+                const nextNode = sortData[sels[0].row + 1];
+                const data = [
+                    {id: node.id, order: nextNode.order},
+                    {id: nextNode.id, order: node.order}
+                ];
+                postData(window.location.pathname + '/update', {update: data}, function (result) {
+                    seOtherObj.loadUpdateData(result);
+                    SpreadJsObj.reLoadRowsData(otherSheet, [sels[0].row, sels[0].row + 1]);
+                    otherSheet.setSelection(sels[0].row + 1, sels[0].col, sels[0].rowCount, sels[0].colCount);
+                });
+            }
+        };
+        otherSheet.bind(spreadNS.Events.EditEnded, seOtherOprObj.editEnded);
+        otherSheet.bind(spreadNS.Events.EditStarting, seOtherOprObj.editStarting);
+        otherSheet.bind(spreadNS.Events.ClipboardPasting, seOtherOprObj.clipboardPasting);
+        SpreadJsObj.addDeleteBind(otherSpread, seOtherOprObj.deletePress);
+        $.contextMenu({
+            selector: '#other-spread',
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, otherSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function (key, opt) {
+                        seOtherOprObj.delete(otherSheet);
+                    },
+                    disabled: function (key, opt) {
+                        const sels = otherSheet.getSelections();
+                        if (!sels || !sels[0]) return true;
+
+                        const row = sels[0].row;
+                        const node = seOtherObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                sprDel: '------------',
+                upMove: {
+                    name: '上移',
+                    icon: 'fa-arrow-up',
+                    callback: function (key, opt) {
+                        seOtherOprObj.upMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = otherSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row === 0) return true;
+
+                        const row = sels[0].row;
+                        const node = seOtherObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                },
+                downMove: {
+                    name: '下移',
+                    icon: 'fa-arrow-down',
+                    callback: function (key, opt) {
+                        seOtherOprObj.downMove();
+                    },
+                    disabled: function (key, opt) {
+                        const sels = otherSheet.getSelections();
+                        if (!sels || !sels[0] || sels[0].row >= seOtherObj.data.length - 1) return true;
+
+                        const row = sels[0].row;
+                        const node = seOtherObj.data[row];
+                        return node === undefined || node === null;
+                    },
+                    visible: function (key, opt) {
+                        return !readOnly;
+                    }
+                }
+            },
+        })
+    }
 });

+ 6 - 9
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -1392,7 +1392,7 @@ const SpreadJsObj = {
                         if (imageClick && Object.prototype.toString.apply(imageClick) === "[object Function]") {
                             const sortData = SpreadJsObj.getSortData(hitinfo.sheet);
                             const data = sortData ? sortData[hitinfo.row] : null;
-                            imageClick(data);
+                            imageClick(data, hitinfo);
                         }
                     }
                 }
@@ -1571,7 +1571,7 @@ const SpreadJsObj = {
                     if (imageClick && Object.prototype.toString.apply(imageClick) === "[object Function]") {
                         const sortData = SpreadJsObj.getSortData(hitinfo.sheet);
                         const data = sortData ? sortData[hitinfo.row] : null;
-                        imageClick(data);
+                        imageClick(data, hitinfo);
                         const cell = hitinfo.sheet.getCell(hitinfo.row, hitinfo.col);
                         cell.tag(null);
                         hitinfo.sheet.repaint(hitinfo.cellRect);
@@ -1766,7 +1766,7 @@ const SpreadJsObj = {
                     if (imageClick && Object.prototype.toString.apply(imageClick) === "[object Function]") {
                         const sortData = SpreadJsObj.getSortData(hitinfo.sheet);
                         const data = sortData ? sortData[hitinfo.row] : null;
-                        imageClick(data);
+                        imageClick(data, hitinfo);
                         const cell = hitinfo.sheet.getCell(hitinfo.row, hitinfo.col);
                         cell.tag(hover);
                         hitinfo.sheet.repaint(hitinfo.cellRect);
@@ -1953,6 +1953,7 @@ const SpreadJsObj = {
          */
         getDatePickerCellType: function () {
             let datepicker;
+
             const DatePickerCellType = function () {};
             DatePickerCellType.prototype = new spreadNS.CellTypes.Text();
             const proto = DatePickerCellType.prototype;
@@ -1963,15 +1964,11 @@ const SpreadJsObj = {
                 const self = this;
                 if (editorContext) {
                     const $editor = $(editorContext);
-                    spreadNS.CellTypes.Base.prototype.activateEditor.apply(this, arguments);
+                    //spreadNS.CellTypes.Text.prototype.activateEditor.apply(this, arguments);
                     $editor.css("position", "absolute");
                     datepicker = $editor.datepicker({
                         language: 'zh',
-                        dateFormat: 'yyy-MM-DD',
-                        autoClose: true,
-                        onSelect: function (formattedDate, date, inst) {
-                            this.value = date;
-                        }
+                        dateFormat: 'yyyy-MM-DD'
                     }).data('datepicker');
                     datepicker.show();
                 }

+ 2 - 0
app/service/stage.js

@@ -272,6 +272,8 @@ module.exports = app => {
                 if (preStage) {
                     const jgclResult = await this.ctx.service.stageJgcl.addInitialStageData(newStage, preStage, transaction);
                     if (!jgclResult) throw '初始化甲供材料数据失败';
+                    const otherResult = await this.ctx.service.stageOther.addInitialStageData(newStage, preStage, transaction);
+                    if (!otherResult) throw '初始化其他台账数据失败';
                 }
 
                 await transaction.commit();

+ 4 - 0
app/service/stage_audit.js

@@ -214,6 +214,7 @@ module.exports = app => {
                 await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, 1, transaction);
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
                 // 更新期数据
                 const tpData = await this.ctx.service.stageBills.getSumTotalPrice(this.ctx.stage);
                 await transaction.update(this.ctx.service.stage.tableName, {
@@ -268,6 +269,7 @@ module.exports = app => {
                     await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, nextAudit.order, transaction);
                     await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                     await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
+                    await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
                     // 流程至下一审批人
                     await transaction.update(this.tableName, {id: nextAudit.id, status: auditConst.status.checking, begin_time: time});
                     // 同步 期信息
@@ -475,6 +477,7 @@ module.exports = app => {
                 await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 1, transaction);
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
 
                 // 同步 期信息
                 await transaction.update(this.ctx.service.stage.tableName, {
@@ -684,6 +687,7 @@ module.exports = app => {
                 await this.ctx.service.stagePay.copyAuditStagePays(this.ctx.stage, this.ctx.stage.times, audit.order + 2, transaction);
                 await this.ctx.service.stageJgcl.updateHistory(this.ctx.stage, transaction);
                 await this.ctx.service.stageBonus.updateHistory(this.ctx.stage, transaction);
+                await this.ctx.service.stageOther.updateHistory(this.ctx.stage, transaction);
 
                 // 本期结束
                 // 生成截止本期数据 final数据

+ 1 - 1
app/service/stage_bonus.js

@@ -90,7 +90,7 @@ module.exports = app => {
                 if (d.tp) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
                 if (d.code) nd.code = d.code;
                 if (d.proof) nd.proof = d.proof;
-                if (d.real_time) nd.real_time = d.real_time;
+                if (d.real_time) nd.real_time = new Date(d.real_time);
                 if (d.memo) nd.memo = d.memo;
                 if (d.order) nd.order = d.order;
                 uDatas.push(nd);

+ 25 - 12
app/service/stage_other.js

@@ -27,18 +27,14 @@ module.exports = app => {
         }
 
         async getPreStageData(sorder) {
-            const sql = 'SELECT c.uuid, Sum(c.tp) as arrive_tp From ' + this.tableName +
-                '  WHERE s.`sorder` < ? And s.`tid` = ?' +
-                '  GROUP By uuid';
+            const sql = 'SELECT uuid, Sum(tp) as tp From ' + this.tableName + ' WHERE sorder < ? And tid = ? GROUP By uuid';
             const sqlParam = [sorder, this.ctx.tender.id];
             const data = await this.db.query(sql, sqlParam);
             return data;
         }
 
         async getEndStageData(sorder) {
-            const sql = 'SELECT c.uuid, Sum(c.tp) as tp ' + this.tableName +
-                '  WHERE s.`order` <= ? And s.`tid` = ?' +
-                '  GROUP By uuid';
+            const sql = 'SELECT uuid, Sum(tp) as tp From' + this.tableName + ' WHERE sorder <= ? And tid = ? GROUP By uuid';
             const sqlParam = [sorder, this.ctx.tender.id];
             const data = await this.db.query(sql, sqlParam);
             return data;
@@ -53,13 +49,16 @@ module.exports = app => {
                     uuid: this.uuid.v4(),
                     add_sid: this.ctx.stage.id,
                     add_uid: this.ctx.session.sessionUser.accountId,
+                    add_time: new Date(),
                     sid: this.ctx.stage.id,
                     sorder: this.ctx.stage.order,
                     tid: this.ctx.tender.id,
-                    create_time: new Date(),
                 };
                 nd.name = d.name;
                 nd.order = d.order;
+
+                if (d.total_price) nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);
+                if (d.tp) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
                 if (d.real_time) nd.real_time = d.real_time;
                 if (d.memo) nd.memo = d.memo;
                 insertData.push(nd);
@@ -90,10 +89,17 @@ module.exports = app => {
                 if (!od) continue;
 
                 const nd = {id: od.id};
-                if (d.name) nd.name = d.name;
+                if (d.name) {
+                    if (od.pre_used) throw '往期已使用,不可修改名称';
+                    nd.name = d.name;
+                }
                 if (d.order) nd.order = d.order;
+                if (d.total_price) {
+                    if (od.pre_used) throw '往期已使用,不可修改金额';
+                    nd.total_price = this.ctx.helper.round(d.total_price, this.ctx.tender.info.decimal.tp);
+                }
                 if (d.tp) nd.tp = this.ctx.helper.round(d.tp, this.ctx.tender.info.decimal.tp);
-                if (d.real_time) nd.real_time = d.real_time;
+                if (d.real_time) nd.real_time = new Date(d.real_time);
                 if (d.memo) nd.memo = d.memo;
                 uDatas.push(nd);
             }
@@ -119,8 +125,12 @@ module.exports = app => {
                 }
                 return result;
             } catch (err) {
-                if (err) result.err = err;
-                return result;
+                if (err.stack) {
+                    throw err;
+                } else {
+                    result.err = err.toString();
+                    return result;
+                }
             }
         }
 
@@ -135,8 +145,11 @@ module.exports = app => {
                 const his = this.ctx.helper._.find(datas, filter);
                 if (his) {
                     his.tp = d.tp;
+                    if (d.sid === d.add_sid) his.total_price = d.total_price;
                 } else {
-                    history.push({ stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, tp: d.tp });
+                    const nHis = { stimes: this.ctx.stage.curTimes, sorder: this.ctx.stage.curOrder, tp: d.tp };
+                    if (d.sid === d.add_sid) his.total_price = d.total_price;
+                    history.push(nHis);
                 }
                 updateDatas.push({ id: d.id, shistory: JSON.stringify(history) });
             }

+ 6 - 0
app/view/stage_extra/bonus.ejs

@@ -14,8 +14,14 @@
         <div class="c-body">
             <div class="sjs-height-0" id="bonus-spread">
             </div>
+            <div z-index="0" style="display: none;">
+                <input class="datepicker-here form-control form-control-sm" data-date-format="yyyy-MM-DD" data-language="zh" type="text" autocomplete="off" id="dp-input">
+            </div>
         </div>
     </div>
+    <div style="display: none">
+        <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+    </div>
 </div>
 <script>
     const stageId = <%- ctx.stage.id %>;

+ 12 - 17
app/view/stage_extra/other.ejs

@@ -12,24 +12,19 @@
     <div class="content-wrap">
         <div class="c-header p-0"></div>
         <div class="c-body">
-            <div class="sjs-height-1" id="main-spread">
+            <div class="sjs-height-1" id="other-spread" z-index="1">
             </div>
-            <div class="bcontent-wrap" id="main-bottom">
-                <div id="main-resize" class="resize-y"  r-Type="height" div1="#bills-spread" div2="#main-bottom" store-id="compare-main" store-version="1.0.0" min="100"></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="#detail" role="tab">明细</a>
-                        </li>
-                    </ul>
-                </div>
-                <div class="tab-content">
-                    <div class="tab-pane active" id="detail">
-                        <div class="sp-wrap" id="sub-spread">
-                        </div>
-                    </div>
-                </div>
+            <div z-index="0" style="display: none;">
+                <input class="datepicker-here form-control form-control-sm" data-date-format="yyyy-MM-DD" data-language="zh" type="text" autocomplete="off" id="dp-input">
             </div>
         </div>
     </div>
-</div>
+    <div style="display: none">
+        <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+    </div>
+</div>
+<script>
+    const stageId = <%- ctx.stage.id %>;
+    const stageUserId = <%- ctx.stage.user_id %>;
+    const readOnly = <%- ctx.stage.readOnly %>;
+</script>

+ 2 - 0
config/web.js

@@ -396,6 +396,8 @@ const JsFiles = {
                 files: [
                     "/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js",
                     "/public/js/decimal.min.js",
+                    "/public/js/datepicker/datepicker.min.js",
+                    "/public/js/datepicker/datepicker.zh.js",
                 ],
                 mergeFiles: [
                     "/public/js/sub_menu.js",