Pārlūkot izejas kodu

Merge branch 'master' into olym

olym 7 gadi atpakaļ
vecāks
revīzija
2dc64c46ed

+ 26 - 0
app/controller/ledger_controller.js

@@ -49,6 +49,32 @@ module.exports = app => {
             };
             await this.layout('ledger/explode.ejs', renderData);
         }
+
+        async getChildren(ctx) {
+            const responseData = {
+                err: 0,
+                msg: '',
+                data: []
+            };
+            try {
+                const tenderId = ctx.session.sessionUser.tenderId;
+                if (!tenderId) {
+                    throw '当前未打开标段';
+                }
+                const data = JSON.parse(ctx.request.body.data);
+                const id = data.id;
+                if (isNaN(id) || id <= 0) {
+                    throw '参数错误';
+                }
+
+                responseData.data = await ctx.service.ledger.getChildrenByParentId(tenderId, id);
+            } catch (err) {
+                responseData.err = 1;
+                responseData.msg = err;
+            }
+
+            ctx.body = responseData;
+        }
     }
 
     return LedgerController;

+ 27 - 0
app/public/js/cookies.js

@@ -0,0 +1,27 @@
+/**
+ * cookies相关操作
+ *
+ * @author CaiAoLin
+ * @date 2018/1/8
+ * @version
+ */
+let Cookies = {
+    get: function(name) {
+        if(document.cookie.length <= 0) {
+            return "";
+        }
+
+        let start = document.cookie.indexOf(name + "=");//获取字符串的起点
+        if(start < 0) {
+            return "";
+        }
+        // 获取值的起点
+        start = start + name.length + 1;
+        // 获取结尾处
+        let end = document.cookie.indexOf(";", start);
+        // 如果是最后一个,结尾就是cookie字符串的结尾
+        end = end === -1 ? document.cookie.length : end;
+        // 截取字符串返回
+        return decodeURI(document.cookie.substring(start, end));
+    },
+};

+ 148 - 5
app/public/js/path_tree.js

