Node.js 切近实战 之图书管理系统(图书查询)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js 切近实战 之图书管理系统(图书查询)相关的知识,希望对你有一定的参考价值。
最近又当上了Master,负责带项目,有时候,遇到的问题我很郁闷。比如一个Story,需求中说的是将单个修改改为批量修改,举个例子,商品信息修改,之前是用一个商品id修改,但是现在改成多个商品id修改。我的意思是直接将文本框宽度高度加大,支持回车换行就行了,然后再将API修改为支持批量查询。这个界面上上面是一个Grid,下面是一个表单,选择Grid的数据后,会加载到下面表单。只能加载一条下去,就因为这个,有人提出如果加载一个下去,那么大个文本框只显示一个选中的商品id,视觉上无法接受。说是要将选择的单个和用于输入的多个文本框分开,控制一下显示隐藏的逻辑。这种做法会引来许多问题,大小文本框的隐藏显示会导致页面跳动,如果Grid查询无数据,初始化加载无数据,都要让大文本框显示。如果查询有数据,则grid数据会选中第一条,要显示小文本框。就因为这个问题,我感觉到做个Master真的是不容易,有时候就因为这些小问题达不成一致,让我很恼火。做管理不容易,我还要积累经验。
今天的话,我们来看一下图书管理系统的图书查询,首先我们先录入一批图书信息。
数据准备完成后,我们看一下页面代码。
#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部分。
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,我们接下来看一下服务端。
router.get(‘/book‘, bookRoutes.getBookList);
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#能取到,则显示,否则显示默认图片。
router.get(‘/book/image/:id‘, bookRoutes.getBookImageById);
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,最后我们看一下上传
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://leelei.blog.51cto.com/856755/1790907
以上是关于Node.js 切近实战 之图书管理系统(图书查询)的主要内容,如果未能解决你的问题,请参考以下文章