├── front
├── src
│ ├── images
│ │ ├── code.png
│ │ ├── logo.png
│ │ ├── user.png
│ │ ├── banner.jpg
│ │ ├── banner2.jpg
│ │ ├── edit_i.png
│ │ ├── edit_img.png
│ │ ├── favicon.ico
│ │ ├── icon_add.png
│ │ ├── icon_qq.png
│ │ ├── edit_code.png
│ │ ├── edit_link.png
│ │ ├── edit_title.png
│ │ ├── icon_edit.png
│ │ ├── icon_fail.png
│ │ ├── icon_weibo.png
│ │ ├── spot_arrow.png
│ │ ├── edit_bolder.png
│ │ ├── icon_attract.png
│ │ ├── icon_delete.png
│ │ ├── icon_search.png
│ │ ├── icon_success.png
│ │ ├── icon_wechat.png
│ │ ├── shutdown_btn.png
│ │ └── icon_logo_text.png
│ ├── until
│ │ └── scss
│ │ │ ├── app.scss
│ │ │ ├── common.scss
│ │ │ ├── _reset.scss
│ │ │ └── _mixin.scss
│ ├── store
│ │ ├── mutation-types.js
│ │ ├── index.js
│ │ └── actions.js
│ ├── app.vue
│ ├── main.js
│ ├── dev
│ │ └── timeTransform.js
│ ├── components
│ │ ├── reusable
│ │ │ ├── popTips.vue
│ │ │ ├── bottomBar.vue
│ │ │ ├── cards.vue
│ │ │ ├── topBar.vue
│ │ │ └── login.vue
│ │ ├── article.vue
│ │ ├── query.vue
│ │ ├── personal.vue
│ │ ├── index.vue
│ │ └── create.vue
│ └── api
│ │ └── index.js
├── build
│ ├── webpack.dev.config.js
│ ├── webpack.prod.config.js
│ └── webpack.base.config.js
├── app.js
├── package.json
└── index.html
├── server
├── public
│ └── images
│ │ ├── user
│ │ ├── 1_01.png
│ │ ├── 2_02.png
│ │ ├── cd.png
│ │ ├── 2days.png
│ │ ├── user2.png
│ │ ├── user_xiaoyu.jpg
│ │ └── 偶像页(日韩。港台。欧美。内陆)灰底版_03.png
│ │ └── article
│ │ ├── 1_01.png
│ │ ├── 1_02.png
│ │ ├── 1_03.png
│ │ ├── 1_07.png
│ │ ├── cover1.png
│ │ ├── cover2.png
│ │ ├── cover3.png
│ │ ├── cover4.png
│ │ ├── cover5.png
│ │ ├── pic1.png
│ │ ├── VIP页面_28.png
│ │ ├── 会员中心x_02.png
│ │ ├── 官网移动端_02.jpg
│ │ ├── banner_02.jpg
│ │ ├── banner_bg.png
│ │ ├── icon_delete.png
│ │ ├── icon_edit.png
│ │ ├── 这才是真正的自我(01)_01.png
│ │ ├── banner_iconlist_02.png
│ │ └── 偶像页(日韩。港台。欧美。内陆)灰底版_03.png
├── routes
│ ├── index.js
│ ├── articles.js
│ └── users.js
├── schema
│ ├── user.js
│ └── article.js
├── config
│ └── mongoose.js
├── models
│ ├── user.js
│ └── article.js
├── package.json
├── bin
│ └── www
├── app.js
└── controllers
│ ├── user.js
│ └── article.js
└── README.md
/front/src/images/code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/code.png
--------------------------------------------------------------------------------
/front/src/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/logo.png
--------------------------------------------------------------------------------
/front/src/images/user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/user.png
--------------------------------------------------------------------------------
/front/src/images/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/banner.jpg
--------------------------------------------------------------------------------
/front/src/images/banner2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/banner2.jpg
--------------------------------------------------------------------------------
/front/src/images/edit_i.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_i.png
--------------------------------------------------------------------------------
/front/src/images/edit_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_img.png
--------------------------------------------------------------------------------
/front/src/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/favicon.ico
--------------------------------------------------------------------------------
/front/src/images/icon_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_add.png
--------------------------------------------------------------------------------
/front/src/images/icon_qq.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_qq.png
--------------------------------------------------------------------------------
/front/src/images/edit_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_code.png
--------------------------------------------------------------------------------
/front/src/images/edit_link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_link.png
--------------------------------------------------------------------------------
/front/src/images/edit_title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_title.png
--------------------------------------------------------------------------------
/front/src/images/icon_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_edit.png
--------------------------------------------------------------------------------
/front/src/images/icon_fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_fail.png
--------------------------------------------------------------------------------
/front/src/images/icon_weibo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_weibo.png
--------------------------------------------------------------------------------
/front/src/images/spot_arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/spot_arrow.png
--------------------------------------------------------------------------------
/front/src/images/edit_bolder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/edit_bolder.png
--------------------------------------------------------------------------------
/front/src/images/icon_attract.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_attract.png
--------------------------------------------------------------------------------
/front/src/images/icon_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_delete.png
--------------------------------------------------------------------------------
/front/src/images/icon_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_search.png
--------------------------------------------------------------------------------
/front/src/images/icon_success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_success.png
--------------------------------------------------------------------------------
/front/src/images/icon_wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_wechat.png
--------------------------------------------------------------------------------
/front/src/images/shutdown_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/shutdown_btn.png
--------------------------------------------------------------------------------
/server/public/images/user/1_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/1_01.png
--------------------------------------------------------------------------------
/server/public/images/user/2_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/2_02.png
--------------------------------------------------------------------------------
/server/public/images/user/cd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/cd.png
--------------------------------------------------------------------------------
/front/src/images/icon_logo_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/front/src/images/icon_logo_text.png
--------------------------------------------------------------------------------
/server/public/images/user/2days.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/2days.png
--------------------------------------------------------------------------------
/server/public/images/user/user2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/user2.png
--------------------------------------------------------------------------------
/server/public/images/article/1_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/1_01.png
--------------------------------------------------------------------------------
/server/public/images/article/1_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/1_02.png
--------------------------------------------------------------------------------
/server/public/images/article/1_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/1_03.png
--------------------------------------------------------------------------------
/server/public/images/article/1_07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/1_07.png
--------------------------------------------------------------------------------
/server/public/images/article/cover1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/cover1.png
--------------------------------------------------------------------------------
/server/public/images/article/cover2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/cover2.png
--------------------------------------------------------------------------------
/server/public/images/article/cover3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/cover3.png
--------------------------------------------------------------------------------
/server/public/images/article/cover4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/cover4.png
--------------------------------------------------------------------------------
/server/public/images/article/cover5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/cover5.png
--------------------------------------------------------------------------------
/server/public/images/article/pic1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/pic1.png
--------------------------------------------------------------------------------
/server/public/images/article/VIP页面_28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/VIP页面_28.png
--------------------------------------------------------------------------------
/server/public/images/article/会员中心x_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/会员中心x_02.png
--------------------------------------------------------------------------------
/server/public/images/article/官网移动端_02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/官网移动端_02.jpg
--------------------------------------------------------------------------------
/server/public/images/user/user_xiaoyu.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/user_xiaoyu.jpg
--------------------------------------------------------------------------------
/server/public/images/article/banner_02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/banner_02.jpg
--------------------------------------------------------------------------------
/server/public/images/article/banner_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/banner_bg.png
--------------------------------------------------------------------------------
/server/public/images/article/icon_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/icon_delete.png
--------------------------------------------------------------------------------
/server/public/images/article/icon_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/icon_edit.png
--------------------------------------------------------------------------------
/front/build/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | var config = require('./webpack.base.config')
2 |
3 | config.devtool = '#source-map'
4 |
5 | module.exports = config
6 |
--------------------------------------------------------------------------------
/server/public/images/article/这才是真正的自我(01)_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/这才是真正的自我(01)_01.png
--------------------------------------------------------------------------------
/server/public/images/article/banner_iconlist_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/banner_iconlist_02.png
--------------------------------------------------------------------------------
/server/public/images/user/偶像页(日韩。港台。欧美。内陆)灰底版_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/user/偶像页(日韩。港台。欧美。内陆)灰底版_03.png
--------------------------------------------------------------------------------
/server/public/images/article/偶像页(日韩。港台。欧美。内陆)灰底版_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Rain/vue2-koa-mongoDB-/HEAD/server/public/images/article/偶像页(日韩。港台。欧美。内陆)灰底版_03.png
--------------------------------------------------------------------------------
/server/routes/index.js:
--------------------------------------------------------------------------------
1 | const router = require('koa-router')()
2 | const article = require('../controllers/article')
3 |
4 | router.get('/', article.getAllArticle)
5 | .get('/index', article.getAllArticle)
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/server/schema/user.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | var mongoose = require('mongoose');
3 |
4 | var userSchema = new mongoose.Schema({
5 | userName: String,
6 | userPassword: String,
7 | userNickName: String,
8 | userImg : String
9 | });
10 |
11 | module.exports = mongoose.model('User', userSchema);
12 |
--------------------------------------------------------------------------------
/server/config/mongoose.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 |
3 | const mongooseDB = function () {
4 | //重点在这一句,赋值一个全局的承诺。
5 | mongoose.Promise = global.Promise
6 | mongoose.connect('mongodb://127.0.0.1:27017/wecut_blog')
7 | return mongoose.connection
8 | }
9 |
10 | module.exports = mongooseDB
11 |
--------------------------------------------------------------------------------
/server/routes/articles.js:
--------------------------------------------------------------------------------
1 | const router = require('koa-router')()
2 | const article = require('../controllers/article')
3 |
4 | router.post('/create', article.createArticle)
5 | .post('/edit', article.editArticle)
6 | .post('/removeAll', article.removeAll)
7 | .get('/getAll', article.getAllArticle)
8 | .get('/:id', article.getArticle)
9 |
10 | module.exports = router;
11 |
--------------------------------------------------------------------------------
/server/routes/users.js:
--------------------------------------------------------------------------------
1 | const router = require('koa-router')();
2 | const user = require('../controllers/user')
3 |
4 |
5 | console.log("匹配前缀路由")
6 | router.post('/login', user.login)
7 | .post('/register', user.register)
8 | .post('/removeAll', user.removeAll)
9 | .get('/checkOut', user.userVerify)
10 | .get('/query', user.query)
11 |
12 |
13 | module.exports = router;
14 |
--------------------------------------------------------------------------------
/front/src/until/scss/app.scss:
--------------------------------------------------------------------------------
1 | /** IMPORTS **/
2 | @import '_mixin.scss';
3 | @import '_reset.scss';
4 |
5 | /** GLOBALS **/
6 | body {
7 | width: 100%;
8 | overflow-x: hidden;
9 | font-family: "Microsoft YaHei", "微软雅黑", STHeiTi, sans-serif,"Helvetica Neue",Helvetica;
10 | font-size: 16px;
11 | @include font-smoothing();
12 | background-color: #f6f6f5;
13 | @include clearfix();
14 | }
--------------------------------------------------------------------------------
/front/src/store/mutation-types.js:
--------------------------------------------------------------------------------
1 | export const SHOW_MSG = 'SHOW_MSG'
2 | export const SHOW_ERROR_MSG = 'SHOW_ERROR_MSG'
3 |
4 |
5 | export const LOGIN_JUDGE = 'LOGIN_JUDGE'
6 | export const USER_LOGIN = 'USER_LOGIN'
7 | export const POP_LOGIN = 'POP_LOGIN'
8 | export const POPSHOW = 'POPSHOW'
9 | export const POPTEXT = 'POPTEXT'
10 | export const GET_ARTICLES = 'GET_ARTICLES'
11 | export const GET_ARTICLE = 'GET_ARTICLE'
12 | export const GET_USER_ARTICLES = 'GET_USER_ARTICLES'
13 |
14 |
--------------------------------------------------------------------------------
/server/models/user.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const UserModel = require('../schema/user');
4 |
5 | class User {
6 | constructor () {
7 | this.model = UserModel;
8 | }
9 | save (opts, fn) {
10 | this.entity = new UserModel(opts)
11 | return this.entity.save(opts)
12 | }
13 | query (opts) {
14 | return this.model.find(opts).exec()
15 | }
16 | queryAll () {
17 | return this.model.find({}).exec()
18 | }
19 | removeAll (fn) {
20 | return this.model.remove({})
21 | }
22 | }
23 |
24 | module.exports = User
25 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Blink-Server",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "co-busboy": "^1.0.2",
10 | "kcors": "^1.2.1",
11 | "debug": "^2.2.0",
12 | "koa": "^1.1.2",
13 | "koa-bodyparser": "^2.0.1",
14 | "koa-json": "^1.1.1",
15 | "koa-jwt": "^1.2.0",
16 | "koa-logger": "^1.3.0",
17 | "koa-onerror": "^1.2.1",
18 | "koa-router": "^5.3.0",
19 | "koa-static": "^1.5.2",
20 | "koa-views": "^3.1.0",
21 | "moment": "^2.14.1",
22 | "mongoose": "^4.6.0"
23 | }
24 | }
--------------------------------------------------------------------------------
/front/app.js:
--------------------------------------------------------------------------------
1 | var koa = require('koa');
2 | var app = koa();
3 |
4 | // var bodyParser = require('body-parser');
5 | // var morgan = require('morgan';)
6 |
7 | var MongoClient = require('mongodb').MongoClient;
8 | var mongoUrl = 'mongodb://localhost:4444/test';
9 | var _db;
10 |
11 | app.use(morgan('dev'));
12 | app.use(bodyParser.json());
13 |
14 | MongoClient.connect(mongoUrl, function(err, db){
15 | if (err) {
16 | console.log(err);
17 | return;
18 | }
19 | console.log('connected to mongo');
20 | _db = db;
21 | app.listen(8888, function () {
22 | console.log('server is running...');
23 | });
24 | })
25 |
--------------------------------------------------------------------------------
/server/schema/article.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | var mongoose = require('mongoose');
3 |
4 | //定义一个schema
5 | var articleSchema = new mongoose.Schema({
6 | articleTitle: String,
7 | articleIntro: String,
8 | mainTag: String,
9 | subTags: Array,
10 | sourceArticle: String,
11 | markedArticle: String,
12 | coverImg: String,
13 | articleImgs: Array,
14 | timeStamp : String,
15 | thisUser : {
16 | _id : String,
17 | userName: String,
18 | userPassword: String,
19 | userImg : String,
20 | userNickName : String,
21 | }
22 | });
23 |
24 | //将schema发布为model
25 | module.exports = mongoose.model('Article', articleSchema);
26 |
--------------------------------------------------------------------------------
/front/build/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack')
2 | var config = require('./webpack.base.config')
3 |
4 | config.plugins = (config.plugins || []).concat([
5 | // this allows uglify to strip all warnings
6 | // from Vue.js source code.
7 | new webpack.DefinePlugin({
8 | 'process.env': {
9 | NODE_ENV: '"production"'
10 | }
11 | }),
12 | // This minifies not only JavaScript, but also
13 | // the templates (with html-minifier) and CSS (with cssnano)!
14 | new webpack.optimize.UglifyJsPlugin({
15 | compress: {
16 | warnings: false
17 | }
18 | }),
19 | new webpack.optimize.OccurenceOrderPlugin()
20 | ])
21 |
22 | module.exports = config
23 |
--------------------------------------------------------------------------------
/front/src/until/scss/common.scss:
--------------------------------------------------------------------------------
1 | @import "mixin";
2 |
3 | //弹出层设置
4 | .pop_layer {
5 | position: fixed;
6 | display: none;
7 | z-index: 3;
8 | width: 100%;
9 | height: 100%;
10 | top: 0;
11 | left: 0;
12 | background: rgba(0,0,0,0.6);
13 | img {
14 | width: 100%;
15 | }
16 | //提示框
17 | .tips {
18 | position: absolute;
19 | @include remValue(width, 350);
20 | top: 50%;
21 | left: 50%;
22 | @include gridStyle(0.3rem);
23 | @include remValue(margin-left, -350/2);
24 | @include remValue(margin-top, -400/2);
25 | padding: 5% 0;
26 | .tips_light {
27 | @include gridRem(51, 73);
28 | margin: 5% auto;
29 | }
30 | .tips_text {
31 | font-family: "微软雅黑","Microsoft Yahei";
32 | font-size: 0.7rem;
33 | color: #8c8c8c;
34 | text-align: center;
35 | margin-bottom: 5%;
36 | }
37 | .tips_text_h {
38 | font-size: 0.9rem;
39 | font-weight: bold;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/front/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/server/models/article.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | //引入定义modle,执行查询更改操作
4 | const ArticleModel = require('../schema/article');
5 |
6 | class Article {
7 | constructor () {
8 | this.model = ArticleModel;
9 | }
10 | save (opts) {
11 | this.entity = new ArticleModel(opts);
12 | return this.entity.save(opts);
13 | }
14 | query (opts) {
15 | return this.model.find(opts)
16 | .sort({ _id: -1 })
17 | .exec()
18 | }
19 | queryAll () {
20 | //query = this.modle.find({}),query下可执行链式操作,每个链式返回query
21 | //如果不提供回调函数,所有这些方法都返回 Query 对象,它们都可以被再次修改(比如增加选项、键等),直到调用 exec 方法。
22 | // query.exec(function (err, docs) {
23 | // // called when the `query.complete` or `query.error` are called
24 | // // internally
25 | // });
26 | //sort为排序,-1为降序,1为升序
27 | return this.model.find({})
28 | .sort({ _id: -1 })
29 | .exec()
30 | }
31 | queryById (id) {
32 | console.log(id)
33 | return this.model.findById(id)
34 | }
35 | remove (id, fn) {
36 | return this.model.findById(id).then(function (doc) {
37 | if (!doc) return fn(null, false);
38 | return doc.remove();
39 | })
40 | }
41 | removeAll (fn) {
42 | return this.model.remove({})
43 | }
44 | }
45 |
46 | module.exports = Article;
47 |
--------------------------------------------------------------------------------
/front/src/main.js:
--------------------------------------------------------------------------------
1 |
2 | import index from './components/index.vue'
3 | import article from './components/article.vue'
4 | import create from './components/create.vue'
5 | import personal from './components/personal.vue'
6 | import query from './components/query.vue'
7 | import store from './store'
8 | // var store = require('./store/index')
9 | import './until/scss/app.scss';
10 |
11 | var Vue = require('vue')
12 | var VueRouter = require('vue-router')
13 | var VueResource = require('vue-resource')
14 | var App = require('./app.vue')
15 |
16 | Vue.use(VueResource);
17 | Vue.use(VueRouter);
18 |
19 | const routes = [
20 | {
21 | path: '/',
22 | component: index
23 | },
24 | {
25 | path: '/index',
26 | component:index
27 | },
28 | {
29 | path: '/article',
30 | component: article
31 | },
32 | {
33 | path: '/query',
34 | component: query
35 | },
36 | {
37 | path: '/personal/:userId',
38 | component: personal,
39 | name : "personal",
40 | },
41 | {
42 | path: '/article/:articleId',
43 | name : "article",
44 | component: article
45 | },
46 | {
47 | path: '/create',
48 | component: create
49 | },
50 | {
51 | path: '/edit/:articleId',
52 | component: create,
53 | name : "edit"
54 | }
55 | ]
56 | const router = new VueRouter({
57 | routes
58 | });
59 |
60 | const app = new Vue({
61 | el: 'app',
62 | router,
63 | store,
64 | render: h => h(App)
65 | })
66 |
--------------------------------------------------------------------------------
/front/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Blink-Front",
3 | "version": "1.0.0",
4 | "description": "Blink-Front",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack-dev-server --inline --hot --host 192.168.2.63 --port 8004 --config build/webpack.dev.config.js",
8 | "build": "webpack --progress --hide-modules --config build/webpack.prod.config.js"
9 | },
10 | "dependencies": {
11 | "github-markdown-css": "^2.4.0",
12 | "highlight.js": "^9.6.0",
13 | "marked": "^0.3.6",
14 | "vue-resource": "^1.0.3",
15 | "vue-router": "^2.1.1",
16 | "vuex": "^2.0.0",
17 | "vue": "^2.1.4"
18 | },
19 | "devDependencies": {
20 | "koa": "^1.2.4",
21 | "babel-core": "^6.3.26",
22 | "babel-loader": "^6.1.0",
23 | "babel-plugin-transform-runtime": "^6.1.2",
24 | "babel-preset-es2015": "^6.1.2",
25 | "babel-preset-stage-0": "^6.1.2",
26 | "babel-runtime": "^5.8.0",
27 | "body-parser": "^1.15.2",
28 | "css-loader": "^0.23.0",
29 | "eslint": "^1.10.3",
30 | "eslint-loader": "^1.1.1",
31 | "extract-text-webpack-plugin": "^0.9.1",
32 | "file-loader": "^0.9.0",
33 | "mongodb": "^2.2.12",
34 | "node-sass": "^3.8.0",
35 | "sass-loader": "^4.0.0",
36 | "style-loader": "^0.13.0",
37 | "stylus-loader": "^1.4.0",
38 | "template-html-loader": "0.0.3",
39 | "url-loader": "^0.5.7",
40 | "vue-hot-reload-api": "^1.2.0",
41 | "vue-html-loader": "^1.0.0",
42 | "vue-loader": "^7.3.0",
43 | "webpack": "^1.12.2",
44 | "webpack-dev-server": "^1.12.0"
45 | }
46 | }
--------------------------------------------------------------------------------
/front/src/dev/timeTransform.js:
--------------------------------------------------------------------------------
1 | function timeTransform (timespan) {
2 | if (!!!timespan) {
3 | return "历史遗留";
4 | }
5 | timespan = parseInt(timespan)*1000;
6 | var dateTime = new Date(timespan);
7 |
8 | var year = dateTime.getFullYear(),
9 | month = dateTime.getMonth() + 1,
10 | day = dateTime.getDate(),
11 | hour = dateTime.getHours(),
12 | minute = dateTime.getMinutes(),
13 | second = dateTime.getSeconds(),
14 | now = new Date(),
15 | now_new = Date.parse(new Date()),
16 |
17 | milliseconds = 0,
18 | timeSpanStr;
19 |
20 | milliseconds = now_new - timespan;
21 |
22 | if (milliseconds <= 1000 * 60 * 1) {
23 | timeSpanStr = '刚刚';
24 | }else if (1000 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60) {
25 | timeSpanStr = Math.round((milliseconds / (1000 * 60))) + '分钟前';
26 | }else if (1000 * 60 * 60 * 1 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24) {
27 | timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60)) + '小时前';
28 | }else if (1000 * 60 * 60 * 24 < milliseconds && milliseconds <= 1000 * 60 * 60 * 24 * 15) {
29 | timeSpanStr = Math.round(milliseconds / (1000 * 60 * 60 * 24)) + '天前';
30 | }else if (milliseconds > 1000 * 60 * 60 * 24 * 15 && year == now.getFullYear()) {
31 | timeSpanStr = month + '-' + day + ' ' + hour + ':' + minute;
32 | }else {
33 | timeSpanStr = year + '-' + month + '-' + day + ' ' + hour + ':' + minute;
34 | }
35 | return timeSpanStr;
36 | };
37 | module.exports = timeTransform;
--------------------------------------------------------------------------------
/front/src/components/reusable/popTips.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | {{ popText }}
8 |
9 |
10 |
11 |
29 |
--------------------------------------------------------------------------------
/front/build/webpack.base.config.js:
--------------------------------------------------------------------------------
1 | var ExtractTextPlugin = require("extract-text-webpack-plugin");
2 |
3 | module.exports = {
4 | entry: './src/main.js',
5 | output: {
6 | path: './dist',
7 | publicPath: '/dist/',
8 | filename: 'build.js'
9 | },
10 | resolve: {
11 | //自动扩展文件后缀名,意味着require模块可以省略不写后缀名
12 | extensions: ['', '.js', '.vue', '.scss'],
13 | //依赖文件,重命名,打包入文件
14 | alias : {
15 | 'vue$': 'vue/dist/vue.common.js',
16 | }
17 | },
18 | externals : {
19 | 'zepto' : 'Zepto',
20 | 'swiper' : 'Swiper',
21 | 'fastClick' : 'FastClick',
22 | },
23 | module: {
24 | loaders: [
25 | {
26 | test: /\.vue$/,
27 | loader: 'vue'
28 | },
29 | {
30 | test: /\.js$/,
31 | loader: 'babel?presets=es2015',
32 | exclude: /node_modules/
33 | },
34 | {
35 | test: /\.(png|jpg|gif)$/,
36 | loader: 'url',
37 | query: {
38 | limit: 12000,
39 | name: '[name].[ext]'
40 | }
41 | },
42 | { test: /\.scss$/, loader: 'style!css!sass'},
43 | {
44 | test: /\.woff(\?v=\d+\.\d+\.\d+)?$/,
45 | loader: "url",
46 | query: {
47 | name: '[name].[ext]?[hash]&mimetype=application/font-woff'
48 | }
49 | }
50 | ]
51 | },
52 | vue: {
53 | loaders: {
54 | css: ExtractTextPlugin.extract(
55 | "style-loader",
56 | "css-loader?sourceMap",
57 | {
58 | publicPath: "./dist/"
59 | }
60 | )
61 | }
62 | },
63 | plugins: [
64 | new ExtractTextPlugin("style.css")
65 | ],
66 | babel: {
67 | presets: ['es2015', 'stage-0'],
68 | plugins: ['transform-runtime']
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/front/index.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | Blink
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/server/bin/www:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 |
7 | var app = require('../app');
8 | var debug = require('debug')('demo:server');
9 | var http = require('http');
10 |
11 | /**
12 | * Get port from environment and store in Express.
13 | */
14 |
15 | var port = normalizePort(process.env.PORT || '3030');
16 | // app.set('port', port);
17 |
18 | /**
19 | * Create HTTP server.
20 | */
21 |
22 | var server = http.createServer(app.callback());
23 |
24 | /**
25 | * Listen on provided port, on all network interfaces.
26 | */
27 |
28 | console.log('http://localhost:' + port)
29 | // server.listen(port);
30 | server.listen(port, '192.168.2.63');
31 | server.on('error', onError);
32 | server.on('listening', onListening);
33 |
34 | /**
35 | * Normalize a port into a number, string, or false.
36 | */
37 |
38 | function normalizePort(val) {
39 | var port = parseInt(val, 10);
40 |
41 | if (isNaN(port)) {
42 | // named pipe
43 | return val;
44 | }
45 |
46 | if (port >= 0) {
47 | // port number
48 | return port;
49 | }
50 |
51 | return false;
52 | }
53 |
54 | /**
55 | * Event listener for HTTP server "error" event.
56 | */
57 |
58 | function onError(error) {
59 | if (error.syscall !== 'listen') {
60 | throw error;
61 | }
62 |
63 | var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
64 |
65 | // handle specific listen errors with friendly messages
66 | switch (error.code) {
67 | case 'EACCES':
68 | console.error(bind + ' requires elevated privileges');
69 | process.exit(1);
70 | break;
71 | case 'EADDRINUSE':
72 | console.error(bind + ' is already in use');
73 | process.exit(1);
74 | break;
75 | default:
76 | throw error;
77 | }
78 | }
79 |
80 | /**
81 | * Event listener for HTTP server "listening" event.
82 | */
83 |
84 | function onListening() {
85 | var addr = server.address();
86 | var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
87 | debug('Listening on ' + bind);
88 | }
89 |
--------------------------------------------------------------------------------
/front/src/api/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueResource from 'vue-resource'
3 | import store from '../store'
4 |
5 | Vue.use(VueResource)
6 | const API_ROOT = 'http://192.168.2.63:3030/'
7 | const indexResource = Vue.resource(API_ROOT + 'index{/id}')
8 | const usersGetResource = Vue.resource(API_ROOT + 'users{/id}');
9 | //formdata上传数据必须搭上emulateJSON参数
10 | const usersResource = Vue.resource(API_ROOT + 'users{/id}', { emulateJSON: true });
11 | const articlePostResource = Vue.resource(API_ROOT + 'article{/id}', { emulateJSON: true });
12 | const articleGetResource = Vue.resource(API_ROOT + 'article{/id}');
13 |
14 | //vue-resource拦截器
15 | Vue.http.interceptors.push((request, next) => {
16 | Vue.http.headers.common['token'] = sessionStorage.token || '';
17 | next((response) => {
18 | //在响应之后传给then之前对response进行修改和逻辑判断
19 | if (response.status == 401) {
20 | console.log("401 : " + JSON.stringify(response.body))
21 | store.state.loginJudge = false;
22 | sessionStorage.removeItem('token');
23 | //文章创建token判断弹窗提示
24 | if (response.url.indexOf('article') > -1) {
25 | store.commit('SHOW_MSG', {state : false, msg: "请重新登录"})
26 | }
27 | }
28 | })
29 | })
30 |
31 | export default {
32 | getArticleById (id) {
33 | return articleGetResource.get({id : id})
34 | },
35 | getArticlesByUser (id) {
36 | return articleGetResource.get({id: 'user', 'userId': id})
37 | },
38 | getALLArticle (opts) {
39 | return articleGetResource.get({id : 'getAll'})
40 | },
41 | getUserLogin (opts){
42 | return usersGetResource.get({id: 'checkOut'})
43 | },
44 | userLogin (opts) {
45 | return usersResource.save({id: 'login'}, opts)
46 | },
47 | userRegister (opts) {
48 | return usersResource.save({id: 'register'},opts)
49 | },
50 | createArticle (opts) {
51 | return articlePostResource.save({id: 'create'},opts)
52 | },
53 | editArticle (opts) {
54 | return articlePostResource.save({id: 'edit'},opts)
55 | },
56 | deleteArticle (id) {
57 | return articleGetResource.get({id: 'delete', 'deleteId': id})
58 | },
59 | deleteAll (opts){
60 | // return articleGetResource.save({id : "removeAll"}, opts)
61 | return usersGetResource.save({id : "removeAll"}, opts)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | var app = require('koa')()
2 | , koa = require('koa-router')()
3 | , logger = require('koa-logger')
4 | , json = require('koa-json')
5 | , views = require('koa-views')
6 | , jwt = require('koa-jwt')
7 | , cors = require('kcors')
8 | , static = require('koa-static')
9 | , onerror = require('koa-onerror');
10 |
11 | var users = require('./routes/users');
12 | var article = require('./routes/articles');
13 |
14 | var db = require('./config/mongoose')();
15 | db.on('error', console.error.bind(console, 'error: connect error!'))
16 | db.once('open', function () {
17 | // 一次打开记录
18 | console.log('connect success!')
19 | })
20 |
21 | // global middlewares
22 | app.use(require('koa-bodyparser')());
23 | app.use(json());
24 | app.use(logger());
25 | //cors解决跨域问题
26 | app.use(cors());
27 | //static配置静态文件,可直接访问该文件夹下的文件
28 | app.use(static(__dirname + '/public/'));
29 | app.use(jwt({
30 | secret: 'Blink_1.0',
31 | passthrough: true
32 | }));
33 |
34 |
35 | //token验证
36 | app.use(function *(next){
37 | var ctx = this,
38 | thisUrl = ctx.request.url;
39 | console.log(thisUrl)
40 | // 如果来源路径不是create或checkOut,直接跳过验证
41 | if (thisUrl.indexOf('create') === -1 && thisUrl.indexOf('checkOut') == -1) {
42 | return yield next;
43 | }
44 | var token = ctx.request.headers.token || '';
45 | if (token) {
46 | var profile = jwt.verify(token, 'Blink_1.0');
47 | if (profile) {
48 | // 设置过期时间为1天
49 | if (Date.now() - profile.original_iat < 12 * 60 * 60 * 1000) {
50 | yield next;
51 | } else {
52 | ctx.status = 401;
53 | ctx.body = {
54 | state: false,
55 | msg: 'token已过期'
56 | };
57 | }
58 | } else {
59 | ctx.status = 401;
60 | ctx.body = {
61 | state: false,
62 | msg: 'token认证失败'
63 | }
64 | }
65 | } else {
66 | ctx.status = 401;
67 | ctx.body = {
68 | state: false,
69 | msg: 'token认证失败'
70 | }
71 | }
72 | });
73 |
74 |
75 | app.use(function *(next){
76 | var start = new Date;
77 | yield next;
78 | var ms = new Date - start;
79 | console.log('%s %s - %s', this.method, this.url, ms);
80 | });
81 |
82 |
83 | //注册路由,router.routes()返回处理匹配到了请求对应的路由的router中间件
84 | //router.use([path], middleware, [...]) Use given middleware(s) before route callback.
85 | //router.use(),匹配到设置的接口地址后,进入回调中间件,再中间件里设置路由路径匹配对应后的回调函数
86 | koa.use('/users', users.routes(), users.allowedMethods());
87 | koa.use('/article', article.routes(), article.allowedMethods());
88 |
89 | app.use(koa.routes());
90 |
91 | app.on('error', function(err, ctx){
92 | logger.error('server error', err, ctx);
93 | });
94 |
95 | module.exports = app;
96 |
--------------------------------------------------------------------------------
/front/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import actions from './actions'
4 | import userHead from '../images/logo.png'
5 | import {
6 | SHOW_MSG,
7 | LOGIN_JUDGE,
8 | POPSHOW,
9 | POPTEXT,
10 | POP_LOGIN,
11 | USER_LOGIN,
12 | GET_ARTICLES,
13 | GET_ARTICLE,
14 | GET_USER_ARTICLES
15 | } from './mutation-types'
16 |
17 | Vue.use(Vuex)
18 |
19 | const state = {
20 | //登录判断
21 | loginJudge : false,
22 | popLogin : false,
23 | popComment : {
24 | popText : "",
25 | popIcon : true,
26 | popShow : false,
27 | },
28 | //登录用户数据存储
29 | user : {},
30 | //文章列表获取存储
31 | articles : [{
32 | articleTitle: "--",
33 | articleIntro: "",
34 | mainTag: "",
35 | subTags: [],
36 | sourceArticle: "--",
37 | markedArticle: "--",
38 | coverImg: "",
39 | timeStamp : "",
40 | articleImgs: [],
41 | thisUser : {
42 | _id : "",
43 | userName : "",
44 | userNickName : "",
45 | userImg : userHead,
46 | }
47 | }],
48 | //单个文章依据ID索引存储
49 | article : {
50 | articleTitle: "",
51 | articleIntro: "",
52 | mainTag: "前端开发",
53 | subTags: ["Node.js"],
54 | sourceArticle: "",
55 | markedArticle: "",
56 | coverImg: "",
57 | timeStamp : "",
58 | articleImgs: [],
59 | thisUser : {
60 | _id : "",
61 | userName : "",
62 | userNickName : "",
63 | userImg : userHead,
64 | }
65 | },
66 | //文章列表依据用户ID索引存储
67 | userArticles : [{
68 | articleTitle: "",
69 | articleIntro: "",
70 | mainTag: "",
71 | subTags: [],
72 | sourceArticle: "",
73 | markedArticle: "",
74 | coverImg: "",
75 | timeStamp : "",
76 | articleImgs: [],
77 | thisUser : {
78 | _id : "",
79 | userName : "",
80 | userNickName : "",
81 | userImg : userHead,
82 | }
83 | }],
84 | token : "",
85 | }
86 |
87 | const mutations = {
88 | //登录状态
89 | [LOGIN_JUDGE]( state, msg ) {
90 | state.loginJudge = msg;
91 | },
92 | //登录状态,token获取
93 | [USER_LOGIN]( state, opts ) {
94 | console.log(opts);
95 | state.user = opts.data;
96 | state.loginJudge = opts.state;
97 | },
98 | //登录注册控件显示
99 | [POP_LOGIN]( state, msg ) {
100 | state.popLogin = msg;
101 | },
102 | //弹窗提示层
103 | [SHOW_MSG] (state, opts) {
104 | state.popComment = {
105 | popShow : true,
106 | popIcon : opts.state,
107 | popText : opts.msg,
108 | };
109 | },
110 | //获取多选文章
111 | [GET_ARTICLES] (state, data) {
112 | state.articles = data;
113 | },
114 | //获取单个文章
115 | [GET_ARTICLE] (state, data) {
116 | state.article = data[0];
117 | },
118 | //获取用户文章
119 | [GET_USER_ARTICLES] (state, data) {
120 | state.userArticles = data;
121 | },
122 | }
123 |
124 | export default new Vuex.Store({
125 | state,
126 | mutations,
127 | actions
128 | })
129 |
--------------------------------------------------------------------------------
/front/src/until/scss/_reset.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /*
3 | * reset stylesheet
4 | * lisarao@gamil.com
5 | *
6 | */
7 |
8 | // 全局 reset 定义
9 | @mixin global-reset {
10 | html, body, div, span,object, iframe,
11 | h1, h2, h3, h4, h5, h6, p,
12 | a, address, big, cite, code,
13 | em, img, q, s,
14 | small, strong, sub, sup, tt,
15 | b, u, i, center,
16 | dl, dt, dd, ol, ul, li,
17 | fieldset, form, label, legend,
18 | table, caption, tbody, tfoot, thead, tr, th, td,
19 | article, aside, canvas, details, embed,
20 | figure, figcaption, footer, header, hgroup,
21 | menu, nav, output,section, summary,
22 | time, mark, audio, video {
23 | @include reset-box-model;
24 | @include text-size-adjust;
25 | }
26 | @include reset-html5;
27 |
28 | ol,ul,li {
29 | @include reset-list-style;
30 | }
31 | table {
32 | @include reset-table;
33 | }
34 | img {
35 | @include reset-image-anchor-border;
36 | }
37 | a img {
38 | @include touch-callout;
39 | }
40 | a {
41 | @include no-underline;
42 | }
43 | #{headings(all)} {
44 | @include font-weight;
45 | }
46 | }
47 | // 盒模式 reset 定义
48 | @mixin reset-box-model {
49 | margin:0;
50 | padding:0;
51 | }
52 | // list style
53 | @mixin reset-list-style {
54 | list-style: none;
55 | }
56 | // table
57 | @mixin reset-table {
58 | border-collapse: collapse;
59 | border-spacing: 0;
60 | }
61 | //image border [ ie7- ]
62 | @mixin reset-image-anchor-border {
63 | border:0 none;
64 | }
65 | // html 5 block
66 | @mixin reset-html5 {
67 | #{elements-of-type(html5-block)}{
68 | display:block;
69 | }
70 | }
71 |
72 | @mixin no-underline {
73 | text-decoration: none;
74 | }
75 |
76 | /* 禁止长按链接与图片弹出菜单 */
77 | @mixin touch-callout {
78 | -webkit-touch-callout : none;
79 | }
80 |
81 | // focus outline
82 | @mixin reset-focus {
83 | outline: 0 ;
84 | }
85 |
86 | // font-size
87 | @mixin text-size-adjust {
88 | -webkit-text-size-adjust:none;
89 | -ms-text-size-adjust:100%;
90 | -webkit-text-size-adjust:100%;
91 | }
92 |
93 | // overflow -x scroll hidden
94 | @mixin set-scroll-x {
95 | overflow-x:hidden;
96 | }
97 | //用户选择文本
98 | @mixin user-select {
99 | -webkit-user-select : none;
100 | user-select : none;
101 | }
102 | //font
103 | @mixin default-font {
104 | font:1em/1.8;
105 | }
106 | //字体平滑
107 | @mixin font-smoothing {
108 | -webkit-font-smoothing:antialiased; //最佳平滑效果
109 | }
110 | //font-weight 400
111 |
112 | @mixin font-weight {
113 | font-weight:400;
114 | }
115 |
116 | //font-style normal
117 | @mixin normal-font {
118 | font-style:normal;
119 | }
120 |
121 | @mixin clearfix {
122 | .fl {
123 | float:left;
124 | display:inline;
125 | }
126 | .fr {
127 | float:right;
128 | display:inline;
129 | }
130 | .clearfix{
131 | zoom:1;
132 | &:after{
133 | content:'';
134 | display:block;
135 | height:0;
136 | clear:both;
137 | visibility:hidden;
138 | }
139 | }
140 | }
141 |
142 | @include global-reset;
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/front/src/components/reusable/bottomBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
Design: LRainBackEnd: LRainFrontEnd: LRainMaintainer: LRain
7 |
8 |
闪现1.0 | 2017.02.24
9 |
10 |
24 |
25 |
26 |
27 |
28 |
31 |
--------------------------------------------------------------------------------
/server/controllers/user.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const jwt = require('koa-jwt')
3 | const User = require('../models/user')
4 | const user = new User()
5 |
6 | //上传控件
7 | var parse = require('co-busboy');
8 | var fs = require('fs');
9 | var os = require('os');
10 | var path = require('path');
11 |
12 | //登录控件
13 | const login = function *(next) {
14 | const ctx = this;
15 | const opts = ctx.request.body;
16 | yield user.query(opts).then(function (doc) {
17 | const user = doc[0] || ''
18 | if (user) {
19 | //签发token
20 | const token = jwt.sign(JSON.stringify({ userId: user._id, original_iat: Date.now()}), 'Blink_1.0');
21 | ctx.body = {
22 | msg: '登录成功',
23 | data: {
24 | _id : user._id,
25 | userName : user.userName,
26 | userNickName : user.userNickName,
27 | userImg : user.userImg,
28 | },
29 | state: true,
30 | token: token
31 | }
32 | } else {
33 | ctx.body = {
34 | msg: '用户名或密码错误',
35 | data: user,
36 | state: false
37 | }
38 | }
39 | })
40 | }
41 |
42 | //注册,添加用户头像
43 | const register = function *(next) {
44 | const ctx = this;
45 | var parts = parse(this.request)
46 | var part;
47 | //此数组用于存储图片的url
48 | var fileNames = [],
49 | opts = { };
50 | while (part = yield parts){
51 | //此时part为返回的流对象
52 | //循环输出流对流,分别获取formdata中的是三个input值
53 | if (part.filename) {
54 | var filename = part.filename;
55 | fileNames.push(filename)
56 | var homeDir = path.resolve(__dirname, '..')
57 | var newpath = homeDir + '/public/images/user/'+ filename;
58 | //生成存储路径,要注意这里的newpath必须是绝对路径,否则Stream报错
59 | var stream = fs.createWriteStream(newpath);
60 | //写入文件流
61 | part.pipe(stream);
62 | }else if(part.length) {
63 | opts[part[0]] = part[1];
64 | }
65 | }
66 | if(fileNames.length > 0){
67 | opts.userImg = 'http://192.168.2.63:3030/images/user/' + fileNames[0];
68 | }
69 | yield user.query({userName: opts.userName}).then(function (doc) {
70 | if (doc.length > 0) {
71 | ctx.body = {
72 | msg: '该账户已存在',
73 | state: false
74 | }
75 | } else {
76 | return user.save(opts)
77 | }
78 | }).then(function (doc) {
79 | if (doc) {
80 | const token = jwt.sign(JSON.stringify({ userId: doc._id, original_iat: Date.now()}), 'Blink_1.0');
81 | ctx.body = {
82 | msg: '注册成功',
83 | data: {
84 | _id : user._id,
85 | userName : user.userName,
86 | userNickName : user.userNickName,
87 | userImg : user.userImg,
88 | },
89 | state: true,
90 | token : token
91 | }
92 | }
93 | })
94 | }
95 |
96 | //token验证
97 | const userVerify = function *(next) {
98 | const ctx = this;
99 | var token = ctx.request.headers.token;
100 | var profile = jwt.verify(token, 'Blink_1.0');
101 | yield user.query({_id: profile.userId}).then(function (doc) {
102 | ctx.body = {
103 | msg: 'token验证成功',
104 | data: doc[0],
105 | state: true
106 | }
107 | })
108 | }
109 |
110 | const query = function *(next) {
111 | const ctx = this;
112 | yield user.queryAll().then(function (doc) {
113 | ctx.body = doc
114 | })
115 | }
116 |
117 | const removeAll = function*(next){
118 | console.log("userRemoveAll");
119 | yield user.removeAll().then(function (doc) {
120 | ctx.body = {
121 | msg: '删除成功',
122 | state: true
123 | }
124 | }).catch(function (error) {
125 | ctx.body = {
126 | msg: '删除失败',
127 | state: false
128 | }
129 | })
130 | }
131 |
132 | module.exports = {
133 | login,
134 | register,
135 | query,
136 | userVerify,
137 | removeAll
138 | }
139 |
--------------------------------------------------------------------------------
/front/src/components/reusable/cards.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
{{item.articleTitle}}
9 |
{{item.articleIntro}}
10 |
11 |
12 |
13 | {{item.thisUser.userNikeName}}
14 |
15 |
16 |
17 |
18 | LRain
19 |
20 |
{{item.mainTag}}|{{timeHandle(item.timeStamp)}}
21 |
22 |
23 | {{stuff}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
43 |
--------------------------------------------------------------------------------
/front/src/until/scss/_mixin.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | //网站背景
4 | $bgc: #f2f5f9;
5 | //主色
6 | $gre : #3dc932;
7 | //字体色重
8 | $strong : #666;
9 | $weak : #aaa;
10 | .pr {position: relative;}
11 | .pa {position: absolute;}
12 | .pf {position: fixed;}
13 | .green {color: $gre};
14 |
15 | //css3 tarnsition统一设置
16 | @mixin css3($time: 0.3s, $speedStyle: ease-out, $shadow: box-shadow) {
17 | transition: $shadow $time $speedStyle;
18 | -webkit-transition: $shadow $time $speedStyle;
19 | -moz-transition: $shadow $time $speedStyle;
20 | -ms-transition: $shadow $time $speedStyle;
21 | -o-transition: $shadow $time $speedStyle;
22 | }
23 | //卡片背景与圆角设置
24 | @mixin gridStyle($br:2px, $backC: #fff) {
25 | background: $backC;
26 | border-radius: $br;
27 | }
28 | //单行文本溢出设置
29 | @mixin textOverflow() {
30 | overflow: hidden;
31 | text-overflow: ellipsis;
32 | white-space: nowrap;
33 | }
34 | //多行文本溢出设置
35 | @mixin textOverflowP($pCount: 6) {
36 | display: -webkit-box !important;
37 | overflow: hidden;
38 | text-overflow: ellipsis;
39 | word-break: break-all;
40 | -webkit-box-orient: vertical;
41 | -webkit-line-clamp: $pCount;
42 | }
43 | //单行字体居中
44 | @mixin fontMiddle($h){
45 | @include remValue(height, $h);
46 | @include remValue(line-height, $h);
47 | }
48 | //矩阵按钮
49 | @mixin gridButton($w, $h, $br:4px, $backC:#fff){
50 | @include gridStyle($br, $backC);
51 | @include gridRem($w, $h);
52 | @include remValue(line-height, $h);
53 | text-align: center;
54 | }
55 | //雪碧图处理
56 | @mixin sprite($icon-name, $isRetina:false, $repeat:no-repeat) {
57 | $sprites: sprite-map("sprite/*.png");
58 | background-repeat: $repeat;
59 | background-image: sprite-url($sprites);
60 | background-position: sprite-position($sprites, $icon-name);
61 | width: image-width(sprite-file($sprites, $icon-name));
62 | height: image-height(sprite-file($sprites, $icon-name));
63 | @if $isRetina {
64 | $retina: sprite-map('sprite-retina/*.png');
65 | @media (min--moz-device-pixel-ratio: 1.3),
66 | (-o-min-device-pixel-ratio: 2.6/2),
67 | (-webkit-min-device-pixel-ratio: 1.3),
68 | (min-device-pixel-ratio: 1.3),
69 | (min-resolution: 1.3dppx) {
70 | background-image: sprite-url($retina);
71 | background-position: 0 round(nth(sprite-position($retina, $icon-name), 2) / 2);
72 | height: round(image-height(sprite-file($retina, $icon-name)) / 2);
73 | width: round(image-width(sprite-file($retina, $icon-name)) / 2);
74 | $double-width: ceil(image-width(sprite-path($retina)) / 2);
75 | $auto-height: auto;
76 | background-size: $double-width $auto-height;
77 | }
78 | }
79 | }
80 |
81 | //rem设置
82 | $browser-default-font-size: 20px !default; //设置rem根字体默认数值20
83 | //去除单位
84 | @function strip-units($number){
85 | @return $number / ($number * 0 + 1);
86 | }
87 | //属性对应rem值设置
88 | @mixin remValue($property, $values...) {
89 | $max: length($values);//返回$values列表的长度值
90 | $pxValues: '';
91 | $remValues: '';
92 |
93 | @for $i from 1 through $max {
94 | $value: strip-units(nth($values, $i));//返回$values列表中的第$i个值,并将单位值去掉
95 | $browser-default-font-size: strip-units($browser-default-font-size);
96 | $pxValues: #{$pxValues + $value / $browser-default-font-size / 2}rem; //rem计算,切图数值除以2再除以根字体
97 |
98 | @if $i < $max {
99 | $pxValues: #{$pxValues + " "};
100 | }
101 | }
102 |
103 | @for $i from 1 through $max {
104 | $value: strip-units(nth($values, $i));
105 | $remValues: #{$remValues + $value}px;
106 |
107 | @if $i < $max {
108 | $remValues: #{$remValues + " "};
109 | }
110 | }
111 |
112 | // #{$property}: $remValues;
113 | #{$property}: $pxValues;
114 | }
115 |
116 | //依据切图尺寸/2后定义div宽高,并返回rem值
117 | @mixin gridRem($width, $height){
118 | $browser-default-font-size : strip-units($browser-default-font-size);
119 | // width: #{$width / 2}px;
120 | width: #{$width / 2 / $browser-default-font-size}rem;
121 | // height: #{$height / 2}px;
122 | height: #{$height / 2 / $browser-default-font-size}rem;
123 | }
124 |
125 |
126 | // @function remReturn($value){
127 | // @return #{$value / strip-units($browser-default-font-size)}rem;
128 | // }
--------------------------------------------------------------------------------
/front/src/components/article.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{thisArticle.articleTitle}}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
{{thisArticle.thisUser.userNickName}}
15 |
{{timeHandle(thisArticle.timeStamp)}}
16 |
{{thisArticle.mainTag}}
17 |
18 | {{item}}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
114 |
--------------------------------------------------------------------------------
/front/src/store/actions.js:
--------------------------------------------------------------------------------
1 |
2 | import api from '../api'
3 | import * as types from './mutation-types'
4 |
5 | export default {
6 | getUserLogin ( {commit}, opts){
7 | opts = opts || "";
8 | return new Promise((resolve, reject) => {
9 | api.getUserLogin(opts).then(function (res) {
10 | if (res.body.state) {
11 | commit(types.USER_LOGIN, res.body)
12 | commit(types.LOGIN_JUDGE, true)
13 | resolve();
14 | }else {
15 | commit(types.LOGIN_JUDGE, false)
16 | reject(res.body.msg)
17 | }
18 | }).catch(function(e){
19 | commit(types.LOGIN_JUDGE, false)
20 | reject(e.body.msg)
21 | })
22 | })
23 | },
24 | userLogin ({ commit }, opts) {
25 | api.userLogin(opts).then(function (res) {
26 | commit(types.SHOW_MSG, res.body)
27 | if (res.body.state) {
28 | // cookies.set('token', res.body.token, { expires: 7 })
29 | //登录状态更新
30 | commit(types.LOGIN_JUDGE, true)
31 | //登录控件显示关闭
32 | commit(types.POP_LOGIN, false)
33 | //用户登录信息更新
34 | commit(types.USER_LOGIN, res.body)
35 | //存储token
36 | sessionStorage.token = res.body.token;
37 | } else {
38 | commit(types.LOGIN_JUDGE, false)
39 | commit(types.POP_LOGIN, true)
40 | }
41 | })
42 | },
43 | userRegister ({commit}, opts) {
44 | api.userRegister(opts).then(function (res) {
45 | commit(types.SHOW_MSG, res.body)
46 | if (res.body.state) {
47 | commit(types.LOGIN_JUDGE, true)
48 | commit(types.POP_LOGIN, false)
49 | commit(types.USER_LOGIN, res.body)
50 | //存储token
51 | sessionStorage.token = res.body.token;
52 | } else {
53 | commit(types.LOGIN_JUDGE, false)
54 | commit(types.POP_LOGIN, true)
55 | }
56 |
57 | }).catch(function(e){
58 | console.log(e);
59 | })
60 | },
61 | createArticle ({commit}, opts) {
62 | api.createArticle(opts).then(function (res) {
63 | commit(types.SHOW_MSG, res.body)
64 | setTimeout(function(){
65 | location.href = "/#/index";
66 | },800)
67 | }).catch(function(e){
68 | console.log(e);
69 | })
70 | },
71 | editArticle ({commit}, opts) {
72 | api.editArticle(opts).then(function (res) {
73 | commit(types.SHOW_MSG, res.body)
74 | }).then(function (){
75 | setTimeout(function(){
76 | location.href = "/#/index";
77 | },800)
78 | }).catch(function(e){
79 | console.log(e);
80 | })
81 | },
82 | deleteArticle ({commit}, id) {
83 | return new Promise((resolve, reject) => {
84 | api.deleteArticle(id).then(function (res) {
85 | commit(types.SHOW_MSG, res.body)
86 | resolve();
87 | }).catch(function(e){
88 | resolve(e);
89 | })
90 | })
91 | },
92 | getALLArticle({ commit }, opts) {
93 | api.getALLArticle(opts).then(function (res) {
94 | commit(types.GET_ARTICLES, res.body.articles)
95 | })
96 | },
97 | getArticleById({ commit }, id) {
98 | return new Promise((resolve, reject) => {
99 | api.getArticleById(id).then(function (res) {
100 | if (res.body.state) {
101 | commit(types.GET_ARTICLE, res.body.article)
102 | resolve();
103 | }else {
104 | reject(res)
105 | }
106 | }).catch(function(e){
107 | reject(e.body.msg)
108 | })
109 | })
110 | },
111 | getArticlesByUser({ commit }, id) {
112 | api.getArticlesByUser(id).then(function (res) {
113 | if (res.body.state) {
114 | if (res.body.article.length !== 0) {
115 | commit(types.GET_USER_ARTICLES, res.body.article)
116 | }else {
117 | commit(types.GET_USER_ARTICLES, [{
118 | articleTitle: "",
119 | articleIntro: "",
120 | mainTag: "",
121 | subTags: [],
122 | sourceArticle: "",
123 | markedArticle: "",
124 | coverImg: "",
125 | timeStamp : "",
126 | articleImgs: [],
127 | thisUser : {
128 | _id : "",
129 | userName : "",
130 | userNickName : "",
131 | userImg : "",
132 | }
133 | }]
134 | )
135 | }
136 | }else {
137 | commit(types.SHOW_MSG, res.body)
138 | setTimeout(function(){
139 | location.href = "/#/index";
140 | },800)
141 | }
142 | })
143 | },
144 | deleteAll({commit}, opts){
145 | api.deleteAll().then(function (res) {
146 | console.log("done");
147 | })
148 | }
149 | };
150 |
151 |
152 |
--------------------------------------------------------------------------------
/server/controllers/article.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Article = require('../models/article')
4 | const article = new Article()
5 |
6 | //上传控件
7 | var parse = require('co-busboy');
8 | var fs = require('fs');
9 | var os = require('os');
10 | var path = require('path');
11 |
12 | //创建文本
13 | const createArticle = function*(next) {
14 | const ctx = this;
15 | var parts = parse(this.request)
16 | var part;
17 | //此数组用于存储图片的url
18 | var fileNames = [],
19 | subTags = [],
20 | articleImgs = [],
21 | opts = {};
22 | while (part = yield parts){
23 | //一定要对流进行处理,否则while循环会被卡住
24 | //input上传文件就是个对象,包含了filename键值
25 | if (part.filename) {
26 | var filename = part.filename;
27 | fileNames.push(filename)
28 | var homeDir = path.resolve(__dirname, '..')
29 | var newpath = homeDir + '/public/images/article/'+ filename;
30 | //生成存储路径,要注意这里的newpath必须是绝对路径,否则Stream报错
31 | var stream = fs.createWriteStream(newpath);
32 | //写入文件流
33 | part.pipe(stream);
34 |
35 | //不是input上传文件则为数组['键','值']形式
36 | }else if(part.length) {
37 | if (part[0].indexOf("subTags") > -1) {
38 | subTags.push(part[1]);
39 | opts["subTags"] = subTags;
40 | }else {
41 | opts[part[0]] = part[1];
42 | }
43 | }
44 | };
45 | var fileLen = fileNames.length;
46 | if(fileLen > 0){
47 | //设定第一个即为封面图,其他的均为文本图像
48 | opts["coverImg"] = 'http://192.168.2.63:3030/images/article/' + fileNames[0];
49 | for (var i = 1; i < fileLen; i++) {
50 | articleImgs.push('http://192.168.2.63:3030/images/article/' + fileNames[i]);
51 | }
52 | opts["articleImgs"] = articleImgs;
53 | }
54 | opts.thisUser = JSON.parse(opts.thisUser);
55 | yield article.save(opts).then(function (doc) {
56 | ctx.body = {
57 | msg: '发布成功',
58 | data: doc,
59 | state: true
60 | }
61 | }).catch(function (error) {
62 | ctx.body = {
63 | msg: '发布失败',
64 | error: error,
65 | state: false
66 | }
67 | })
68 | }
69 | //修改文本
70 | const editArticle = function*(next) {
71 | const ctx = this;
72 | var parts = parse(this.request)
73 | var part;
74 | //此数组用于存储图片的url
75 | var fileNames = [],
76 | subTags = [],
77 | articleImgs = [],
78 | opts = {};
79 | while (part = yield parts){
80 | if(part.length) {
81 | if (part[0].indexOf("subTags") > -1) {
82 | subTags.push(part[1]);
83 | opts["subTags"] = subTags;
84 | }else if(part[0].indexOf("textPic") > -1){
85 | articleImgs.push(part[1]);
86 | opts["articleImgs"] = articleImgs;
87 | }else {
88 | opts[part[0]] = part[1];
89 | opts["articleImgs"] = "";
90 | }
91 | }
92 | };
93 | opts.thisUser = JSON.parse(opts.thisUser);
94 | console.log(JSON.stringify(opts));
95 | yield article.remove(opts.thisId).then(function(){
96 | return article.save(opts);
97 | }).then(function (doc) {
98 | ctx.body = {
99 | msg: '修改成功',
100 | data: doc,
101 | state: true
102 | }
103 | }).catch(function (error) {
104 | ctx.body = {
105 | msg: '修改失败',
106 | error: error,
107 | state: false
108 | }
109 | })
110 | }
111 |
112 | //获取全部文章
113 | const getAllArticle = function*(next) {
114 | const ctx = this;
115 | let results = [];
116 | yield article.query({}).then(function (doc) {
117 | for (let i = 0; i < doc.length; i++) {
118 | results.push(doc[i])
119 | }
120 | ctx.body = {
121 | message: '获取成功',
122 | articles: results
123 | }
124 | }).catch(function (error) {
125 | ctx.body = {
126 | msg: '获取失败',
127 | error: error,
128 | state: false
129 | }
130 | })
131 | }
132 |
133 | //处理单个文章
134 | const getArticle = function*(next) {
135 | const ctx = this;
136 | let hrefs = this.request.href,
137 | hrefsLoc = hrefs.indexOf('article/') + 8,
138 | queryByUser = false,
139 | deleteByUser = false;
140 | if (hrefs.indexOf("?userId=") > -1) {
141 | queryByUser = true;
142 | hrefsLoc = hrefs.indexOf("?userId=") + 8;
143 | }else if(hrefs.indexOf("?deleteId=") > -1){
144 | deleteByUser = true;
145 | hrefsLoc = hrefs.indexOf("?deleteId=") + 10;
146 | }
147 | let searchId = hrefs.substr(hrefsLoc, hrefs.length);
148 | //根据用户id索引
149 | if (queryByUser) {
150 | yield article.query({'thisUser._id' : searchId}).then(function (doc) {
151 | // if (doc[0]) {
152 | ctx.body = {
153 | msg: '获取成功',
154 | article: doc,
155 | state : true,
156 | }
157 | // }else {
158 | // ctx.body = {
159 | // msg: '非法的id请求',
160 | // state : false,
161 | // }
162 | // }
163 | }).catch(function (error) {
164 | ctx.body = {
165 | msg: '获取失败',
166 | error: error,
167 | state: false
168 | }
169 | })
170 | }else if(deleteByUser) {
171 | //删除文章
172 | yield article.remove(searchId).then(function (doc) {
173 | ctx.body = {
174 | msg: '删除成功',
175 | state: true
176 | }
177 | }).catch(function (error) {
178 | ctx.body = {
179 | msg: '删除失败',
180 | state: false
181 | }
182 | })
183 | }else {
184 | //根据文章id索引
185 | yield article.query({_id : searchId}).then(function (doc) {
186 | if (doc[0]) {
187 | ctx.body = {
188 | msg: '获取成功',
189 | article: doc,
190 | state : true,
191 | }
192 | }else {
193 | ctx.body = {
194 | msg: '非法的id请求',
195 | state : false,
196 | }
197 | }
198 | }).catch(function (error) {
199 | ctx.body = {
200 | msg: '获取失败',
201 | error: error,
202 | state: false
203 | }
204 | })
205 | }
206 | }
207 |
208 | const removeAll = function*(next){
209 | console.log("removeAll");
210 | yield article.removeAll().then(function (doc) {
211 | ctx.body = {
212 | msg: '删除成功',
213 | state: true
214 | }
215 | }).catch(function (error) {
216 | ctx.body = {
217 | msg: '删除失败',
218 | state: false
219 | }
220 | })
221 | }
222 |
223 | module.exports = {
224 | getArticle,
225 | getAllArticle,
226 | createArticle,
227 | editArticle,
228 | removeAll
229 | }
230 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 从设计到前端重构后端交互数据库存储一手操作,新手全栈入门
4 |
5 |
6 | (请先创建用户发布一篇文章以供数据测试)
7 | # Blink前后端
8 |
9 | 运行方式:(一定要安装mongodb) mongodb安装教程: https://segmentfault.com/a/1190000002547229
10 |
11 | //启动mongo服务
12 | cd: /mongo
13 | ./mongod
14 |
15 | //安装前端依赖,启动服务
16 | cd: /fornt
17 | npm install all
18 | npm run dev
19 |
20 | //安装后端依赖,启动服务
21 | cd: /server
22 | npm install all
23 | npm run dev
24 |
25 |
26 | ## 跳过设计流程(这里把自己内部分享直接复制来了)
27 | ## “闪现”制作流程之(二):再讲前端
28 | 前端用的就是vue渲染,当然还有webpac,vuex,vue-router,vue-resource等冚家大礼包。只不过这次全面升级,变成冚家2.0
29 | 记录点自己遇到的坑吧:
1,vue-router2.0的动态路由
30 | //html
31 | <router-link :to="{
32 | name: 'article', params: { articleId: item._id }
33 | }">
34 | </router-link>
35 |
36 | //js
37 | this.$router.push({ path: ' ', params:' ' })
38 |
1.1,main.js里要用特殊的姿♂势导入路由才可执行:
39 | const app = new Vue({
40 | el: 'app',
41 | router,
42 | store,
43 | render: h => h(App)
44 | })
45 |
46 | 2,在vue组件里面只允许一个根节点,即:
47 | <template>
48 | //要是>1的同级层就算再不乐意也得创建一个空div包裹所有层
49 | <div>
50 | .....
51 | </div>
52 | </template>
53 |
3,使用formdata传输input上传图片
54 | <form id="form" name="multiUploadForm" method="post" enctype="multipart/form-data">
55 | <input name="thisInput">
56 | </from>
57 |
58 | var data = new FormData(document.getElementById("form"));
59 | //formdata也可以使用append方法添加数据
60 | //data.append("other", value)
61 |
3.1,form的enctype选择“multipart/form-data”才可以上传多图
3.2,要想vue-resouce正确提交formdata数据就必须加“emulateJSON: true”参数
62 | Vue.resource(API_ROOT + 'users{/id}', { emulateJSON: true });
63 |
3.4,每一条formdata数据必须带上name参数
64 | 4,marked+hightLight.js没什么好说,监听编辑框输入,预览框对应输出,对于图片处理我有自己的方法:将上传图片存入数组1,转为二进制保存在数组2,图片显示名字不显示地址(临时预览不上传服务器),预览端输出数组2显示图片,若用户更换图片顺序,索引图片名称对应位置重新排序数组2
65 | //生成base64图片,临时预览,FileReader跟formdata一样为H5新增方法
66 | var reader = new FileReader();
67 | reader.readAsDataURL(thisFile);
68 | //监听图片上传完毕,onload事件
69 | reader.onload = function(e){
70 | self.insertPicAry.push( {
71 | result : this.result,
72 | name : thisFile.name,
73 | file : thisFile,
74 | );
75 | self.picAddress = thisFile.name;
76 | }
77 |
其他文本输出逻辑思路有:插入编辑功能的自动选中、区分、输出;用户手动选中区分、输出;二次进入编辑时赋值;各种状态判断等
78 | 5,token验证
涉及到用户修改数据都会加入token验证,还必须得是异步函数,验证完了才能执行下一步。vue-rouce提供了拦截器interceptos,在请求发出前获取用户登录后存储在sessionStorge的用户token,插入请求头,发出请求
79 | //vue-resource拦截器
80 | Vue.http.interceptors.push((request, next) => {
81 | Vue.http.headers.common['token'] = sessionStorage.token || '';
82 | next((response) => {
83 | //在响应之后传给then之前对response进行修改和逻辑判断
84 | if (response.status == 401) {
85 | store.state.loginJudge = false;
86 | sessionStorage.removeItem('token');
87 | }
88 | })
89 | })
90 |
91 |
92 | //token验证使用promise对象
93 | getUserLogin ( {commit}, opts){
94 | opts = opts || "";
95 | return new Promise((resolve, reject) => {
96 | api.getUserLogin(opts).then(function (res) {
97 | if (res.body.state) {
98 | resolve();
99 | }else {
100 | reject(res.body.msg)
101 | }
102 | }).catch(function(e){
103 | reject(e.body.msg)
104 | })
105 | })
106 | },
107 |
108 |
109 |
110 | ## “闪现”制作流程之(三): 三话后台
111 | 使用koa搭建服务器,使用MongoDB存储数据
112 | 讲讲main.js里各类插件处理各类问题:
113 | 1,路由匹配:koa-router
114 | let koa = require('koa-router')();
115 |
116 | //注册路由,router.routes()返回处理匹配到了请求对应的路由的router中间件
117 | //router.use([path], middleware, [...]) Use given middleware(s) before route callback.
118 | //router.use(),匹配到设置的接口地址后,进入回调中间件,再中间件里设置路由路径匹配对应后的回调函数
119 | koa.use('/users', users.routes(), users.allowedMethods());
120 |
2,前后端分别配置两个域名,当然地址是本机,只是端口号不一样,既然是跨域就必须解决接口跨域的问题:kcros
3,想要通过地址访问本地存储的图片就必须暴露此服务器地址(文件夹位置):koa-static
121 | let cors = require('kcors'),
122 | static = require('koa-static');
123 |
124 | //cors解决跨域问题
125 | app.use(cors());
126 | //static配置静态文件,可直接访问该文件夹下的文件
127 | app.use(static(__dirname + '/public/'));
128 |
4,token签发认证:koa-jwt
129 | app.use(jwt({
130 | secret: 'Blink_1.0',
131 | passthrough: true
132 | }));
133 |
134 |
135 | app.use(function *(next){
136 | var ctx = this,
137 | thisUrl = ctx.request.url;
138 | console.log(thisUrl)
139 | // 如果不是create或者checkOut路径,直接跳过该中间件
140 | if (thisUrl.indexOf('create') === -1 && thisUrl.indexOf('checkOut') == -1) {
141 | return yield next;
142 | }
143 | var token = ctx.request.headers.token || '';
144 | if (token) {
145 | var profile = jwt.verify(token, 'Blink_1.0');
146 | if (profile) {
147 | // 设置过期时间为1天
148 | if (Date.now() - profile.original_iat < 12 * 60 * 60 * 1000) {
149 | yield next;
150 | } else {
151 | ctx.status = 401;
152 | ctx.body = {
153 | state: false,
154 | msg: 'token已过期'
155 | };
156 | }
157 | } else {
158 | ctx.status = 401;
159 | ctx.body = {
160 | state: false,
161 | msg: 'token认证失败'
162 | }
163 | }
164 | } else {
165 | ctx.status = 401;
166 | ctx.body = {
167 | state: false,
168 | msg: 'token认证失败'
169 | }
170 | }
171 | });
172 |
5,想要全局使用mongodb,就得赋值一个全局承诺
173 | const mongoose = require('mongoose')
174 |
175 | const mongooseDB = function () {
176 | //重点在这一句,赋值一个全局的承诺。
177 | mongoose.Promise = global.Promise
178 | mongoose.connect('mongodb://127.0.0.1:27017/my_blog')
179 | return mongoose.connection
180 | }
181 |
敲黑板,划重点:
流程(二)前端里说过,图片是通过formdata形式提交数据,如何将数据流转为绝对地址保存在本地服务器:co-busboy
182 | //上传控件
183 | var parse = require('co-busboy');
184 | var fs = require('fs');
185 | var os = require('os');
186 | var path = require('path');
187 |
188 | while (part = yield parts){
189 | //一定要对流进行处理,否则while循环会被卡住
190 | //input上传文件就是个对象,包含了filename键值
191 | if (part.filename) {
192 | var filename = part.filename;
193 | fileNames.push(filename)
194 | var homeDir = path.resolve(__dirname, '..')
195 | var newpath = homeDir + '/public/images/article/'+ filename;
196 | //生成存储路径,要注意这里的newpath必须是绝对路径,否则Stream报错
197 | var stream = fs.createWriteStream(newpath);
198 | //写入文件流
199 | part.pipe(stream);
200 |
201 | //不是input上传文件则为数组['键','值']形式
202 | }else if(part.length) {
203 | if (part[0].indexOf("subTags") > -1) {
204 | subTags.push(part[1]);
205 | opts["subTags"] = subTags;
206 | }else {
207 | opts[part[0]] = part[1];
208 | }
209 | }
210 |
211 |
212 |
--------------------------------------------------------------------------------
/front/src/components/reusable/topBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |
闪现·BLINK
9 |
10 |
11 |
登录
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
27 |
28 | -
29 | 首页
30 |
31 |
32 | -
33 | 分类索引
34 |
35 |
36 | -
37 | 创建手记
38 | 创建手记
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
78 |
--------------------------------------------------------------------------------
/front/src/components/reusable/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 登录
6 | 注册
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
25 |
26 |
27 |
{{ loginBtn }}
28 |
29 |
30 |
31 |
32 |
134 |
--------------------------------------------------------------------------------
/front/src/components/query.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 主题方向
7 | {{item.mainTag}}
8 |
9 |
10 | 标签分类
11 | {{item}}
12 |
13 |
14 |
15 |
16 |
17 | 筛选为空~
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
170 |
--------------------------------------------------------------------------------
/front/src/components/personal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
个人主页
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 | -
15 |
{{item.articleTitle}}
16 | {{item.articleIntro}}
17 | {{item.markedArticle}}
18 |
22 |
23 |
24 |
25 |
26 |
27 | -
28 |
{{item.articleTitle}}
29 | {{item.articleIntro}}
30 | {{item.markedArticle}}
31 |
35 |
36 |
37 |
38 |
39 |
40 |
{{dataList[0].thisUser.userNickName}}
41 |
手记总数: {{dataList.length}}
42 |
43 | {{item[0].mainTag}}({{item.length}})
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 删除文章
54 |
55 |
56 |
确定删除此文章吗?
57 |
确认删除
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
204 |
--------------------------------------------------------------------------------
/front/src/components/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
![]()
11 |
12 |
13 |
14 |
最新动态
15 |
16 |
{{coverTime}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
{{articles[0].articleTitle}}
25 |
{{coverText}}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
119 |
--------------------------------------------------------------------------------
/front/src/components/create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{mainTitle}}
5 |
6 |
7 | 标题
8 |
9 |
10 | 简介
11 |
12 |
13 |
封面图
14 |
上传图片
15 |
20 |
{{ coverPicUploadMsg }}
21 |
22 |
38 |
39 |
40 | 主题方向
41 | {{item.mainTag}}
42 |
43 |
44 |
45 | 标签分类
46 | {{item}}
47 |
48 |
发布文章
49 |
50 |
51 |
52 |
53 |
54 |
55 | 插入链接
56 |
57 |
58 |
59 |
插入
60 |
61 |
62 |
63 | 插入图片
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
插入
75 |
76 |
77 |
78 |
79 |
82 |
83 |
84 |
85 |
560 |
--------------------------------------------------------------------------------