@@ -2,47 +2,190 @@
 const createNewPathTree = function (setting) {
     const treeSetting = JSON.parse(JSON.stringify(setting));
     const itemsPre = 'id_';
+    const postData = function (url, data, successCallback) {
+        $.ajax({
+            type:"POST",
+            url: url,
+            data: {'data': JSON.stringify(data)},
+            dataType: 'json',
+            cache: false,
+            timeout: 5000,
+            beforeSend: function(xhr) {
+                let csrfToken = Cookies.get('csrfToken');
+                xhr.setRequestHeader('x-csrf-token', csrfToken);
+            },
+            success: function(result){
+                if (result.err === 0) {
+                    if (successCallback) {
+                        successCallback(result.data);
+                    }
+                } else {
+                    toast('error: ' + result.message, 'error', 'exclamation-circle');
+                }
+            },
+            error: function(jqXHR, textStatus, errorThrown){
+                toast('error ' + textStatus + " " + errorThrown, 'error', 'exclamation-circle');
+            }
+        });
+    };
 
     const PathTree = function () {
+        // 无索引
+        this.datas = [];
         // 以key为索引
         this.items = {};
         // 以排序为索引
         this.nodes = [];
     };
     const proto = PathTree.prototype;
+    /**
+     * 树结构根据显示排序
+     */
     proto.sortTreeNode = function () {
-        this.nodes.sort(function (a, b) {
-            return a.level === b.level ? a.order - b.order : a.level - b.level;
-        });
+        const self = this;
+        const addSortNodes = function (nodes) {
+            for (let i = 0; i < nodes.length; i++) {
+                self.nodes.push(nodes[i]);
+                addSortNodes(self.getChildren(nodes[i]));
+            }
+        };
+        self.nodes = [];
+        addSortNodes(this.getChildren(null));
     };
+    /**
+     * 加载数据(初始化), 并给数据添加部分树结构必须数据
+     * @param datas
+     */
     proto.loadDatas = function (datas) {
+        // 清空旧数据
+        this.items = {};
+        this.nodes = [];
+        // 加载全部数据
         for (const data of datas) {
             const keyName = itemsPre + data[treeSetting.id];
             this.items[keyName] = JSON.parse(JSON.stringify(data));
-            this.nodes.push(this.items[keyName]);
+            this.datas.push(this.items[keyName]);
         }
         this.sortTreeNode();
         for (const node of this.nodes) {
             const children = this.getChildren(node);
             node.expanded = children.length > 0;
+            node.visible = true;
         }
     };
+    /**
+     * 加载数据(动态),只加载不同部分
+     * @param {Array} datas
+     * @privateA
+     */
+    proto._loadData = function (datas) {
+        for (const data of datas) {
+            let node = this.getItems(data[treeSetting]);
+            if (node) {
+                for (const prop of node.propertyNameList) {
+                    if (data[prop]) {
+                        node[prop] = data[prop];
+                    }
+                }
+            } else {
+                const keyName = itemsPre + data[treeSetting.id];
+                const node = JSON.parse(JSON.stringify(data));
+                this.items[keyName] = node;
+                this.datas.push(node);
+                node.expanded = false;
+                node.visible = true;
+            }
+        }
+        this.sortTreeNode();
+    }
+    /**
+     * 根据id获取树结构节点数据
+     * @param {Number} id
+     * @returns {Object}
+     */
     proto.getItems = function (id) {
         return this.items[itemsPre + id];
     };
+    /**
+     * 查找node的parent
+     * @param {Object} node
+     * @returns {Object}
+     */
     proto.getParent = function (node) {
         return this.getItems(node[treeSetting.pid]);
     };
+    /**
+     * 查询node的已下载子节点
+     * @param {Object} node
+     * @returns {Array}
+     */
     proto.getChildren = function (node) {
         const pid = node ? node[treeSetting.id] : treeSetting.rootId;
-        return this.nodes.filter(function (x) {
+        const children = this.datas.filter(function (x) {
             return x[treeSetting.pid] === pid;
         });
+        children.sort(function (a, b) {
+            return a.order - b.order;
+        });
+        return children;
     };
+    /**
+     * 查询node的已下载的全部后代
+     * @param {Object} node
+     * @returns {Array}
+     */
+    proto.getPosterity = function (node) {
+        const reg = new RegExp('^' + node.full_path);
+        return this.datas.filter(function (x) {
+            return reg.test(x.full_path);
+        })
+    };
+    /**
+     * 查询node是否是父节点的最后一个子节点
+     * @param {Object} node
+     * @returns {boolean}
+     */
     proto.isLastSibling = function (node) {
         const siblings = this.getChildren(this.getParent(node));
         return node.order === siblings.length;
     };
+    /**
+     * 刷新子节点是否可见
+     * @param {Object} node
+     * @private
+     */
+    proto._refreshChildrenVisible = function (node) {
+        const children = this.getChildren(node);
+        for (const child of children) {
+            child.visible = node.expanded && node.visible;
+            this._refreshChildrenVisible(child);
+        }
+    }
+    /**
+     * 设置节点是否展开, 并控制子节点可见
+     * @param {Object} node
+     * @param {Boolean} expanded
+     */
+    proto.setExpanded = function (node, expanded) {
+        node.expanded = expanded;
+        this._refreshChildrenVisible(node);
+    };
+
+    /**
+     * 以下方法需等待响应
+     */
+    /**
+     * 加载子节点, callback中应刷新界面
+     * @param {Object} node
+     * @param {function} callback
+     */
+    proto.loadChildren = function (node, callback) {
+        const self = this;
+        postData('get-children', {id: node[treeSetting.id]}, function (data) {
+            self._loadData(data);
+            callback();
+        });
+    }
 
     return new PathTree();
 }

+ 27 - 21
app/public/js/spreadjs_rela/extend_celltype.js

@@ -97,7 +97,7 @@ SpreadJsExtendCellType = {
                     drawLine(canvas, centerX, centerY - halfExpandLength, centerX, centerY + halfExpandLength, 'black');
                 }
             }
-        }
+        };
 
         let TreeNodeCellType = function (){};
         TreeNodeCellType.prototype = new GC.Spread.Sheets.CellTypes.Text();
@@ -131,7 +131,7 @@ SpreadJsExtendCellType = {
                 const node = options.row < tree.nodes.length ? tree.nodes[options.row] : null;
                 if (node) {
                     const showTreeLine = true;
-                    const centerX = Math.floor(x) + (node.level - 1) * indent + (node.level - 1) * levelIndent + indent / 2;
+                    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) {
@@ -170,7 +170,7 @@ SpreadJsExtendCellType = {
                         }
                     };
                     // 重定位x
-                    x = x + (node.level) * indent +  (node.level - 1) * levelIndent;
+                    x = x + (node.level + 1) * indent +  (node.level) * levelIndent;
                 }
             }
             // Drawing Text
