Explorar o código

1. 修复单元测试问题
2. 台账编辑Bug
3. 批量插入功能,界面调整

MaiXinRong %!s(int64=7) %!d(string=hai) anos
pai
achega
a8a301b511

+ 0 - 1
app/controller/ledger_controller.js

@@ -315,7 +315,6 @@ module.exports = app => {
                     throw '参数错误';
                 }
 
-                console.log(data.batchType);
                 switch (data.batchType) {
                     case 'batchInsertChild':
                         responseData.data = await ctx.service.ledger.batchInsertChild(tenderId, data.id, data.batchData);

+ 16 - 5
app/public/js/ledger.js

@@ -514,6 +514,7 @@ $(document).ready(function() {
                         batchInsertObj = new BatchInsertObj($('#batch'), key);
                     } else {
                         batchInsertObj.batchType = key;
+                        batchInsertObj.initView();
                     }
                     $('#batch').modal('show');
                 },
@@ -530,6 +531,7 @@ $(document).ready(function() {
                         batchInsertObj = new BatchInsertObj($('#batch'), key);
                     } else {
                         batchInsertObj.batchType = key;
+                        batchInsertObj.initView();
                     }
                     $('#batch').modal('show');
                 },
@@ -697,7 +699,6 @@ $(document).ready(function() {
                 emptyRows: 6,
             };
             this.xmSpread = SpreadJsObj.createNewSpread($('.batch-l-t')[0]);
-            SpreadJsObj.initSheet(this.xmSpread.getActiveSheet(), this.xmSpreadSetting);
 
             this.gclSpreadSetting = {
                 cols: [
@@ -710,10 +711,8 @@ $(document).ready(function() {
                 emptyRows: 2,
             };
             this.gclSpread = SpreadJsObj.createNewSpread($('.batch-l-b')[0]);
-            SpreadJsObj.initSheet(this.gclSpread.getActiveSheet(), this.gclSpreadSetting);
-            this.gclSpread.getActiveSheet().setColumnWidth(0, 45, GC.Spread.Sheets.SheetArea.rowHeader);
-            this.gclSpread.getActiveSheet().getCell(0, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单1');
-            this.gclSpread.getActiveSheet().getCell(1, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单2');
+
+            this.initView();
 
             this.dealSpreadSetting = {
                 cols: [
@@ -764,6 +763,18 @@ $(document).ready(function() {
                 });
             });
         }
+        initView() {
+            const xmSheet = this.xmSpread.getActiveSheet();
+            SpreadJsObj.initSheet(xmSheet, this.xmSpreadSetting);
+            xmSheet.clear(0, 0, xmSheet.getRowCount(), xmSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+
+            const gclSheet = this.gclSpread.getActiveSheet();
+            SpreadJsObj.initSheet(gclSheet, this.gclSpreadSetting);
+            gclSheet.setColumnWidth(0, 45, GC.Spread.Sheets.SheetArea.rowHeader);
+            gclSheet.getCell(0, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单1');
+            gclSheet.getCell(1, 0, GC.Spread.Sheets.SheetArea.rowHeader).text('清单2');
+            gclSheet.clear(0, 0, gclSheet.getRowCount(), gclSheet.getColumnCount(), GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+        }
         getBatchData () {
             const result = [];
             const xmSheet = this.xmSpread.getActiveSheet(), gclSheet = this.gclSpread.getActiveSheet();

+ 355 - 28
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -6,6 +6,7 @@
  * @version
  */
 
+const spreadNS = GC.Spread.Sheets;
 const SpreadJsObj = {
     /**
      * 创建Spread(默认1张表,3行数据)
@@ -13,7 +14,7 @@ const SpreadJsObj = {
      * @returns {GC.Spread.Sheets.Workbook}
      */
     createNewSpread: function (obj) {
-        const spread = new GC.Spread.Sheets.Workbook(obj, {sheetCount: 1});
+        const spread = new spreadNS.Workbook(obj, {sheetCount: 1});
         spread.options.tabStripVisible = false;
         spread.options.scrollbarMaxAlign = true;
         spread.options.cutCopyIndicatorVisible = false;
@@ -42,13 +43,19 @@ const SpreadJsObj = {
      * @param {GC.Spread.Sheets.Worksheet} sheet
      * @param {function} operation
      */
-    massOperationSheet: function (sheet, operation) {
+    beginMassOperation: function (sheet) {
         sheet.suspendPaint();
         sheet.suspendEvent();
-        operation();
+    },
+    endMassOperation: function (sheet) {
         sheet.resumeEvent();
         sheet.resumePaint();
     },
+    massOperationSheet: function (sheet, operation) {
+        this.beginMassOperation(sheet);
+        operation();
+        this.endMassOperation(sheet);
+    },
     /**
      * 获取Obj左顶点位置(部分功能需通过spreadjs左顶点位置计算)
      * @param obj
@@ -76,13 +83,13 @@ const SpreadJsObj = {
         return sheet.hitTest(x, y);
     },
     getTargetSelection: function (sheet, target) {
-        if (target.hitTestType === GC.Spread.Sheets.SheetArea.colHeader) {
+        if (target.hitTestType === spreadNS.SheetArea.colHeader) {
             return sheet.getRange(-1, target.col, sheet.getRowCount(), 1);
-        } else if (target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader) {
+        } else if (target.hitTestType === spreadNS.SheetArea.rowHeader) {
             return sheet.getRange(target.row, -1, 1, sheet.getColumnCount());
-        } else if (target.hitTestType === GC.Spread.Sheets.SheetArea.viewport) {
+        } else if (target.hitTestType === spreadNS.SheetArea.viewport) {
             return sheet.getRange(target.row, target.col, 1, 1);
-        } else if (target.hitTestType === GC.Spread.Sheets.SheetArea.corner) {
+        } else if (target.hitTestType === spreadNS.SheetArea.corner) {
             return sheet.getRange(-1, -1, sheet.getRowCount(), sheet.getColumnCount());
         };
     },
@@ -150,11 +157,11 @@ const SpreadJsObj = {
             sheet.setColumnCount(sheet.zh_setting.cols.length);
             for (const i in sheet.zh_setting.cols) {
                 const col = sheet.zh_setting.cols[i];
-                const cell = sheet.getCell(0, i, GC.Spread.Sheets.SheetArea.colHeader);
+                const cell = sheet.getCell(0, i, spreadNS.SheetArea.colHeader);
                 cell.text(col.title);
                 sheet.setColumnWidth(i, col.width);
             }
-            sheet.rowOutlines.direction(GC.Spread.Sheets.Outlines.OutlineDirection.backward);
+            sheet.rowOutlines.direction(spreadNS.Outlines.OutlineDirection.backward);
             sheet.showRowOutline(false);
             if (sheet.zh_setting.defaultRowHeight) {
                 sheet.defaults.rowHeight = sheet.zh_setting.defaultRowHeight;
@@ -177,12 +184,13 @@ const SpreadJsObj = {
      * @param sheet
      */
     reLoadSheetData: function (sheet) {
-        this.protectedSheet(sheet);
-        this.massOperationSheet(sheet, function () {
+        const self = this;
+        const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
+        this.beginMassOperation(sheet);
+        try {
             // 设置总行数
-            const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
             const totalRow = sortData.length + sheet.zh_setting.emptyRows;
-            sheet.setRowCount(totalRow, GC.Spread.Sheets.SheetArea.viewport);
+            sheet.setRowCount(totalRow, spreadNS.SheetArea.viewport);
             // 控制空白行
             const emptyRows = sheet.getRange(sortData.length, -1, sheet.zh_setting.emptyRows, -1);
             emptyRows.locked(sheet.zh_dataType === 'tree');
@@ -203,17 +211,19 @@ const SpreadJsObj = {
 
                 if (col.cellType === 'tree') {
                     if (!sheet.extendCellType.tree) {
-                        sheet.extendCellType.tree = SpreadJsExtendCellType.getTreeNodeCellType();
+                        sheet.extendCellType.tree = self.CellType.getTreeNodeCellType();
                     }
                     sheet.getRange(-1, j, -1, 1).cellType(sheet.extendCellType.tree);
                 } else if (col.cellType === 'tip') {
                     if (!sheet.extendCellType.tip) {
-                        sheet.extendCellType.tip = SpreadJsExtendCellType.getTipCellType();
+                        sheet.extendCellType.tip = self.CellType.getTipCellType();
                     }
                     sheet.getRange(-1, j, -1, 1).cellType(sheet.extendCellType.tip);
                 }
             });
-        });
+        } catch (err) {
+            this.endMassOperation(sheet);
+        }
     },
     /**
      * 重新加载部分数据行
@@ -222,10 +232,12 @@ const SpreadJsObj = {
      * @param {Number} count
      */
     reLoadRowData: function (sheet, row, count) {
-        this.massOperationSheet(sheet, function () {
-            const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
+        const self = this;
+        const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
+        this.beginMassOperation(sheet);
+        try {
             // 清空原单元格数据
-            sheet.clear(row, -1, count, -1, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+            sheet.clear(row, -1, count, -1, spreadNS.SheetArea.viewport, spreadNS.StorageType.data);
             // 单元格重新写入数据
             for (let i = row; i < row + count; i++) {
                 const data = sortData[i];
@@ -245,17 +257,19 @@ const SpreadJsObj = {
 
                 if (col.cellType === 'tree') {
                     if (!sheet.extendCellType.tree) {
-                        sheet.extendCellType.tree = SpreadJsExtendCellType.getTreeNodeCellType();
+                        sheet.extendCellType.tree = self.CellType.getTreeNodeCellType();
                     }
                     sheet.getRange(row, j, count, 1).cellType(sheet.extendCellType.tree);
                 } else if (col.cellType === 'tip') {
                     if (!sheet.extendCellType.tip) {
-                        sheet.extendCellType.tip = SpreadJsExtendCellType.getTipCellType();
+                        sheet.extendCellType.tip = self.CellType.getTipCellType();
                     }
                     sheet.getRange(row, j, count, 1).cellType(sheet.extendCellType.tip);
                 }
             });
-        });
+        } catch (err) {
+            this.endMassOperation(sheet);
+        }
     },
     /**
      * 重新加载部分行数据
@@ -263,11 +277,14 @@ const SpreadJsObj = {
      * @param {Array} rows
      */
     reLoadRowsData: function (sheet, rows) {
-        this.massOperationSheet(sheet, function () {
-            const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
+        const self = this;
+        const sortData = sheet.zh_dataType === 'tree' ? sheet.zh_tree.nodes : sheet.zh_data;
+
+        this.beginMassOperation(sheet);
+        try {
             for (const row of rows) {
                 // 清空原单元格数据
-                sheet.clear(row, -1, 1, -1, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data);
+                sheet.clear(row, -1, 1, -1, spreadNS.SheetArea.viewport, spreadNS.StorageType.data);
                 const data = sortData[row];
                 // 单元格重新写入数据
                 sheet.zh_setting.cols.forEach(function (col, j) {
@@ -282,19 +299,21 @@ const SpreadJsObj = {
                     if (col.cellType) {
                         if (col.cellType === 'tree') {
                             if (!sheet.extendCellType.tree) {
-                                sheet.extendCellType.tree = SpreadJsExtendCellType.getTreeNodeCellType();
+                                sheet.extendCellType.tree = self.CellType.getTreeNodeCellType();
                             }
                             sheet.getRange(row, j, 1, 1).cellType(sheet.extendCellType.tree);
                         } else if (col.cellType === 'tip') {
                             if (!sheet.extendCellType.tip) {
-                                sheet.extendCellType.tip = SpreadJsExtendCellType.getTipCellType();
+                                sheet.extendCellType.tip = self.CellType.getTipCellType();
                             }
                             sheet.getRange(row, j, 1, 1).cellType(sheet.extendCellType.tip);
                         }
                     }
                 });
             };
-        });
+        } catch (err) {
+            this.endMassOperation(sheet);
+        }
     },
     /**
      * 根据data加载sheet数据,合并了一般数据和树结构数据的加载
@@ -309,6 +328,7 @@ const SpreadJsObj = {
         } else {
             sheet.zh_data = data;
         }
+        this.protectedSheet(sheet);
         this.reLoadSheetData(sheet);
     },
     /**
@@ -356,5 +376,312 @@ const SpreadJsObj = {
             copyData.push(rowText.join('\t'));
         }
         return copyData.join('\n');
+    },
+
+    CellType: {
+        /**
+         * 获取树结构CellType
+         * 通过SpreadJsObj.loadSheetData(sheet, 'tree', tree)加载树结构数据
+         * 要求tree类型为PathTree, 节点必须含有{id, pid, level, order, is_leaf}数据
+         * @returns {TreeNodeCellType}
+         */
+        getTreeNodeCellType: function () {
+            const indent = 20;
+            const levelIndent = -5;
+            const halfBoxLength = 5;
+            const halfExpandLength = 3;
+
+            /**
+             * 画一条线段
+             * @param canvas - 画布
+             * @param x1 - 线段起点 x
+             * @param y1 - 线段起点 y
+             * @param x2 - 线段终点 x
+             * @param y2 - 线段终点 y
+             * @param color - 线段颜色
+             */
+            const drawLine = function (canvas, x1, y1, x2, y2, color) {
+                canvas.save();
+                // 设置偏移量
+                canvas.translate(0.5, 0.5);
+                canvas.beginPath();
+                canvas.moveTo(x1, y1);
+                canvas.lineTo(x2, y2);
+                canvas.strokeStyle = color;
+                canvas.stroke();
+                canvas.restore();
+            };
+            /**
+             * 画一个方框
+             * @param {Object} canvas - 画布
+             * @param {Object} rect - 方框区域
+             * @param {String} lineColor - 画线颜色
+             * @param {String} fillColor - 填充颜色
+             */
+            const drawBox = function (canvas, rect, lineColor, fillColor) {
+                canvas.save();
+                // 设置偏移量
+                canvas.translate(0.5, 0.5);
+                canvas.strokeStyle = lineColor;
+                canvas.beginPath();
+                canvas.moveTo(rect.left, rect.top);
+                canvas.lineTo(rect.left, rect.bottom);
+                canvas.lineTo(rect.right, rect.bottom);
+                canvas.lineTo(rect.right, rect.top);
+                canvas.lineTo(rect.left, rect.top);
+                canvas.stroke();
+                canvas.fillStyle = fillColor;
+                canvas.fill();
+                canvas.restore();
+            };
+            /**
+             * 画树结构-展开收起按钮
+             * @param {Object} canvas - 画布
+             * @param {Number} x - 单元格左顶点坐标 x
+             * @param {Number} y - 单元格左顶点坐标 y
+             * @param {Number} w - 单元格宽度
+             * @param {Number} h - 单元格高度
+             * @param {Number} centerX - 按钮中央坐标
+             * @param {Number} centerY - 按钮中央坐标
+             * @param {Boolean} expanded - 当前节点展开收起状态
+             */
+            const drawExpandBox = function (canvas, x, y, w, h, centerX, centerY, expanded) {
+                let rect = {
+                    top: centerY - halfBoxLength,
+                    bottom: centerY + halfBoxLength,
+                    left: centerX - halfBoxLength,
+                    right: centerX + halfBoxLength
+                };
+                let h1, h2, offset = 1;
+
+                if (rect.left < x + w) {
+                    // 方框超出单元格宽度时,超出部分不画。
+                    rect.right = Math.min(rect.right, x + w);
+                    drawBox(canvas, rect, 'black', 'white');
+
+                    // 画中心十字
+                    // 画十字横线
+                    h1 = centerX - halfExpandLength;
+                    h2 = Math.min(centerX + halfExpandLength, x + w);
+                    if (h2 > h1) {
+                        drawLine(canvas, h1, centerY, h2, centerY, 'black');
+                    }
+                    // 画十字竖线
+                    if (!expanded && (centerX < x + w)) {
+                        drawLine(canvas, centerX, centerY - halfExpandLength, centerX, centerY + halfExpandLength, 'black');
+                    }
+                }
+            };
+
+            let TreeNodeCellType = function (){};
+            TreeNodeCellType.prototype = new spreadNS.CellTypes.Text();
+            const proto = TreeNodeCellType.prototype;
+
+            /**
+             * 绘制方法
+             * @param {Object} canvas - 画布
+             * @param value - cell.value
+             * @param {Number} x - 单元格左顶点坐标 x
+             * @param {Number} y - 单元格左顶点坐标 y
+             * @param {Number} w - 单元格宽度
+             * @param {Number} h - 单元格高度
+             * @param {Object} style - cell.style
+             * @param {Object} options
+             */
+            proto.paint = function (canvas, value, x, y, w, h, style, options) {
+                // 清理 画布--单元格范围 旧数据
+                if (style.backColor) {
+                    canvas.save();
+                    canvas.fillStyle = style.backColor;
+                    canvas.fillRect(x, y, w, h);
+                    canvas.restore();
+                } else {
+                    canvas.clearRect(x, y, w, h);
+                }
+
+                const tree = options.sheet.zh_tree;
+                // 使用TreeCellType前,需定义sheet.tree
+                if (tree) {
+                    const node = options.row < tree.nodes.length ? tree.nodes[options.row] : null;
+                    if (node) {
+                        const showTreeLine = true;
+                        const centerX = Math.floor(x) + (node.level) * indent + (node.level) * levelIndent + indent / 2;
+                        const centerY = Math.floor((y + (y + h)) / 2);
+                        // Draw Sibling Line
+                        if (showTreeLine) {
+                            // Draw Horizontal Line
+                            if (centerX < x + w) {
+                                const x1 = centerX + indent / 2;
+                                drawLine(canvas, centerX, centerY, Math.min(x1, x + w), centerY, 'gray');
+                            }
+                            // Draw Vertical Line
+                            if (centerX < x + w) {
+                                const y1 = tree.isLastSibling(node) ? centerY : y + h;
+                                const parent = tree.getParent(node);
+                                const y2 = y1 - centerY;
+                                if (node.order === 1 && !parent) {
+                                    drawLine(canvas, centerX, centerY, centerX, y1, 'gray');
+                                } else {
+                                    drawLine(canvas, centerX, y, centerX, y1, 'gray');
+                                }
+                            }
+                        }
+                        // Draw Expand Box
+                        if (!node.is_leaf) {
+                            drawExpandBox(canvas, x, y, w, h, centerX, centerY, node.expanded);
+                        }
+                        // Draw Parent Line
+                        if (showTreeLine) {
+                            let parent = tree.getParent(node), parentCenterX = centerX - indent - levelIndent;
+                            while (parent) {
+                                if (!tree.isLastSibling(parent)) {
+                                    if (parentCenterX < x + w) {
+                                        drawLine(canvas, parentCenterX, y, parentCenterX, y + h, 'gray');
+                                    }
+                                }
+                                parent = tree.getParent(parent);
+                                parentCenterX -= (indent + levelIndent);
+                            }
+                        };
+                        // 重定位x
+                        x = x + (node.level + 1) * indent +  (node.level) * levelIndent;
+                    }
+                }
+                // Drawing Text
+                spreadNS.CellTypes.Text.prototype.paint.apply(this, arguments);
+            };
+            /**
+             * 获取点击信息
+             * @param {Number} x
+             * @param {Number} y
+             * @param {Object} cellStyle
+             * @param {Object} cellRect
+             * @param {Object} context
+             * @returns {{x: *, y: *, row: *, col: *|boolean|*[]|number|{}|UE.dom.dtd.col, cellStyle: *, cellRect: *, sheet: *|StyleSheet, sheetArea: *}}
+             */
+            proto.getHitInfo = function (x, y, cellStyle, cellRect, context) {
+                return {
+                    x: x,
+                    y: y,
+                    row: context.row,
+                    col: context.col,
+                    cellStyle: cellStyle,
+                    cellRect: cellRect,
+                    sheet: context.sheet,
+                    sheetArea: context.sheetArea
+                };
+            };
+            /**
+             * 鼠标点击 树结构按钮 响应展开收起(未加载子节点时,先加载子节点)
+             * @param {Object} hitinfo - 见getHitInfo
+             */
+            proto.processMouseDown = function (hitinfo) {
+                const offset = -1;
+
+                const tree = hitinfo.sheet.zh_tree;
+                if (!tree) { return; }
+
+                const node = tree.nodes[hitinfo.row];
+                if (!node) { return; }
+
+                let centerX = hitinfo.cellRect.x + offset + (node.level) * indent + (node.level) * levelIndent + indent / 2;
+                let centerY = (hitinfo.cellRect.y + offset + (hitinfo.cellRect.y + offset + hitinfo.cellRect.height)) / 2;
+
+                // 点击展开节点时,如果已加载子项,则展开,反之这加载子项,展开
+                if (Math.abs(hitinfo.x - centerX) < halfBoxLength && Math.abs(hitinfo.y - centerY) < halfBoxLength) {
+                    if (!node.expanded && !node.is_leaf && tree.getChildren(node).length === 0) {
+                        tree.loadChildren(node, function () {
+                            node.expanded = true;
+                            const children = tree.getChildren(node);
+                            hitinfo.sheet.addRows(hitinfo.row + 1, children.length);
+                            SpreadJsObj.reLoadRowData(hitinfo.sheet, hitinfo.row + 1, children.length);
+                        });
+                    } else {
+                        tree.setExpanded(node, !node.expanded);
+                        SpreadJsObj.massOperationSheet(hitinfo.sheet, function () {
+                            const posterity = tree.getPosterity(node);
+                            for (const child of posterity) {
+                                hitinfo.sheet.setRowVisible(tree.nodes.indexOf(child), child.visible, hitinfo.sheetArea);
+                            }
+                        });
+                        hitinfo.sheet.repaint();
+                    }
+                }
+            }
+
+            return new TreeNodeCellType();
+        },
+        /**
+         * 获取带悬浮提示CellType
+         * @returns {TipCellType}
+         */
+        getTipCellType: function () {
+            const TipCellType = function () {};
+            // 继承 SpreadJs定义的 普通的TextCellType
+            TipCellType.prototype = new spreadNS.CellTypes.Text();
+            const proto = TipCellType.prototype;
+
+            /**
+             * 获取点击信息
+             * @param {Number} x
+             * @param {Number} y
+             * @param {Object} cellStyle
+             * @param {Object} cellRect
+             * @param {Object} context
+             * @returns {{x: *, y: *, row: *, col: *|boolean|*[]|number|{}|UE.dom.dtd.col, cellStyle: *, cellRect: *, sheet: *|StyleSheet, sheetArea: *}}
+             */
+            proto.getHitInfo = function (x, y, cellStyle, cellRect, context) {
+                return {
+                    x: x,
+                    y: y,
+                    row: context.row,
+                    col: context.col,
+                    cellStyle: cellStyle,
+                    cellRect: cellRect,
+                    sheet: context.sheet,
+                    sheetArea: context.sheetArea
+                };
+            };
+            /**
+             * 鼠标进入单元格事件 - 显示悬浮提示
+             * @param {Object} hitinfo - 见getHitInfo返回值
+             */
+            proto.processMouseEnter = function (hitinfo) {
+                const text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
+                const setting = hitinfo.sheet.setting;
+                if (setting.pos && text && text !== '') {
+                    if (!this._toolTipElement) {
+                        let div = $('#autoTip')[0];
+                        if (!div) {
+                            div = document.createElement("div");
+                            $(div).css("position", "absolute")
+                                .css("border", "1px #C0C0C0 solid")
+                                .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)")
+                                .css("font", "9pt Arial")
+                                .css("background", "white")
+                                .css("padding", 5)
+                                .attr("id", 'autoTip');
+                            $(div).hide();
+                            document.body.insertBefore(div, null);
+                        }
+                        this._toolTipElement = div;
+                        $(this._toolTipElement).text(text).css("top", setting.pos.y + hitinfo.y + 15).css("left", setting.pos.x + hitinfo.x + 15);
+                        $(this._toolTipElement).show("fast");
+                    }
+                }
+            };
+            /**
+             * 鼠标移出单元格事件 - 隐藏悬浮提示
+             * @param {Object} hitinfo - 见getHitInfo返回值
+             */
+            proto.processMouseLeave = function (hitinfo) {
+                if (this._toolTipElement) {
+                    $(this._toolTipElement).hide();
+                    this._toolTipElement = null;
+                }
+            };
+
+            return new TipCellType();
+        }
     }
 };

+ 7 - 7
app/service/ledger.js

@@ -25,6 +25,7 @@ const readOnlyFields = ['id', 'tender_id', 'ledger_id', 'ledger_pid', 'order', '
 const calcFields = ['quantity', 'unit_price', 'total_price'];
 const zeroRange = 0.0000000001;
 const rootId = -1;
+const keyPre = 'tender_node_maxId:';
 
 module.exports = app => {
 
@@ -493,7 +494,7 @@ module.exports = app => {
             if (!data) {
                 data = {};
             }
-            const cacheKey = 'tender_node_maxId:' + tenderId;
+            const cacheKey = keyPre + tenderId;
             let maxId = parseInt(await this.cache.get(cacheKey));
             if (!maxId) {
                 maxId = await this._getMaxNodeId(tenderId);
@@ -530,12 +531,12 @@ module.exports = app => {
             }
             const pid = parentData ? parentData.ledger_id : rootId;
 
-            const cacheKey = 'tender_node_maxId: ' + tenderId;
+            const cacheKey = keyPre + tenderId;
             let maxId = parseInt(await this.cache.get(cacheKey));
             if (!maxId) {
                 maxId = await this._getMaxNodeId(tenderId);
-                this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime);
             }
+            this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime);
 
             data.tender_id = tenderId;
             data.ledger_id = maxId + 1;
@@ -550,7 +551,6 @@ module.exports = app => {
             }
             const result = await this.transaction.insert(this.tableName, data);
 
-            this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime);
             return [result, data];
         }
 
@@ -1305,7 +1305,7 @@ module.exports = app => {
                     incre += node.total_price ? node.total_price : 0;
                     const datas = await this.getDataByFullPath(tenderId, node.full_path + '%');
 
-                    const cacheKey = 'tender_node_maxId:' + tenderId;
+                    const cacheKey = keyPre + tenderId;
                     let maxId = parseInt(await this.cache.get(cacheKey));
                     if (!maxId) {
                         maxId = await this._getMaxNodeId(tenderId);
@@ -1486,7 +1486,7 @@ module.exports = app => {
         async _sortBatchInsertData(tenderId, xmj, order, parentData) {
             const result = [], newIds = [];
             let tp = 0;
-            const cacheKey = 'tender_node_maxId:' + tenderId;
+            const cacheKey = keyPre + tenderId;
             let maxId = parseInt(await this.cache.get(cacheKey));
             if (!maxId) {
                 maxId = await this._getMaxNodeId(tenderId);
@@ -1497,7 +1497,7 @@ module.exports = app => {
                 tender_id: tenderId,
                 ledger_id: maxId + 1,
                 ledger_pid: parentData.ledger_id,
-                is_leaf: false,
+                is_leaf: xmj.children.length === 0,
                 order: order,
                 level: parentData.level + 1,
                 name: xmj.name,

+ 7 - 5
test/app/service/deal_bills.test.js

@@ -14,13 +14,15 @@ const excel = require('node-xlsx');
 describe('test/app/service/deal_bills.test.js', () => {
     it('test import Excel data', function* () {
         const ctx = app.mockContext();
-        const file = app.baseDir  + '/test/app/test_file/deal-load-test.xls';
+        const file = app.baseDir  + '/test/app/test_file/deal-upload-test.xls';
         const sheets = excel.parse(file), testTenderId = 3;
 
         const result = yield ctx.service.dealBills.importData(sheets[0], testTenderId);
-        assert(result.length === 1);
-        const bills = result[0];
-        assert(bills.code === '101-1');
-        assert(bills.tender_id === testTenderId);
+        assert(result);
+
+        const bills = yield ctx.service.dealBills.getAllDataByCondition({where: {tender_id: testTenderId}});
+        assert(bills.length === 1);
+        assert(bills[0].code === '101-1');
+        assert(bills[0].tender_id === testTenderId);
     });
 });

+ 1 - 0
test/app/service/ledger.test.js

@@ -994,6 +994,7 @@ describe('test/app/service/ledger.test.js', () => {
         // 选中1-1-3(id=14)
         const result = yield ctx.service.ledger.batchInsertChild(testTenderId, 14, batchData);
 
+        console.log(result.create);
         assert(result.create.length === 6);
 
         assert(result.update.length === 3);