2019独角兽企业重金招聘Python工程师标准>>>

今天我们来看一下Excel在线部分的文件和文件组。首先我们来看一下页面,调一下胃口。俗话说无图无真相,先看图。

没错,还是Telerik Kendo UI,其实我面试的时候当听到别人说自己用的是EasyUI和ExtJs的时候,我就不那么上心,但是如果有人用的是Kendo UI forHTML5&JS的话,我就会多关注一点。

先看一下页面整体代码。

很简单,还是BootStrap布局,jade模板。注意下最底下的css样式,在jade模板中,如果想要在页面定义css,就要像上面这样写。注意这里我们引用了一个部分页,popup.jade,里面其实就是第一幅图里面New,Rename等按钮弹出的modal页。

OK,首先我们看到的是左边的树,这个树叫kendoTreeView,我们来看一下这个树是怎么生成的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

var url = "/filegroup/list/" + userObj.UserID;

var dataSource = new kendo.data.HierarchicalDataSource({

        transport: {

            read: {

                url: url,

                dataType: "json"

            }

        },

        schema: {

            model: {

                id: "_id",

                children: "subgroup",

                expanded: true,

                hasChildren: function (node) {

                    return (node.subgroup && node.subgroup.length > 0);

                },

                spriteCssClass: "folder"

            }

        }

});

    

$("#treeview-filegroup").kendoTreeView({

        dataSource: dataSource,

        dataTextField: ["filecount"],

        dataValueField: ["_id"],

        change: function (e) {

            var tree = e.sender;

            selNode = tree.select();

            var data = tree.dataItem(selNode);

            

            if (data._id) {

                selGroupId = data._id;

                $("#chk_all").prop('checked'false);

                getFilelist(data._id);

            }

        }

    });

OK,这段代码就是生成树的代码,注意这里的dataSource,当页面加载以后,会请求url,filegroup/list/{0},我们来看一下后台这个api。

1

router.get('/filegroup/list/:userId', fileRoutes.fileGroupList);

再看一下fileGroupList方法。

1

2

3

4

5

exports.fileGroupList = function (req, res) {

    fileGroupModel.find({ 'userid': req.params.userId }, null, { sort: { name: 1 } } , function (error, fileGroup) {

        res.json(fileGroup);

    });

}

其实就是根据传入的userid查询了一下fileGroup Collection,查出来后,注意这里的schema,它的model定义树节点id是我们mongodb的主键,children是subgroup(上节讲过group和subgroup的model定义,不明白的去上节看),hasChildrenC返回是否有子节点。spriteCssClass设置父节点样式,注意我们第一幅图定义的页面样式就用在这里。OK,接下里我们看树的change事件,当有选中的节点时,将右边列表表头的全选复选框uncheck,并根据选中的_id去mongodb查询group下面的数据,我们来看一下getFilelist方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

function getFilelist(groupId) {

    if (!groupId) return;

    

    $.get("/filegroup/" + groupId, function (result) {

        var grid = $("#file_list").data("kendoGrid");

        if (result) {

            var dataSource = new kendo.data.DataSource({

                pageSize: 10,

                data: result,

                schema: {

                    parse: function (response) {

                        $.each(response, function (idx, elem) {

                            if (elem.createdate && typeof elem.createdate === "string") {

                                elem.createdate = kendo.parseDate(elem.createdate, "yyyy-MM-ddTHH:mm:ss.fffZ");

                            }

                            

                            if (elem.lasteditdate && typeof elem.lasteditdate === "string") {

                                elem.lasteditdate = kendo.parseDate(elem.lasteditdate, "yyyy-MM-ddTHH:mm:ss.fffZ");

                            }

                        });

                        return response;

                    }

                }

            });

            

            grid.setDataSource(dataSource);

        }

        else {

            grid.dataSource.data([]);

        }

    });

}

直接调用rest api filegroup/{0}查询数据,得到结果以后,构造kendo Grid的dataSource,对日期进行格式化。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

router.get('/filegroup/:id', fileRoutes.fileGroup);

