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

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

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

 

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

Node.js 切近实战(四) 之图书管理系统(图书查询)_第1张图片

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

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语法的高亮显示,所以我把图贴进来。

Node.js 切近实战(四) 之图书管理系统(图书查询)_第2张图片

两个查询条件,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: "" +

                    "" ,

                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页。

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

接下来是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 });

        });

    });

}

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

Node.js 切近实战(四) 之图书管理系统(图书查询)_第4张图片

无条件查询

Node.js 切近实战(四) 之图书管理系统(图书查询)_第5张图片

注意这里的暂无图片,如果/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字段。

上传失败,如下

Node.js 切近实战(四) 之图书管理系统(图书查询)_第6张图片

如果成功,如下

Node.js 切近实战(四) 之图书管理系统(图书查询)_第7张图片

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

Node.js 切近实战(四) 之图书管理系统(图书查询)_第8张图片

 

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

结束语

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

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

 

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

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

你可能感兴趣的:(Node.js 切近实战(四) 之图书管理系统(图书查询))