实现分类功能
1 设计分类数据模型
创建category schema及model
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var ObjectId = Schema.Types.ObjectId
var CategorySchema = new Schema({
name: String,
movies: [{type: ObjectId, ref: 'Movie'}],
meta: {
createAt: {
type: Date,
default: Date.now()
},
updateAt: {
type: Date,
default: Date.now()
}
}
})
// var ObjectId = mongoose.Schema.Types.ObjectId
CategorySchema.pre('save', function(next) {
if (this.isNew) {
this.meta.createAt = this.meta.updateAt = Date.now()
}
else {
this.meta.updateAt = Date.now()
}
next()
})
CategorySchema.statics = {
fetch: function(cb) {
return this
.find({})
.sort('meta.updateAt')
.exec(cb)
},
findById: function(id, cb) {
return this
.findOne({_id: id})
.exec(cb)
}
}
module.exports = CategorySchema
2 分类后台录入及分类存储
修改index jade如下,添加分类panel
extends ../layout
block content
.container
.row
each cat in categories
.panel.panel-default
.panel-heading
h3
a(href='/results?cat=#{cat._id}&p=0') #{cat.name}
.panel-body
if cat.movies && cat.movies.length > 0
each item in movies
.col-md-2
.thumbnail
a(href="/movie/#{item._id}")
if item.poster.indexOf('http:') > -1
img(src="#{item.poster}", alt="#{item.title}")
else
img(src="/upload/#{item.poster}", alt="#{item.title}")
.caption
h4 #{item.title}
p: a.btn.btn-primary(href="/movie/#{item._id}", role="button") 观看预告片
修改index controller如下,查询categories
var mongoose = require('mongoose')
var Movie = require('../models/movie')
var Category = require('../models/Category')
exports.index = function(req, res) {
Category
.find({})
.populate({
path: 'movies',
select: 'title poster',
options: { limit: 6 }
})
.exec(function(err, categories) {
if (err) {
console.log(err)
}
res.render('index', {
title: 'imooc 首页',
categories: categories
})
})
}
admin jade中添加分类label
.form-group
label.col-sm-2.control-label(for="inputCategory") 电影分类
.col-sm-10
input#inputCategory.form-control(type="text", name="movie[category]", value=movie.category)
movie schema中添加category字段
var ObjectId = Schema.Types.ObjectId
......
category: {
type: ObjectId,
ref: 'Category'
},
创建分类录入页category_admin jade
extends ../layout
block content
.container
.row
form.form-horizontal(method="post", action="/admin/category")
.form-group
label.col-sm-2.control-label(for="inputCategory") 电影分类
.col-sm-10
input#inputCategory.form-control(type="text", name="category[name]", value=category.name)
.form-group
.col-sm-offset-2.col-sm-10
button.btn.btn-default(type="submit") 录入
添加category controller,保存后到category list页面
var mongoose = require('mongoose')
var Category = mongoose.model('Category')
// admin new page
exports.new = function(req, res) {
res.render('category_admin', {
title: 'imooc 后台分类录入页',
category: {}
})
}
// admin post movie
exports.save = function(req, res) {
var _category = req.body.category
var category = new Category(_category)
category.save(function(err, category) {
if (err) {
console.log(err)
}
res.redirect('/admin/category/list')
})
}
// catelist page
exports.list = function(req, res) {
Category.fetch(function(err, catetories) {
if (err) {
console.log(err)
}
res.render('categorylist', {
title: 'imooc 分类列表页',
catetories: catetories
})
})
}
创建category list页面
extends ../layout
block content
.container
.row
table.table.table-hover.table-bordered
thead
tr
th 名字
th 时间
th 查看
th 修改
th 删除
tbody
each item in catetories
tr(class="item-id-#{item._id}")
td #{item.name}
td #{moment(item.meta.updateAt).format('MM/DD/YYYY')}
td: a(target="_blank", href="../movie/#{item._id}") 查看
td: a(target="_blank", href="../admin/update/#{item._id}") 修改
td
button.btn.btn-danger.del(type="button", data-id="#{item._id}") 删除
在routes中添加路由
var Category = require('../app/controllers/category')
// Category
app.get('/admin/category/new', User.signinRequired, User.adminRequired, Category.new)
app.post('/admin/category', User.signinRequired, User.adminRequired, Category.save)
app.get('/admin/category/list', User.signinRequired, User.adminRequired, Category.list)
3 电影录入增加分类
添加分类选择
.form-group
label.col-sm-2.control-label(for="inputCategory") 电影分类
.col-sm-10
input#inputCategory.form-control(type="text", name="movie[categoryName]", value=movie.categoryName)
.form-group
label.col-sm-2.control-label 分类选择
each cat in categories
label.radio-inline
if movie.category
input(type="radio", name="movie[category]", value=cat._id, checked=cat._id.toString()==movie.category.toString())
else
input(type="radio", name="movie[category]", value=cat._id)
| #{cat.name}
修改movie controller
var Category = require('../models/category')
......
exports.new = function(req, res) {
Category.find({},function(err,categories){
res.render('admin', {
title: 'imooc 后台录入页',
categories: categories,
movie: {}
})
})
}
通过/admin/category/new
新建分类,查看后台录入页
修改movie controller的save方法,同时保存category
} else {
_movie = new Movie(movieObj)
var categoryId = movieObj.category
_movie.save(function(err,movie) {
if (err){
console.log(err)
}
if (categoryId) {
Category.findById(categoryId, function(err, category) {
category.movies.push(movie._id)
category.save(function(err, category) {
res.redirect('/movie/' + movie._id)
})
})
}
})
修改update方法,获取categories
if (id) {
Movie.findById(id,function(err,movie){
Category.find({}, function(err, categories) {
res.render('admin', {
title: 'imooc 后台更新页',
movie: movie,
categories: categories
})
})
})
}
4 获取豆瓣API数据
豆瓣电影的API地址格式为 https://api.douban.com/v2/movie/subject/
+id
,
在admin.js中添加
$('#douban').blur(function() {
var douban = $(this)
var id = douban.val()
if (id) {
$.ajax({
url: 'https://api.douban.com/v2/movie/subject/' + id,
cache: true,
type: 'get',
dataType: 'jsonp',
crossDomain: true,
jsonp: 'callback',
success: function(data) {
$('#inputTitle').val(data.title)
$('#inputDoctor').val(data.directors[0].name)
$('#inputCountry').val(data.countries[0])
$('#inputPoster').val(data.images.large)
$('#inputYear').val(data.year)
$('#inputSummary').val(data.summary)
}
})
}
})
在admin jade中引入js,添加输入框
.form-group
label.col-sm-2.control-label 豆瓣同步
.col-sm-10
input#douban.form-control(type="text")
......
script(src='/js/admin.js')
启动服务,输入豆瓣id,点击空白处,将调用ajax请求自动填写数据
5 电影录入增加分类自定义
修改movie controller的save方法,当输入分类名称时,新建category并save,save成功同时更新movie对象的category
} else {
_movie = new Movie(movieObj)
var categoryId = movieObj.category
var categoryName = movieObj.categoryName
_movie.save(function(err,movie) {
if (err){
console.log(err)
}
if (categoryId) {
Category.findById(categoryId, function(err, category) {
category.movies.push(movie._id)
category.save(function(err, category) {
res.redirect('/movie/' + movie._id)
})
})
}
else if (categoryName) {
var category = new Category({
name: categoryName,
movies: [movie._id]
})
category.save(function(err, category) {
movie.category = category._id
movie.save(function(err, movie) {
res.redirect('/movie/' + movie._id)
})
})
}
6 添加分类列表及分页
添加results.jade
extends ../layout
block content
.container
.row
.panel.panel-default
.panel-heading
h3 #{keyword}
.panel-body
if movies && movies.length > 0
each item in movies
.col-md-2
.thumbnail
a(href="/movie/#{item._id}")
img(src="#{item.poster}", alt="#{item.title}")
.caption
h4 #{item.title}
p: a.btn.btn-primary(href="/movie/#{item._id}", role="button") 观看预告片
ul.pagination
- for (var i = 0; i < totalPage; i++) {
- if (currentPage == (i + 1)) {
li.active
span #{currentPage}
- }
- else {
li
a(href='/results?#{query}&p=#{i}') #{i + 1}
- }
- }
在route中添加路由app.get('/results', Index.search)
在index controller中添加search方法
// search page
exports.search = function(req, res) {
var catId = req.query.cat
var q = req.query.q
var page = req.query.p
var count = 2
var index = page * count
if (catId) {
Category
.find({_id: catId})
.populate({
path: 'movies',
select: 'title poster'
})
.exec(function(err, categories) {
if (err) {
console.log(err)
}
var category = categories[0] || {}
var movies = category.movies || []
var results = movies.slice(index, index + count)
res.render('results', {
title: 'imooc 结果列表页面',
keyword: category.name,
currentPage: (page + 1),
query: 'cat=' + catId,
totalPage: Math.ceil(movies.length / count),
movies: results
})
})
}
查看结果
7 添加搜索功能
修改header jade,添加搜索框
.row
.page-header.clearfix
h1= title
.col-md-4
small 重度科幻迷
.col-md-8
form(method='GET', action='/results')
.input-group.col-sm-4.pull-right
input.form-control(type='text', name='q')
span.input-group-btn
button.btn.btn-default(type='submit') 搜索
在search方法中根据catId判断,通过搜索框是通过Movie来找,通过new RegExp(q + '.*', 'i')
来模糊匹配
// search page
exports.search = function(req, res) {
var catId = req.query.cat
var q = req.query.q
// var page = parseInt(req.query.p, 10) || 0
var page = req.query.p
var count = 3
var index = page * count
if (catId) {
Category
.find({_id: catId})
.populate({
path: 'movies',
select: 'title poster'
})
.exec(function(err, categories) {
if (err) {
console.log(err)
}
var category = categories[0] || {}
var movies = category.movies || []
var results = movies.slice(index, index + count)
res.render('results', {
title: 'imooc 结果列表页面',
keyword: category.name,
currentPage: (page + 1),
query: 'cat=' + catId,
totalPage: Math.ceil(movies.length / count),
movies: results
})
})
}
else {
Movie
.find({title: new RegExp(q + '.*', 'i')})
.exec(function(err, movies) {
if (err) {
console.log(err)
}
var results = movies.slice(index, index + count)
res.render('results', {
title: 'imooc 结果列表页面',
keyword: q,
currentPage: (page + 1),
query: 'q=' + q,
totalPage: Math.ceil(movies.length / count),
movies: results
})
})
}
}
8 海报上传功能
添加文件上传组件,form添加enctype属性
form.form-horizontal(method="post", action="/admin/movie/new" enctype="multipart/form-data")
.form-group
label.col-sm-2.control-label(for="uploadPoster") 海报上传
.col-sm-10
input#uploadPoster(type="file", name="uploadPoster")
movie controller中添加savePoster,save方法中更新poster
// admin poster
exports.savePoster = function(req, res, next) {
var posterData = req.files.uploadPoster
var filePath = posterData.path
var originalFilename = posterData.originalFilename
if (originalFilename) {
fs.readFile(filePath, function(err, data) {
var timestamp = Date.now()
var type = posterData.type.split('/')[1]
var poster = timestamp + '.' + type
var newPath = path.join(__dirname, '../../', '/public/upload/' + poster)
fs.writeFile(newPath, data, function(err) {
req.poster = poster
next()
})
})
}
else {
next()
}
}
exports.save = function(req,res){
var id = req.body.movie._id
var movieObj = req.body.movie
var _movie
if (req.poster) {
movieObj.poster = req.poster
}
安装connect-multiparty npm i connect-multiparty --save-dev
,并在app js中添加
var multipart = require('connect-multiparty');
app.use(multipart());
8 添加PV统计
在list jade中添加PV列,movie schema中添加PV
pv: {
type: Number,
default: 0
},
在movie controller中,detail中更新movie,每次进入detail页面PV将增加1
Movie.update({_id: id}, {$inc: {pv: 1}}, function(err) {
if (err) {
console.log(err)
}
})