exports.fileGroup = function (req, res) {

    var groupId = req.params.id;

    fileGroupModel.findById(groupId).populate('file').exec(function (error, doc) {

        if (!doc || doc.length == 0) {

            fileGroupModel.findOne({ 'subgroup._id': groupId })

            .populate('subgroup')

            .populate('subgroup.file')

            .exec(function (error, docs) {

                if (docs) {

                    var subGroupIndex = -1;

                    docs.subgroup.forEach(function (element, index, arra) {

                        if (subGroupIndex > -1) return;

                        

                        if (element._id == groupId) {

                            subGroupIndex = index;

                        }

                    });

                    

                    if (subGroupIndex > -1) {

                        res.json(docs.subgroup[subGroupIndex].file);

                    }

                }

            });

        }

        else {

            res.json(doc.file);

        }

    });

}

这里要注意,首先我们也不知道这里传入的是groupId还是subgroupId,所以我们先拿groupId查询,如果查到了,就返回数据,如果没查到,就拿_id去查subgroup,查询subgroup,注意这里要使用subgroup._id作为查询条件,因为subgroup是嵌入在group中的,是一个整体。查完之后注意这里的两个populate,如果没有populate的话,意味着这些嵌入的subgroup以及引用的file都不会被包含在查询结果中,我们来看一下查询的结果,在后台加一句console.log(docs)即可。有两个subgroup,两个下面都有文件。

我们用robomongo也许看的更清晰一些,两个group,一个下面有7个文件,一个有2个文件。

此时这个结果的话,我们还得找到我们页面传递的_id对应的subgroup下面的file。所以在代码中又有了循环匹配id确定subgroup索引下标的过程,找到后,直接根据索引拿出file。是不是很麻烦,如果你有什么好的办法,可以给我留言。

接下来我们来看一下kendo grid,首先看一下这个全选。

1

2

3

4

5

6

$("#chk_all").click(function () {

    var isChecked = $(this).prop("checked");

    $("#file_list table:eq(1)").find("tr").each(function () {

        $(this).children("td:first").find("input[type='checkbox']").first().prop('checked', isChecked);

    });

});

看起来很简单,找到第一个table找到所有tr,再找到第一个td,锁定checkbox让它选中或者不选中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

$("#file_list").kendoGrid({

        scrollable: true,

        selectable: true,

        allowCopy: true,

        resizable: false,

        sortable: true,

        pageable: {

            refresh: true,

            pageSizes: [10, 20, 50, 100],

            buttonCount: 5

        },

        toolbar: [

            { name: 'share', imageClass: 'glyphicon glyphicon-share-alt' },

            { name: 'unshare', imageClass: 'glyphicon glyphicon-lock' },

            { name: 'batch_delete', text: "Delete" , imageClass: 'glyphicon glyphicon-trash' }, 

            { name: 'export', imageClass: 'k-icon k-i-excel' }],

        columns: [{

                template: "<div class='center-align-text'>" +

                    "<input id='chkId_#=_id#' type='checkbox' class='k-checkbox' value='#=_id#' onclick='chkHeader_click'/>"

                    "<label class='k-checkbox-label' for='chkId_#=_id#'></label></div>",

                field: "",

                title: "<div class='center-align-text'>" +

                    "<input type='checkbox' class='k-checkbox' id='chk_all'/>"

                    "<label class='k-checkbox-label' for='chk_all'></label></div>",

                width: 40

            },

            {

                field: "fullname"

                title: "File Name"

            }, {

                field: "isshared"

                title: "Shared" ,

                width: 85, 

                template: "<div><input type='checkbox' class='k-checkbox' value='#=isshared#' #=isshared ? \"checked='checked'\":\"\" # />"

                "<label class='k-checkbox-label'></label></div>",

                sortable: false

            },

            {

                command: [

                    {

                        name: "preview",

                        text: ""

                        imageClass: 'glyphicon glyphicon-search',

                        click: showDetails

                    }, {

                        name: "delete",

                        text: ""

                        imageClass: 'glyphicon glyphicon-trash',

                        click: confirmFileDelete

                    }, {

                        name: "rename",

                        text: "",

                        imageClass: 'glyphicon glyphicon-list-alt',

                        click: fileRename

                    }, {

                        name: "edits"

                        text: ""

                        imageClass: 'glyphicon glyphicon-pencil',

                        click: viewfile

                    }

                ], 

                width: 310,

                title: "Operation"

            }]

    });

