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

最近又当上了Master,负责带项目,有时候,遇到的问题我很郁闷。比如一个Story,需求中说的是将单个修改改为批量修改,举个例子,商品信息修改,之前是用一个商品id修改,但是现在改成多个商品id修改。我的意思是直接将文本框宽度高度加大,支持回车换行就行了,然后再将API修改为支持批量查询。这个界面上上面是一个Grid,下面是一个表单,选择Grid的数据后,会加载到下面表单。只能加载一条下去,就因为这个,有人提出如果加载一个下去,那么大个文本框只显示一个选中的商品id,视觉上无法接受。说是要将选择的单个和用于输入的多个文本框分开,控制一下显示隐藏的逻辑。这种做法会引来许多问题,大小文本框的隐藏显示会导致页面跳动,如果Grid查询无数据,初始化加载无数据,都要让大文本框显示。如果查询有数据,则grid数据会选中第一条,要显示小文本框。就因为这个问题,我感觉到做个Master真的是不容易,有时候就因为这些小问题达不成一致,让我很恼火。做管理不容易,我还要积累经验。

今天的话,我们来看一下图书管理系统的图书查询,首先我们先录入一批图书信息。

数据准备完成后,我们看一下页面代码。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#book_retrieve(ng-controller='bookRetrieveCtrl')

 .row

  .col-md-4

   label Book Name:

    span.k-textbox.k-space-right(style='margin-left:5px;width:70%')

     input(type='text' ng-keydown='getBook($event)' ng-model='Search.BookName')

     a.k-icon.k-i-search(href="javascript:void(0)" ng-click='getBook()')

  .col-md-4

   label ISBN:

    span.k-textbox.k-space-right(style='margin-left:5px;width:70%')

     input(type='text' ng-keydown='getBook($event)' ng-model='Search.ISBN')

     a.k-icon.k-i-search(href='javascript:void(0)' ng-click='getBook()')

 hr.panel-line

 kendo-grid(options='bookGridOptions' k-data-source='BookList')

 div(kendo-window='modals' k-width='500' k-modal='true' k-visible='false' k-title='"Book Image Upload"' k-on-close='uploadClose()')

  .row.row-margin

   .col-md-11

     kendo-upload(type='file' name='files' k-multiple='false' k-success='onUploadSuccess' k-upload='upload' k-error='onUploadError' k-async='{saveUrl:"/book/upload", autoUpload: false}')

 

block scripts

  script(type='text/javascript' src='/javascripts/local/book/bookRetrieve.js')

由于51cto使用的百度的这个富文本编辑器不支持jade语法的高亮显示,所以我把图贴进来。

两个查询条件,BookName,ISBN,支持回车查询。

然后绑定数据,我们使用kendo-grid,绑定的datasource是BookList。然后我们定义了一个弹出页,用kendo-upload来上传图片,上传图片的路径为/book/upload。

ok,接着我们看一下js部分。

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

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

var appModule = angular.module('bookRetrieveModule', ["kendo.directives"]);

appModule.config(function ($locationProvider) {

    $locationProvider.html5Mode(true);

});

