Node.js 切近實戰(四) 之圖書管理系統(圖書查詢)

最近又當上了Master,負責帶項目,有時候,遇到的問題我很鬱悶。好比一個Story,需求中說的是將單個修改改成批量修改,舉個例子,商品信息修改,以前是用一個商品id修改,可是如今改爲多個商品id修改。個人意思是直接將文本框寬度高度加大,支持回車換行就好了,而後再將API修改成支持批量查詢。這個界面上上面是一個Grid,下面是一個表單,選擇Grid的數據後,會加載到下面表單。只能加載一條下去,就由於這個,有人提出若是加載一個下去,那麼大個文本框只顯示一個選中的商品id,視覺上沒法接受。說是要將選擇的單個和用於輸入的多個文本框分開,控制一下顯示隱藏的邏輯。這種作法會引來許多問題,大小文本框的隱藏顯示會致使頁面跳動,若是Grid查詢無數據,初始化加載無數據,都要讓大文本框顯示。若是查詢有數據,則grid數據會選中第一條,要顯示小文本框。就由於這個問題,我感受到作個Master真的是不容易,有時候就由於這些小問題達不成一致,讓我很惱火。作管理不容易,我還要積累經驗。javascript

 

今天的話,咱們來看一下圖書管理系統的圖書查詢,首先咱們先錄入一批圖書信息。html

wKiom1dmz5_Au-guAABOCwNIpDM526.png

數據準備完成後,咱們看一下頁面代碼。html5

1java

2json

3bootstrap

4api

5微信

6app

7async

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語法的高亮顯示,因此我把圖貼進來。

wKioL1dm0KiS_zveAADJdaU4Oak593.png

兩個查詢條件,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頁。

wKioL1dm1bDwrrL5AAAjs6Ar8AM325.png

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

        });

    });

}

按兩個條件能夠模糊查詢。

wKioL1dm1inDi48NAAEZ_KjRPMQ278.png

無條件查詢

wKiom1dm1ovhulxaAAFcmmVoD0A898.png

注意這裏的暫無圖片,若是/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字段。

上傳失敗,以下

wKioL1dm3j6QH1BjAADfoenxFFk609.png

若是成功,以下

wKioL1dm4GHSlVYgAADy5i37I1I810.png

OK,最後咱們看一下總體效果

wKiom1dm4YjAhVTkAAL8Ss7iiJc075.png

 

wKioL1dm4ZnyQnrPAAQP7v-e4Ks572.png

OK,到此的話,圖書查詢就所有結束了,下節咱們繼續看圖書Gallery。

結束語

免費學習更多精品課程,登陸樂搏學院官網http://h.learnbo.cn/

或關注咱們的官方微博微信,還有更多驚喜哦~

 

本文出自 「技術創造價值」 博客,請務必保留此出處http://leelei.blog.51cto.com/856755/1790907

相關文章
相關標籤/搜索