注意grid中的Shared列,使用的是kendo的模板#=#这种写法。最后我们再看一下command列,这个列的话其实就是定义一些按钮,每个按钮都定义好了click事件。就看第一个showDetails,看看js代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

function showDetails(e) {

    var wnd = $("#wd_details").kendoWindow({

        title: "File Detail Info",

        modal: true,

        visible: false,

        resizable: false,

        minWidth: 300

    }).data("kendoWindow");

    

    var detailsTemplate = kendo.template($("#popup_detail").html());

    e.preventDefault();

    

    var dataItem = this.dataItem($(e.currentTarget).closest("tr"));

    wnd.content(detailsTemplate(dataItem));

    wnd.center().open();

}

在这里其实就是弹出一个kendo window,这个弹出页中又加载了kendo 模板popup_detail,我们来看一下这个template。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

script#popup_detail(type="text/x-kendo-template")

 dl

  dt

   label File Name:

  dd #=fullname#

  dt

   label Shared:

  dd #=isshared#

  dt

   label CreateDate:

  dd #=kendo.toString(createdate,'MM/dd/yyyy HH:mm tt')#

  dt

   label LastEditUser:

  dd #=lastedituser#

  dt

   label LastEditDate:

  dd #=kendo.toString(lasteditdate,'MM/dd/yyyy HH:mm tt')#

只要我们将一个对象给模板,模板就会自己替换对应的内容。在这里我们会先拿到点击行的对象,然后通过.content赋给模板,最后弹出modal页。

就是这么简单,点击重命名按钮,就会弹出重命名页面。

rename功能其实很简单,看看前台和后台。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

$("#btn_fileRename").click(function () {

    var fileName = $.trim($("#new_fileName").val());

    if (!fileName) {

        showMsg("info""Please input new file name!");

        return;

    }

    

    var postData = {

        id: $("#hfd_fileId").val(),

        filename: fileName

    };

    

    $.ajax({

        url: '/file/rename'

        type: 'PUT'

        dataType: 'json',

        data: { postData: JSON.stringify(postData) },

        success: function (res) {

            if (!res.isSuc) {

                showMsg('error', res.msg);

                return;

            }

            

            $("#wd_fileRename").data("kendoWindow").close();

            getFilelist(selGroupId);

        }

    });

});

1

2

3

4

5

6

7

8

9

10

11

12

13

router.put('/file/rename', fileRoutes.fileRename);

exports.fileRename = function (req, res) {

    var data = JSON.parse(req.body.postData);

    fileModel.findByIdAndUpdate(data.id, { $set: { name: data.filename, lasteditdate: Date.now() } }

        function (error, result) {

        if (error) {

            res.json({ isSuc: false, msg: error.message });

        }

        else {

            res.json({ isSuc: true });

        }

    });

}

nodejs,用起来就是这么爽,好了今天就到这里,明天我们继续会讲剩下的group&subgroup创建,文件删除,共享设置等功能,敬请期待。

结束语

免费学习更多精品课程,登录乐搏学院官网http://h.learnbo.cn/

本文出自 “技术创造价值” 博客,请务必保留此出处http://leelei.blog.51cto.com/856755/1795764

转载于:https://my.oschina.net/learnbo/blog/760054