appModule.controller('bookRetrieveCtrl'function ($scope, $http, $location) {

    Messenger.options = {

        extraClasses: 'messenger-fixed messenger-on-top messenger-on-center',

        theme: 'flat'

    }

    

    $scope.showMsg = function (type, msg) {

        Messenger().post({

            message: msg,

            type: type,

            hideAfter: 2,

            showCloseButton: true

        });

    }

    

    $scope.BookId = '';

    $scope.Search = {};

    

    $scope.uploadWindow = {

        open: function () {

            $scope.modals.center().open();

        }

    };

    

    $scope.bookGridOptions = {

        height: 700,

        sortable: true,

        pageable: {

            refresh: true,

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

            buttonCount: 5

        },

        resizable: true,

        selectable: "single",

        columns: [{

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

                    "<a href='/book/image/#=Image#'><img src='/book/image/#=Image#' class='img-inline'/></a></div>" ,

                field: "Image",

                title: "Image",

                width: 130

            }, {

                field: "Title",

                title: "Title"

            }, {

                field: "Author",

                title: "Author"

            }, {

                field: "Price",

                title: "Price",

                width: 60,

            }, {

                field: "ISBN",

                title: "ISBN"

            }, {

                field: "Press",

                title: "Press"

            }, {

                command: [

                    {

                        name: "Upload",

                        text: "Upload"

                        imageClass: 'k-icon k-insertImage',

                        click: function (e) {

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

                            $scope.BookId = dataItem._id;

                            $scope.uploadWindow.open();

                        }

                    }],

                width: 150,

            }], dataBound: function (rowBoundEvent) {

            $("div.center-align-text a").popImage();

        }

    }

    

    $scope.getBook = function (event) {

        if (!event || event.keyCode == 13) {

            $scope.BookList = new kendo.data.DataSource({

                "pageSize": 15,

                "serverPaging"true,

                transport: {

                    read: function (e) {

                        var url = '/book?pageIndex=' + (e.data.page-1) + '&pageSize=' + e.data.pageSize;

                        if ($scope.Search.BookName) {

                            url += '?bookName=' + $scope.Search.BookName

                        }

                        

                        if ($scope.Search.ISBN) {

                            url += '&ISBN=' + $scope.Search.ISBN;

                        }

                        

                        $http.get(url).success(function (data) {

                            e.success(data);

                        });

                    }

                },

                schema: {

                    data: function (dataset) {

                        return dataset.books || [];

                    },

                    total: function (dataset) {

                        return dataset.totalCount || 0;

                    }

                }

            });

        }

    }

    

    $scope.onUploadError = function (error) {

        $scope.showMsg('error', angular.fromJson(error.XMLHttpRequest.responseText).error);

    }

    

    $scope.onUploadSuccess = function (e) {

        var response = e.response;

        if (response.isSuc) {

            $scope.showMsg('success''Upload completed successfully!');

        }

        else {

            $scope.showMsg('error', response.msg);

        }

    }

    

    $scope.upload = function (e) {

        e.sender.options.async.saveUrl = "/book/upload?bookId=" + $scope.BookId;

    }

});

angular.element('#book_retrieve').data('$injector''');

angular.bootstrap(angular.element('#book_retrieve'), ['bookRetrieveModule']);

