實(shí)現(xiàn)分類功能
1 設(shè)計(jì)分類數(shù)據(jù)模型
創(chuàng)建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") 觀看預(yù)告片
修改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'
},
創(chuàng)建分類錄入頁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
})
})
}
創(chuàng)建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數(shù)據(jù)
豆瓣電影的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')
啟動服務(wù)珍坊,輸入豆瓣id,點(diǎn)擊空白處正罢,將調(diào)用ajax請求自動填寫數(shù)據(jù)
5 電影錄入增加分類自定義
修改movie controller的save方法阵漏,當(dāng)輸入分類名稱時,新建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") 觀看預(yù)告片
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 結(jié)果列表頁面',
keyword: category.name,
currentPage: (page + 1),
query: 'cat=' + catId,
totalPage: Math.ceil(movies.length / count),
movies: results
})
})
}
查看結(jié)果
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方法中根據(jù)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 結(jié)果列表頁面',
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 結(jié)果列表頁面',
keyword: q,
currentPage: (page + 1),
query: 'q=' + q,
totalPage: Math.ceil(movies.length / count),
movies: results
})
})
}
}
8 海報(bào)上傳功能
添加文件上傳組件叹洲,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") 海報(bào)上傳
.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統(tǒng)計(jì)
在list jade中添加PV列疹味,movie schema中添加PV
pv: {
type: Number,
default: 0
},
在movie controller中,detail中更新movie帜篇,每次進(jìn)入detail頁面PV將增加1
Movie.update({_id: id}, {$inc: {pv: 1}}, function(err) {
if (err) {
console.log(err)
}
})