Node + MongoDB 建站 2期 -3

实现分类功能

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新建分类,查看后台录入页

Node + MongoDB 建站 2期 -3_第1张图片

修改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请求自动填写数据


Node + MongoDB 建站 2期 -3_第2张图片

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
        })
      })
  }

查看结果


Node + MongoDB 建站 2期 -3_第3张图片

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)
    }
  })

你可能感兴趣的:(Node + MongoDB 建站 2期 -3)