├── .bowerrc ├── .gitignore ├── .jshintrc ├── README.md ├── app.js ├── bower.json ├── gulpfile.js ├── images.md ├── models ├── category.js ├── comment.js ├── movie.js └── user.js ├── package.json ├── public ├── images │ ├── fail.png │ ├── headpic.png │ └── success.png ├── js │ ├── admin.js │ └── comment.js └── uploads │ ├── 1498615764391.jpeg │ ├── 1498615798157.jpeg │ └── 1498629813712.jpeg ├── routes ├── admin │ ├── admin.js │ ├── categoryAdmin.js │ └── userCenter.js ├── middleware │ └── auth.js ├── movie │ └── index.js ├── status │ └── status.js └── user │ └── user.js ├── schemas ├── category.js ├── comment.js ├── movie.js └── user.js ├── screenshot ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.png └── views ├── includes ├── head.jade └── header.jade ├── layout.jade └── pages ├── category.jade ├── categoryList.jade ├── categoryResult.jade ├── detail.jade ├── index.jade ├── movieAdmin.jade ├── movieList.jade ├── search.jade ├── signIn.jade ├── signUp.jade ├── status.jade ├── userCenter.jade └── userList.jade /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "public/libs" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*/node_modules 2 | /public/libs 3 | node_modules -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "asi":true, 3 | "lastsemic":true, 4 | "esversion":6 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于 NodeJs+MongoDB+jQuery+Bootstrap 搭建的豆瓣电影网站 2 | ### 一、简介 3 | 本项目是基于Nodejs的练手项目,期间参考了慕课网[Scott老师的课程](http://www.imooc.com/learn/197)(代码结构及路由全都重新改造),在老师所讲的基础上增加了许多的功能并且加以完善,项目基于 Express 4.15 版本,代码采用 Es6 编写,代码已加注释,提高了阅读性和维护性,可供参考: 4 | #### 1、项目前端搭建: 5 | * 使用jQuery和Bootsrap完成网站前端JS脚本和样式处理; 6 | * 前后端的数据请求交互通过Ajax完成; 7 | * 引入了Moment.js格式化前端页面显示时间; 8 | #### 2、项目后端搭建: 9 | * 使用NodeJs的express框架完成电影网站后端搭建; 10 | * 使用mongodb完成数据存储,通过mongoose模块完成对mongodb数据的构建; 11 | * 使用jade模板引擎完成页面创建渲染; 12 | * 使用Moment.js格式化电影存储时间; 13 | #### 3、本地开发环境搭建: 14 | * 使用gulp集成jshint对JS语法检查,加入browser-sync与nodemon,实现实时刷新及服务器的自动重启等功能。 15 | #### 4、网站整体功能: 16 | 网站正常访问无需管理员权限,对电影的评论及个人中心资料的修改,需要用户登录,对网站数据的修改添加删除需要管理员的权限,默认一个管理员,具体功能如下: 17 | * 实现了用户的基本注册,登录,登出及管理功能; 18 | * 实现了搜索功能,模糊关键字可搜索电影名字及电影类别下的电影; 19 | * 用户登录做session处理,失效期暂为5天; 20 | * 用户可以对电影进行评论及个人中心资料的修改(可上传用户头像); 21 | * 电影添加分类及录入,数据可以同步豆瓣ID; 22 | * 对电影数据作分页处理,分页查询数据库数据; 23 | * 管理员可以对网站数据进行增加删除修改(需要管理员权限); 24 | * 管理员可从后台查看所有的电影、用户、评论、访问量等数据; 25 | ### 二、网站整体效果,截图看[这里](https://github.com/chenjun1127/Movie-Site/blob/master/images.md) 26 | ### 三、运行环境及Node版本: 27 | 作为windows平台的忠实粉丝(≥▽≤/),当前平台下node v6.9.2 ,运行正常! 28 | ### 四、安装 29 | * MongoDB 安装[mongodb](https://www.mongodb.org/downloads#production)完成相关配置; 30 | * 克隆项目,进入项目目录; 31 | ```javascript 32 | git clone git@github.com:chenjun1127/Movie-Site.git 33 | cd movie-site 34 | ``` 35 | * 安装依赖 36 | ```javascript 37 | npm install 38 | bower install 39 | ``` 40 | ### 五、运行及使用 41 | 上面步骤完成后,打开app.js入口文件,可以看到当前连接的是本地的 express-demo 这个数据库,接下来启动数据库相关服务;在当前目录通过命令 42 | ```bash 43 | gulp 44 | ``` 45 | 启动项目,稍等片刻后,gulp会自动打开chrome浏览器,就可以看到 http://localhost:3000/ 主页,项目就运行成功了。注意:若端口已占用可在app.js文件中将gulp的代理端口3000换成未占用的端口! 46 | ```bash 47 | gulp dist 48 | ``` 49 | Tips:gulp dist 命令可以检测项目JS是否有错误! 50 | ### 六、项目页面 51 | 当使用管理员账号(chenjun,123456)登录时,在网站右上角会出现下拉菜单,通过点击菜单可以进入各个页面,如果自己注册的账号,默认为普通用户(role为0),普通用户有权限限制,是无法进入到电影的列表、录入、分类、用户等管理页面的!当然,可自行修改数据库里的当前账号的role值,当role大于10的时候,就有管理员权限了!基本的界面路由如下: 52 | 基本页面: 53 | * 首页:localhost:3000/ 54 | * 详情页:localhost:3000/movie/:id 55 | * 当前电影分页类别页:localhost:3000/movie/category/result?cat=id&pageSize=1 56 | 57 | 用户关联: 58 | 59 | * 用户注册:localhost:3000/signUp 60 | * 用户登录:localhost:3000/signIn 61 | * 用户个人中心:localhost:3000/user/center?userId=id 62 | 63 | 后台管理: 64 | 65 | * 当前电影列表:localhost:3000/admin/movie/list 66 | * 电影录入:localhost:3000/admin/movie/add 67 | * 电影数据修改:localhost:3000/admin/movie/update/:id 68 | * 电影分类列表:localhost:3000/admin/category/list 69 | * 电影分类修改:localhost:3000/admin/category/update/:id 70 | * 电影分类新增:localhost:3000/admin/category/new 71 | * 用户列表:localhost:3000/admin/user/list 72 | ### 七、项目结构 73 | ```bash 74 | ├── app.js 项目入口文件 75 | ├── routs 路由总目录 76 | │ ├── admin 后台管理 77 | │ │ ├── xxx.js 78 | │ │ └── ... 79 | │ ├── middleware 权限中间限 80 | │ │ ├── xxx.js 81 | │ │ └── ... 82 | │ ├── status 状态页面 83 | │ │ ├── xxx.js 84 | │ │ └── ... 85 | │ └── user 用户相关 86 | │ ├── xxx.js 87 | │ └── ... 88 | ├── node_modules node模块目录 89 | ├── public 静态文件目录 90 | │ ├── images 图片目录 91 | │ ├── libs bower安装目录 92 | │ │ ├── bootstrap 93 | │ │ └── jquery 94 | │ ├── js jade模板交互js 95 | │ │ ├── xxx.js 96 | │ │ └── ... 97 | │ └── uploads 用户上传的图片目录 98 | │ ├── xxx.jpeg 99 | │ └── ... 100 | ├── models 模型 101 | │ ├── xxx.js 102 | │ └── ... 103 | ├── schemas 模式 104 | │ ├── xxx.js 105 | │ └── ... 106 | ├── views 视图文件目录 107 | │ ├── includes 108 | │ │ ├── xxx.jade 109 | │ │ └── ... 110 | │ └── pages 111 | │ ├── xxx.jade 112 | │ └── ... 113 | ├── README.md 114 | ├── .bowerrc 115 | ├── .gitignore 116 | ├── .jshintrc 117 | ├── bower.json 118 | ├── gulpfile.js gulp文件 119 | └── package.json 120 | ``` 121 | screenshot与images.md为截图说明文件,是为了方便查看,与本项目无关! 122 | ### 八、项目总结 123 | 整个项目基于NodeJs+MongoDB+jQuery+Bootstrap搭建而成,UI部分基于bootstrap,整体UI细节有待优化完善;部分功能有细微瑕疵,譬如数据的添加暂未考虑部分字段为空的情况、用户登录注册未做表单校验等等,这些都需要完善!整个项目涉及到的知识点非常的全面,有很好的参考价值! 124 | ### 九、重要提示 125 | 运行此项目一定要先启动 mongo 服务,并连接 mongoDB 数据库。此项目的数据库备份在[这里](https://github.com/chenjun1127/mongoDB-tips)。 126 | 127 | 如果问题请联系QQ:402074940 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 165 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //导入依赖模块 2 | var express = require('express'); 3 | var path = require('path') 4 | var bodyParser = require('body-parser'); 5 | var mongoose = require('mongoose'); 6 | var session = require('express-session'); 7 | var MongoStore = require('connect-mongo')(session); 8 | // 连接数据库 9 | var dbUrl = 'mongodb://localhost/express-demo'; 10 | mongoose.Promise = global.Promise; 11 | mongoose.connect(dbUrl); 12 | 13 | //设置端口 14 | var port = process.env.PORT || 8100; 15 | var app = express(); 16 | var index = require('./routes/movie/index'); 17 | var admin = require('./routes/admin/admin'); 18 | var user = require('./routes/user/user'); 19 | var category = require('./routes/admin/categoryAdmin'); 20 | var userCenter = require('./routes/admin/userCenter'); 21 | var status = require('./routes/status/status'); 22 | app.set('views', './views/pages'); 23 | app.set('view engine', 'jade'); 24 | app.use(bodyParser.json()); 25 | app.use(bodyParser.urlencoded({ 26 | extended: true 27 | })); 28 | app.use(express.static(path.join(__dirname, 'public'))) // 设置静态目录 29 | var moment = require('moment'); 30 | moment.locale('zh-cn'); 31 | app.locals.moment = moment // 定义整个项目使用moment 32 | app.use(session({ 33 | secret: 'express-movie-demo', 34 | name: 'login-user', //这里的name值得是cookie的name,默认cookie的name是:connect.sid 35 | cookie: { 36 | maxAge: 1000 * 60 * 60 * 24 * 5 37 | }, //设置maxAge是80000ms,即80s后session和相应的cookie失效过期 38 | store: new MongoStore({ 39 | url: dbUrl, 40 | collection: 'sessions' 41 | }), 42 | resave: false, 43 | saveUninitialized: true 44 | })) 45 | app.listen(port, () => { 46 | console.log('server running on port: ' + port); 47 | }); 48 | 49 | 50 | app.use((req, res, next) => { 51 | // console.log("user in session:"+req.session.user) 52 | var _user = req.session.user; 53 | if(_user){ 54 | res.locals.user = _user; 55 | } 56 | next(); 57 | }) 58 | 59 | app.use((err, req, res, next) => { 60 | var err = new Error('Not Found'); 61 | err.status = 404; 62 | next(err); 63 | }) 64 | 65 | 66 | // index route 67 | app.use('/', index); 68 | // status:success || fail 69 | app.use('/', status); 70 | // user route 71 | app.use('/', user); 72 | // admin route 73 | app.use('/', admin); 74 | // admin category 75 | app.use('/', category); 76 | // user center 77 | app.use('/', userCenter); 78 | 79 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movie-site", 3 | "description": "bower", 4 | "main": "app.js", 5 | "authors": [ 6 | "chenjun " 7 | ], 8 | "license": "ISC", 9 | "homepage": "", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "bootstrap": "^3.3.7" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var browserSync = require('browser-sync'); 3 | var reload = browserSync.reload; 4 | var nodemon = require('gulp-nodemon'); 5 | var jshint = require('gulp-jshint'); 6 | 7 | gulp.task('browser-sync', ['nodemon'], function() { 8 | browserSync.init(null, { 9 | proxy: 'http://localhost:8100', 10 | files: ['**'], 11 | browser: 'chrome', 12 | notify: false, 13 | port: 3000 14 | }); 15 | // gulp.watch('sass/*.scss', ['sass-watch']); 16 | }); 17 | 18 | gulp.task('js', function() { 19 | return gulp.src(['./routes/**/*.js', './schemas/*.js', './public/js/*.js']) // 检查文件:js目录下所有的js文件 20 | .pipe(jshint()) // 进行检查 21 | .pipe(jshint.reporter('default')) // 对代码进行报错提示 22 | }); 23 | 24 | gulp.task('nodemon', function(cb) { 25 | var called = false; 26 | return nodemon({ 27 | script: 'app.js' 28 | }).on('start', function() { 29 | if (!called) { 30 | cb(); 31 | called = true; 32 | } 33 | }); 34 | }); 35 | 36 | gulp.task('clean', function(cb) { 37 | del(['./dist'], cb) 38 | }); 39 | 40 | gulp.task('dist', ['js']); 41 | 42 | gulp.task('default', ['browser-sync']); -------------------------------------------------------------------------------- /images.md: -------------------------------------------------------------------------------- 1 | ![首页](/screenshot/1.png) 2 | ![首页](/screenshot/2.png) 3 | ![首页](/screenshot/3.png) 4 | ![首页](/screenshot/4.png) 5 | ![首页](/screenshot/5.png) 6 | -------------------------------------------------------------------------------- /models/category.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var CategorySchema = require('../schemas/category'); 3 | var Category = mongoose.model('Category', CategorySchema); 4 | module.exports = Category; 5 | -------------------------------------------------------------------------------- /models/comment.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var CommentSchema = require('../schemas/comment'); 3 | var Comment = mongoose.model('Comment', CommentSchema); 4 | module.exports = Comment; 5 | -------------------------------------------------------------------------------- /models/movie.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var MovieSchema = require('../schemas/movie'); 3 | var Movie = mongoose.model('Movie', MovieSchema); 4 | module.exports = Movie; 5 | -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var UserSchema = require('../schemas/user'); 3 | var User = mongoose.model('User', UserSchema); 4 | module.exports = User; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movie-site", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node app.js" 9 | }, 10 | "author": "chenjun", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt-nodejs": "0.0.3", 14 | "body-parser": "^1.17.2", 15 | "connect-mongo": "^1.3.2", 16 | "express": "^4.15.3", 17 | "express-session": "^1.15.3", 18 | "jade": "^1.11.0", 19 | "moment": "^2.18.1", 20 | "mongoose": "^4.10.2", 21 | "connect-multiparty": "^2.0.0" 22 | }, 23 | "devDependencies": { 24 | "browser-sync": "^2.18.12", 25 | "gulp": "^3.9.1", 26 | "gulp-jshint": "^2.0.4", 27 | "gulp-nodemon": "^2.2.1", 28 | "jshint": "^2.9.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/images/fail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/images/fail.png -------------------------------------------------------------------------------- /public/images/headpic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/images/headpic.png -------------------------------------------------------------------------------- /public/images/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/images/success.png -------------------------------------------------------------------------------- /public/js/admin.js: -------------------------------------------------------------------------------- 1 | function del(ele, url) { 2 | var id = $(ele).data("id"); 3 | var tr = $(ele).parents("tr"); 4 | $.ajax({ 5 | type: 'DELETE', 6 | url: url + '?id=' + id, 7 | }).done(function(res) { 8 | if (res.success == 1) { 9 | if (tr.length > 0) { 10 | tr.remove(); 11 | } 12 | } 13 | }) 14 | } -------------------------------------------------------------------------------- /public/js/comment.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | moment().locale('zh_cn'); 3 | // $("#comment-btn").click(function() { 4 | // $.ajax({ 5 | // url: '/admin/user/comment', 6 | // type: 'POST', 7 | // dataType: 'json', 8 | // async: true, 9 | // data: $("#form-comment").serialize(), 10 | // success: function(data) { 11 | // console.log("data", data); 12 | // var html = "", 13 | // chtml = ""; 14 | // $.each(data, function(index, item) { 15 | // html += "
" + item.from.name + "" + moment(item.meta.createAt).fromNow() + "