Node.js 切近实战(七) 之Excel在线(文件文件组)相关推荐

  1. Node.js 切近实战(八) 之Excel在线(文件权限)

    2019独角兽企业重金招聘Python工程师标准>>> 最近美国又他妈的皮痒了,在南海找事,还说什么中国必须接受南海仲裁结果,我去你大爷的,你以为你是谁啊.说实话只要我们要决一死战的 ...

  2. Node.js 切近实战(十一) 之实时通讯

    2019独角兽企业重金招聘Python工程师标准>>> 今天我们主要看一下Socket.IO实时通讯,先看一下界面. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...

  3. Node.js 切近实战(四) 之图书管理系统(图书查询)

    2019独角兽企业重金招聘Python工程师标准>>> 最近又当上了Master,负责带项目,有时候,遇到的问题我很郁闷.比如一个Story,需求中说的是将单个修改改为批量修改,举个 ...

  4. Koa与Node.js开发实战(1)——Koa安装搭建(视频演示)

    2019独角兽企业重金招聘Python工程师标准>>> 学习架构: 由于Koa2已经支持ES6及更高版本,包括支持async方法,所以请读者保证Node.js版本在7.6.0以上.如 ...

  5. 《Node.js开发实战详解》学习笔记

    <Node.js开发实战详解>学习笔记 --持续更新中 一.NodeJS设计模式 1 . 单例模式 顾名思义,单例就是保证一个类只有一个实例,实现的方法是,先判断实例是否存在,如果存在则直 ...

  6. 腾讯高级工程师带你完整体验Node.js开发实战

    前几天,跟我一朋友聊天,他现在是阿里的架构师,说:「他们根本不知道,现在的电商大促有多么依赖 Node.js.」 说真的,我倒并不意外.作为一个定位明确的高性能 Web 服务器,Node.js 目前非 ...

  7. Vue.js+Node.js开发实战:从入门到项目上线

    <Vue.js+Node.js开发实战:从入门到项目上线>以JavaScript语言为基础,以一个完整的网站开发过程为主线,介绍了一整套面向Web项目的开发技术,如使用Node.js搭建服 ...

  8. 《Node.js开发实战》代码下载、简介与前言

    请下载代码评估:https://pan.baidu.com/s/1qYC3cVa   (密码: bba3). 内容简介 本书以实战开发为原则,以Node.js原生知识和框架实战为主线,详细介绍Node ...

  9. node.js/VUE项目中导出excel表格的多种实现方法(D2admin适用)

    方法一.vue+js-xlsx 1.vue项目内安装两个依赖:xlsx.file-saver  npm install xlsx --save  npm install file-saver --sa ...

最新文章

  1. ONES 万事联合创始人 amp; CTO 冯斌:企业服务产品的探索实践
  2. 你知道什么是AVL树吗?
  3. 计算机设置内存储器的必要性,计算机设置内存储器的必要性
  4. JS 全局对象 全局变量 作用域 (改自TOM大叔博文)
  5. 让表单文本框只读不可编辑的方法
  6. Selenium with Python 001 - 安装篇
  7. 微服务发展的历史_“美丽新羌 光照未来” 新羌社区开展微视频宣传片拍摄活动...
  8. 最新emlog程序仿小刀模板源码
  9. matlab 拼接矩阵,Matlab 不同行数矩阵拼接
  10. 二叉树遍历——深度优先遍历、广度优先遍历
  11. 计算机网络重置点命令,重置网络命令
  12. 《自然语言处理简明教程》读书笔记:第二章 词汇自动处理
  13. 如何看待IT培训这件事情?IT培训出来的人都一无是处吗?
  14. 世界上第一个徒步环球旅行的人
  15. 小数点后两位向上取值
  16. html引入let,let.html
  17. vue获取div高度
  18. sdkman 管理开发中使用的sdk
  19. 如何搭建web服务器
  20. 网页乱码与设置utf-8乱码

热门文章

  1. android 微信支付 2,Android微信支付获取二次签名Sign的方法
  2. php server 不支持,SQL server不支持utf8 php却用utf8的矛盾问题解决方法
  3. python dic字典使用
  4. 阿里云 ssh 登陆请使用(公)ip
  5. canvas操作图片,进行面板画图,旋转等
  6. canvas绘图粒子扩散效果【原创】
  7. 将apache的htaccess转换为nginx的重写规则
  8. Java时间类之间的转换案例——计算自己活了多少天
  9. 利用veiw建立Centos5.5 双机智能DNS手册
  10. Golang sort 排序