imooc 网项目源码: https://github.com/shinytang6/imoocMovie
项目地址: https://github.com/husky520/movie-site.git
0. 准备工作
homebrew 安装 mongodb
brew install mongodb
注意:
如果出现以下问题
mongodb: A full installation of Xcode.app 8.3.2 is required to compile this software.
Installing just the Command Line Tools is not sufficient.
Xcode 8.3.2 cannot be installed on macOS 10.11.
You must upgrade your version of macOS.
Error: An unsatisfied requirement failed this build.
可以直接安装低版本的 mongodb, 例如 [email protected]
创建默认数据库存放位置:
sudo mkdir /data
sudo mkdir /data/db
启动数据库
sudo mongod
1. 项目简介
项目 UI 使用 bootstrap, 后台使用 node.js 搭建, 具体为 express 搭建服务器, moogoose 连接 mongodb 数据库, jade 作为模版引擎, 另外还有 moment.js 用来格式化时间等 ...
目录结构预览
- movie-site // 项目目录
- schemas // mongoose schemas
movie.js // scheme
- models // mongoose models
movie.js // model
- views // 静态资源目录
- includes // 页面所包含的公共部分模版
head.jade // bootstrap 和 jquery 引入
header.jade // 页面基本信息
- pages // 不同页面内容模版
detail.jade // 电影详情页
list.jade // 电影列表页
index.jade // 电影网站首页
admin.jade // 后台录入页
layout.jade // 布局框架
app.js // 入口文件
页面路由
首页 http://localhost:3000/
电影列表 http://localhost:3000/list
电影详情 http://localhost:3000/movie/:id
后台录入 http://localhost:3000/admin/movie
后台更新 http://localhost:3000/admin/update/:id
2. 基本页面和初步的入口文件
- 新建 app.js 入口文件
const express = require('express')
const path = require('path')
const bodyParser = require('body-parser')
const port = process.env.PORT || 3000
const app = express()
app.set('views', './views')
app.set('view engine', 'jade')
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())
app.use(express.static(path.join(__dirname, 'bower_components')))
app.listen(port)
console.log('Movie Site 已启动 ! 端口为: ', port)
// 配置路由
// index page
app.get('/', (req, res) => {
res.render('pages/index', {
title: '电影首页',
movies: movies // 传入 mock 数据
})
})
// movie page
app.get('/movie/:id', (req, res) => {
res.render('pages/detail', {
title: '电影详情: ' + movie.title,
movie: movie
})
})
// list page
app.get('/list', (req, res) => {
res.render('pages/list', {
title: '电影列表',
movies: movies
})
})
// admin page
app.get('/admin/movie', (req, res) => {
res.render('pages/admin', {
title: '后台录入',
movie: { // 录入时传入空数据给页面表单
doctor: '',
country: '',
title: '',
year: '',
poster: '',
language: '',
flash: '',
summary: ''
}
})
})
// admin update movie 和录入同页面
app.get('/admin/update/:id', function (req, res) {
res.render('pages/admin', {
title: '更新电影:' + movie.title,
movie: movie // 更新时传入原来数据, 在此基础上修改
})
})
// admin post movie 该页面为接收录入, 更新页提交的数据
app.post('/admin/movie/new', function (req, res) {
// ...
})
- 各页面模版
layout.jade
doctype
html
head
meta(charset="utf-8")
title #{title}
include ./includes/head
body
include ./includes/header
block content
head.jade - ( 此处使用绝对路径, 否则子页面无法正确访问到静态资源 ! )
link(href="/bootstrap/dist/css/bootstrap.css", rel="stylesheet")
scritp(src="/jquery/dist/jquery.js")
script(src="/bootstrap/dist/js/bootstrap.js")
header.jade
.container
.row
.page-header
h1= title
index.jade
extends ../layout
block content
.container
.row
each item in movies
.col-6.col-md-3
.thumbnail
a(href="/movie/#{item._id}")
img(src="#{item.poster}", alt="#{item.title}")
.caption
h3 #{item.title}
p: a.btn.btn-primary(href="/movie/#{item._id}", role="button") 欢迎观看预告片
list.jade
extends ../layout
block content
.container
.row
table.table.table-hover.table-bordered
thead
tr
th 电影名字
th 导演
th 国家
th 上映年份
th 录入时间
th 查看
th 更新
th 删除
tbody
each item in movies
tr(class="item-id-#{item._id}")
td #{item.title}
td #{item.doctor}
td #{item.country}
td #{item.year}
td #{moment(item.meta.createdAt).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}") 删除
detail.jade
extends ../layout
block content
.container
.row
.col-12
video(src="#{movie.flash}", width="100%", controls="controls")
.col-12
dl.dl-horizontal
dt 电影名字
dd= movie.title
dt 导演
dd= movie.doctor
dt 国家
dd= movie.country
dt 语言
dd= movie.language
dt 上映年份
dd= movie.year
dt 简介
dd= movie.summary
admin.jade
extends ../layout
block content
.container
form(method="post", action="/admin/movie/new")
//- 使用隐藏的 input 标签提交电影的 id
input(type="hidden", name="_id", value="#{movie._id}")
.form-group.row
label.col-sm-2.col-form-label(for="inputTitle") 电影名字
.col-sm-10
input#inputTitle.form-control(type="text", name="title", value="#{movie.title}")
.form-group.row
label.col-sm-2.col-form-label(for="inputDoctor") 电影导演
.col-sm-10
input#inputDoctor.form-control(type="text", name="doctor", value="#{movie.doctor}")
.form-group.row
label.col-sm-2.col-form-label(for="inputCountry") 国家
.col-sm-10
input#inputCountry.form-control(type="text", name="country", value="#{movie.country}")
.form-group.row
label.col-sm-2.col-form-label(for="inputLanguage") 语种
.col-sm-10
input#inputLanguage.form-control(type="text", name="language", value="#{movie.language}")
.form-group.row
label.col-sm-2.col-form-label(for="inputPoster") 海报地址
.col-sm-10
input#inputPoster.form-control(type="text", name="poster", value="#{movie.poster}")
.form-group.row
label.col-sm-2.col-form-label(for="inputFlash") 片源地址
.col-sm-10
input#inputFlash.form-control(type="text", name="flash", value="#{movie.flash}")
.form-group.row
label.col-sm-2.col-form-label(for="inputYear") 上映年份
.col-sm-10
input#inputYear.form-control(type="text", name="year", value="#{movie.year}")
.form-group.row
label.col-sm-2.col-form-label(for="inputSummary") 电影简介
.col-sm-10
textarea#inputSummary.form-control(name="summary")
.form-group.row
.offset-sm-2.col-sm-10
button.btn.btn-primary(type="submit") 录入
- mongoose 创建数据库模型
在 schemas 目录下创建 movie.js 文件, 导出 MovieSchema
movie.js
const mongoose = require('mongoose')
// 设计数据库结构
const MovieSchema = new mongoose.Schema({
doctor: String,
title: String,
language: String,
country: String,
summary: String,
flash: String,
poster: String,
year: Number,
meta: {
createAt: {
type: Date,
default: Date.now()
},
updateAt: {
type: Date,
default: Date.now()
}
}
})
// Schema 钩子, 以下用以在保存时创建或更新修改时间
MovieSchema.pre('save', function (next) {
if (this.isNew) {
this.meta.createAt = this.meta.updateAt = Date.now()
} else {
this.meta.updateAt = Date.now()
}
next()
})
// 静态方法, fetch 查询所有数据, findById 查询单条数据
MovieSchema.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 = MovieSchema
在 models 下创建 movie.js, 导出 model
const mongoose = require('mongoose')
const MovieSchema = require('../schemas/movie')
const Movie = mongoose.model('Movie', MovieSchema)
module.exports = Movie
- 连接数据库
app.js
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/movie-site') // movie-site 为数据库名
- 数据的增删改查
首先引入创建的 model
const Movie = require('./models/movie')
增
// 创建一个数据
const data = new Movie({
// 数据内容
// title: 'title'
// ....
})
// 存入数据
Movie.save(function (err, data) {
if (err) {
console.error(err)
}
// 其他操作 ...
})
删
// 删除 _id 为 id 的项, 并为结果作出相应的回应
Movie.remove({'_id': id}, function (err) {
if (err) {
console.error(err)
res.json({status: 0})
} else {
res.json({status: 1})
}
})
改
// 这里使用一个插件 ‘underscore’
const underscore = require('underscore')
// 在原数据的基础上拓展新数据 (更新数据)
const data = underscore.extend(oldData, newData)
// 存入数据库
Movie.save(function (err, data) {
if (err) {
console.error(err)
}
// 其他操作 ...
})
查
// 使用在 ‘/schemas/ movie.js’ 中所封装的静态方法进行查询
// 查询所有数据
Movie.fetch(function (err, data) {
if (err) {
console.error(err)
}
// 其他对已查询到的 data 的操作 ...
})
// 按 id 查找数据
Movie.findById(function (err, data) {
if (err) {
console.error(err)
}
// 其他对已查询到的 data 的操作 ...
})
3. 注意点
- jQuery.slim.js 是精简版, 其中不含 $.ajax
- 在列表页删除了一条数据后, 可以刷新页面, 以保证列表页和数据库的同步 (无法通过重定向实现刷新)
最终的源码参考开头的链接