@@ -198,7 +198,7 @@ SpreadJsExtendCellType = {
             };
         };
         /**
-         * 鼠标点击 树结构按钮 响应展开收起
+         * 鼠标点击 树结构按钮 响应展开收起(未加载子节点时,先加载子节点)
          * @param {Object} hitinfo - 见getHitInfo
          */
         proto.processMouseDown = function (hitinfo) {
@@ -207,26 +207,32 @@ SpreadJsExtendCellType = {
             const tree = hitinfo.sheet.zh_tree;
             if (!tree) { return; }
 
-            const node = tree.items[hitinfo.row];
-            if (!node || node.children.length === 0) { return; }
+            const node = tree.nodes[hitinfo.row];
+            if (!node) { return; }
 
-            let centerX = hitinfo.cellRect.x + offset + (node.level - 1) * indent + node.level * levelIndent + indent / 2;
+            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;
 
-            // Todo 点击展开节点时,如果已加载子项,则展开,反之这加载子项,展开
-            /*if (hitinfo.x > centerX - halfBoxLength && hitinfo.x < centerX + halfBoxLength && hitinfo.y > centerY - halfBoxLength && hitinfo.y < centerY + halfBoxLength) {
-                node.setExpanded(!node.expanded);
-                // 重置子项是否可见
-                TREE_SHEET_HELPER.massOperationSheet(hitinfo.sheet, function () {
-                    let iCount = node.posterityCount(), i, child;
-                    for (i = 0; i < iCount; i++) {
-                        child = tree.items[hitinfo.row + i + 1];
-                        hitinfo.sheet.setRowVisible(hitinfo.row + i + 1, child.visible, hitinfo.sheetArea);
-                    }
-                    hitinfo.sheet.invalidateLayout();
-                });
-                hitinfo.sheet.repaint();
-            }*/
+            // 点击展开节点时,如果已加载子项,则展开,反之这加载子项,展开
+            if (Math.abs(hitinfo.x - centerX) < halfBoxLength && Math.abs(hitinfo.y - centerY) < halfBoxLength) {
+                if (!node.expanded && !node.isleaf && 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();

+ 61 - 19
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -136,6 +136,10 @@ const SpreadJsObj = {
             let selections = sheet.getSelections();
         });
     },
+    /**
+     * 根据sheet.zh_setting初始化sheet表头
+     * @param {GC.Spread.Sheets.Worksheet} sheet
+     */
     initSheetHeader: function (sheet) {
         if (!sheet.zh_setting) { return; }
 
@@ -154,11 +158,20 @@ const SpreadJsObj = {
             }
         });
     },
+    /**
+     * 初始化sheet, 设置sheet.zh_setting, 并初始化表头
+     * @param {GC.Spread.Sheets.Worksheet} sheet
+     * @param setting
+     */
     initSheet: function (sheet, setting) {
         sheet.zh_setting = setting;
         this.initSheetHeader(sheet);
         sheet.extendCellType = {};
     },
