├── .vscode └── launch.json ├── README.md └── express-mongodb-vue ├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── README.md ├── app.js ├── build ├── build.js ├── check-versions.js ├── logo.png ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js └── webpack.prod.conf.js ├── config ├── db.js ├── dev.env.js ├── index.js └── prod.env.js ├── index.html ├── models └── heroSchema.js ├── package-lock.json ├── package.json ├── router └── hero.js ├── src ├── App.vue ├── assets │ ├── css │ │ └── index.css │ ├── icon │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ └── image │ │ └── loginBg.jpg ├── components │ ├── Detail.vue │ ├── List.vue │ └── Login.vue ├── main.js ├── permission.js ├── router │ └── index.js └── utils │ ├── mongoSql.js │ ├── request.js │ ├── user.js │ └── validate.js └── static └── .gitkeep /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "启动程序", 11 | "program": "${workspaceFolder}/express-mongodb-vue/app.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## express+mongodb+vue实现增删改查-全栈之路2.0 2 | 3 | 前后端分离实现增删改查Demo 4 | 5 | ## 效果图 6 | 7 | ### 登陆页 8 | ![登陆页](https://user-gold-cdn.xitu.io/2018/12/20/167cb14e1dd84392?w=1460&h=718&f=gif&s=268679) 9 | ### 查询 10 | ![查询](https://user-gold-cdn.xitu.io/2018/12/20/167cb15638df70eb?w=1460&h=718&f=gif&s=81164) 11 | ### 新增 12 | ![新增](https://user-gold-cdn.xitu.io/2018/12/20/167cb15e8d017438?w=1460&h=718&f=gif&s=478159) 13 | ### 修改 14 | ![修改](https://user-gold-cdn.xitu.io/2018/12/20/167cb161ae684a67?w=1460&h=718&f=gif&s=297382) 15 | ### 删除 16 | ![删除](https://user-gold-cdn.xitu.io/2018/12/20/167cb19584fc3497?w=1412&h=677&f=gif&s=347169) 17 | ### 详情页 18 | ![详情](https://user-gold-cdn.xitu.io/2018/12/21/167d0a710750ad9d?w=1413&h=677&f=gif&s=4728826) 19 | ## 技术栈 20 | `vue` `axios` `vue-router` `express` `mongo` `element` `iconfont` `scss` 21 | 22 | ## 前言 23 | 半年前写过一个[express+mongodb+vue][1]的项目,其中大致的给大家展示了从零构建一个前后台项目所需要的技术点和思路,以及在开发过程中遇到的一些坑。 24 | 25 | 之后收到一些小伙伴的私信包括[github][2]上提出的**issue**。总结一下就是一下以下两点。 26 | 27 | 1. 项目启动报错的问题 28 | 2. 希望案例可以更加的丰富(分页、条件查询) 29 | 30 | **其中项目报错404的问题,是因为该项目是一个前后端项目,不仅仅需要通过`npm run dev`启动前端,还需要通过终端`node app.js`启动后台。这里大家一定要注意!** 31 | 32 | > 本次版本是之前版本的升级版,项目中对部分代码做了一定的优化,也增加了一些新的模块和功能点,使得项目更加完善,大致有以下模块。 33 | 34 | 35 | ## 新增模块 36 | 37 | - 登陆页面 38 | - 条件查询 39 | - 分页查询 40 | - 本地缓存 41 | - 图标使用 42 | - scss使用 43 | - ...... 44 | 45 | ## 提示 46 | 本篇主要是围绕**本次版本新增**的一些技术点展开陈述。不会过多的给大家讲解实现整个前后端项目的思路。如果你对整个项目的搭建思路还不是很明确的话,建议您先去阅读上一个版本[express+mongodb+vue][3]。 47 | 48 | **强烈建议去我的[github][4]上,将项目下载到本地,启动项目后,顺着本文的思路与我进行灵魂深处的探讨,如果有任何问题的话,欢迎私信我!** 49 | 50 | # 正文 51 | 52 | ## 封装常用工具类函数 53 | 54 | 因为在真实的项目中,我们需要频繁的用到`ajax`获取数据,之前的版本,是采用`vue-resource`完成的,但是由于官方不再维护,所以本次版本中采用官方建议使用的[axios][5]。我们可以方便实现自定义`axios`实例,拦截器,请求添加字段等功能。 55 | 56 | 在`src`目录下面,有个`utils`文件夹,里面用来存放一些工具类函数,这些工具类函数应该是具有通用性的。也就是在不同的组件页面中都可以引用。实现一次定义,多次使用的目的。具体的工具类函数分类大致有以下一些点。 57 | 58 | - axios实例 59 | - 常用正则校验函数 60 | - cookie、sessionStorage添加、获取、删除方法 61 | - ...... 62 | 63 | **总之从事开发的同学们一定要有简化业务逻辑的思维,经常用到的模块,独立出来。** 64 | 65 | >Tips:文件命名和函数命名尽量标准一点,不要想起啥就是啥,尽量用对应的英文去命名,英文不好的去百度呗,磨刀不误砍柴工! 66 | 67 | ## 登陆页面的那点事 68 | 69 | 合理的登陆逻辑应该是以下两点: 70 | 71 | 1. 用户在查询英雄列表之前,首先需要登录,否则**重定向**到登录页面 72 | 2. 对于已经登录的用户,可以直接访问列表页 73 | 74 | 为了实现上述逻辑,我们可以使用[vue-router][6]中提供的前置守卫导航`beforeEach`配合`路由重定向`实现。具体代码参考`permission.js`文件。 75 | 76 | > Tips:这里要提醒以下大家路由导航中的`next`使用一定要注意,其中传参和不传参是有不同的效果的。我在开发的过程中,就因为这个,遇到了**无限循环**的坑。 77 | 78 | ## 更加丰富的图标选择 79 | 80 | 本项目使用的前端框架[element][7]中虽然为我们提供了一些常用的图标,但是在真实的开发场景中,是无法满足的。如果你还在用图片实现icon的话,我只想送你两个字——**牛逼**! 81 | 82 | [阿里巴巴iconfont][8]图标库,可以帮助我们解决框架提供图标不完善的问题,其中使用方法有三种,它们之间的利和弊可自行前往了解。本项目中使用的是`unicode`方式。 83 | 84 | 我们在开发一个项目之前,可在`阿里巴巴iconfont`上新建一个项目,然后去图标库中查找对应的图标,添加到项目中。再下载到本地,引入项目中即可。 85 | 86 | 项目`src`目录下面的`assets`文件夹中,主要存放一些静态资源,比如`css` `image` `icon`等。 87 | 88 | 然后在`main.js`文件中引入对应图标的css文件。 89 | 90 | #main.js 91 | 92 | import "./assets/icon/iconfont.css" 93 | 94 | 95 | 96 | > Tips:其中对于图标库的前缀命名和图标的命名一定要规范,否则后期可能会遇到很大的麻烦。 97 | 重要的事说三遍: 98 | 命名规范! 99 | 命名规范! 100 | 命名规范! 101 | 102 | ## scss的使用 103 | 104 | 之前的版本中,关于样式是用`css`进行命名的,这样就会出现以下这种情形... 105 | 106 | .container{ 107 | width:... 108 | margin:... 109 | } 110 | 111 | .container header{ 112 | padding:... 113 | border-radius:... 114 | box-shadow:... 115 | } 116 | .container header .title{ 117 | background:... 118 | color:... 119 | font-size:... 120 | } 121 | 122 | 这种方式虽然没有问题,但是书写起来及其蛋疼,而且一旦形如这种的代码多了,代码看起来也会很不美观。 123 | 124 | 为了使性能更加好,逼格更加高,代码更加美观,所以我去学了下如何使用`scss`,大致分为以下三步骤: 125 | 126 | 第一步:cmd终端或者**vscode**终端输入: 127 | npm install sass-loader --save-dev 128 | npm install node-sass --sava-dev 129 | 130 | 第二步:在build文件夹下的webpack.base.conf.js的rules里面添加配置 131 | { 132 | test: /\.scss$/, 133 | loaders: ['style', 'css', 'sass'] 134 | } 135 | 136 | 第三步: 使用scss时候在所在的style样式标签上添加lang=”scss”即可应用对应的语法,否则报错 137 | 138 | 再用scss语法去书写上面的css规则,则变成以下这种格式: 139 | 140 | .container{ 141 | width:... 142 | margin:... 143 | header:{ 144 | padding:... 145 | border-radius:... 146 | box-shadow:... 147 | .title{ 148 | background:... 149 | color:... 150 | font-size:... 151 | } 152 | } 153 | } 154 | 155 | **两种风格的区别和优劣,我相信不用多说,你也应该明白了。** 156 | 157 | > Tips:css的预处理器有less、sass、scss等,它们之间有各自的特点和风格,但是万变不离其宗,你要做的就是打好css基本功。 158 | 159 | ## sessionStorage实现本地缓存 160 | 161 | 就拿本案例的实际场景来说吧,当用户从列表页跳转到详情页面时,再返回列表页面时,列表页面的查询条件和查询结果应该还存在那里。而不是需要用户再次输入查询条件进行二次查询,这样做的好处主要有以下两点: 162 | 163 | >更加符合实际使用场景,减少用户使用成本 164 | 165 | 毕竟前端工程师是要用自己最大的技术能力让用户体验更佳卓越! 166 | 167 | 这里我的**思路**是: 168 | 169 | 1.用户输入查询条件后,进行列表查询 170 | 171 | 2.用户点击某条数据的相信按钮,跳转到详情页面(这时我们要去保存用户的查询条件和当前的页数) 172 | 173 | 3.用户从详情页返回列表页(在mounted钩子函数中,判断缓存中是否存在缓存数据,如果存在的话,则用缓存数据去进行查询) 174 | 175 | 注意用户每次进行查询后,我们需要将缓存给删除,否则用户可能刷新页面后缓存仍然存在,这里我们将添加缓存的时机选在(用户点击详情按钮的那一刻) 176 | 177 | **大致代码:** 178 | 179 | #List.vue组件中 180 | 181 | #点击详情按钮函数 182 | toDetail(id){ 183 | var queryParmas = { 184 | ... 185 | ... 186 | ... 187 | }; 188 | //在本地缓存中存储查询条件 189 | sessionStorage.queryParmas = JSON.stringify(queryParmas); 190 | 191 | } 192 | 193 | #查询函数 194 | search(){ 195 | ... 196 | ... 197 | ... 198 | //每次查询数据后,删除缓存 199 | sessionStorage.removeItem("queryParmas"); 200 | } 201 | 202 | #mounted钩子函数 203 | mounted(){ 204 | //进入页面判断是否存在缓存,如果有缓存,直接查询 205 | var sessionObj = sessionStorage.getItem("queryParmas"); 206 | if(sessionObj){ 207 | //取出缓存数据,包括上次查询条件和上次查询页数,进行查询 208 | } 209 | } 210 | 211 | > Tips:element中分页的使用中会存在一些坑,当使用上述缓存数据进行查询时,可能会出现页码的一些bug。这里我也没有细找原因,但是通过使用vue中的$nextTick方法控制分页的显隐,可以解决这个bug。具体的有兴趣可以了解下。 212 | 213 | 214 | ## 总结 215 | 216 | 本篇文章主要是围绕一些功能点和方案实现进行展开,同时也提出了一些个人建议。细心的你一定会发现,其实我提炼出的很多点,都可以围绕**前端性能优化**进行展开。其中里面还有很多好玩的,包括`http` `浏览器渲染机制` `重排、重绘` `函数节流、防抖`等需要我们去学习,这些会鞭策着我们,不断地去优化自己的程序。最终写出更加优质的代码! 217 | 218 | ## 最后的祝福 219 | 220 | **天青色等烟雨,而我在等你!动动你们的☝️️️,点个赞再走!** 221 | 222 | **2019即将到来,愿所有人牛逼!在这给你们🙏个早年!** 223 | 224 | **原创不易,且👣且珍惜!** 225 | 226 | [Github传送门][9] 227 | 228 | ☝️☝️☝️☝️☝️ 229 | 230 | **ruiwei88888@163.com** 231 | 232 | ☝️☝️☝️☝️☝️有任何问题,欢迎邮箱私信我! 233 | 234 | 235 | 236 | [1]: https://juejin.im/post/5aabc2caf265da239376d5ff 237 | [2]: https://github.com/weirui88888 238 | [3]: https://juejin.im/post/5aabc2caf265da239376d5ff 239 | [4]: https://github.com/weirui88888 240 | [5]: https://www.kancloud.cn/yunye/axios/234845 241 | [6]: https://router.vuejs.org/zh/guide/advanced/navigation-guards.html 242 | [7]: http://element-cn.eleme.io/#/zh-CN/ 243 | [8]: https://www.iconfont.cnment-cn.eleme.io/#/zh-CN/ 244 | [9]: https://github.com/weirui88888 245 | -------------------------------------------------------------------------------- /express-mongodb-vue/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /express-mongodb-vue/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /express-mongodb-vue/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /express-mongodb-vue/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /express-mongodb-vue/README.md: -------------------------------------------------------------------------------- 1 | ### 注意点 -------------------------------------------------------------------------------- /express-mongodb-vue/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const hero = require('./router/hero'); 3 | const mongoose = require("mongoose"); 4 | const bodyParser = require("body-parser"); 5 | const cookieParser = require('cookie-parser');// 6 | 7 | //这一句是连接上数据库 8 | var db = mongoose.connect('mongodb://localhost:27017/myDbs'); 9 | 10 | //这里的myDbs是数据库的名字,不是表的名字 11 | 12 | 13 | const app = express() 14 | app.use(bodyParser.json()); 15 | app.use(bodyParser.urlencoded({ extended: false })); 16 | app.use(cookieParser()); 17 | app.use('/api',hero) 18 | app.listen(3000,() => { 19 | console.log('app listening on port 3000.') 20 | }) 21 | 22 | 23 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/build/logo.png -------------------------------------------------------------------------------- /express-mongodb-vue/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | }, 68 | { 69 | test: /\.scss$/, 70 | loaders: ['style', 'css', 'sass'] 71 | } 72 | ] 73 | }, 74 | node: { 75 | // prevent webpack from injecting useless setImmediate polyfill because Vue 76 | // source contains it (although only uses it if it's native). 77 | setImmediate: false, 78 | // prevent webpack from injecting mocks to Node native modules 79 | // that does not make sense for the client 80 | dgram: 'empty', 81 | fs: 'empty', 82 | net: 'empty', 83 | tls: 'empty', 84 | child_process: 'empty' 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../static'), 64 | to: config.dev.assetsSubDirectory, 65 | ignore: ['.*'] 66 | } 67 | ]) 68 | ] 69 | }) 70 | 71 | module.exports = new Promise((resolve, reject) => { 72 | portfinder.basePort = process.env.PORT || config.dev.port 73 | portfinder.getPort((err, port) => { 74 | if (err) { 75 | reject(err) 76 | } else { 77 | // publish the new Port, necessary for e2e tests 78 | process.env.PORT = port 79 | // add port to devServer config 80 | devWebpackConfig.devServer.port = port 81 | 82 | // Add FriendlyErrorsPlugin 83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 84 | compilationSuccessInfo: { 85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 86 | }, 87 | onErrors: config.dev.notifyOnErrors 88 | ? utils.createNotifierCallback() 89 | : undefined 90 | })) 91 | 92 | resolve(devWebpackConfig) 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /express-mongodb-vue/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vendor modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /express-mongodb-vue/config/db.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mongodb : "mongodb://localhost:27017/hero" 3 | } 4 | 5 | 6 | -------------------------------------------------------------------------------- /express-mongodb-vue/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /express-mongodb-vue/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | '/api': { 15 | target: 'http://localhost:3000', 16 | changeOrigin: true, 17 | } 18 | }, 19 | 20 | // Various Dev Server settings 21 | host: 'localhost', // can be overwritten by process.env.HOST 22 | port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 23 | autoOpenBrowser: false, 24 | errorOverlay: true, 25 | notifyOnErrors: true, 26 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 27 | 28 | 29 | /** 30 | * Source Maps 31 | */ 32 | 33 | // https://webpack.js.org/configuration/devtool/#development 34 | devtool: 'cheap-module-eval-source-map', 35 | 36 | // If you have problems debugging vue-files in devtools, 37 | // set this to false - it *may* help 38 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 39 | cacheBusting: true, 40 | 41 | cssSourceMap: true 42 | }, 43 | 44 | build: { 45 | // Template for index.html 46 | index: path.resolve(__dirname, '../dist/index.html'), 47 | 48 | // Paths 49 | assetsRoot: path.resolve(__dirname, '../dist'), 50 | assetsSubDirectory: 'static', 51 | assetsPublicPath: '/', 52 | 53 | /** 54 | * Source Maps 55 | */ 56 | 57 | productionSourceMap: true, 58 | // https://webpack.js.org/configuration/devtool/#production 59 | devtool: '#source-map', 60 | 61 | // Gzip off by default as many popular static hosts such as 62 | // Surge or Netlify already gzip all static assets for you. 63 | // Before setting to `true`, make sure to: 64 | // npm install --save-dev compression-webpack-plugin 65 | productionGzip: false, 66 | productionGzipExtensions: ['js', 'css'], 67 | 68 | // Run the build command with an extra argument to 69 | // View the bundle analyzer report after build finishes: 70 | // `npm run build --report` 71 | // Set to `true` or `false` to always turn it on or off 72 | bundleAnalyzerReport: process.env.npm_config_report 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /express-mongodb-vue/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /express-mongodb-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | express-mongodb 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /express-mongodb-vue/models/heroSchema.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const heroSchema = mongoose.Schema({ 4 | heroName :String, 5 | age : Number, 6 | heroSex : String, 7 | address : String, 8 | heroPosition : [], 9 | imgArr:[], 10 | favourite:String, 11 | explain:String, 12 | }, { collection: 'myhero'}) 13 | //这里mongoose.Schema要写上第二个参数,明确指定到数据库中的哪个表取数据 14 | 15 | 16 | const Hero = module.exports = mongoose.model('hero',heroSchema); 17 | -------------------------------------------------------------------------------- /express-mongodb-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-mongodb", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "rui.wei ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.18.0", 14 | "body-parser": "^1.18.2", 15 | "cookie-parser": "^1.4.3", 16 | "echarts": "^4.2.0-rc.2", 17 | "element-ui": "^2.2.1", 18 | "express": "^4.16.2", 19 | "js-cookie": "^2.2.0", 20 | "mongoose": "^5.0.10", 21 | "nodemon": "^1.17.1", 22 | "normalize.css": "^8.0.1", 23 | "nprogress": "^0.2.0", 24 | "require_optional": "^1.0.1", 25 | "vue": "^2.5.2", 26 | "vue-resource": "^1.5.0", 27 | "vue-router": "^3.0.1" 28 | }, 29 | "devDependencies": { 30 | "autoprefixer": "^7.1.2", 31 | "babel-core": "^6.22.1", 32 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 33 | "babel-loader": "^7.1.1", 34 | "babel-plugin-syntax-jsx": "^6.18.0", 35 | "babel-plugin-transform-runtime": "^6.22.0", 36 | "babel-plugin-transform-vue-jsx": "^3.5.0", 37 | "babel-preset-env": "^1.3.2", 38 | "babel-preset-stage-2": "^6.22.0", 39 | "chalk": "^2.0.1", 40 | "copy-webpack-plugin": "^4.0.1", 41 | "css-loader": "^0.28.0", 42 | "extract-text-webpack-plugin": "^3.0.0", 43 | "file-loader": "^1.1.4", 44 | "friendly-errors-webpack-plugin": "^1.6.1", 45 | "gulp": "^3.9.1", 46 | "html-webpack-plugin": "^2.30.1", 47 | "node-notifier": "^5.1.2", 48 | "node-sass": "^4.11.0", 49 | "nodemon": "^1.17.1", 50 | "optimize-css-assets-webpack-plugin": "^3.2.0", 51 | "ora": "^1.2.0", 52 | "portfinder": "^1.0.13", 53 | "postcss-import": "^11.0.0", 54 | "postcss-loader": "^2.0.8", 55 | "postcss-url": "^7.2.1", 56 | "rimraf": "^2.6.0", 57 | "sass-loader": "^7.1.0", 58 | "semver": "^5.3.0", 59 | "shelljs": "^0.7.6", 60 | "uglifyjs-webpack-plugin": "^1.1.1", 61 | "url-loader": "^0.5.8", 62 | "vue-loader": "^13.3.0", 63 | "vue-particles": "^1.0.9", 64 | "vue-style-loader": "^3.0.1", 65 | "vue-template-compiler": "^2.5.2", 66 | "webpack": "^3.6.0", 67 | "webpack-bundle-analyzer": "^2.9.0", 68 | "webpack-dev-server": "^2.9.1", 69 | "webpack-merge": "^4.1.0" 70 | }, 71 | "engines": { 72 | "node": ">= 6.0.0", 73 | "npm": ">= 3.0.0" 74 | }, 75 | "browserslist": [ 76 | "> 1%", 77 | "last 2 versions", 78 | "not ie <= 8" 79 | ] 80 | } 81 | -------------------------------------------------------------------------------- /express-mongodb-vue/router/hero.js: -------------------------------------------------------------------------------- 1 | 2 | //引入express模块 3 | const express = require("express"); 4 | //定义路由级中间件 5 | const router = express.Router(); 6 | //引入数据模型模块 7 | const Hero = require("../models/heroSchema"); 8 | 9 | //登录接口 10 | router.post('/login', function(req, res, next){ 11 | //用户名、密码、验证码 12 | var username = req.body.username; 13 | var password = req.body.password; 14 | 15 | if(username === 'ruiwei88888@163.com' && password === '123456'){ 16 | res.cookie('user',username); 17 | return res.send({ 18 | status: "success", 19 | info:"欢迎来到德莱联盟" 20 | }); 21 | } 22 | 23 | return res.send({ 24 | status: "fail", 25 | info: '账号或密码错误' 26 | }); 27 | }); 28 | 29 | // 查询所有英雄信息路由 30 | router.post("/getHeroList", (req, res) => { 31 | var heroPosition = new RegExp(req.body.heroPosition), 32 | heroSex = req.body.heroSex, 33 | heroName = req.body.heroName, 34 | pageNumber = req.body.pageNumber, 35 | pageRow = req.body.pageRow; 36 | 37 | // 根据查询入参个数,动态生成sql查询语句 38 | var sqlObj = {}; 39 | 40 | if(heroPosition){ 41 | sqlObj.heroPosition = heroPosition; 42 | } 43 | if(heroSex){ 44 | sqlObj.heroSex = heroSex; 45 | } 46 | if(heroName){ 47 | sqlObj.heroName = heroName; 48 | } 49 | 50 | 51 | var heroList = Hero.find(sqlObj); 52 | 53 | //对查询的结果进行筛选,skip跳过结果集中的前多少 54 | heroList.skip((pageNumber - 1)*pageRow); 55 | //对剩下来的数据进行限制返回个数 56 | heroList.limit(pageRow) 57 | 58 | 59 | 60 | // 实现分页的关键步骤 61 | heroList.exec(function(err,result){ 62 | if(err){ 63 | res.json({ 64 | status:"fail", 65 | error:err 66 | }); 67 | }else{ 68 | Hero.find(sqlObj,function(err,heros){ 69 | res.json({ 70 | status:"success", 71 | heroList:result, 72 | total:heros.length 73 | }); 74 | }) 75 | } 76 | }) 77 | }); 78 | 79 | // 通过ObjectId查询单个英雄信息路由 80 | router.get("/getHeroDetail/:id", (req, res) => { 81 | Hero.findById(req.params.id) 82 | .then(hero => { 83 | res.json(hero); 84 | }) 85 | .catch(err => { 86 | res.json(err); 87 | }); 88 | }); 89 | 90 | // 添加一个英雄信息路由 91 | router.post("/addHero", (req, res) => { 92 | // 使用Hero model上的create方法储存数据 93 | console.log(req) 94 | Hero.create(req.body, (err, hero) => { 95 | if (err) { 96 | res.json({ 97 | status:"fail", 98 | error:err 99 | }); 100 | } else { 101 | res.json({ 102 | status:"success", 103 | message:"新增成功" 104 | }); 105 | } 106 | }); 107 | 108 | console.log(req.body) 109 | }); 110 | 111 | //更新一条英雄信息数据路由 112 | router.put("/modifyHero/:id", (req, res) => { 113 | console.log(req.params) 114 | Hero.findOneAndUpdate( 115 | { _id: req.params.id }, 116 | { 117 | $set: { 118 | heroName: req.body.heroName, 119 | age: req.body.age, 120 | heroSex: req.body.heroSex, 121 | address: req.body.address, 122 | heroPosition: req.body.heroPosition, 123 | favourite: req.body.favourite, 124 | explain: req.body.explain 125 | } 126 | }, 127 | { 128 | new: true 129 | } 130 | ) 131 | .then(hero => res.json({ 132 | status:"success", 133 | message:"修改成功" 134 | })) 135 | .catch(err => res.json({ 136 | status:"fail", 137 | error:"修改失败" 138 | })); 139 | }); 140 | 141 | // 添加图片路由 142 | router.put("/addHeroPic/:id", (req, res) => { 143 | Hero.findOneAndUpdate( 144 | { _id: req.params.id }, 145 | { 146 | $push: { 147 | imgArr: req.body.url 148 | } 149 | }, 150 | { 151 | new: true 152 | } 153 | ) 154 | .then(hero => res.json({ 155 | status:"success", 156 | message:"新增图片成功" 157 | })) 158 | .catch(err => res.json({ 159 | status:"fail", 160 | message:"新增图片失败" 161 | })); 162 | }); 163 | 164 | //删除一条英雄信息路由 165 | router.delete("/deleteHero/:id", (req, res) => { 166 | Hero.findOneAndRemove({ 167 | _id: req.params.id 168 | }) 169 | .then(hero => res.json({ 170 | status:"success", 171 | message:"删除成功" 172 | })) 173 | .catch(err => res.json({ 174 | status:"fail", 175 | message:"删除失败" 176 | })); 177 | }); 178 | 179 | module.exports = router; 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/css/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | box-sizing: border-box; 4 | } 5 | 6 | body { 7 | height: 100%; 8 | -moz-osx-font-smoothing: grayscale; 9 | -webkit-font-smoothing: antialiased; 10 | text-rendering: optimizeLegibility; 11 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 12 | } 13 | 14 | #app{ 15 | height: 100%; 16 | } 17 | 18 | .clearfix::after{ 19 | content: ""; 20 | display: block; 21 | font-size: 0; 22 | clear:both; 23 | } -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/icon/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1545277210035'); /* IE9*/ 4 | src: url('iconfont.eot?t=1545277210035#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'), 6 | url('iconfont.ttf?t=1545277210035') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1545277210035#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .myIcon-man:before { content: "\e648"; font-size: 20px;color: blue;} 19 | 20 | .myIcon-woman:before { content: "\e649"; font-size: 20px; color: red;} 21 | 22 | .myIcon-login-out:before { content: "\e64c"; cursor: pointer;} 23 | 24 | .myIcon-user:before { content: "\e634"; color: white;padding: 0 10px; } 25 | 26 | .myIcon-password:before { content: "\e603"; color: white;padding: 0 10px;} 27 | 28 | .myIcon-eye:before { content: "\e612"; color: white;padding: 0 10px; cursor: pointer;} 29 | 30 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/icon/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/src/assets/icon/iconfont.eot -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/icon/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/src/assets/icon/iconfont.ttf -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/src/assets/icon/iconfont.woff -------------------------------------------------------------------------------- /express-mongodb-vue/src/assets/image/loginBg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/src/assets/image/loginBg.jpg -------------------------------------------------------------------------------- /express-mongodb-vue/src/components/Detail.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 166 | 167 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/components/List.vue: -------------------------------------------------------------------------------- 1 | 228 | 229 | 678 | 679 | 743 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 105 | 106 | 107 | 108 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import VueResource from 'vue-resource' 7 | 8 | // 引入echart 9 | // import echarts from 'echarts' //引入echarts 10 | // Vue.prototype.$echarts = echarts //引入组件 11 | 12 | 13 | // 引入粒子背景 14 | import VueParticles from 'vue-particles' 15 | Vue.use(VueParticles) 16 | 17 | // normalize.css 格式化css 18 | import 'normalize.css/normalize.css' // A modern alternative to CSS resets 19 | 20 | import "./assets/css/index.css" 21 | 22 | //引入element组件化框架 23 | import ElementUI from "element-ui" 24 | //引入样式文件 25 | import 'element-ui/lib/theme-chalk/index.css' 26 | import "./assets/icon/iconfont.css" 27 | import "@/permission" 28 | Vue.config.productionTip = false 29 | 30 | 31 | Vue.use(ElementUI); 32 | Vue.use(VueResource) 33 | 34 | /* eslint-disable no-new */ 35 | new Vue({ 36 | el: '#app', 37 | router, 38 | components: { App }, 39 | template: '' 40 | }) 41 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | 3 | import { getToken } from "@/utils/user" 4 | 5 | router.beforeEach((to, from, next) => { 6 | // 如果cookie中存在数据,直接进入对应页面 7 | if(getToken()){ 8 | // console.log(to) 9 | // if(to.path == "/login"){ 10 | // next("/list") 11 | // }else{ 12 | // next() 13 | // } 14 | next() 15 | }else{ 16 | //当cookie用户没有数据时,直接进入login页面 17 | if(to.path == "/login"){ 18 | next() 19 | }else{ 20 | next({ path : "/"}) 21 | } 22 | } 23 | }) -------------------------------------------------------------------------------- /express-mongodb-vue/src/router/index.js: -------------------------------------------------------------------------------- 1 | //路由页面 2 | 3 | import Vue from 'vue' 4 | import Router from 'vue-router' 5 | import List from '@/components/List' 6 | import Detail from '@/components/Detail' 7 | import Login from '@/components/Login' 8 | 9 | 10 | 11 | Vue.use(Router) 12 | 13 | export default new Router({ 14 | routes: [ 15 | { 16 | path: '/', 17 | name: 'ToLogin', 18 | redirect: '/login', 19 | }, 20 | { 21 | path: '/login', 22 | name: 'Login', 23 | component: Login 24 | }, 25 | { 26 | path: '/list', 27 | name: 'List', 28 | component: List 29 | }, 30 | { 31 | path : '/league/:id', 32 | name : 'Detail', 33 | component : Detail 34 | } 35 | ] 36 | }) 37 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/utils/mongoSql.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: rui.wei 3 | * @Date: 2018-12-18 18:35:08 4 | * @Last Modified by: rui.wei 5 | * @Last Modified time: 2018-12-20 11:34:21 6 | * 该页面记录常见mongodb增删改查sql 7 | */ 8 | 9 | 10 | // 根据条件查询(查询某个字段包含某个值) 11 | //db.getCollection('myhero').find({heroPosition:/mid/,heroSex:"man"}) 12 | 13 | // 更改字段名 14 | //db.test.update({}, {$rename : {"abc" : "def"}}, false, true) 15 | 16 | // 更新字段值 17 | // db.getCollection('myhero').update({heroName:"亚索"},{$set:{heroSex:"man"}}) 18 | // db.getCollection('myhero').update({heroName:"盲僧"},{$set:{heroPosition:["jungle","support"]}}) 19 | 20 | // 删除某一条数据 21 | // db.getCollection('myhero').remove({address:"约达尔人"}) 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | 4 | // 创建axios实例 5 | const service = axios.create({ 6 | baseURL: "http://localhost:8081/api", // api 的 base_url,相当于http://localhost:3000 7 | timeout: 5000 // 请求超时时间 8 | }) 9 | 10 | // 里面可以加上axios里面的拦截器,本需求没有必要,就没加上,可自行根据项目选择 11 | 12 | export default service -------------------------------------------------------------------------------- /express-mongodb-vue/src/utils/user.js: -------------------------------------------------------------------------------- 1 | // 设置cookie工具文件 2 | import Cookies from 'js-cookie' 3 | 4 | const TokenKey = 'user' 5 | 6 | export function getToken() { 7 | return Cookies.get(TokenKey) 8 | } 9 | 10 | export function setToken(token) { 11 | return Cookies.set(TokenKey, token) 12 | } 13 | 14 | export function removeToken() { 15 | return Cookies.remove(TokenKey) 16 | } 17 | -------------------------------------------------------------------------------- /express-mongodb-vue/src/utils/validate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by weirui on 18/12/18. 3 | * 工具类函数 4 | */ 5 | 6 | export function isvalidUsername(str) { 7 | const valid_map = ['ruiwei88888@163.com', 'admin'] 8 | return valid_map.indexOf(str.trim()) >= 0 9 | } 10 | 11 | /* 合法uri*/ 12 | export function validateURL(textval) { 13 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ 14 | return urlregex.test(textval) 15 | } 16 | 17 | /* 小写字母*/ 18 | export function validateLowerCase(str) { 19 | const reg = /^[a-z]+$/ 20 | return reg.test(str) 21 | } 22 | 23 | /* 大写字母*/ 24 | export function validateUpperCase(str) { 25 | const reg = /^[A-Z]+$/ 26 | return reg.test(str) 27 | } 28 | 29 | /* 大小写字母*/ 30 | export function validatAlphabets(str) { 31 | const reg = /^[A-Za-z]+$/ 32 | return reg.test(str) 33 | } 34 | -------------------------------------------------------------------------------- /express-mongodb-vue/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weirui88888/express-mongodb-node/ac31391720c81d611781dd73eec60b322dcaac15/express-mongodb-vue/static/.gitkeep --------------------------------------------------------------------------------