" + item.content + "


" 16 | // }) 17 | // $(".comment-list").append(html); 18 | // } 19 | // }) 20 | // }) 21 | }) 22 | 23 | 24 | $(".comment_link").click(function() { 25 | $("textarea").focus(); 26 | var toId = $(this).data("tid"); 27 | var commentId = $(this).data("cid"); 28 | if ($(".comment-tid").length > 0) { 29 | return 30 | } else { 31 | $("").attr({ 32 | type: "hidden", 33 | name: "comment[tid]", 34 | value: toId 35 | }).appendTo("#form-comment"); 36 | } 37 | if ($(".comment-cid").length > 0) { 38 | return 39 | } else { 40 | $("").attr({ 41 | type: "hidden", 42 | name: "comment[cid]", 43 | value: commentId 44 | }).appendTo("#form-comment"); 45 | } 46 | }) -------------------------------------------------------------------------------- /public/uploads/1498615764391.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/uploads/1498615764391.jpeg -------------------------------------------------------------------------------- /public/uploads/1498615798157.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/uploads/1498615798157.jpeg -------------------------------------------------------------------------------- /public/uploads/1498629813712.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/public/uploads/1498629813712.jpeg -------------------------------------------------------------------------------- /routes/admin/admin.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var Movie = require('../../models/movie'); 4 | var User = require('../../models/user'); 5 | var Comment = require('../../models/comment'); 6 | var Category = require('../../models/category'); 7 | // 权限中间件 8 | var {requiredLogin,requiredAdmin} = require ('../middleware/auth'); 9 | // 没有挂载路径的中间件,应用的每个请求都会执行该中间件 10 | router.use(requiredLogin); 11 | // admin movie list 12 | // 挂载至 /xx/xx的中间件,任何指向 /xx/xx 的请求都会执行它 13 | router.get('/admin/movie/list', requiredAdmin, (req, res) => { 14 | // console.log(res.locals.admin) 15 | Movie.find({}).populate("category","name") 16 | .exec((err,movies)=>{ 17 | if (err) { 18 | console.log(err) 19 | } else { 20 | res.render('movieList', { 21 | title: '当前电影列表', 22 | movies: movies 23 | }) 24 | } 25 | }) 26 | }) 27 | 28 | // userlist 29 | router.get('/admin/user/list', requiredAdmin, (req, res) => { 30 | User.fetch((err, users) => { 31 | if (err) { 32 | console.log(err) 33 | } else { 34 | res.render('userList', { 35 | title: '用户列表页', 36 | users: users 37 | }) 38 | } 39 | }) 40 | }) 41 | 42 | // admin movie save 43 | router.get('/admin/movie/add', requiredAdmin, (req, res) => { 44 | Category.find({}, (err, categories) => { 45 | res.render('movieAdmin', { 46 | title: '后台录入页', 47 | categories: categories, 48 | movie: { 49 | title: '', 50 | doctor: '', 51 | country: '', 52 | year: '', 53 | poster: '', 54 | flash: '', 55 | summary: '', 56 | language: '' 57 | } 58 | }) 59 | }) 60 | }); 61 | 62 | // admin update movie 63 | router.get('/admin/movie/update/:id', requiredAdmin, (req, res) => { 64 | var id = req.params.id; 65 | if (id) { 66 | Movie.findById(id, (err, movie) => { 67 | Category.find({}, (err, categories) => { 68 | res.render('movieAdmin', { 69 | title: '更新页 >' + movie.title, 70 | movie: movie, 71 | categories: categories 72 | }) 73 | }) 74 | }) 75 | } 76 | }) 77 | 78 | // admin post movie 79 | router.post('/admin/movie/new', requiredAdmin, (req, res) => { 80 | var id = req.body.movie._id; 81 | var movieObj = req.body.movie; 82 | var _movie; 83 | if (id) { 84 | // console.log("更新"); 85 | Movie.findById(id, (err, movie) => { 86 | if (err) { 87 | console.log(err) 88 | } 89 | _movie = Object.assign(movie, movieObj); 90 | _movie.save((err, movie) => { 91 | if (err) { 92 | console.log(err) 93 | } 94 | // 添加 95 | Category.findById(movie.category, (err, category) => { 96 | if (category.movies.indexOf(movie._id) > -1) { 97 | return 98 | } else { 99 | category.movies.push(movie._id); 100 | } 101 | category.save((err, category) => { 102 | if (err) { 103 | console.log(err); 104 | } 105 | }); 106 | // 删除 107 | Category.findOne({ "movies": movie._id }, (err, category) => { 108 | if(category.movies.length > 0){ 109 | category.movies.forEach((e, i) => { 110 | if (e.toString() === movie._id.toString()) { 111 | category.movies.splice(i, 1) 112 | } 113 | }) 114 | category.save((err, category) => { 115 | if (err) { 116 | console.log(err); 117 | } else { 118 | console.log("保存成功"); 119 | } 120 | }) 121 | } 122 | }) 123 | }) 124 | 125 | res.redirect('/'); 126 | }) 127 | }) 128 | } else { 129 | _movie = new Movie(movieObj); 130 | var categoryId = movieObj.category; 131 | var categoryName = movieObj.categoryName; 132 | if (categoryId || categoryName) { 133 | _movie.save((err, movie) => { 134 | if (err) { 135 | console.log(err) 136 | } 137 | if (categoryId) { 138 | Category.findById(categoryId, (err, category) => { 139 | category.movies.push(_movie.id); 140 | category.save((err, category) => { 141 | if (err) { 142 | console.log(err) 143 | } 144 | res.redirect('/movie/' + movie._id) 145 | }) 146 | }) 147 | } else if (categoryName) { 148 | var category = new Category({ 149 | name: categoryName, 150 | movies: [movie._id] 151 | }) 152 | category.save((err, category) => { 153 | movie.category = category._id; 154 | movie.save((err, movie) => { 155 | res.redirect('/movie/' + movie._id); 156 | }) 157 | }) 158 | } 159 | }) 160 | } else { 161 | var msg = "请选择电影分类!"; 162 | res.redirect(`/status?return_url=/admin/movie/add&code=0&tips=${msg}`); 163 | } 164 | } 165 | }) 166 | 167 | // admin del movie 168 | router.delete('/admin/movie/list', requiredAdmin, (req, res) => { 169 | var id = req.query.id; 170 | if (id) { 171 | Category.findOne({"movies":id},(err,category)=>{ 172 | var index = category.movies.indexOf(id); 173 | category.movies.splice(index,1); 174 | category.save((err,category)=>{ 175 | if(err) console.log(err); 176 | }) 177 | }) 178 | Movie.remove({_id: id}, (err, movie) => { 179 | if (err) { 180 | console.log(err) 181 | } else { 182 | res.json({success: 1}) 183 | } 184 | }) 185 | } 186 | }) 187 | 188 | // admin del user 189 | router.delete('/admin/user/list', requiredAdmin, (req, res) => { 190 | var id = req.query.id; 191 | if (id) { 192 | User.remove({ 193 | _id: id 194 | }, (err, user) => { 195 | if (err) { 196 | console.log(err) 197 | } else { 198 | res.json({ 199 | success: 1 200 | }) 201 | } 202 | }) 203 | } 204 | }) 205 | router.post('/admin/user/comment', (req, res) => { 206 | // ajax提交评论直接评论已经实现,对评论人的评论暂未实现 207 | var _comment = req.body.comment; 208 | var comment = new Comment(_comment); 209 | var content = _comment.content 210 | var movieId = _comment.movie 211 | console.log(_comment) 212 | if (_comment.cid) { 213 | Comment.findById(_comment.cid, (err, comment) => { 214 | var reply = { 215 | from: _comment.from, 216 | to: _comment.tid, 217 | content: _comment.content 218 | } 219 | comment.reply.push(reply); 220 | console.log(comment); 221 | comment.save((err, comment) => { 222 | if (err) { 223 | console.log(err) 224 | } else { 225 | console.log("回复评论人", comment) 226 | res.redirect("/movie/" + movieId) 227 | } 228 | }) 229 | }) 230 | } else { 231 | comment.save((err, comment) => { 232 | if (err) { 233 | console.log(err) 234 | } else { 235 | console.log("直接评论", comment) 236 | // Comment.find({content: content}) 237 | // .populate("from","name") 238 | // .exec((err, comments) => { 239 | // res.send(comments) 240 | // }) 241 | res.redirect("/movie/" + movieId) 242 | } 243 | }) 244 | } 245 | 246 | }) 247 | module.exports = router; -------------------------------------------------------------------------------- /routes/admin/categoryAdmin.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var Movie = require('../../models/movie'); 4 | var User = require('../../models/user'); 5 | var Comment = require('../../models/comment'); 6 | var Category = require('../../models/category'); 7 | // 权限中间件 8 | var {requiredLogin,requiredAdmin} = require ('../middleware/auth'); 9 | // 没有挂载路径的中间件,应用的每个请求都会执行该中间件 10 | router.use(requiredLogin); 11 | // admin category 12 | // 挂载至 /xx/xx的中间件,任何指向 /xx/xx 的请求都会执行它 13 | router.get('/admin/category/new',requiredAdmin, (req, res) => { 14 | res.render('category', { 15 | title: '电影分类录入页', 16 | category:{}, 17 | }) 18 | }) 19 | 20 | // admin post movie 21 | router.post('/admin/category',requiredAdmin, (req, res) => { 22 | var categoryObj = req.body.category; 23 | var id = categoryObj._id; 24 | var name = categoryObj.name; 25 | if(id){ 26 | Category.findById(id,(err,category)=>{ 27 | category.name = name; 28 | category.save((err, category) => { 29 | if (err) { 30 | console.log(err) 31 | } 32 | res.redirect('/admin/category/list' ); 33 | }) 34 | }) 35 | }else{ 36 | var category = new Category(categoryObj); 37 | category.save((err, category) => { 38 | if (err) { 39 | console.log(err) 40 | } 41 | res.redirect('/admin/category/list' ); 42 | }) 43 | } 44 | }) 45 | // admin category list 46 | router.get('/admin/category/list',requiredAdmin, (req, res) => { 47 | Category.fetch((err, categories) => { 48 | if (err) { 49 | console.log(err) 50 | } else { 51 | res.render('categoryList', { 52 | title: '电影分类列表页', 53 | categories: categories 54 | }) 55 | } 56 | }) 57 | }) 58 | 59 | router.get('/admin/category/update/:id',requiredAdmin,(req,res) => { 60 | var id = req.params.id; 61 | if (id) { 62 | Category.findById(id, (err, category) => { 63 | console.log(category) 64 | res.render('category', { 65 | title: '电影分类更新页 > ' + category.name , 66 | category: category 67 | }) 68 | }) 69 | } 70 | }) 71 | 72 | router.delete('/admin/category/del',requiredAdmin,(req,res) =>{ 73 | var id = req.query.id; 74 | if (id) { 75 | Movie.find({category:id},(err,movies)=>{ 76 | console.log(movies); 77 | movies.forEach((ele,index)=>{ 78 | ele.remove(); 79 | }) 80 | }) 81 | Category.remove({_id: id}, (err, movie) => { 82 | if (err) { 83 | console.log(err) 84 | } else { 85 | res.json({success: 1}); 86 | } 87 | }) 88 | } 89 | }) 90 | 91 | module.exports = router; -------------------------------------------------------------------------------- /routes/admin/userCenter.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var User = require('../../models/user'); 4 | var {requiredLogin,requiredAdmin} = require ('../middleware/auth'); 5 | var path = require('path'); 6 | var fs = require('fs'); 7 | var multipart = require('connect-multiparty'); 8 | var multipartMiddleware = multipart(); 9 | var savePic = (req,res,next)=>{ 10 | var posterData = req.files.uploadPic 11 | var filePath = posterData.path 12 | var originalFilename = posterData.originalFilename 13 | // console.log(posterData+'\n',filePath+'\n',originalFilename+'\n'); 14 | if (originalFilename) { 15 | fs.readFile(filePath,(err,data)=>{ 16 | var timestamp = Date.now() 17 | var type = posterData.type.split('/')[1] 18 | var headPic = timestamp + '.' + type 19 | var newPath = path.join(__dirname, '../../', '/public/uploads/' + headPic) 20 | // console.log(newPath) 21 | fs.writeFile(newPath,data,(err,data)=>{ 22 | // console.log("数据写入成功!"); 23 | req.headPic = headPic 24 | next(); 25 | }) 26 | }) 27 | }else{ 28 | next(); 29 | } 30 | } 31 | 32 | router.get('/user/center',requiredLogin,(req,res)=>{ 33 | var userId = req.query.userId; 34 | // console.log(userId) 35 | User.findById(userId,(err, user) => { 36 | res.render('userCenter', { 37 | title: '个人中心', 38 | user: user 39 | }); 40 | }) 41 | }) 42 | router.post('/admin/user/info',multipartMiddleware,requiredLogin,savePic,(req,res)=>{ 43 | var userObj = req.body.user; 44 | var headPic = req.headPic; 45 | var userId = userObj._id; 46 | // console.log("userObj", userObj) 47 | var _User; 48 | User.findById(userId,(err,user)=>{ 49 | console.log(user) 50 | _User = Object.assign(user,userObj); 51 | if(headPic){ 52 | _User.img = headPic; 53 | } 54 | _User.firstSave = false; 55 | _User.save((err, user) => { 56 | if (err) { 57 | console.log(err) 58 | } else { 59 | res.redirect(`/status?return_url=/user/center?userId=${userId}&code=1&tips=保存成功`) 60 | } 61 | }) 62 | }) 63 | }) 64 | 65 | module.exports = router; -------------------------------------------------------------------------------- /routes/middleware/auth.js: -------------------------------------------------------------------------------- 1 | // 权限中间件 2 | var requiredLogin = (req, res, next) => { 3 | var _user = res.locals.user 4 | if (!_user) { 5 | res.locals.admin = false; 6 | return res.redirect('/signIn'); 7 | } 8 | next(); 9 | } 10 | var requiredAdmin = (req, res, next) => { 11 | var _user = res.locals.user 12 | if (_user.role < 10 || _user.role === 'undefined') { 13 | console.log("没有权限"); 14 | res.locals.admin = false; 15 | return res.redirect("/signIn"); 16 | } 17 | next(); 18 | } 19 | 20 | 21 | module.exports = { 22 | requiredLogin, 23 | requiredAdmin 24 | } -------------------------------------------------------------------------------- /routes/movie/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var Movie = require('../../models/movie'); 4 | var Comment = require('../../models/comment'); 5 | var Category = require('../../models/category'); 6 | // index 7 | router.get('/', (req, res) => { 8 | Category.find({}).limit(4) 9 | .populate({path: "movies",options: {limit: 4}}) 10 | .exec((err, categories) => { 11 | if (err) { 12 | console.log(err) 13 | } else { 14 | // console.log("categories",categories) 15 | res.render('index', { 16 | title: '首页', 17 | categories: categories 18 | }) 19 | } 20 | }) 21 | }) 22 | 23 | // detail 24 | router.get('/movie/:id', (req, res) => { 25 | var id = req.params.id; 26 | Movie.update({_id: id}, {'$inc': {'pv': 1}}, (err) => { 27 | if (err) console.log(err); 28 | }) 29 | Movie.findById(id, (err, movie) => { 30 | Comment.find({movie: id}) 31 | .populate("from", "name img") 32 | .populate("reply.from reply.to", "name img") 33 | .exec((err, comments) => { 34 | console.log("comments",comments) 35 | res.render('detail', { 36 | title: '详情页 > ' + movie.title, 37 | movie: movie, 38 | comments: comments 39 | }); 40 | }) 41 | }) 42 | }) 43 | router.get('/movie/category/result', (req, res) => { 44 | var size = 8; // 一页8个 45 | var categoryId = req.query.cat; 46 | var pageSize = parseInt(req.query.pageSize); 47 | var totalSize = 0; // 一共有多少数据; 48 | res.locals.categoryId = categoryId; 49 | Category.findById(categoryId, (err, category) => { 50 | totalSize = category.movies.length; 51 | var page = Math.ceil(totalSize / size) //分多少页 52 | Category.find({_id: categoryId}) 53 | .populate({ path: "movies", options: { limit: size ,skip: (pageSize-1) * size} }) 54 | .exec((err, categoris) => { 55 | // console.log(categoris) 56 | if (err) console.log(err); 57 | res.render('categoryResult', { 58 | title: '当前类别', 59 | categoris: categoris, 60 | page:page, 61 | categoryId:categoryId, 62 | pageSize:pageSize, 63 | }) 64 | }) 65 | 66 | }) 67 | }) 68 | 69 | // search 70 | router.post('/search',(req,res)=>{ 71 | // var pageSize = req.query.pageSize; 72 | var q = req.body.query; 73 | var reg = new RegExp(q+'.*','i'); 74 | var totalSize = 0; 75 | Movie.find({title:reg},(err,movies)=>{ 76 | if(movies.length<=0){ 77 | Category.find({name:reg}).populate("movies","poster title").exec((err,categories)=>{ 78 | categories.forEach((ele,index)=>{ 79 | // console.log(ele.movies.length) 80 | totalSize += ele.movies.length; 81 | }) 82 | res.render('search',{ 83 | title: '搜索页', 84 | categories:categories, 85 | number:totalSize, 86 | keywords:q, 87 | }) 88 | }) 89 | }else{ 90 | res.render('search',{ 91 | title: '搜索页', 92 | movies:movies, 93 | number:movies.length, 94 | keywords:q 95 | }) 96 | } 97 | }) 98 | 99 | }) 100 | 101 | module.exports = router; -------------------------------------------------------------------------------- /routes/status/status.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | router.get('/status',(req,res)=>{ 5 | var code = parseInt(req.query.code); 6 | var tips = req.query.tips; 7 | var return_url = req.query.return_url; 8 | res.render("status",{ 9 | title:"友情提示页", 10 | tips:tips, 11 | code:code, 12 | href:return_url 13 | }) 14 | }) 15 | 16 | module.exports = router; -------------------------------------------------------------------------------- /routes/user/user.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var User = require('../../models/user'); 4 | // signIn 5 | router.post('/user/signIn', (req, res) => { 6 | var _user = req.body.user; 7 | var name = _user.name; 8 | var password = _user.password; 9 | User.findOne({ 10 | name: name 11 | }, (err, user) => { 12 | if (err) console.log(err); 13 | if (!user) { 14 | console.log('用户不存在'); 15 | var msg = "用户不存在,去注册吧!" 16 | res.redirect(`/status?return_url=/signIn&code=0&tips=${msg}`) 17 | } else { 18 | console.log('用户存在:' + name, password) 19 | // res.redirect('/') 20 | user.comparePassword(password, (err, isMatch) => { 21 | console.log(password,isMatch) 22 | if (err) console.log(err); 23 | if (isMatch) { 24 | req.session.user = user; 25 | res.redirect('/') 26 | console.log('登录成功:Password is matched') 27 | } else { 28 | console.log('登录失败:Password is not matched'); 29 | var msg ="登录失败,密码可能错误,请重新登录!" 30 | res.redirect(`/status?return_url=/signIn&code=0&tips=${msg}`) 31 | } 32 | }) 33 | } 34 | }) 35 | }) 36 | 37 | // signUp 38 | router.post('/user/signUp', (req, res) => { 39 | var userObj = req.body.user; 40 | console.log("userObj",userObj) 41 | // 如果是已经注册过的,直接重定向到首页 42 | User.findOne({ 43 | name: userObj.name 44 | }, (err, user) => { 45 | if (err) console.log(err); 46 | if (user) { 47 | var msg= "账号存在,去登录吧!" 48 | res.redirect(`/status?return_url=/signIn&code=0&tips=${msg}`) 49 | } else { 50 | var _user = new User(userObj); 51 | _user.firstSave = true; 52 | _user.save((err, user) => { 53 | if (err) { 54 | console.log(err) 55 | } 56 | var msg= "注册成功,去登录吧!" 57 | res.redirect(`/status?return_url=/signIn&code=1&tips=${msg}`) 58 | }) 59 | } 60 | }) 61 | }) 62 | 63 | // loginout 64 | router.get('/loginout', (req, res) => { 65 | // console.log("1",req.session.user) 66 | // console.log("2",res.locals.user) 67 | delete req.session.user; 68 | delete res.locals.user; 69 | res.redirect('/'); 70 | }) 71 | router.get('/signIn', (req, res) => { 72 | res.render('signIn', { 73 | title: '登录' 74 | }) 75 | }) 76 | router.get('/signUp', (req, res) => { 77 | res.render('signUp', { 78 | title: '注册' 79 | }) 80 | }) 81 | 82 | module.exports = router; -------------------------------------------------------------------------------- /schemas/category.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var ObjectId = mongoose.Schema.Types.ObjectId; 3 | var CategorySchema = new mongoose.Schema({ 4 | name:String, 5 | movies: [{type: ObjectId, ref: 'Movie'}], 6 | meta: { 7 | createAt: { 8 | type: Date, 9 | default: Date.now() 10 | }, 11 | updateAt: { 12 | type: Date, 13 | default: Date.now() 14 | } 15 | } 16 | }) 17 | // 为模式添加新的方法 18 | CategorySchema.pre('save', function (next) { 19 | if (this.isNew) { 20 | this.meta.createAt = this.meta.updateAt = Date.now(); 21 | } else { 22 | this.meta.updateAt = Date.now(); 23 | } 24 | next() 25 | }) 26 | CategorySchema.statics = { 27 | fetch: function (cb) { 28 | return this 29 | .find({}) 30 | .sort('meta.updateAt') 31 | .exec(cb) 32 | }, 33 | findById: function (id, cb) { 34 | return this 35 | .findOne({_id: id}) 36 | .exec(cb) 37 | } 38 | } 39 | module.exports = CategorySchema; 40 | -------------------------------------------------------------------------------- /schemas/comment.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var ObjectId = mongoose.Schema.Types.ObjectId; 3 | var CommentSchema = new mongoose.Schema({ 4 | movie: { 5 | type: ObjectId, 6 | ref: 'Movie' 7 | }, 8 | from: { 9 | type: ObjectId, 10 | ref: 'User' 11 | }, 12 | to: { 13 | type: ObjectId, 14 | ref: 'User' 15 | }, 16 | reply: [{ 17 | from: {type: ObjectId,ref: 'User'}, 18 | to: {type: ObjectId,ref: 'User'}, 19 | content:String, 20 | }], 21 | content: String, 22 | meta: { 23 | createAt: { 24 | type: Date, 25 | default: Date.now() 26 | }, 27 | updateAt: { 28 | type: Date, 29 | default: Date.now() 30 | } 31 | } 32 | }) 33 | 34 | // 为模式添加新的方法 35 | CommentSchema.pre('save', function(next) { 36 | if (this.isNew) { 37 | this.meta.createAt = this.meta.updateAt = Date.now(); 38 | } else { 39 | this.meta.updateAt = Date.now(); 40 | } 41 | next() 42 | }) 43 | 44 | CommentSchema.statics = { 45 | fetch: function(cb) { 46 | return this 47 | .find({}) 48 | .sort('meta.updateAt') 49 | .exec(cb) 50 | }, 51 | findById: function(id, cb) { 52 | return this 53 | .findOne({ 54 | _id: id 55 | }) 56 | .exec(cb) 57 | } 58 | } 59 | module.exports = CommentSchema; -------------------------------------------------------------------------------- /schemas/movie.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var ObjectId = mongoose.Schema.Types.ObjectId; 3 | var MovieSchema = new mongoose.Schema({ 4 | doctor: String, 5 | title: String, 6 | language: String, 7 | country: String, 8 | poster: String, 9 | summary: String, 10 | year: Number, 11 | flash:String, 12 | pv:{ 13 | type:Number, 14 | default:0 15 | }, 16 | category: { 17 | type: ObjectId, 18 | ref: 'Category' 19 | }, 20 | meta: { 21 | createAt: { 22 | type: Date, 23 | default: Date.now() 24 | }, 25 | updateAt: { 26 | type: Date, 27 | default: Date.now() 28 | } 29 | } 30 | }) 31 | // 为模式添加新的方法 32 | MovieSchema.pre('save', function (next) { 33 | if (this.isNew) { 34 | this.meta.createAt = this.meta.updateAt = Date.now(); 35 | } else { 36 | this.meta.updateAt = Date.now(); 37 | } 38 | next() 39 | }) 40 | MovieSchema.statics = { 41 | fetch: function (cb) { 42 | return this 43 | .find({}) 44 | .sort('meta.updateAt') 45 | .exec(cb) 46 | }, 47 | findById: function (id, cb) { 48 | return this 49 | .findOne({_id: id}) 50 | .exec(cb) 51 | } 52 | } 53 | module.exports = MovieSchema; 54 | -------------------------------------------------------------------------------- /schemas/user.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var bcrypt = require('bcrypt-nodejs'); 3 | var ObjectId = mongoose.Schema.Types.ObjectId; 4 | const SALT_WORK_FACTOR = 10; 5 | var UserSchema = new mongoose.Schema({ 6 | name:{ 7 | type:String, 8 | unique:true 9 | }, 10 | password:{ 11 | type:String, 12 | unique:true 13 | }, 14 | role:{ 15 | type:Number, 16 | default:0 17 | }, 18 | sex:{ 19 | type:String, 20 | default:'男' 21 | }, 22 | img:String, 23 | email:String, 24 | tel:Number, 25 | firstSave:{ 26 | type:Boolean, 27 | default:false 28 | }, 29 | meta: { 30 | createAt: { 31 | type: Date, 32 | default: Date.now() 33 | }, 34 | updateAt: { 35 | type: Date, 36 | default: Date.now() 37 | } 38 | } 39 | }) 40 | // 为模式添加新的方法 41 | UserSchema.pre('save', function (next) { 42 | var user = this; 43 | if (this.isNew) { 44 | this.meta.createAt = this.meta.updateAt = Date.now(); 45 | } else { 46 | this.meta.updateAt = Date.now(); 47 | } 48 | if(this.firstSave){ 49 | bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) { 50 | if (err) return next(err) 51 | bcrypt.hash(user.password, null,null,function (err, hash){ 52 | if (err) { 53 | return next(err) 54 | } 55 | user.password = hash 56 | next() 57 | }) 58 | }) 59 | }else{ 60 | next(); 61 | } 62 | }) 63 | 64 | // 实例方法: 65 | UserSchema.methods = { 66 | comparePassword: function (_password, cb) { 67 | var hash = this.password; 68 | var isMatch = bcrypt.compareSync(_password, hash); 69 | cb(null, isMatch); 70 | } 71 | } 72 | 73 | // 静态方法 74 | UserSchema.statics = { 75 | fetch: function (cb) { 76 | return this 77 | .find({}) 78 | .sort('meta.updateAt') 79 | .exec(cb) 80 | }, 81 | findById: function (id, cb) { 82 | return this 83 | .findOne({_id: id}) 84 | .exec(cb) 85 | } 86 | } 87 | module.exports = UserSchema; 88 | -------------------------------------------------------------------------------- /screenshot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/screenshot/1.png -------------------------------------------------------------------------------- /screenshot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/screenshot/2.png -------------------------------------------------------------------------------- /screenshot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/screenshot/3.png -------------------------------------------------------------------------------- /screenshot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/screenshot/4.png -------------------------------------------------------------------------------- /screenshot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjun1127/movie-site/b6156cdc299a51132ad39ff64c830c1a0ba5a206/screenshot/5.png -------------------------------------------------------------------------------- /views/includes/head.jade: -------------------------------------------------------------------------------- 1 | link(rel="stylesheet", href="/libs/bootstrap/dist/css/bootstrap.min.css") 2 | script(src="/libs/jquery/dist/jquery.min.js") 3 | script(src="/libs/bootstrap/dist/js/bootstrap.min.js") -------------------------------------------------------------------------------- /views/includes/header.jade: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .col-md-4 4 | h3(style="margin-top:15px;") #{title} 5 | if title=="首页" 6 | .col-md-4 7 | form#form-search(action="/search",method="post",onSubmit="return false") 8 | .input-group(style="padding-top:10px") 9 | input.form-control(type="text",onkeydown="onKeyDown(this)",name="query",placeholder="输入关键字并回车!") 10 | span.input-group-addon 11 | i.glyphicon.glyphicon-search 12 | .col-md-4.pull-right 13 | if user 14 | h5.text-right(style="margin-top: 10px") 15 | span 欢迎您, 16 | a(href="/user/center?userId=#{user._id}",style="color:#337ab7;font-style:normal;margin-right:10px;")  #{user.name} 17 | span.dropdown(style="display: inline-block;") 18 | button.btn.btn-primary.dropdown-toggle(id="dLable",type="button",data-toggle="dropdown",aria-haspopup="true",aria-expanded="false") 后台管理 19 | span.caret 20 | ul(class="dropdown-menu",aria-labelledby="dLable") 21 | li 22 | a(href="/admin/movie/list") 电影列表 23 | li 24 | a(href="/admin/movie/add") 电影录入 25 | li 26 | a(href="/admin/category/list") 电影分类 27 | li 28 | a(href="/admin/category/new") 新增分类 29 | li 30 | a(href="/admin/user/list") 用户列表 31 | li 32 | a(href="/") 返回首页 33 | a.btn.btn-danger(href="/loginout",style="margin-left:10px;") 注销 34 | else 35 | h5.text-right(style="margin-top: 10px") 36 | a(href="/",class="btn btn-primary pull-right",style="margin:0 0 10px 10px",data-toggle="modal",data-target="#signin-modal") 登录 37 | a(href="/",class="btn btn-success pull-right",data-toggle="modal",data-target="#signup-modal") 注册 38 | hr(style="margin:10px 0;margin-top:0;border-color:#d9d9d9") 39 | #signin-modal.modal.fade 40 | .modal-dialog 41 | .modal-container(style="background:#fff;border-radius: 6px;-webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);") 42 | form(method="POST",action="/user/signIn") 43 | .modal-header 44 | h4.modal-title 登录 45 | .modal-body 46 | .form-group 47 | label(for="signinName") ID: 48 | input.form-control(type="text", id="signinName" ,placeholder="ID",name="user[name]") 49 | .form-group 50 | label(for="signPassword") Password: 51 | input.form-control(type="password", id="signPassword" ,placeholder="Password",name="user[password]") 52 | .modal-footer 53 | button.btn.btn-default(type="button",data-dismiss="modal") close 54 | button.btn.btn-success(type="submit") submit 55 | 56 | #signup-modal.modal.fade 57 | .modal-dialog 58 | .modal-container(style="background:#fff;border-radius: 6px;-webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);") 59 | form(method="POST",action="/user/signUp") 60 | .modal-header 61 | h4.modal-title 注册 62 | .modal-body 63 | .form-group 64 | label(for="signupName") ID: 65 | input.form-control(type="text", id="signupName" ,placeholder="ID",name="user[name]") 66 | .form-group 67 | label(for="signupPassword") Password: 68 | input.form-control(type="password", id="signupPassword" ,placeholder="Password",name="user[password]") 69 | .modal-footer 70 | button.btn.btn-default(type="button",data-dismiss="modal") close 71 | button.btn.btn-success(type="submit") submit 72 | script. 73 | function onKeyDown(ele){ 74 | var e = event || window.event 75 | var $val = $(ele).val(); 76 | if(e && e.keyCode==13){ // enter 键 77 | console.info($val) 78 | if($val==""||$val==null){ 79 | return false 80 | }else{ 81 | console.log($val); 82 | $("#form-search").attr('onSubmit','return true'); 83 | } 84 | return false 85 | } 86 | } -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(content="IE=edge",http-equiv="X-UA-Compatible") 6 | meta(name="viewport",content="width=device-width, initial-scale=1") 7 | title #{title} 8 | include ./includes/head.jade 9 | body 10 | include ./includes/header.jade 11 | block content 12 | 13 | -------------------------------------------------------------------------------- /views/pages/category.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | form.form-horizontal(method="post",action="/admin/category") 7 | if category._id 8 | input(type="hidden", name="category[_id]", value="#{category._id}") 9 | .form-group 10 | label.col-sm-2.control-label(for="inputCategory") 电影分类 11 | .col-sm-10 12 | input#inputCategory.form-control(type="text",name="category[name]",value=category.name) 13 | .form-group 14 | .col-sm-offset-2.col-sm-10 15 | button.btn.btn-primary(type="submit") 录入 -------------------------------------------------------------------------------- /views/pages/categoryList.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | table.table.table-hover.table-bordered 7 | thead 8 | tr 9 | th(style="text-align:center;width:60px;") 序号 10 | th 电影类别 11 | th(style="text-align:center;") 合计(部) 12 | th(style="text-align:center;") 录入时间 13 | th(style="text-align:center;") 查看 14 | th(style="text-align:center;") 更新 15 | th(style="text-align:center;") 删除 16 | tbody 17 | each item,index in categories 18 | tr(class="item-id-#{item._id}") 19 | td(style="vertical-align: middle;text-align:center;") #{index+1} 20 | td(style="vertical-align: middle;") #{item.name} 21 | td(style="vertical-align: middle;text-align:center;") #{item.movies.length} 22 | td(style="vertical-align: middle;text-align:center;") #{moment(item.meta.createAt).format("MM/DD/YYYY")} 23 | td(style="vertical-align: middle;text-align:center;"): a(target="_blank",href="/movie/category/result?cat=#{item._id}&pageSize=1") 查看 24 | td(style="vertical-align: middle;text-align:center;"): a(target="_blank",href="../category/update/#{item._id}") 修改 25 | td(style="vertical-align: middle;text-align:center;") 26 | button.btn.btn-danger.del(type="button",data-id="#{item._id}",data-toggle="modal") 删除 27 | #del-modal.modal.fade 28 | .modal-dialog 29 | .modal-container(style="background:#fff;border-radius: 6px;-webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);") 30 | .modal-header 31 | h4.modal-title 提示 32 | .modal-body 33 | h4(style="color:#c9302c") 确定删除该分类吗?删除后该分类下数据也被删除! 34 | .modal-footer 35 | button.btn.btn-default(type="button",data-dismiss="modal") 取消 36 | button.btn.btn-success(type="button") 确定 37 | script. 38 | var categoryId = null; 39 | var tr = null; 40 | $(".del").click(function(){ 41 | $("#del-modal").modal(); 42 | categoryId = $(this).data("id"); 43 | tr = $(this).parents("tr"); 44 | }) 45 | $(".btn-success").click(function(){ 46 | if(categoryId){ 47 | $.ajax({ 48 | type: 'DELETE', 49 | url: '/admin/category/del' + "?id=" + categoryId, 50 | }).done(function(res) { 51 | if (res.success == 1) { 52 | if (tr.length > 0) { 53 | tr.remove(); 54 | } 55 | } 56 | }) 57 | } 58 | $("#del-modal").modal('hide'); 59 | }) -------------------------------------------------------------------------------- /views/pages/categoryResult.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | .panel.panel-default 7 | each cat in categoris 8 | .panel-heading 9 | h3 #{cat.name} 10 | .panel-body 11 | if cat.movies && cat.movies.length > 0 12 | .row 13 | each item,index in cat.movies 14 | .col-md-3 15 | .thumbnail 16 | a(href="/movie/"+item._id) 17 | img(src=item.poster,alt="#{item.title}",style="height:350px;") 18 | .caption 19 | h5=item.title 20 | a.btn.btn-primary(href="movie/#{item._id}",role="button") 观看预告片 21 | nav(style="text-align: center") 22 | ul.pagination.pagination-lg(style="margin:10px 0") 23 | if pageSize-1 <= 0 24 | li.previous.disabled 25 | a(href='javascript:;') « 26 | else 27 | li.previous 28 | a(href='/movie/category/result?cat=#{categoryId}&pageSize=#{pageSize-1}') « 29 | -for(var i = 0; i < page; i++){ 30 | -if( pageSize == (i+1)){ 31 | li.active 32 | span #{pageSize} 33 | -}else{ 34 | li 35 | a(href='/movie/category/result?cat=#{categoryId}&pageSize=#{i+1}') #{i+1} 36 | -} 37 | -} 38 | if pageSize+1 > page 39 | li.next.disabled 40 | a(href='javascript:;') » 41 | else 42 | li.next 43 | a(href='/movie/category/result?cat=#{categoryId}&pageSize=#{pageSize+1}') » -------------------------------------------------------------------------------- /views/pages/detail.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-7 6 | embed(src="#{movie.flash}",allowFullScreen="true",quality="high",width="720",height="600",align="middle",AllowScriptAccess ="always",type="application/x-shockware-flash") 7 | .col-md-5 8 | dl.dl-horizontal 9 | dt 电影名字 10 | dd=movie.title 11 | dt 导演 12 | dd=movie.doctor 13 | dt 国家 14 | dd=movie.country 15 | dt 语言 16 | dd=movie.language 17 | dt 上映年份 18 | dd=movie.year 19 | dt 简介 20 | dd=movie.summary 21 | h3(style="padding-bottom:12px;border-bottom:1px solid #d9d9d9;") 评论 22 | .comment-list 23 | each item in comments 24 | .row 25 | .col-md-1 26 | a.comment_link(href="javascript:;",data-cid="#{item._id}",data-tid="#{item.from._id}") 27 | img(src="/uploads/#{item.from.img}", style="width:64px;height:64px;border-radius:50%") 28 | .col-md-11 29 | h5(style="color:#337ab7") #{item.from.name} 30 | span.pull-right(style="color:#999") #{moment(item.meta.createAt,'YYYYDDMM').fromNow()} 31 | p #{item.content} 32 | .comment-reply 33 | if item.reply && item.reply.length>0 34 | each reply in item.reply 35 | .row 36 | .col-md-1 37 | img(src="/uploads/#{reply.from.img}", style="width:64px;height:64px;border-radius:50%") 38 | .col-md-11 39 | h5(style="color:#337ab7") 40 | span #{reply.from.name} 41 | span(style="color:#666") 回复 42 | span #{reply.to.name} 43 | p #{reply.content} 44 | hr 45 | hr 46 | .row 47 | form(id="form-comment",method="POST",action="/admin/user/comment") 48 | .col-md-1 49 | img(src="/images/headpic.png", style="width:64px;height:64px;border-radius:50%") 50 | .col-md-11 51 | textarea.form-control(cols="3",name="comment[content]",style="height:70px;",placeholder="说点什么吧!") 52 | if user 53 | .form-group.pull-right(style="margin:15px;") 54 | input(type="hidden",name="comment[movie]",value="#{movie._id}") 55 | input(type="hidden",name="comment[from]",value="#{user._id}") 56 | button#comment-btn.btn.btn-primary(type="submit") 发表评论 57 | else 58 | .form-group.pull-right(style="margin:15px;") 59 | a(href="/",data-toggle="modal",data-target="#signin-modal") 登录后评论 60 | script(src="//cdn.bootcss.com/moment.js/2.18.1/moment.min.js") 61 | script(src="//cdn.bootcss.com/moment.js/2.18.1/locale/zh-cn.js") 62 | script(src="/js/comment.js") -------------------------------------------------------------------------------- /views/pages/index.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | each cat in categories 7 | .panel.panel-default 8 | .panel-heading.clearfix(style="position:relative") 9 | h3 #{cat.name} 10 | if cat.movies && cat.movies.length >= 4 11 | a(href="/movie/category/result?cat=#{cat._id}&pageSize=1",style="position: absolute;right: 15px;top:35px;font-size:14px;") MORE >> 12 | .panel-body 13 | if cat.movies && cat.movies.length > 0 14 | .row 15 | each item in cat.movies 16 | .col-md-3 17 | .thumbnail 18 | a(href="/movie/"+item._id) 19 | img(src=item.poster,alt="#{item.title}",style="height:350px;") 20 | .caption 21 | h5=item.title 22 | a.btn.btn-primary(href="movie/#{item._id}",role="button") 观看预告片 -------------------------------------------------------------------------------- /views/pages/movieAdmin.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | form.form-horizontal(method="post",action="/admin/movie/new") 6 | .form-group 7 | label.col-sm-2.control-label(for="inputDouban") 同步豆瓣 8 | .col-sm-10 9 | input#inputDouban.form-control(type="text",placeholder="请输入豆瓣电影ID",value="") 10 | if movie._id 11 | input(type="hidden", name="movie[_id]", value=movie._id) 12 | .form-group 13 | label.col-sm-2.control-label(for="inputCategory") 电影分类 14 | .col-sm-10 15 | input#inputCategory.form-control(type="text",name="movie[categoryName]",value=movie.categoryName) 16 | .form-group 17 | label.col-sm-2.control-label 分类选择 18 | .col-sm-10 19 | each cat in categories 20 | label.radio-inline 21 | if movie.category 22 | input(type="radio", name="movie[category]", value=cat._id, checked=cat._id.toString()==movie.category.toString()) 23 | else 24 | input(type="radio", name="movie[category]", value=cat._id) 25 | | #{cat.name} 26 | .form-group 27 | label.col-sm-2.control-label(for="inputTitle") 电影名字 28 | .col-sm-10 29 | input#inputTitle.form-control(type="text",name="movie[title]",value="#{movie.title}") 30 | .form-group 31 | label.col-sm-2.control-label(for="inputDoctor") 电影导演 32 | .col-sm-10 33 | input#inputDoctor.form-control(type="text",name="movie[doctor]",value="#{movie.doctor}") 34 | .form-group 35 | label.col-sm-2.control-label(for="inputLanguage") 语种 36 | .col-sm-10 37 | input#inputLanguage.form-control(type="text",name="movie[language]",value="#{movie.language}") 38 | .form-group 39 | label.col-sm-2.control-label(for="inputCountry") 国家 40 | .col-sm-10 41 | input#inputCountry.form-control(type="text",name="movie[country]",value="#{movie.country}") 42 | .form-group 43 | label.col-sm-2.control-label(for="inputPoster") 封面图片 44 | .col-sm-10 45 | input#inputPoster.form-control(type="text",name="movie[poster]",value="#{movie.poster}") 46 | .form-group 47 | label.col-sm-2.control-label(for="inputFlash") 片源地址 48 | .col-sm-10 49 | input#inputFlash.form-control(type="text",name="movie[flash]",value="#{movie.flash}") 50 | .form-group 51 | label.col-sm-2.control-label(for="inputYear") 上映年份 52 | .col-sm-10 53 | input#inputYear.form-control(type="text",name="movie[year]",value="#{movie.year}") 54 | .form-group 55 | label.col-sm-2.control-label(for="inputSummary") 电影简介 56 | .col-sm-10 57 | input#inputSummary.form-control(type="text",name="movie[summary]",value="#{movie.summary}") 58 | .form-group 59 | .col-sm-offset-2.col-sm-10 60 | button.btn.btn-primary(type="submit") 录入 61 | script. 62 | $("#inputDouban").blur(function() { 63 | var doubanMovieId = $(this).val(); 64 | $.ajax({ 65 | url: `https://api.douban.com/v2/movie/subject/${doubanMovieId}`, 66 | method: 'get', 67 | dataType: "jsonp", 68 | jsonp: "callback", 69 | success: function(data) { 70 | console.log(data); 71 | var directors = data.directors; 72 | var directorArray = []; 73 | $.each(directors,function(index,ele){ 74 | directorArray[index] = ele.name 75 | }) 76 | $("#inputTitle").val(data.title); 77 | $("#inputDoctor").val(directorArray.join(" / ")); 78 | $("#inputCountry").val(data.countries.join(" / ")); 79 | $("#inputPoster").val(data.images.large); 80 | $("#inputYear").val(data.year); 81 | $("#inputSummary").val(data.summary); 82 | } 83 | }) 84 | }) -------------------------------------------------------------------------------- /views/pages/movieList.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | table.table.table-hover.table-bordered 7 | thead 8 | tr 9 | th(style="text-align:center;width:60px;") 序号 10 | th 电影名字 11 | th 导演 12 | th(style="text-align:center;") 国家 13 | th(style="text-align:center;") 上映年份 14 | th(style="text-align:center;") 录入时间 15 | th(style="text-align:center;") 所属分类 16 | th(style="text-align:center;") 统计pv 17 | th(style="text-align:center;") 查看 18 | th(style="text-align:center;") 更新 19 | th(style="text-align:center;") 删除 20 | tbody 21 | each item,index in movies 22 | tr(class="item-id-#{item._id}") 23 | td(style="vertical-align: middle;text-align:center;") #{index+1} 24 | td(style="vertical-align: middle;") #{item.title} 25 | td(style="vertical-align: middle;") #{item.doctor} 26 | td(style="vertical-align: middle;text-align:center;") #{item.country} 27 | td(style="vertical-align: middle;text-align:center;") #{item.year} 28 | td(style="vertical-align: middle;text-align:center;") #{moment(item.meta.createAt).format("MM/DD/YYYY")} 29 | td(style="vertical-align: middle;text-align:center;") #{item.category.name} 30 | td(style="vertical-align: middle;text-align:center;") #{item.pv} 31 | td(style="vertical-align: middle;text-align:center;"): a(target="_blank",href="/movie/#{item._id}") 查看 32 | td(style="vertical-align: middle;text-align:center;"): a(target="_blank",href="/admin/movie/update/#{item._id}") 修改 33 | td(style="vertical-align: middle;text-align:center;") 34 | button.btn.btn-danger.del(type="button",data-id="#{item._id}",onclick="del(this,'/admin/movie/list')") 删除 35 | script(src="/js/admin.js") -------------------------------------------------------------------------------- /views/pages/search.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | .panel.panel-default 7 | .panel-heading 8 | h3 9 | span 搜索 10 | span(style="color:red;")=keywords 11 | span 的结果 12 | span 共#{number}条 13 | .panel-body 14 | if movies 15 | .row 16 | each item,index in movies 17 | .col-md-3 18 | .thumbnail 19 | a(href="/movie/"+item._id) 20 | img(src=item.poster,alt="#{item.title}",style="height:350px;") 21 | .caption 22 | h5=item.title 23 | a.btn.btn-primary(href="movie/#{item._id}",role="button") 观看预告片 24 | if categories 25 | .row 26 | each cat in categories 27 | //- h4(style="margin:0 15px 15px 15px;border-bottom:1px solid #d9d9d9;padding-bottom:13px;")=cat.name 28 | each item in cat.movies 29 | .col-md-3 30 | .thumbnail 31 | a(href="/movie/"+item._id) 32 | img(src=item.poster,alt="#{item.title}",style="height:350px;") 33 | .caption 34 | h5=item.title 35 | a.btn.btn-primary(href="movie/#{item._id}",role="button") 观看预告片 36 | //- if number > 8 37 | //- nav(style="text-align: center") 38 | //- ul.pagination.pagination-lg(style="margin:10px 0") 39 | //- if pageSize-1 <= 0 40 | //- li.previous.disabled 41 | //- a(href='javascript:;') « 42 | //- else 43 | //- li.previous 44 | //- a(href='/search?pageSize=#{pageSize-1}') « 45 | //- -for(var i = 0; i < page; i++){ 46 | //- -if( pageSize == (i+1)){ 47 | //- li.active 48 | //- span #{pageSize} 49 | //- -}else{ 50 | //- li 51 | //- a(href='/search?pageSize=#{i+1}') #{i+1} 52 | //- -} 53 | //- -} 54 | //- if pageSize+1 > page 55 | //- li.next.disabled 56 | //- a(href='javascript:;') » 57 | //- else 58 | //- li.next 59 | //- a(href='/search?pageSize=#{pageSize+1}') » -------------------------------------------------------------------------------- /views/pages/signIn.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(content="IE=edge",http-equiv="X-UA-Compatible") 6 | meta(name="viewport",content="width=device-width, initial-scale=1") 7 | title #{title} 8 | include ../includes/head.jade 9 | body 10 | .container(style="background:#fff;") 11 | form(method="POST",action="/user/signIn") 12 | .row(style="padding-top:15px;font-size:18px;") 13 | .col-md-6 登录 14 | if !admin 15 | .col-md-6.text-right(style="color:#d9534f;") 当前账户没有登录或无权限访问,请重试! 16 | hr(style="margin:15px 0;border-color:#d9d9d9") 17 | .form-group 18 | label(for="signinName") ID: 19 | input.form-control(type="text", id="signinName" ,placeholder="ID",name="user[name]") 20 | .form-group 21 | label(for="signPassword") Password: 22 | input.form-control(type="password", id="signPassword" ,placeholder="Password",name="user[password]") 23 | .form-group.text-center 24 | button.btn.btn-default(type="button",data-dismiss="modal") close 25 | button.btn.btn-success(type="submit",style="margin-left:10px;") submit -------------------------------------------------------------------------------- /views/pages/signUp.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(content="IE=edge",http-equiv="X-UA-Compatible") 6 | meta(name="viewport",content="width=device-width, initial-scale=1") 7 | title #{title} 8 | include ../includes/head.jade 9 | body 10 | .container(style="background:#fff;") 11 | form(method="POST",action="/user/signUp") 12 | .row(style="padding-top:15px;font-size:18px;") 13 | .col-md-12 注册 14 | hr(style="margin:15px 0;border-color:#d9d9d9") 15 | .form-group 16 | label(for="signupName") ID: 17 | input.form-control(type="text", id="signupName" ,placeholder="ID",name="user[name]") 18 | .form-group 19 | label(for="signupPassword") Password: 20 | input.form-control(type="password", id="signupPassword" ,placeholder="Password",name="user[password]") 21 | .form-group.text-center 22 | button.btn.btn-default(type="button",data-dismiss="modal") close 23 | button.btn.btn-success(type="submit",style="margin-left:10px;") submit -------------------------------------------------------------------------------- /views/pages/status.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .status(style="margin:140px auto;width:520px;border:1px solid #d9d9dd;border-radius:5px;text-align:center;padding:60px 0;") 5 | if code == 1 6 | img(src="/images/success.png", width="100") 7 | else 8 | img(src="/images/fail.png", width="100") 9 | h3(style="padding:10px 0") #{tips} 10 | a#button.btn.btn-success.btn-lg(href="javascript:;",data-href="#{href}") 11 | span#number 3 12 | span 秒后返回.. 13 | script. 14 | var s = 3; 15 | function num(){ 16 | $("#number").html(s); 17 | s --; 18 | if(s<=0){ 19 | window.location.href=$("#button").data("href"); 20 | } 21 | } 22 | function SetNum(){ 23 | setInterval("num()",1000); 24 | } 25 | window.onload=function(){ 26 | SetNum(); 27 | } 28 | -------------------------------------------------------------------------------- /views/pages/userCenter.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | form.form-horizontal(method="post",action="/admin/user/info",enctype="multipart/form-data") 7 | input(type="hidden",name="user[_id]",value="#{user._id}") 8 | .form-group 9 | label.col-sm-2.control-label(for="inputUserName") 用户名 10 | .col-sm-10 11 | input#inputUserName.form-control(type="text",name="user[name]",value=user.name,disabled="disabled") 12 | .form-group 13 | label.col-sm-2.control-label(for="inputSex") 性别 14 | .col-sm-10 15 | label.radio-inline 16 | input(type="radio", name="user[sex]",value="男",checked=user.sex=="男") 17 | span 男 18 | label.radio-inline 19 | input(type="radio", name="user[sex]",value="女",checked=user.sex=="女") 20 | span 女 21 | .form-group 22 | label.col-sm-2.control-label(for="inputEmail") 邮箱 23 | .col-sm-10 24 | input#inputEmail.form-control(type="text",name="user[email]",value=user.email) 25 | .form-group 26 | label.col-sm-2.control-label(for="inputTel") 手机 27 | .col-sm-10 28 | input#inputTel.form-control(type="text",name="user[tel]",value=user.tel) 29 | .form-group 30 | label.col-sm-2.control-label(for="inputHeadPic") 头像 31 | .col-sm-10 32 | if !user.img 33 | img(src="/images/headpic.png",style="width:80px;height:80px;") 34 | else 35 | img(src="/uploads/#{user.img}",style="width:80px;height:80px;") 36 | .form-group 37 | label.col-sm-2.control-label(for="uploadPic") 38 | .col-sm-10 39 | button.btn.btn-default(style="position:relative") 头像上传 40 | input#uploadPic(type="file",name="uploadPic",style="position: absolute;left: 0;top: 0;z-index: 1;width: 100%;opacity: 0;cursor: pointer;height:100%") 41 | .form-group 42 | .col-sm-offset-2.col-sm-10 43 | button.btn.btn-primary(type="submit") 录入 -------------------------------------------------------------------------------- /views/pages/userList.jade: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | .container 4 | .row 5 | .col-md-12 6 | table.table.table-hover.table-bordered 7 | thead 8 | tr 9 | th(style="text-align:center;width:60px;") 序号 10 | th 用户名 11 | th 邮箱 12 | th 手机号 13 | th 头像 14 | th 注册时间 15 | th(style="text-align:center;width:120px;") 操作 16 | tbody 17 | each item,index in users 18 | tr(class="item-id-#{item._id}") 19 | td(style="vertical-align: middle;text-align:center;") #{index+1} 20 | td(style="vertical-align: middle;") #{item.name} 21 | td(style="vertical-align: middle;") #{item.email} 22 | td(style="vertical-align: middle;") #{item.tel} 23 | td(style="vertical-align: middle;") #{item.img} 24 | td(style="vertical-align: middle;") #{moment(item.meta.createAt).format("MM/DD/YYYY")} 25 | td(style="vertical-align: middle;text-align:center") 26 | button.btn.btn-danger.del-user(type="button",data-id="#{item._id}",style="margin:0 5px;",onclick="del(this,'/admin/user/list')") 删除 27 | script(src="/js/admin.js") --------------------------------------------------------------------------------