+    /**
+     * 整个sheet重新加载数据
+     * @param sheet
+     */
     reLoadSheetData: function (sheet) {
         this.protectedSheet(sheet);
         this.massOperationSheet(sheet, function () {
@@ -170,47 +183,76 @@ const SpreadJsObj = {
             const emptyRows = sheet.getRange(sortData.length, -1, sheet.zh_setting.emptyRows, -1);
             emptyRows.locked(sheet.zh_dataType === 'tree');
             // 单元格写入数据
-            for (const i in sortData) {
-                const data = sortData[i];
-                for (const j in sheet.zh_setting.cols) {
-                    const col = sheet.zh_setting.cols[j];
-                    const cell = sheet.getCell(i, j, GC.Spread.Sheets.SheetArea.viewport);
+            sortData.forEach(function (data, i) {
+                sheet.zh_setting.cols.forEach(function (col, j) {
+                    const cell = sheet.getCell(i, j);
                     if (col.field !== '' && data[col.field]) {
                         cell.value(data[col.field]);
-                        console.log('row: ' + i + ' col: ' + j + ' value: ' + cell.value());
                     }
+                })
+            });
+            // 设置列单元格格式
+            sheet.zh_setting.cols.forEach(function (col, j) {
+                if (!col.cellType) { return; }
+
+                if (col.cellType === 'tree') {
+                    if (!sheet.extendCellType.tree) {
+                        sheet.extendCellType.tree = SpreadJsExtendCellType.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.getRange(-1, j, -1, 1).cellType(sheet.extendCellType.tip);
                 }
-            }
-            /*sortData.forEach(function (data, i) {
+            });
+        });
+    },
+    /**
+     * 重新加载部分数据行
+     * @param sheet
+     * @param {Number} row
+     * @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;
+            // 单元格重新写入数据
+            for (let i = row; i < row + count; i++) {
+                const data = sortData[i];
+                console.log(data);
                 sheet.zh_setting.cols.forEach(function (col, j) {
                     const cell = sheet.getCell(i, j);
                     if (col.field !== '' && data[col.field]) {
                         cell.value(data[col.field]);
                     }
-                })
-            });*/
-            console.log(sheet.getCell(5, 0).value());
-            console.log(sheet.getCell(6, 0).value());
-            console.log(sortData);
+                });
+            };
             // 设置列单元格格式
-            for (const j in sheet.zh_setting.cols) {
-                const col = sheet.zh_setting.cols[j];
-                if (!col.cellType) { continue; }
+            sheet.zh_setting.cols.forEach(function (col, j) {
+                if (!col.cellType) { return; }
 
                 if (col.cellType === 'tree') {
                     if (!sheet.extendCellType.tree) {
                         sheet.extendCellType.tree = SpreadJsExtendCellType.getTreeNodeCellType();
                     }
-                    sheet.getRange(-1, j, -1, 1).cellType(sheet.extendCellType.tree);
+                    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.getRange(-1, j, -1, 1).cellType(sheet.extendCellType.tip);
+                    sheet.getRange(row, j, count, 1).cellType(sheet.extendCellType.tip);
                 }
-            }
+            });
         });
     },
+    /**
+     * 根据data加载sheet数据,合并了一般数据和树结构数据的加载
+     * @param {GC.Spread.Sheets.Worksheet} sheet
+     * @param {String} dataType - 1.'zh_data' 2.'zh_tree'
+     * @param {Array|PathTree} data - 对dataType对应
+     */
     loadSheetData: function (sheet, dataType, data){
         sheet.zh_dataType = dataType;
         if (dataType === 'tree') {

+ 1 - 0
app/router.js

@@ -33,6 +33,7 @@ module.exports = app => {
 
     // 台账管理相关
     app.get('/ledger/explode', sessionAuth, 'ledgerController.explode');
+    app.post('/ledger/get-children', sessionAuth, 'ledgerController.getChildren');
 
     // 个人账号相关
     app.get('/profile/info', sessionAuth, 'profileController.info');

+ 28 - 1
app/service/ledger.js

@@ -80,7 +80,7 @@ module.exports = app => {
          * @param {Number} showLevel - 显示层数
          * @return {Array} - 返回数据
          */
-        async getDataByTenderId(tenderId, showLevel = 2) {
+        async getDataByTenderId(tenderId, showLevel = 4) {
             if (tenderId <= 0) {
                 return [];
             }
@@ -200,6 +200,33 @@ module.exports = app => {
         }
 
         /**
+         * 根据 父节点id 获取子节点
+         * @param tenderId
+         * @param nodeId
+         * @returns {Promise<*>}
+         */
+        async getChildrenByParentId(tenderId, nodeId) {
+            if ((nodeId <= 0) || (tenderId <= 0)) {
+                return undefined;
+            }
+
+            this.initSqlBuilder();
+            this.sqlBuilder.setAndWhere('tender_id', {
+                value: tenderId,
+                operate: '=',
+            });
+            this.sqlBuilder.setAndWhere('ledger_pid', {
+                value: nodeId,
+                operate: '=',
+            });
+
+            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+            const data = await this.db.query(sql, sqlParam);
+
+            return data;
+        }
+
+        /**
          * 根据full_path获取数据 full_path Like ‘1.2.3%’(传参full_path = '1.2.3%')
          * @param {Number} tenderId - 标段id
          * @param {String} full_path - 路径

+ 1 - 0
app/view/layout/layout.ejs

@@ -19,6 +19,7 @@
     <script src="/public/js/global.js"></script>
     <script src="/public/js/vue/vue.js"></script>
     <script src="/public/js/component/input.js"></script>
+    <script src="/public/js/cookies.js"></script>
 </head>
 
 <body>

+ 1 - 1
app/view/ledger/explode.ejs

@@ -61,7 +61,7 @@
             </ul>
         </div>
         <div class="c-body col-8">
-            <div id="ledger-spread" style="border: 1px solid gray; height: 200px"></div>
+            <div id="ledger-spread" style="border: 1px solid gray; height: 750px"></div>
             <!--<table class="table table-bordered">-->
                 <!--<tr>-->
                     <!--<th></th>-->