首先我们初始化一个messager,然后$scope.bookId用来记录要上传的图书的id,$scope.Search用来绑定两个查询条件。接着$scope.uploadWindow来初始化一个modal页,用于弹出上传图片modal页(div(kendo-window='modals')。接着我们定义了kendoGrid,注意这里的Command,拿到当前行绑定的id,然后赋给$scope.BookId,再弹出上传modal页。

接下来是dataBound事件,即每绑定完成一行,就会触发这个事件,这里我们将div下所有的超链接让他支持弹出图片预览。

接下来的$scop.GetBook就是调用api查询了,没什么可说的。下面处理图片上传的回调方法也没什么好说的。

OK,我们接下来看一下服务端。

1

router.get('/book', bookRoutes.getBookList);

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

exports.getBookList = function (req, res) {

    var bookName = req.query.bookName;

    var ISBN = req.query.ISBN;

    var pageIndex = req.query.pageIndex;

    var pageSize = req.query.pageSize;

    

    var query = bookModel.find({});

    if (bookName) {

        query = query.where({ 'Title': { "$regex": bookName, "$options""i" } });

    }

    

    if (ISBN) {

        var regexp = new RegExp("^" + ISBN);

        query = query.where({ 'ISBN': regexp });

    }

    

    query.count().exec(function (error, count) {

        query.limit(pageSize)

        .skip(pageIndex * pageSize)

        .sort('-PressDate')

        .exec('find'function (error, doc) {

            res.json({ books: doc, totalCount: count });

        });

    });

}

按两个条件可以模糊查询。

无条件查询

注意这里的暂无图片,如果/book/image/#=Image#能取到,则显示,否则显示默认图片。

1

router.get('/book/image/:id', bookRoutes.getBookImageById);

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

var fs = require('fs');

var Grid = require('gridfs-stream');

var gfs = new Grid(mongoose.connection.db, mongoose.mongo);

exports.getBookImageById = function (req, res) {

    gfs.exist({ _id: req.params.id }, function (error, exists) {

        if (!exists) {

            var rstream = fs.createReadStream('./public/images/noimage.jpg');

            rstream.pipe(res);

        }

        else {

            var readstream = gfs.createReadStream({

                _id: new mongoose.Types.ObjectId(req.params.id)

            });

            

            readstream.on('error'function (err) {

                console.log(err);

                res.send(500, err);

            });

            

            readstream.pipe(res);

        }

    })

}

在这里,大家应该还记得上篇文章中提到的book model的定义,Image是一个ObjectId,其实就是GridFs中存储的图片的id。所以在这里读取的时候,只需要传id,就会查出图片并向客户端输出文件流。

OK,最后我们看一下上传

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

var bookSchemas = require('../model/bookinfo.js');

var bookMsgRes = require('../framework/message/book_msg.js');

var validator = require('validator');

var fs = require('fs');

var Busboy = require('busboy');

var mongoose = require('mongoose');

var Grid = require('gridfs-stream');

var gfs = new Grid(mongoose.connection.db, mongoose.mongo);

exports.fileupload = function (req, res, next) {

    if (!/multipart\/form-data/i.test(req.headers['content-type'])) {

        return res.status(500).end('Wrong content type');

    }

    

    var busboy = new Busboy({

        headers: req.headers, limits: {

            files: 1,

            fileSize: 1024 * 1024 * 2

        }

    });

    

    busboy.on('file'function (fieldname, file, filename, encoding, mimetype) {

        if (mimetype != 'image/jpeg'

            && mimetype != 'image/png'

            && mimetype != 'image/bmp') {

            res.status(403).json({ isSuc: false, error: 'Can\'t upload(' + filename + ') if file\'s type is not image(jpg,png,bmp)!' });

            return;

        }

        

        var fileId = new mongoose.Types.ObjectId();

        var readstream = gfs.createWriteStream({

            _id: fileId,

            mode: 'w',

            content_type: mimetype,

            filename: filename,

            metadata: {

                uploaddate: Date.now()

            }

        });

        

        file.pipe(readstream);

        bookModel.findByIdAndUpdate(req.query.bookId, { $set: { Image: fileId } }, function (error, doc) { });

    });

    

    busboy.on('finish'function () {

        res.json({ isSuc: true });

    });

    

    busboy.on('error'function (err) {

        res.status(500).end({ isSuc: false, msg: err });

    });

    

    req.pipe(busboy);

};

在这里我们需要使用busBoy上传图片把并存储至gridfs,上传成功的话,修改book的Image字段。

上传失败,如下

如果成功,如下

OK,最后我们看一下整体效果

OK,到此的话,图书查询就全部结束了,下节我们继续看图书Gallery。

结束语

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

或关注我们的官方微博微信,还有更多惊喜哦~

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

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

Node.js 切近实战(四) 之图书管理系统(图书查询)相关推荐

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

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

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

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

  3. Node.js 切近实战(七) 之Excel在线(文件文件组)

    2019独角兽企业重金招聘Python工程师标准>>> 今天我们来看一下Excel在线部分的文件和文件组.首先我们来看一下页面,调一下胃口.俗话说无图无真相,先看图. 没错,还是Te ...

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

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

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

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

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

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

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

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

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

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

  9. node.js取参四种方法req.body,req.params,req.param,req.body

    node.js取参四种方法req.body,req.params,req.param,req.body 参考:https://my.oschina.net/u/2519530/blog/535309 ...

最新文章

  1. 编者序:初衷、计划、要求、优势、目标和展望
  2. C/C++服务器开发的必备利器–libconfig
  3. Torque2D MIT 实战记录: 塔防进度(3)
  4. 有些错误,即便时光倒流也无法弥补——《时光幻境》
  5. WinPcap笔记(5):不用回调方法捕获数据包
  6. 6位顺序号生成_分布式id生成策略,我和面试官扯了一个半小时
  7. 云湖共生-释放企业数据价值
  8. php $db-gt;query 行数,php – 如何在CodeIgniter中组合query()和limit()方法
  9. 基于exosip的uas-uac的一个简单例子(转载)
  10. 关于Java String对象创建问题解惑
  11. Windows 7上的Android Studio安装失败,未找到JDK
  12. 5G商业化进程提速 运营商推进网络智能化转型
  13. 二叉树的高度_完全二叉树的子节点数
  14. OpenWrt Image Builder 制作带插件的HG255D固件记录
  15. 学生管理系统总结收获——限制字符
  16. APP测试与WEB测试
  17. 【手拉手 带你准备电赛】使用定时器中断更改PWM占空比
  18. 如何将bmp转化为jpg?
  19. NET 模拟Htpp请求
  20. 猜数字游戏-人出题,电脑猜(转贴)

热门文章

  1. Linux查看磁盘UUID的几种方式
  2. Android studio安装app时报错 Error: Activity class {} does not exist
  3. java字符型常量可以改变_在JAVA语言中,下列正确的字符型常量是
  4. Science|180种野生动物肠道菌群测序结果有哪些信息值得我们关注
  5. 词集模型、词袋模型、词向量模型
  6. java:jvm参数设置
  7. 目录导航《100天精通Python丨快速入门到黑科技》
  8. 互联网上的音频和视频
  9. Listener概述
  10. php美元符号 perl,Perl运算符:$| ;美元符号加上加号