├── admin ├── static │ └── .gitkeep ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ ├── utils.js │ └── webpack.prod.conf.js ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── src │ ├── assets │ │ ├── logo.png │ │ ├── img │ │ │ └── bg.jpg │ │ └── css │ │ │ └── base.css │ ├── vuex │ │ ├── types.js │ │ ├── states.js │ │ ├── index.js │ │ ├── actions.js │ │ └── mutations.js │ ├── components │ │ ├── Me.vue │ │ ├── Error.vue │ │ ├── HelloWorld.vue │ │ ├── Aslider.vue │ │ ├── Home.vue │ │ ├── Login.vue │ │ ├── Article.vue │ │ ├── EditArticle.vue │ │ ├── Types.vue │ │ └── List.vue │ ├── App.vue │ ├── main.js │ ├── api │ │ └── index.js │ ├── axios │ │ └── index.js │ └── router │ │ └── index.js ├── .babelrc ├── .gitignore ├── .editorconfig ├── .postcssrc.js ├── README.md ├── package.json └── index.html ├── front2 ├── static │ ├── .gitkeep │ └── img │ │ └── content │ │ ├── banner.jpg │ │ ├── circle.png │ │ ├── news1.jpg │ │ ├── news10.jpg │ │ ├── news11.jpg │ │ ├── news12.jpg │ │ ├── news13.jpg │ │ ├── news2.jpg │ │ ├── news3.jpg │ │ ├── news4.jpg │ │ ├── news5.jpg │ │ ├── news6.jpg │ │ ├── news7.jpg │ │ ├── news8.jpg │ │ ├── news9.jpg │ │ ├── slide1.jpg │ │ ├── slide2.jpg │ │ ├── spec1.jpg │ │ ├── spec2.jpg │ │ ├── spec3.jpg │ │ ├── spec4.jpg │ │ ├── article.jpg │ │ ├── article2.jpg │ │ ├── comment.png │ │ └── twitter_fon.jpg ├── src │ ├── assets │ │ ├── css │ │ │ ├── test.css │ │ │ ├── fontello.css │ │ │ ├── reset.css │ │ │ └── typography.css │ │ ├── logo.png │ │ ├── fonts │ │ │ ├── fontello.eot │ │ │ ├── fontello.ttf │ │ │ └── fontello.woff │ │ ├── img │ │ │ ├── content │ │ │ │ ├── article.jpg │ │ │ │ ├── banner.jpg │ │ │ │ ├── circle.png │ │ │ │ ├── comment.png │ │ │ │ ├── news1.jpg │ │ │ │ ├── news10.jpg │ │ │ │ ├── news11.jpg │ │ │ │ ├── news12.jpg │ │ │ │ ├── news13.jpg │ │ │ │ ├── news2.jpg │ │ │ │ ├── news3.jpg │ │ │ │ ├── news4.jpg │ │ │ │ ├── news5.jpg │ │ │ │ ├── news6.jpg │ │ │ │ ├── news7.jpg │ │ │ │ ├── news8.jpg │ │ │ │ ├── news9.jpg │ │ │ │ ├── slide1.jpg │ │ │ │ ├── slide2.jpg │ │ │ │ ├── spec1.jpg │ │ │ │ ├── spec2.jpg │ │ │ │ ├── spec3.jpg │ │ │ │ ├── spec4.jpg │ │ │ │ ├── article2.jpg │ │ │ │ └── twitter_fon.jpg │ │ │ └── png-sprite │ │ │ │ └── 96dpi │ │ │ │ └── sprite.png │ │ └── js │ │ │ └── main.js │ ├── vuex │ │ ├── states.js │ │ ├── types.js │ │ ├── index.js │ │ ├── actions.js │ │ └── mutations.js │ ├── main.js │ ├── router │ │ └── index.js │ ├── api │ │ └── index.js │ ├── components │ │ ├── HelloWorld.vue │ │ ├── Error.vue │ │ ├── Footer.vue │ │ ├── Search.vue │ │ ├── Header.vue │ │ ├── Detail.vue │ │ └── List.vue │ └── App.vue ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ ├── utils.js │ └── webpack.prod.conf.js ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── .babelrc ├── .gitignore ├── .editorconfig ├── .postcssrc.js ├── README.md ├── index.html └── package.json ├── codeimg ├── admin.png ├── front.png └── article.png ├── server ├── uploads │ ├── 1511862077020.jpg │ ├── 1511862597885.png │ ├── 1511862720206.jpg │ ├── 1511925169452.jpg │ ├── 1511925200028.jpg │ ├── 1511946039405.png │ └── 1512005562051.png ├── .gitignore ├── middlewares │ └── response.js ├── config │ └── index.js ├── token │ ├── createToken.js │ └── checkToken.js ├── package.json ├── app.js ├── controllers │ ├── user.js │ ├── type.js │ ├── front.js │ └── article.js ├── bin │ └── www ├── routes │ └── index.js └── lib │ └── mysql.js └── README.md /admin/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front2/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front2/src/assets/css/test.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background:red; 3 | } -------------------------------------------------------------------------------- /codeimg/admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/codeimg/admin.png -------------------------------------------------------------------------------- /codeimg/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/codeimg/front.png -------------------------------------------------------------------------------- /admin/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/admin/build/logo.png -------------------------------------------------------------------------------- /codeimg/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/codeimg/article.png -------------------------------------------------------------------------------- /front2/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/build/logo.png -------------------------------------------------------------------------------- /admin/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /admin/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/admin/src/assets/logo.png -------------------------------------------------------------------------------- /front2/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /front2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/logo.png -------------------------------------------------------------------------------- /admin/src/assets/img/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/admin/src/assets/img/bg.jpg -------------------------------------------------------------------------------- /front2/src/vuex/states.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | type:'', 3 | activeType:'' 4 | }; 5 | export default state; -------------------------------------------------------------------------------- /server/uploads/1511862077020.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511862077020.jpg -------------------------------------------------------------------------------- /server/uploads/1511862597885.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511862597885.png -------------------------------------------------------------------------------- /server/uploads/1511862720206.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511862720206.jpg -------------------------------------------------------------------------------- /server/uploads/1511925169452.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511925169452.jpg -------------------------------------------------------------------------------- /server/uploads/1511925200028.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511925200028.jpg -------------------------------------------------------------------------------- /server/uploads/1511946039405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1511946039405.png -------------------------------------------------------------------------------- /server/uploads/1512005562051.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/server/uploads/1512005562051.png -------------------------------------------------------------------------------- /front2/src/assets/fonts/fontello.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/fonts/fontello.eot -------------------------------------------------------------------------------- /front2/src/assets/fonts/fontello.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/fonts/fontello.ttf -------------------------------------------------------------------------------- /front2/static/img/content/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/banner.jpg -------------------------------------------------------------------------------- /front2/static/img/content/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/circle.png -------------------------------------------------------------------------------- /front2/static/img/content/news1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news1.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news10.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news11.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news12.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news13.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news2.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news3.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news4.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news5.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news6.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news7.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news8.jpg -------------------------------------------------------------------------------- /front2/static/img/content/news9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/news9.jpg -------------------------------------------------------------------------------- /front2/static/img/content/slide1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/slide1.jpg -------------------------------------------------------------------------------- /front2/static/img/content/slide2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/slide2.jpg -------------------------------------------------------------------------------- /front2/static/img/content/spec1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/spec1.jpg -------------------------------------------------------------------------------- /front2/static/img/content/spec2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/spec2.jpg -------------------------------------------------------------------------------- /front2/static/img/content/spec3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/spec3.jpg -------------------------------------------------------------------------------- /front2/static/img/content/spec4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/spec4.jpg -------------------------------------------------------------------------------- /admin/src/vuex/types.js: -------------------------------------------------------------------------------- 1 | export const LOGIN = 'LOGIN'; 2 | export const LOGOUT = 'LOGOUT'; 3 | export const USERNAME = 'USERNAME' -------------------------------------------------------------------------------- /front2/src/assets/fonts/fontello.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/fonts/fontello.woff -------------------------------------------------------------------------------- /front2/static/img/content/article.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/article.jpg -------------------------------------------------------------------------------- /front2/static/img/content/article2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/article2.jpg -------------------------------------------------------------------------------- /front2/static/img/content/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/comment.png -------------------------------------------------------------------------------- /front2/src/assets/img/content/article.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/article.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/banner.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/circle.png -------------------------------------------------------------------------------- /front2/src/assets/img/content/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/comment.png -------------------------------------------------------------------------------- /front2/src/assets/img/content/news1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news1.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news10.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news11.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news12.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news13.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news2.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news3.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news4.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news5.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news6.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news7.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news8.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/news9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/news9.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/slide1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/slide1.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/slide2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/slide2.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/spec1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/spec1.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/spec2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/spec2.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/spec3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/spec3.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/spec4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/spec4.jpg -------------------------------------------------------------------------------- /front2/static/img/content/twitter_fon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/static/img/content/twitter_fon.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/article2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/article2.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/content/twitter_fon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/content/twitter_fon.jpg -------------------------------------------------------------------------------- /front2/src/assets/img/png-sprite/96dpi/sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yxliam/vue-node/HEAD/front2/src/assets/img/png-sprite/96dpi/sprite.png -------------------------------------------------------------------------------- /front2/src/vuex/types.js: -------------------------------------------------------------------------------- 1 | export const HANDLE_TYPE = 'HANDLE_TYPE'; 2 | export const HANDLE_ACTIVE = 'HANDLE_ACTIVE' 3 | export const SEARCH_TEXT = 'SEARCH_TEXT' -------------------------------------------------------------------------------- /admin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false 5 | }], 6 | "stage-2" 7 | ], 8 | "plugins": ["transform-runtime"] 9 | } 10 | -------------------------------------------------------------------------------- /admin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | 5 | # Editor directories and files 6 | .idea 7 | .vscode 8 | *.suo 9 | *.ntvs* 10 | *.njsproj 11 | *.sln 12 | -------------------------------------------------------------------------------- /front2/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false 5 | }], 6 | "stage-2" 7 | ], 8 | "plugins": ["transform-runtime"] 9 | } 10 | -------------------------------------------------------------------------------- /front2/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | 5 | # Editor directories and files 6 | .idea 7 | .vscode 8 | *.suo 9 | *.ntvs* 10 | *.njsproj 11 | *.sln 12 | -------------------------------------------------------------------------------- /admin/src/vuex/states.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | token: window.sessionStorage.getItem('token'), 3 | username:window.sessionStorage.getItem('username') 4 | }; 5 | export default state; -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | 5 | 6 | # Editor directories and files 7 | .idea 8 | .vscode 9 | *.suo 10 | *.ntvs* 11 | *.njsproj 12 | *.sln 13 | -------------------------------------------------------------------------------- /admin/src/components/Me.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | -------------------------------------------------------------------------------- /admin/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 | -------------------------------------------------------------------------------- /front2/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 | -------------------------------------------------------------------------------- /admin/.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 | -------------------------------------------------------------------------------- /admin/src/components/Error.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 15 | -------------------------------------------------------------------------------- /front2/.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 | -------------------------------------------------------------------------------- /server/middlewares/response.js: -------------------------------------------------------------------------------- 1 | module.exports = async (ctx,next)=>{ 2 | //请求成功 3 | //code 1:err 2:success 4 | ctx.jsonReturn = ({code,data,msg}) =>{ 5 | ctx.body = { code , data, msg }; 6 | }; 7 | //传递给下一个中间件 8 | await next(); 9 | 10 | } -------------------------------------------------------------------------------- /admin/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /front2/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /admin/src/vuex/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import state from './states.js' 4 | import mutations from './mutations.js' 5 | import actions from './actions.js' 6 | Vue.use( Vuex ) 7 | export default new Vuex.Store({ 8 | state, 9 | mutations, 10 | actions 11 | }) 12 | -------------------------------------------------------------------------------- /front2/src/vuex/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import state from './states.js' 4 | import mutations from './mutations.js' 5 | import actions from './actions.js' 6 | Vue.use( Vuex ) 7 | export default new Vuex.Store({ 8 | state, 9 | mutations, 10 | actions 11 | }) 12 | -------------------------------------------------------------------------------- /admin/src/vuex/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | export default{ 4 | UserLogin({commit},data){ 5 | commit(types.LOGIN,data); 6 | }, 7 | UserLogout({commit}){ 8 | commit(types.LOGOUT) 9 | }, 10 | UserName({commit},data){ 11 | commit(types.USERNAME,data) 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /front2/src/vuex/actions.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | export default{ 4 | handleType({commit},data){ 5 | commit(types.HANDLE_TYPE,data); 6 | }, 7 | handleList({commit},data){ 8 | commit(types.HANDLE_ACTIVE,data); 9 | }, 10 | handleSearch({commit},data){ 11 | commit(types.SEARCH_TEXT,data) 12 | } 13 | 14 | 15 | } -------------------------------------------------------------------------------- /server/config/index.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | dev:{ 3 | //启动端口 4 | port:3000, 5 | //数据库配置 6 | database:{ 7 | DATABASE: 'vue_new', 8 | USERNAME: 'root', 9 | PASSWORD: 'root', 10 | PORT: '3306', 11 | HOST: 'localhost' 12 | } 13 | }, 14 | 15 | } 16 | 17 | module.exports = config -------------------------------------------------------------------------------- /server/token/createToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | //登录时:核对用户名和密码成功后,应用将用户的id(图中的user_id)作为JWT Payload的一个属性 4 | module.exports = function(user_id){ 5 | const token = jwt.sign({ 6 | user_id: user_id 7 | }, 'yxl', { 8 | expiresIn: '3600s' //过期时间设置为60妙。那么decode这个token的时候得到的过期时间为 : 创建token的时间 + 设置的值 9 | }); 10 | return token; 11 | }; -------------------------------------------------------------------------------- /front2/src/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | const mutations = { 4 | [types.HANDLE_TYPE]:(state,data)=>{ 5 | state.type = data 6 | }, 7 | [types.HANDLE_ACTIVE]:(state,data)=>{ 8 | state.activeType = data 9 | }, 10 | [types.SEARCH_TEXT]:(state,data)=>{ 11 | state.search = data 12 | } 13 | } 14 | 15 | export default mutations -------------------------------------------------------------------------------- /admin/src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## vue+node 博客系统 2 | ### 技术栈 vue2+vuex+axios+vue-router+koa2+mysql 3 | ### front2 是前台展示  admin 是管理后台 server是nodejs服务接口 4 | ### 前台展示 5 | ![首页](https://raw.githubusercontent.com/yxl720/vue-node/master/codeimg/front.png) 6 | ![内容页](https://raw.githubusercontent.com/yxl720/vue-node/master/codeimg/article.png) 7 | ### 后台展示 8 | ![内容页](https://raw.githubusercontent.com/yxl720/vue-node/master/codeimg/admin.png) 9 | 10 | ``` 11 | 页面太多就没有全部展示了,因为自己的虚拟机空间不足,所以没有部署上去 12 | ``` 13 | [工具站传送门](https://www.toolbon.com/) 14 | -------------------------------------------------------------------------------- /front2/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 store from './vuex' 7 | 8 | import './assets/css/main.css' 9 | 10 | 11 | Vue.config.productionTip = false 12 | 13 | /* eslint-disable no-new */ 14 | new Vue({ 15 | el: '#app', 16 | router, 17 | store, 18 | template: '', 19 | components: { App } 20 | }) 21 | -------------------------------------------------------------------------------- /admin/README.md: -------------------------------------------------------------------------------- 1 | # admin 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /front2/README.md: -------------------------------------------------------------------------------- 1 | # front2 2 | 3 | > A Vue.js project 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /admin/src/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | import * as types from './types.js' 2 | 3 | const mutations = { 4 | [types.LOGIN]:(state,data)=>{ 5 | window.sessionStorage.setItem('token',data) 6 | state.token = data 7 | }, 8 | [types.LOGOUT]:(state,data)=>{ 9 | window.sessionStorage.removeItem('token') 10 | window.sessionStorage.removeItem('username') 11 | state.token = null; 12 | }, 13 | [types.USERNAME]:(state,data)=>{ 14 | window.sessionStorage.setItem('username',data) 15 | state.username = data 16 | } 17 | } 18 | 19 | export default mutations -------------------------------------------------------------------------------- /admin/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 ElementUI from 'element-ui' 7 | import 'element-ui/lib/theme-chalk/index.css' 8 | import "./assets/css/base.css" 9 | 10 | 11 | import store from './vuex' 12 | 13 | Vue.use(ElementUI) 14 | 15 | 16 | Vue.config.productionTip = false 17 | 18 | /* eslint-disable no-new */ 19 | new Vue({ 20 | el: '#app', 21 | router, 22 | store, 23 | template: '', 24 | components: { App } 25 | }) 26 | -------------------------------------------------------------------------------- /admin/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 | 10 | module.exports = { 11 | loaders: utils.cssLoaders({ 12 | sourceMap: sourceMapEnabled, 13 | extract: isProduction 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: 'src', 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /front2/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 | 10 | module.exports = { 11 | loaders: utils.cssLoaders({ 12 | sourceMap: sourceMapEnabled, 13 | extract: isProduction 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: 'src', 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node bin/www", 7 | "dev": "./node_modules/.bin/nodemon bin/www", 8 | "prd": "pm2 start bin/www", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "dependencies": { 12 | "debug": "^2.6.3", 13 | "jsonwebtoken": "^8.1.0", 14 | "koa": "^2.2.0", 15 | "koa-bodyparser": "^3.2.0", 16 | "koa-convert": "^1.2.0", 17 | "koa-json": "^2.0.2", 18 | "koa-logger": "^2.0.1", 19 | "koa-multer": "^1.0.2", 20 | "koa-onerror": "^1.2.1", 21 | "koa-router": "^7.1.1", 22 | "koa-static": "^3.0.0", 23 | "koa-views": "^5.2.1", 24 | "moment": "^2.19.2", 25 | "mysql": "^2.15.0", 26 | "pug": "^2.0.0-rc.1" 27 | }, 28 | "devDependencies": { 29 | "nodemon": "^1.8.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/token/checkToken.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | //检查token是否过期 3 | module.exports = async ( ctx, next ) => { 4 | if(ctx.request.header['authorization']){ 5 | let token = ctx.request.header['authorization'].split(' ')[1]; 6 | //解码token 7 | let decoded = jwt.decode(token, 'yxl'); 8 | //console.log(decoded);的输出 :{ user_id: '123123123', iat: 1494405235, exp: 1494405235 } 9 | if(token && decoded.exp <= new Date()/1000){ 10 | ctx.status = 401; 11 | ctx.body = { 12 | code:1, 13 | msg:'token过期', 14 | data:[] 15 | }; 16 | }else{ 17 | //如果权限没问题,那么交个下一个控制器处理 18 | return next(); 19 | } 20 | }else{ 21 | ctx.status = 401; 22 | ctx.body = { 23 | code:1, 24 | msg:'token err', 25 | data:[] 26 | } 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const app = new Koa() 3 | const views = require('koa-views') 4 | const json = require('koa-json') 5 | const onerror = require('koa-onerror') 6 | const bodyparser = require('koa-bodyparser') 7 | const logger = require('koa-logger') 8 | const static = require("koa-static"); 9 | //返回数据中间件 10 | const response = require('./middlewares/response.js') 11 | 12 | const index = require('./routes/index') 13 | 14 | // error handler 15 | app.use(response) 16 | 17 | onerror(app) 18 | app.use(static(__dirname + '/uploads')); 19 | 20 | // middlewares 21 | app.use(bodyparser({ 22 | enableTypes:['json', 'form', 'text'] 23 | })) 24 | app.use(json()) 25 | app.use(logger()) 26 | 27 | 28 | 29 | // logger 30 | app.use(async (ctx, next) => { 31 | const start = new Date() 32 | await next() 33 | const ms = new Date() - start 34 | console.log(`${ctx.method} ${ctx.url} - ${ms}ms`) 35 | }) 36 | 37 | // routes 38 | app.use(index.routes(), index.allowedMethods()) 39 | 40 | // error-handling 41 | app.on('error', (err, ctx) => { 42 | console.error('server error', err, ctx) 43 | }); 44 | 45 | module.exports = app 46 | -------------------------------------------------------------------------------- /admin/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, function (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, 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 | -------------------------------------------------------------------------------- /front2/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, function (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, 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 | -------------------------------------------------------------------------------- /front2/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import HelloWorld from '@/components/HelloWorld' 4 | 5 | Vue.use(Router) 6 | 7 | const Home = resolve => require(['@/components/Home.vue'], resolve); 8 | const Error = resolve => require(['@/components/Error.vue'], resolve); 9 | const Post = resolve => require(['@/components/Detail.vue'], resolve); 10 | const List = resolve => require(['@/components/List.vue'], resolve); 11 | const Search = resolve => require(['@/components/Search.vue'], resolve); 12 | 13 | 14 | export default new Router({ 15 | mode: 'history', 16 | scrollBehavior(to, from, savedPosition){ 17 | if(savedPosition){ 18 | return savedPosition; 19 | } 20 | if(to.meta.scrollTop === true){ 21 | return { x: 0, y: 0 }; 22 | } 23 | }, 24 | routes: [ 25 | { 26 | path: '/', 27 | name: 'Home', 28 | component: Home 29 | }, 30 | { 31 | path:'/post/:aid', 32 | name:'Post', 33 | component:Post, 34 | meta: { scrollTop: true } 35 | },{ 36 | path:'/list_front/:tid', 37 | name:'List', 38 | component:List 39 | }, 40 | { 41 | path:'/search/:text', 42 | name:'Search', 43 | component:Search 44 | }, 45 | { 46 | path:'*', 47 | name:'Error', 48 | component:Error 49 | } 50 | ] 51 | }) 52 | -------------------------------------------------------------------------------- /front2/src/api/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export default{ 4 | //获取类型 5 | frontGetAllType(data){ 6 | return axios.post('/api/type_all_front',data) 7 | }, 8 | //根据tid 获取列表 9 | getTidList(data){ 10 | return axios.post('/api/type_article_list_front',data) 11 | }, 12 | //根据id 获取前4阅读量 13 | getTop4List(data){ 14 | return axios.post('/api/get_for_article_front',data) 15 | }, 16 | //获取推荐文章 17 | getRecommendArticle(data){ 18 | return axios.post('/api/get_recommend_front',data) 19 | }, 20 | //获取文章 21 | getArticle(data){ 22 | return axios.post('/api/get_article_front',data) 23 | }, 24 | //修改文章阅读 25 | updateCons(data){ 26 | return axios.post('/api/update_article_consult_front',data) 27 | }, 28 | //获取轮播 29 | getSliderList(){ 30 | return axios.post('/api/select_slider_front') 31 | }, 32 | //首页列表图 33 | getHomeList(data){ 34 | return axios.post('/api/home_list_front',data) 35 | }, 36 | //首页最新文章 37 | getNewList(data){ 38 | return axios.post('/api/home_new_front',data) 39 | }, 40 | //首页类型列表 41 | getTypeList(data){ 42 | return axios.post('/api/home_type_list_front',data) 43 | }, 44 | //搜索 45 | searchList(data){ 46 | return axios.post('/api/home_search_front',data) 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /admin/src/api/index.js: -------------------------------------------------------------------------------- 1 | import axios from '../axios' 2 | 3 | export default{ 4 | // 登录 5 | localLogin(data){ 6 | return axios.post('/api/login',data) 7 | }, 8 | //添加栏目 9 | localAddType(data){ 10 | return axios.post('/api/add_type',data) 11 | }, 12 | //查询所有栏目 13 | localTypeAll(data){ 14 | return axios.post('/api/type_all',data) 15 | }, 16 | //删除栏目 17 | localTypeDel(data){ 18 | return axios.post('/api/del_type',data) 19 | }, 20 | //编辑栏目 21 | localTypeEdit(data){ 22 | return axios.post('/api/edit_type',data) 23 | }, 24 | //添加文章 25 | localAddArticle(data){ 26 | return axios.post('/api/add_article',data) 27 | }, 28 | //文章列表 29 | localArticleList(data){ 30 | return axios.post('/api/get_article_list',data) 31 | }, 32 | //文章查找 33 | localgetArticle(data){ 34 | return axios.post('/api/get_article',data) 35 | }, 36 | //编辑文章 37 | localEditArticle(data){ 38 | return axios.post('/api/edit_article',data) 39 | }, 40 | //修改文章状态 41 | localUpdateState(data){ 42 | return axios.post('/api/update_state',data) 43 | }, 44 | //删除文章 45 | localDelArticle(data){ 46 | return axios.post('/api/del_article',data) 47 | }, 48 | //缩略图上传 49 | localUpload(data){ 50 | return axios.post('/api/upload',data) 51 | }, 52 | //推荐文章 53 | localRecommend(data){ 54 | return axios.post('/api/set_recommend',data) 55 | }, 56 | //设置轮播 57 | localSetSlider(data){ 58 | return axios.post('/api/set_slider',data) 59 | } 60 | } -------------------------------------------------------------------------------- /admin/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 | function exec (cmd) { 7 | return require('child_process').execSync(cmd).toString().trim() 8 | } 9 | 10 | const versionRequirements = [ 11 | { 12 | name: 'node', 13 | currentVersion: semver.clean(process.version), 14 | versionRequirement: packageConfig.engines.node 15 | } 16 | ] 17 | 18 | if (shell.which('npm')) { 19 | versionRequirements.push({ 20 | name: 'npm', 21 | currentVersion: exec('npm --version'), 22 | versionRequirement: packageConfig.engines.npm 23 | }) 24 | } 25 | 26 | module.exports = function () { 27 | const warnings = [] 28 | for (let i = 0; i < versionRequirements.length; i++) { 29 | const mod = versionRequirements[i] 30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 31 | warnings.push(mod.name + ': ' + 32 | chalk.red(mod.currentVersion) + ' should be ' + 33 | chalk.green(mod.versionRequirement) 34 | ) 35 | } 36 | } 37 | 38 | if (warnings.length) { 39 | console.log('') 40 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 41 | console.log() 42 | for (let i = 0; i < warnings.length; i++) { 43 | const warning = warnings[i] 44 | console.log(' ' + warning) 45 | } 46 | console.log() 47 | process.exit(1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /front2/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 | function exec (cmd) { 7 | return require('child_process').execSync(cmd).toString().trim() 8 | } 9 | 10 | const versionRequirements = [ 11 | { 12 | name: 'node', 13 | currentVersion: semver.clean(process.version), 14 | versionRequirement: packageConfig.engines.node 15 | } 16 | ] 17 | 18 | if (shell.which('npm')) { 19 | versionRequirements.push({ 20 | name: 'npm', 21 | currentVersion: exec('npm --version'), 22 | versionRequirement: packageConfig.engines.npm 23 | }) 24 | } 25 | 26 | module.exports = function () { 27 | const warnings = [] 28 | for (let i = 0; i < versionRequirements.length; i++) { 29 | const mod = versionRequirements[i] 30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 31 | warnings.push(mod.name + ': ' + 32 | chalk.red(mod.currentVersion) + ' should be ' + 33 | chalk.green(mod.versionRequirement) 34 | ) 35 | } 36 | } 37 | 38 | if (warnings.length) { 39 | console.log('') 40 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 41 | console.log() 42 | for (let i = 0; i < warnings.length; i++) { 43 | const warning = warnings[i] 44 | console.log(' ' + warning) 45 | } 46 | console.log() 47 | process.exit(1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /admin/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 33 | 34 | 35 | 51 | -------------------------------------------------------------------------------- /front2/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 33 | 34 | 35 | 51 | -------------------------------------------------------------------------------- /admin/src/axios/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import store from '../vuex' 3 | import router from '../router' 4 | 5 | //设置全局axios默认值 6 | axios.defaults.timeout = 5000; //5000的超时验证 7 | axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; 8 | 9 | //创建一个axios实例 10 | const instance = axios.create(); 11 | instance.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'; 12 | 13 | axios.interceptors.request.use = instance.interceptors.request.use; 14 | 15 | //request拦截器 16 | instance.interceptors.request.use( 17 | config => { 18 | //每次发送请求之前检测都vuex存有token,那么都要放在请求头发送给服务器 19 | if(store.state.token){ 20 | config.headers.Authorization = `token ${store.state.token}`; 21 | } 22 | return config; 23 | }, 24 | err => { 25 | return Promise.reject(err); 26 | } 27 | ); 28 | //respone拦截器 29 | instance.interceptors.response.use( 30 | response => { 31 | return response; 32 | }, 33 | error => { //默认除了2XX之外的都是错误的,就会走这里 34 | if(error.response){ 35 | switch(error.response.status){ 36 | case 401: 37 | store.dispatch('UserLogout'); //可能是token过期,清除它 38 | router.replace({ //跳转到登录页面 39 | path: '/login', 40 | query: { redirect: router.currentRoute.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由 41 | }); 42 | } 43 | } 44 | return Promise.reject(error.response); 45 | } 46 | ); 47 | 48 | export default instance; 49 | -------------------------------------------------------------------------------- /admin/src/assets/css/base.css: -------------------------------------------------------------------------------- 1 | /*css 初始化 */ 2 | html, body, ul, li, ol, dl, dd, dt, p, h1, h2, h3, h4, h5, h6, form, fieldset, legend, img { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | *{ 7 | font-family:"Microsoft YaHei", "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 8 | } 9 | 10 | /*各浏览器显示不同,去掉蓝色边框*/ 11 | fieldset, img, input, button { 12 | border: none; 13 | padding: 0; 14 | margin: 0; 15 | outline-style: none; 16 | } 17 | 18 | ul, ol { 19 | list-style: none; 20 | } 21 | 22 | input { 23 | padding-top: 0; 24 | padding-bottom: 0; 25 | font-family: "Microsoft yaHei"; 26 | } 27 | 28 | /*去掉行内替换元素空白缝隙*/ 29 | select, input { 30 | vertical-align: middle; 31 | } 32 | 33 | select, input, textarea { 34 | margin: 0; 35 | } 36 | 37 | /*防止拖动 影响布局*/ 38 | textarea { 39 | resize: none; 40 | } 41 | 42 | /*去掉行内替换元素空白缝隙*/ 43 | img { 44 | border: 0; 45 | vertical-align: middle; 46 | } 47 | 48 | table { 49 | border-collapse: collapse; 50 | } 51 | 52 | /*清除浮动*/ 53 | .clearfix:before, .clearfix:after { 54 | content: ""; 55 | display: table; 56 | } 57 | 58 | .clearfix:after { 59 | clear: both; 60 | } 61 | 62 | .clearfix { 63 | *zoom: 1; /*IE/7/6*/ 64 | } 65 | li{ 66 | list-style: none; 67 | } 68 | a{ 69 | text-decoration: none; 70 | } 71 | h1, h2, h3, h4, h5, h6 { 72 | text-decoration: none; 73 | font-weight: normal; 74 | font-size: 100%; 75 | } 76 | /*浮动*/ 77 | .fl{ 78 | float: left; 79 | } 80 | .fr{ 81 | float: right; 82 | } 83 | .ml25{ 84 | margin-left: 25px; 85 | } 86 | .body { 87 | width: 100%; 88 | position: relative; 89 | } -------------------------------------------------------------------------------- /front2/src/components/Error.vue: -------------------------------------------------------------------------------- 1 | 28 | 33 | 45 | -------------------------------------------------------------------------------- /front2/src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 35 | 40 | 43 | -------------------------------------------------------------------------------- /server/controllers/user.js: -------------------------------------------------------------------------------- 1 | const Model = require('../lib/mysql.js'); 2 | const createToken = require('../token/createToken.js'); 3 | class UserController{ 4 | //用户登录 5 | static async loginPost(ctx){ 6 | //es6 解构 7 | //这里解构需要和 ctx.request.body 的对象属性相同 8 | const { username,password } = ctx.request.body; 9 | await Model.dologin(username,password) 10 | .then(result=>{ 11 | var res = JSON.parse(JSON.stringify(result)) 12 | if(res.length <= 0){ 13 | ctx.jsonReturn({ 14 | code:1, 15 | msg:'用户名或者密码错误' 16 | }) 17 | }else{ 18 | let token = createToken(username); 19 | var updateinfo = Model.updateUserToken([token,res[0]['id']]) 20 | if(updateinfo){ 21 | ctx.jsonReturn({ 22 | code:2, 23 | data:{ 24 | token:token, 25 | username:username 26 | }, 27 | msg:'登录成功' 28 | }) 29 | 30 | }else{ 31 | ctx.jsonReturn({ 32 | code:1, 33 | msg:'登录失败' 34 | }) 35 | } 36 | } 37 | }).catch(err=>{ 38 | ctx.jsonReturn({ 39 | code:1, 40 | msg:'服务器异常' 41 | }) 42 | }) 43 | } 44 | 45 | } 46 | 47 | module.exports = UserController; -------------------------------------------------------------------------------- /admin/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 | module.exports = { 12 | context: path.resolve(__dirname, '../'), 13 | entry: { 14 | app: './src/main.js' 15 | }, 16 | output: { 17 | path: config.build.assetsRoot, 18 | filename: '[name].js', 19 | publicPath: process.env.NODE_ENV === 'production' 20 | ? config.build.assetsPublicPath 21 | : config.dev.assetsPublicPath 22 | }, 23 | resolve: { 24 | extensions: ['.js', '.vue', '.json'], 25 | alias: { 26 | 'vue$': 'vue/dist/vue.esm.js', 27 | '@': resolve('src'), 28 | } 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.vue$/, 34 | loader: 'vue-loader', 35 | options: vueLoaderConfig 36 | }, 37 | { 38 | test: /\.js$/, 39 | loader: 'babel-loader', 40 | include: [resolve('src'), resolve('test')] 41 | }, 42 | { 43 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 44 | loader: 'url-loader', 45 | options: { 46 | limit: 10000, 47 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 48 | } 49 | }, 50 | { 51 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 52 | loader: 'url-loader', 53 | options: { 54 | limit: 10000, 55 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 56 | } 57 | }, 58 | { 59 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 60 | loader: 'url-loader', 61 | options: { 62 | limit: 10000, 63 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 64 | } 65 | } 66 | ] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /front2/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 | module.exports = { 12 | context: path.resolve(__dirname, '../'), 13 | entry: { 14 | app: './src/main.js' 15 | }, 16 | output: { 17 | path: config.build.assetsRoot, 18 | filename: '[name].js', 19 | publicPath: process.env.NODE_ENV === 'production' 20 | ? config.build.assetsPublicPath 21 | : config.dev.assetsPublicPath 22 | }, 23 | resolve: { 24 | extensions: ['.js', '.vue', '.json'], 25 | alias: { 26 | 'vue$': 'vue/dist/vue.esm.js', 27 | '@': resolve('src'), 28 | } 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.vue$/, 34 | loader: 'vue-loader', 35 | options: vueLoaderConfig 36 | }, 37 | { 38 | test: /\.js$/, 39 | loader: 'babel-loader', 40 | include: [resolve('src'), resolve('test')] 41 | }, 42 | { 43 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 44 | loader: 'url-loader', 45 | options: { 46 | limit: 10000, 47 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 48 | } 49 | }, 50 | { 51 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 52 | loader: 'url-loader', 53 | options: { 54 | limit: 10000, 55 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 56 | } 57 | }, 58 | { 59 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 60 | loader: 'url-loader', 61 | options: { 62 | limit: 10000, 63 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 64 | } 65 | }, 66 | 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /front2/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 57 | 58 | 76 | -------------------------------------------------------------------------------- /admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "yxl720 <676557432@qq.com>", 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 | "mavon-editor": "^2.4.0", 14 | "vue": "^2.5.2", 15 | "vue-router": "^3.0.1" 16 | }, 17 | "devDependencies": { 18 | "autoprefixer": "^7.1.2", 19 | "babel-core": "^6.22.1", 20 | "babel-loader": "^7.1.1", 21 | "babel-plugin-transform-runtime": "^6.22.0", 22 | "babel-preset-env": "^1.3.2", 23 | "babel-preset-stage-2": "^6.22.0", 24 | "babel-register": "^6.22.0", 25 | "chalk": "^2.0.1", 26 | "copy-webpack-plugin": "^4.0.1", 27 | "css-loader": "^0.28.0", 28 | "eventsource-polyfill": "^0.9.6", 29 | "extract-text-webpack-plugin": "^3.0.0", 30 | "file-loader": "^1.1.4", 31 | "friendly-errors-webpack-plugin": "^1.6.1", 32 | "html-webpack-plugin": "^2.30.1", 33 | "node-notifier": "^5.1.2", 34 | "node-sass": "^4.7.2", 35 | "optimize-css-assets-webpack-plugin": "^3.2.0", 36 | "ora": "^1.2.0", 37 | "portfinder": "^1.0.13", 38 | "postcss-import": "^11.0.0", 39 | "postcss-loader": "^2.0.8", 40 | "rimraf": "^2.6.0", 41 | "sass-loader": "^6.0.6", 42 | "semver": "^5.3.0", 43 | "shelljs": "^0.7.6", 44 | "url-loader": "^0.5.8", 45 | "vue-loader": "^13.3.0", 46 | "vue-style-loader": "^3.0.1", 47 | "vue-template-compiler": "^2.5.2", 48 | "webpack": "^3.6.0", 49 | "webpack-bundle-analyzer": "^2.9.0", 50 | "webpack-dev-server": "^2.9.1", 51 | "webpack-merge": "^4.1.0" 52 | }, 53 | "engines": { 54 | "node": ">= 4.0.0", 55 | "npm": ">= 3.0.0" 56 | }, 57 | "browserslist": [ 58 | "> 1%", 59 | "last 2 versions", 60 | "not ie <= 8" 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /admin/src/components/Aslider.vue: -------------------------------------------------------------------------------- 1 | 19 | 24 | 61 | -------------------------------------------------------------------------------- /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 || '3000'); 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 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : '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' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | front2 7 | 53 | 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /front2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | front2 7 | 53 | 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /front2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "front2", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "yxl720 <676557432@qq.com>", 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.17.1", 14 | "marked": "^0.3.6", 15 | "mavon-editor": "^2.4.7", 16 | "node-loader": "^0.6.0", 17 | "node-sass": "^4.7.2", 18 | "sass-loader": "^6.0.6", 19 | "vue": "^2.5.2", 20 | "vue-loading-template": "^0.1.7", 21 | "vue-router": "^3.0.1", 22 | "vuex": "^3.0.1" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^7.1.2", 26 | "babel-core": "^6.22.1", 27 | "babel-loader": "^7.1.1", 28 | "babel-plugin-transform-runtime": "^6.22.0", 29 | "babel-preset-env": "^1.3.2", 30 | "babel-preset-stage-2": "^6.22.0", 31 | "babel-register": "^6.22.0", 32 | "chalk": "^2.0.1", 33 | "copy-webpack-plugin": "^4.0.1", 34 | "css-loader": "^0.28.0", 35 | "eventsource-polyfill": "^0.9.6", 36 | "extract-text-webpack-plugin": "^3.0.0", 37 | "file-loader": "^1.1.4", 38 | "friendly-errors-webpack-plugin": "^1.6.1", 39 | "html-webpack-plugin": "^2.30.1", 40 | "webpack-bundle-analyzer": "^2.9.0", 41 | "node-notifier": "^5.1.2", 42 | "postcss-import": "^11.0.0", 43 | "postcss-loader": "^2.0.8", 44 | "semver": "^5.3.0", 45 | "shelljs": "^0.7.6", 46 | "optimize-css-assets-webpack-plugin": "^3.2.0", 47 | "ora": "^1.2.0", 48 | "rimraf": "^2.6.0", 49 | "url-loader": "^0.5.8", 50 | "vue-loader": "^13.3.0", 51 | "vue-style-loader": "^3.0.1", 52 | "vue-template-compiler": "^2.5.2", 53 | "portfinder": "^1.0.13", 54 | "webpack": "^3.6.0", 55 | "webpack-dev-server": "^2.9.1", 56 | "webpack-merge": "^4.1.0" 57 | }, 58 | "engines": { 59 | "node": ">= 4.0.0", 60 | "npm": ">= 3.0.0" 61 | }, 62 | "browserslist": [ 63 | "> 1%", 64 | "last 2 versions", 65 | "not ie <= 8" 66 | ] 67 | } 68 | -------------------------------------------------------------------------------- /front2/src/assets/css/fontello.css: -------------------------------------------------------------------------------- 1 | /* SVG - fonts */ 2 | @font-face { 3 | font-family: 'fontello'; 4 | src: url("../fonts/fontello.eot?44020949"); 5 | src: url("../fonts/fontello.eot?44020949#iefix") format("embedded-opentype"), url("../fonts/fontello.woff?44020949") format("woff"), url("../fonts/fontello.ttf?44020949") format("truetype"); 6 | font-weight: normal; 7 | font-style: normal; 8 | } 9 | 10 | [class^="icon-"]:before, [class*=" icon-"]:before { 11 | font-family: "fontello"; 12 | font-style: normal; 13 | font-weight: normal; 14 | speak: none; 15 | display: inline-block; 16 | text-decoration: inherit; 17 | width: 1em; 18 | margin-right: .2em; 19 | text-align: center; 20 | /* opacity: .8; */ 21 | /* For safety - reset parent styles, that can break glyph codes*/ 22 | font-variant: normal; 23 | text-transform: none; 24 | /* fix buttons height, for twitter bootstrap */ 25 | line-height: 1em; 26 | /* Animation center compensation - margins should be symmetric */ 27 | /* remove if not needed */ 28 | margin-left: .2em; 29 | /* you can be more comfortable with increased icons size */ 30 | /* font-size: 120%; */ 31 | /* Font smoothing. That was taken from TWBS */ 32 | -webkit-font-smoothing: antialiased; 33 | -moz-osx-font-smoothing: grayscale; 34 | /* Uncomment for 3D effect */ 35 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 36 | } 37 | 38 | .icon-sun:before { 39 | content: '\e800'; 40 | } 41 | 42 | .icon-euro:before { 43 | content: '\e801'; 44 | } 45 | 46 | .icon-search:before { 47 | content: '\e802'; 48 | } 49 | 50 | .icon-down-open:before { 51 | content: '\e803'; 52 | } 53 | 54 | .icon-up-open:before { 55 | content: '\e804'; 56 | } 57 | 58 | .icon-play:before { 59 | content: '\e805'; 60 | } 61 | 62 | .icon-facebook:before { 63 | content: '\e806'; 64 | } 65 | 66 | .icon-gplus:before { 67 | content: '\e807'; 68 | } 69 | 70 | .icon-twitter:before { 71 | content: '\e808'; 72 | } 73 | 74 | .icon-eye:before { 75 | content: '\e809'; 76 | } 77 | 78 | .icon-comment-empty:before { 79 | content: '\e80a'; 80 | } 81 | 82 | .icon-reply:before { 83 | content: '\e80b'; 84 | } 85 | 86 | .icon-arrows-cw:before { 87 | content: '\e80c'; 88 | } 89 | 90 | /* end SVG - fonts */ -------------------------------------------------------------------------------- /admin/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import store from '../vuex' 4 | 5 | const Home = resolve => require(['@/components/Home.vue'], resolve); 6 | const Error = resolve => require(['@/components/Error.vue'], resolve); 7 | const Login = resolve => require(['@/components/Login.vue'], resolve); 8 | const Article = resolve => require(['@/components/Article.vue'], resolve); 9 | const List = resolve => require(['@/components/List.vue'], resolve); 10 | const Types = resolve => require(['@/components/Types.vue'], resolve); 11 | const Me = resolve => require(['@/components/Me.vue'], resolve); 12 | const EditArticle = resolve => require(['@/components/EditArticle.vue'], resolve); 13 | Vue.use(Router) 14 | 15 | const router = new Router({ 16 | mode: 'history', 17 | routes: [ 18 | { 19 | path: '/admin', 20 | name: 'Home', 21 | component: Home, 22 | redirect:'/admin/article', 23 | meta:{ 24 | auth:true, 25 | title:'首页' 26 | }, 27 | children:[ 28 | { 29 | path:'article', 30 | component:Article, 31 | meta:{ 32 | auth:true, 33 | title:'发布文章' 34 | } 35 | }, 36 | { 37 | path:'editArticle/:id', 38 | component:EditArticle, 39 | meta:{ 40 | auth:true, 41 | title:'编辑文章' 42 | } 43 | } 44 | ,{ 45 | path:'list', 46 | component:List, 47 | meta:{ 48 | auth:true, 49 | title:'文章管理' 50 | } 51 | },{ 52 | path:'types', 53 | component:Types, 54 | meta:{ 55 | auth:true, 56 | title:'栏目管理' 57 | } 58 | },{ 59 | path:'about', 60 | component:Me, 61 | meta:{ 62 | auth:true, 63 | title:'关于我' 64 | } 65 | } 66 | ] 67 | }, 68 | { 69 | path:'/login', 70 | component:Login, 71 | meta:{ 72 | title:'登录' 73 | } 74 | }, 75 | { 76 | path:'*', 77 | name:'Error', 78 | component:Error, 79 | meta:{ 80 | title:'Error' 81 | } 82 | } 83 | ] 84 | }) 85 | //在全局导航钩子中检查 meta 字段 86 | router.beforeEach((to, from, next) => { 87 | let token = store.state.token; 88 | document.title = to.meta.title || document.title 89 | if(to.meta.auth){ 90 | if(token){ 91 | return next(); 92 | } 93 | next({ 94 | path: '/login', 95 | query: { redirect: to.fullPath } 96 | }) 97 | }else{ 98 | next(); 99 | } 100 | }); 101 | 102 | export default router; 103 | -------------------------------------------------------------------------------- /admin/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 baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | const portfinder = require('portfinder') 10 | 11 | const devWebpackConfig = merge(baseWebpackConfig, { 12 | module: { 13 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 14 | }, 15 | // cheap-module-eval-source-map is faster for development 16 | devtool: config.dev.devtool, 17 | 18 | // these devServer options should be customized in /config/index.js 19 | devServer: { 20 | clientLogLevel: 'warning', 21 | historyApiFallback: true, 22 | hot: true, 23 | compress: true, 24 | host: process.env.HOST || config.dev.host, 25 | port: process.env.PORT || config.dev.port, 26 | open: config.dev.autoOpenBrowser, 27 | overlay: config.dev.errorOverlay ? { 28 | warnings: false, 29 | errors: true, 30 | } : false, 31 | publicPath: config.dev.assetsPublicPath, 32 | proxy: config.dev.proxyTable, 33 | quiet: true, // necessary for FriendlyErrorsPlugin 34 | watchOptions: { 35 | poll: config.dev.poll, 36 | } 37 | }, 38 | plugins: [ 39 | new webpack.DefinePlugin({ 40 | 'process.env': require('../config/dev.env') 41 | }), 42 | new webpack.HotModuleReplacementPlugin(), 43 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 44 | new webpack.NoEmitOnErrorsPlugin(), 45 | // https://github.com/ampedandwired/html-webpack-plugin 46 | new HtmlWebpackPlugin({ 47 | filename: 'index.html', 48 | template: 'index.html', 49 | inject: true 50 | }), 51 | ] 52 | }) 53 | 54 | module.exports = new Promise((resolve, reject) => { 55 | portfinder.basePort = process.env.PORT || config.dev.port 56 | portfinder.getPort((err, port) => { 57 | if (err) { 58 | reject(err) 59 | } else { 60 | // publish the new Port, necessary for e2e tests 61 | process.env.PORT = port 62 | // add port to devServer config 63 | devWebpackConfig.devServer.port = port 64 | 65 | // Add FriendlyErrorsPlugin 66 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 67 | compilationSuccessInfo: { 68 | messages: [`Your application is running here: http://${config.dev.host}:${port}`], 69 | }, 70 | onErrors: config.dev.notifyOnErrors 71 | ? utils.createNotifierCallback() 72 | : undefined 73 | })) 74 | 75 | resolve(devWebpackConfig) 76 | } 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /front2/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 baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | const portfinder = require('portfinder') 10 | 11 | const devWebpackConfig = merge(baseWebpackConfig, { 12 | module: { 13 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 14 | }, 15 | // cheap-module-eval-source-map is faster for development 16 | devtool: config.dev.devtool, 17 | 18 | // these devServer options should be customized in /config/index.js 19 | devServer: { 20 | clientLogLevel: 'warning', 21 | historyApiFallback: true, 22 | hot: true, 23 | compress: true, 24 | host: process.env.HOST || config.dev.host, 25 | port: process.env.PORT || config.dev.port, 26 | open: config.dev.autoOpenBrowser, 27 | overlay: config.dev.errorOverlay ? { 28 | warnings: false, 29 | errors: true, 30 | } : false, 31 | publicPath: config.dev.assetsPublicPath, 32 | proxy: config.dev.proxyTable, 33 | quiet: true, // necessary for FriendlyErrorsPlugin 34 | watchOptions: { 35 | poll: config.dev.poll, 36 | } 37 | }, 38 | plugins: [ 39 | new webpack.DefinePlugin({ 40 | 'process.env': require('../config/dev.env') 41 | }), 42 | new webpack.HotModuleReplacementPlugin(), 43 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 44 | new webpack.NoEmitOnErrorsPlugin(), 45 | // https://github.com/ampedandwired/html-webpack-plugin 46 | new HtmlWebpackPlugin({ 47 | filename: 'index.html', 48 | template: 'index.html', 49 | inject: true 50 | }), 51 | ] 52 | }) 53 | 54 | module.exports = new Promise((resolve, reject) => { 55 | portfinder.basePort = process.env.PORT || config.dev.port 56 | portfinder.getPort((err, port) => { 57 | if (err) { 58 | reject(err) 59 | } else { 60 | // publish the new Port, necessary for e2e tests 61 | process.env.PORT = port 62 | // add port to devServer config 63 | devWebpackConfig.devServer.port = port 64 | 65 | // Add FriendlyErrorsPlugin 66 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 67 | compilationSuccessInfo: { 68 | messages: [`Your application is running here: http://${config.dev.host}:${port}`], 69 | }, 70 | onErrors: config.dev.notifyOnErrors 71 | ? utils.createNotifierCallback() 72 | : undefined 73 | })) 74 | 75 | resolve(devWebpackConfig) 76 | } 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /server/routes/index.js: -------------------------------------------------------------------------------- 1 | const router = require('koa-router')() 2 | const multer = require('koa-multer');//加载koa-multer模块 3 | //checkToken作为中间件存在 4 | const checkToken = require('../token/checkToken.js'); 5 | const User = require('../controllers/user.js'); 6 | const Type = require('../controllers/type.js'); 7 | const Article = require('../controllers/article.js'); 8 | const Front = require('../controllers/front.js') 9 | 10 | //配置 11 | var storage = multer.diskStorage({ 12 | //文件保存路径 13 | destination: function (req, file, cb) { 14 | cb(null, 'uploads/') 15 | }, 16 | //修改文件名称 17 | filename: function (req, file, cb) { 18 | var fileFormat = (file.originalname).split("."); 19 | cb(null,Date.now() + "." + fileFormat[fileFormat.length - 1]); 20 | } 21 | }) 22 | //加载配置 23 | var upload = multer({ storage: storage }); 24 | 25 | //后台接口 26 | //登录 27 | router.post('/login',User.loginPost); 28 | //添加栏目 29 | router.post('/add_type',checkToken,Type.createType) 30 | //查询所有栏目 31 | router.post('/type_all',checkToken,Type.selectTypeAll) 32 | //删除栏目 33 | router.post('/del_type',checkToken,Type.delectTypeById); 34 | //修改栏目 35 | router.post('/edit_type',checkToken,Type.editTypeById); 36 | //添加文章 37 | router.post('/add_article',checkToken,Article.createArticle) 38 | //文章列表 39 | router.post('/get_article_list',checkToken,Article.getArticleList) 40 | //查找文章 41 | router.post('/get_article',checkToken,Article.getArticle) 42 | //编辑文章 43 | router.post('/edit_article',checkToken,Article.editArticle) 44 | //文章状态 45 | router.post('/update_state',checkToken,Article.updateState) 46 | //文章删除 47 | router.post('/del_article',checkToken,Article.delArticleById) 48 | //缩略图上传 49 | router.post('/upload', upload.single('file'), async (ctx, next) => { 50 | console.log( ctx ); 51 | ctx.body = { 52 | filename: 'http://'+ctx.host+'/'+ctx.req.file.filename//返回文件名 53 | } 54 | }) 55 | //推荐 56 | router.post('/set_recommend',checkToken,Article.setRecommend) 57 | //设置轮播 58 | router.post('/set_slider',checkToken,Article.setSliderCon) 59 | 60 | 61 | //前台接口 62 | router.post('/type_all_front',Type.selectTypeAll) 63 | //根据tid 查找相关文章 64 | router.post('/type_article_list_front',Front.articleList) 65 | //前4阅读 66 | router.post('/get_for_article_front',Front.getMostConstuList) 67 | //推荐文章 68 | router.post('/get_recommend_front',Front.getRecommend) 69 | //获取文章 70 | router.post('/get_article_front',Front.findArticleById) 71 | //修改阅读 72 | router.post('/update_article_consult_front',Front.updateConsult) 73 | //轮播查询 74 | router.post('/select_slider_front',Front.selectSlider) 75 | //首页文章列表 76 | router.post('/home_list_front',Front.homeList) 77 | //首页最新文章 78 | router.post('/home_new_front',Front.newList) 79 | //首页类型文章 80 | router.post('/home_type_list_front',Front.getTypeList) 81 | //搜索 82 | router.post('/home_search_front',Front.searchArticleList) 83 | 84 | 85 | module.exports = router 86 | -------------------------------------------------------------------------------- /admin/src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 22 | 48 | 108 | -------------------------------------------------------------------------------- /admin/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 pkg = 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 | return path.posix.join(assetsSubDirectory, _path) 12 | } 13 | 14 | exports.cssLoaders = function (options) { 15 | options = options || {} 16 | 17 | const cssLoader = { 18 | loader: 'css-loader', 19 | options: { 20 | sourceMap: options.sourceMap 21 | } 22 | } 23 | 24 | var postcssLoader = { 25 | loader: 'postcss-loader', 26 | options: { 27 | sourceMap: options.sourceMap 28 | } 29 | } 30 | 31 | // generate loader string to be used with extract text plugin 32 | function generateLoaders (loader, loaderOptions) { 33 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 34 | if (loader) { 35 | loaders.push({ 36 | loader: loader + '-loader', 37 | options: Object.assign({}, loaderOptions, { 38 | sourceMap: options.sourceMap 39 | }) 40 | }) 41 | } 42 | 43 | // Extract CSS when that option is specified 44 | // (which is the case during production build) 45 | if (options.extract) { 46 | return ExtractTextPlugin.extract({ 47 | use: loaders, 48 | fallback: 'vue-style-loader' 49 | }) 50 | } else { 51 | return ['vue-style-loader'].concat(loaders) 52 | } 53 | } 54 | 55 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 56 | return { 57 | css: generateLoaders(), 58 | postcss: generateLoaders(), 59 | less: generateLoaders('less'), 60 | sass: generateLoaders('sass', { indentedSyntax: true }), 61 | scss: generateLoaders('sass'), 62 | stylus: generateLoaders('stylus'), 63 | styl: generateLoaders('stylus') 64 | } 65 | } 66 | 67 | // Generate loaders for standalone style files (outside of .vue) 68 | exports.styleLoaders = function (options) { 69 | const output = [] 70 | const loaders = exports.cssLoaders(options) 71 | for (const extension in loaders) { 72 | const loader = loaders[extension] 73 | output.push({ 74 | test: new RegExp('\\.' + extension + '$'), 75 | use: loader 76 | }) 77 | } 78 | return output 79 | } 80 | 81 | exports.createNotifierCallback = function () { 82 | const notifier = require('node-notifier') 83 | 84 | return (severity, errors) => { 85 | if (severity !== 'error') { 86 | return 87 | } 88 | const error = errors[0] 89 | 90 | const filename = error.file && error.file.split('!').pop() 91 | notifier.notify({ 92 | title: pkg.name, 93 | message: severity + ': ' + error.name, 94 | subtitle: filename || '', 95 | icon: path.join(__dirname, 'logo.png') 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /front2/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 pkg = 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 | return path.posix.join(assetsSubDirectory, _path) 12 | } 13 | 14 | exports.cssLoaders = function (options) { 15 | options = options || {} 16 | 17 | const cssLoader = { 18 | loader: 'css-loader', 19 | options: { 20 | sourceMap: options.sourceMap 21 | } 22 | } 23 | 24 | var postcssLoader = { 25 | loader: 'postcss-loader', 26 | options: { 27 | sourceMap: options.sourceMap 28 | } 29 | } 30 | 31 | // generate loader string to be used with extract text plugin 32 | function generateLoaders (loader, loaderOptions) { 33 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 34 | if (loader) { 35 | loaders.push({ 36 | loader: loader + '-loader', 37 | options: Object.assign({}, loaderOptions, { 38 | sourceMap: options.sourceMap 39 | }) 40 | }) 41 | } 42 | 43 | // Extract CSS when that option is specified 44 | // (which is the case during production build) 45 | if (options.extract) { 46 | return ExtractTextPlugin.extract({ 47 | use: loaders, 48 | fallback: 'vue-style-loader' 49 | }) 50 | } else { 51 | return ['vue-style-loader'].concat(loaders) 52 | } 53 | } 54 | 55 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 56 | return { 57 | css: generateLoaders(), 58 | postcss: generateLoaders(), 59 | less: generateLoaders('less'), 60 | sass: generateLoaders('sass', { indentedSyntax: true }), 61 | scss: generateLoaders('sass'), 62 | stylus: generateLoaders('stylus'), 63 | styl: generateLoaders('stylus') 64 | } 65 | } 66 | 67 | // Generate loaders for standalone style files (outside of .vue) 68 | exports.styleLoaders = function (options) { 69 | const output = [] 70 | const loaders = exports.cssLoaders(options) 71 | for (const extension in loaders) { 72 | const loader = loaders[extension] 73 | output.push({ 74 | test: new RegExp('\\.' + extension + '$'), 75 | use: loader 76 | }) 77 | } 78 | return output 79 | } 80 | 81 | exports.createNotifierCallback = function () { 82 | const notifier = require('node-notifier') 83 | 84 | return (severity, errors) => { 85 | if (severity !== 'error') { 86 | return 87 | } 88 | const error = errors[0] 89 | 90 | const filename = error.file && error.file.split('!').pop() 91 | notifier.notify({ 92 | title: pkg.name, 93 | message: severity + ': ' + error.name, 94 | subtitle: filename || '', 95 | icon: path.join(__dirname, 'logo.png') 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /admin/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.4 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://127.0.0.1:3000/', 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/api': '/' 19 | } 20 | } 21 | }, 22 | 23 | // Various Dev Server settings 24 | host: 'localhost', // can be overwritten by process.env.HOST 25 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 26 | autoOpenBrowser: false, 27 | errorOverlay: true, 28 | notifyOnErrors: true, 29 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 30 | 31 | // Use Eslint Loader? 32 | // If true, your code will be linted during bundling and 33 | // linting errors and warnings will be shown in the console. 34 | useEslint: true, 35 | // If true, eslint errors and warnings will also be shown in the error overlay 36 | // in the browser. 37 | showEslintErrorsInOverlay: false, 38 | 39 | /** 40 | * Source Maps 41 | */ 42 | 43 | // https://webpack.js.org/configuration/devtool/#development 44 | devtool: 'eval-source-map', 45 | 46 | // If you have problems debugging vue-files in devtools, 47 | // set this to false - it *may* help 48 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 49 | cacheBusting: true, 50 | 51 | // CSS Sourcemaps off by default because relative paths are "buggy" 52 | // with this option, according to the CSS-Loader README 53 | // (https://github.com/webpack/css-loader#sourcemaps) 54 | // In our experience, they generally work as expected, 55 | // just be aware of this issue when enabling this option. 56 | cssSourceMap: false, 57 | }, 58 | 59 | build: { 60 | // Template for index.html 61 | index: path.resolve(__dirname, '../dist/index.html'), 62 | 63 | // Paths 64 | assetsRoot: path.resolve(__dirname, '../dist'), 65 | assetsSubDirectory: 'static', 66 | assetsPublicPath: '/', 67 | 68 | /** 69 | * Source Maps 70 | */ 71 | 72 | productionSourceMap: true, 73 | // https://webpack.js.org/configuration/devtool/#production 74 | devtool: '#source-map', 75 | 76 | // Gzip off by default as many popular static hosts such as 77 | // Surge or Netlify already gzip all static assets for you. 78 | // Before setting to `true`, make sure to: 79 | // npm install --save-dev compression-webpack-plugin 80 | productionGzip: false, 81 | productionGzipExtensions: ['js', 'css'], 82 | 83 | // Run the build command with an extra argument to 84 | // View the bundle analyzer report after build finishes: 85 | // `npm run build --report` 86 | // Set to `true` or `false` to always turn it on or off 87 | bundleAnalyzerReport: process.env.npm_config_report 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /front2/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.4 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://127.0.0.1:3000/', 16 | changeOrigin: true, 17 | pathRewrite: { 18 | '^/api': '/' 19 | } 20 | } 21 | }, 22 | 23 | // Various Dev Server settings 24 | host: 'localhost', // can be overwritten by process.env.HOST 25 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 26 | autoOpenBrowser: false, 27 | errorOverlay: true, 28 | notifyOnErrors: true, 29 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 30 | 31 | // Use Eslint Loader? 32 | // If true, your code will be linted during bundling and 33 | // linting errors and warnings will be shown in the console. 34 | useEslint: true, 35 | // If true, eslint errors and warnings will also be shown in the error overlay 36 | // in the browser. 37 | showEslintErrorsInOverlay: false, 38 | 39 | /** 40 | * Source Maps 41 | */ 42 | 43 | // https://webpack.js.org/configuration/devtool/#development 44 | devtool: 'eval-source-map', 45 | 46 | // If you have problems debugging vue-files in devtools, 47 | // set this to false - it *may* help 48 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 49 | cacheBusting: true, 50 | 51 | // CSS Sourcemaps off by default because relative paths are "buggy" 52 | // with this option, according to the CSS-Loader README 53 | // (https://github.com/webpack/css-loader#sourcemaps) 54 | // In our experience, they generally work as expected, 55 | // just be aware of this issue when enabling this option. 56 | cssSourceMap: false, 57 | }, 58 | 59 | build: { 60 | // Template for index.html 61 | index: path.resolve(__dirname, '../dist/index.html'), 62 | 63 | // Paths 64 | assetsRoot: path.resolve(__dirname, '../dist'), 65 | assetsSubDirectory: 'static', 66 | assetsPublicPath: '/', 67 | 68 | /** 69 | * Source Maps 70 | */ 71 | 72 | productionSourceMap: true, 73 | // https://webpack.js.org/configuration/devtool/#production 74 | devtool: '#source-map', 75 | 76 | // Gzip off by default as many popular static hosts such as 77 | // Surge or Netlify already gzip all static assets for you. 78 | // Before setting to `true`, make sure to: 79 | // npm install --save-dev compression-webpack-plugin 80 | productionGzip: false, 81 | productionGzipExtensions: ['js', 'css'], 82 | 83 | // Run the build command with an extra argument to 84 | // View the bundle analyzer report after build finishes: 85 | // `npm run build --report` 86 | // Set to `true` or `false` to always turn it on or off 87 | bundleAnalyzerReport: process.env.npm_config_report 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /front2/src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | /**********************************/ 2 | /* RESET.CSS */ 3 | /**********************************/ 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | -webkit-text-size-adjust: none; 9 | } 10 | 11 | label { 12 | cursor: pointer; 13 | } 14 | 15 | a { 16 | margin: 0; 17 | padding: 0; 18 | vertical-align: baseline; 19 | background: transparent; 20 | text-decoration: none; 21 | color: #000; 22 | } 23 | 24 | table { 25 | border-collapse: collapse; 26 | border-spacing: 0; 27 | } 28 | 29 | td, td img { 30 | vertical-align: top; 31 | } 32 | 33 | input, select, button, textarea { 34 | margin: 0; 35 | font-size: 100%; 36 | } 37 | 38 | input[type="checkbox"] { 39 | vertical-align: bottom; 40 | } 41 | 42 | input[type="radio"] { 43 | vertical-align: text-bottom; 44 | } 45 | 46 | sub { 47 | vertical-align: sub; 48 | font-size: smaller; 49 | } 50 | 51 | sup { 52 | vertical-align: super; 53 | font-size: smaller; 54 | } 55 | 56 | /*****************************************************************************************/ 57 | html, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, 58 | a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, 59 | small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, 60 | fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, input { 61 | border: 0; 62 | outline: 0; 63 | font-size: 100%; 64 | vertical-align: baseline; 65 | background: transparent; 66 | font-family: 'Roboto', sans-serif; 67 | } 68 | 69 | body { 70 | font-size: 14px; 71 | font-family: 'Roboto', sans-serif; 72 | } 73 | 74 | button { 75 | border: none; 76 | padding: 0; 77 | font-size: 0; 78 | line-height: 0; 79 | background: none; 80 | cursor: pointer; 81 | } 82 | 83 | body { 84 | line-height: 1; 85 | } 86 | 87 | ol, ul { 88 | list-style: none; 89 | } 90 | 91 | blockquote, q { 92 | quotes: none; 93 | } 94 | 95 | blockquote:before, blockquote:after, 96 | q:before, q:after { 97 | content: ''; 98 | } 99 | 100 | /* remember to define focus styles! */ 101 | :focus { 102 | outline: 0; 103 | } 104 | 105 | /* remember to highlight inserts somehow! */ 106 | ins { 107 | text-decoration: none; 108 | } 109 | 110 | del { 111 | text-decoration: line-through; 112 | } 113 | 114 | /* tables still need 'cellspacing="0"' in the markup */ 115 | table { 116 | border-collapse: collapse; 117 | border-spacing: 0; 118 | } 119 | 120 | /*------------------------------------------------------------------*/ 121 | img { 122 | line-height: 0; 123 | font-size: 0; 124 | } 125 | 126 | menu { 127 | padding: 0; 128 | margin: 0; 129 | } 130 | 131 | .clearfix:before, .clearfix:after { 132 | content: "\0020"; 133 | display: block; 134 | height: 0; 135 | visibility: hidden; 136 | } 137 | 138 | .clearfix:after { 139 | clear: both; 140 | } 141 | 142 | .clearfix { 143 | zoom: 1; 144 | } 145 | 146 | /**********************************/ 147 | /* END RESET.CSS */ 148 | /**********************************/ 149 | -------------------------------------------------------------------------------- /front2/src/assets/js/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Affix 3 | * Nav menu mobile 4 | /*/ 5 | /* ***************** start document load **********************/ 6 | $(document).ready(function() { 7 | "use strict"; 8 | /* 9 | |-------------------------------------------------------------------------- 10 | | Affix 11 | |-------------------------------------------------------------------------- 12 | | 13 | | 14 | | 15 | */ 16 | $('#myAffix').affix({ 17 | offset: { 18 | top: 100, 19 | bottom: function() { 20 | return (this.bottom = $('.footer').outerHeight(true)) 21 | } 22 | } 23 | }); 24 | /* header select city */ 25 | $('.weather__city__list ul li').on('click', function(e) { 26 | e.preventDefault(); 27 | 28 | $(this).siblings("li").removeClass("active"); 29 | $(this).removeClass('active').addClass('active'); 30 | 31 | var text = $(this).find("a").html(); 32 | $(".weather__city").find('em').html(text); 33 | return false; 34 | }); 35 | /* end header select city */ 36 | }); 37 | /* ***************** end document load *********************/ 38 | /* ************** start document resize ********************/ 39 | $(window).resize(function() { 40 | "use strict"; 41 | var height = 0; 42 | var autoHeight1 = $('.col-img_auto-height'); 43 | var autoHeight2 = $('.col-border_auto-height'); 44 | var autoHeight3 = $('.col-border_auto-height-small'); 45 | $(autoHeight1).css('height', 'auto'); 46 | autoHeight1.each(function() { 47 | if ($(this).height() > height) { 48 | height = $(this).height(); 49 | } 50 | }); 51 | autoHeight1.css('height', height); 52 | 53 | $(autoHeight2).css('height', 'auto'); 54 | autoHeight2.each(function() { 55 | 56 | if ($(this).height() > height) { 57 | height = $(this).height(); 58 | } 59 | }); 60 | autoHeight2.css('height', height + 25); 61 | 62 | $(autoHeight3).css('height', 'auto'); 63 | autoHeight3.each(function() { 64 | 65 | if ($(this).height() > height) { 66 | height = $(this).height(); 67 | } 68 | }); 69 | autoHeight3.css('height', height + 25); 70 | }).resize(); 71 | /* **************** end document resize ********************/ 72 | $(function() { 73 | "use strict"; 74 | /* 75 | |-------------------------------------------------------------------------- 76 | | Nav menu mobile 77 | |-------------------------------------------------------------------------- 78 | | 79 | | 80 | | 81 | */ 82 | var $menu = $(".overlapblackbg, .slideLeft"); 83 | var $wsmenucontent = $(".wsmenucontent"); 84 | var openMenu = function() { 85 | $($menu).removeClass("menuclose").addClass("menuopen") 86 | }; 87 | var closeMenu = function() { 88 | $($menu).removeClass("menuopen").addClass("menuclose") 89 | }; 90 | $("#navToggle").on('click', function() { 91 | if ($wsmenucontent.hasClass("menuopen")) { 92 | $(closeMenu) 93 | } else { 94 | $(openMenu) 95 | } 96 | return false; 97 | }); 98 | $wsmenucontent.on('click', function() { 99 | if ($wsmenucontent.hasClass("menuopen")) { 100 | $(closeMenu) 101 | } 102 | return false; 103 | }); 104 | $("#navToggle,.overlapblackbg").on('click', function() { 105 | $(".wsmenucontainer").toggleClass("mrginleft"); 106 | return false; 107 | }); 108 | $(".wsmenu-list li").has(".wsmenu-submenu, .wsmenu-submenu-sub, .wsmenu-submenu-sub-sub").prepend(''); 109 | $(".wsmenu-list li").has(".megamenu").prepend(''); 110 | $(".wsmenu-mobile").on('click', function() { 111 | $(".wsmenu-list").slideToggle("slow"); 112 | return false; 113 | }); 114 | $(".wsmenu-click").on('click', function() { 115 | $(this).siblings(".wsmenu-submenu").slideToggle("slow"); 116 | $(this).children(".wsmenu-arrow").toggleClass("wsmenu-rotate"); 117 | $(this).siblings(".wsmenu-submenu-sub").slideToggle("slow"); 118 | $(this).siblings(".wsmenu-submenu-sub-sub").slideToggle("slow"); 119 | $(this).siblings(".megamenu").slideToggle("slow"); 120 | return false; 121 | }); 122 | }); -------------------------------------------------------------------------------- /admin/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 23 | 82 | 83 | 125 | -------------------------------------------------------------------------------- /server/controllers/type.js: -------------------------------------------------------------------------------- 1 | const Model = require('../lib/mysql.js'); 2 | const createToken = require('../token/createToken.js'); 3 | const moment = require('moment') 4 | class TypeController{ 5 | //用户登录 6 | static async createType(ctx){ 7 | //es6 解构 8 | //这里解构需要和 ctx.request.body 的对象属性相同 9 | const { typeName } = ctx.request.body; 10 | let time = moment().format('YYYY-MM-DD HH:mm') 11 | if(!typeName){ 12 | ctx.jsonReturn({ 13 | code:1, 14 | msg:'栏目不能为空' 15 | }) 16 | } 17 | await Model.selectTypeModelByType(typeName) 18 | .then(seleInfo=>{ 19 | var selectInfoRes = JSON.parse(JSON.stringify(seleInfo)) 20 | 21 | if(selectInfoRes.length > 0){ 22 | ctx.jsonReturn({ 23 | code:1, 24 | msg:'添加失败,该栏目已存在' 25 | }) 26 | }else{ 27 | let inserInfo = Model.addTypeModel([typeName,time]) 28 | if(inserInfo){ 29 | ctx.jsonReturn({ 30 | code:2, 31 | msg:'添加栏目成功' 32 | }) 33 | }else{ 34 | ctx.jsonReturn({ 35 | code:1, 36 | msg:'添加栏目失败' 37 | }) 38 | } 39 | } 40 | }).catch(err=>{ 41 | ctx.jsonReturn({ 42 | code:1, 43 | msg:'添加失败,服务器异常' 44 | }) 45 | }) 46 | } 47 | static async selectTypeAll(ctx){ 48 | await Model.selectTypeAllModel() 49 | .then(res=>{ 50 | var selectAllInfo = JSON.parse(JSON.stringify(res)) 51 | if(selectAllInfo.length > 0){ 52 | ctx.jsonReturn({ 53 | code:2, 54 | data:{ 55 | list:selectAllInfo 56 | }, 57 | msg:'ok' 58 | }) 59 | }else{ 60 | ctx.jsonReturn({ 61 | code:1, 62 | data:{ 63 | list:[] 64 | }, 65 | msg:'err' 66 | }) 67 | } 68 | }).catch(err=>{ 69 | ctx.jsonReturn({ 70 | code:1, 71 | data:{ 72 | list:[] 73 | }, 74 | msg:err 75 | }) 76 | }) 77 | 78 | } 79 | static async delectTypeById (ctx){ 80 | const {id} = ctx.request.body; 81 | if(!id || isNaN(id)){ 82 | ctx.jsonReturn({ 83 | code:1, 84 | data:[], 85 | msg:'id err' 86 | }) 87 | }else{ 88 | await Model.delTypeByIdModel(id) 89 | .then(res=>{ 90 | console.log( res ); 91 | if(res){ 92 | ctx.jsonReturn({ 93 | code:2, 94 | msg:'删除成功' 95 | }) 96 | }else{ 97 | ctx.jsonReturn({ 98 | code:1, 99 | msg:'删除失败' 100 | }) 101 | } 102 | }).catch(err=>{ 103 | ctx.jsonReturn({ 104 | code:1, 105 | msg:'删除失败,服务器异常' 106 | }) 107 | }) 108 | } 109 | } 110 | static async editTypeById(ctx){ 111 | const { typeName,id } = ctx.request.body; 112 | if(!id || isNaN(id)){ 113 | ctx.jsonReturn({ 114 | code:1, 115 | msg:'id err' 116 | }) 117 | } 118 | let time = moment().format('YYYY-MM-DD HH:mm') 119 | await Model.editTypeByIdModel([typeName,time,id]) 120 | .then(res=>{ 121 | if(res){ 122 | ctx.jsonReturn({ 123 | code:2, 124 | msg:'更新栏目成功' 125 | }) 126 | }else{ 127 | ctx.jsonReturn({ 128 | code:1, 129 | msg:'更新栏目失败' 130 | }) 131 | } 132 | }).catch(err=>{ 133 | ctx.jsonReturn({ 134 | code:1, 135 | msg:'服务异常,更新失败' 136 | }) 137 | }) 138 | } 139 | } 140 | module.exports = TypeController; -------------------------------------------------------------------------------- /admin/src/components/Article.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 145 | 146 | 155 | -------------------------------------------------------------------------------- /front2/src/components/Search.vue: -------------------------------------------------------------------------------- 1 | 56 | 91 | 119 | -------------------------------------------------------------------------------- /admin/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 | 13 | const env = require('../config/prod.env') 14 | 15 | const webpackConfig = merge(baseWebpackConfig, { 16 | module: { 17 | rules: utils.styleLoaders({ 18 | sourceMap: config.build.productionSourceMap, 19 | extract: true, 20 | usePostCSS: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify 35 | new webpack.optimize.UglifyJsPlugin({ 36 | compress: { 37 | warnings: false 38 | }, 39 | sourceMap: config.build.productionSourceMap, 40 | parallel: true 41 | }), 42 | // extract css into its own file 43 | new ExtractTextPlugin({ 44 | filename: utils.assetsPath('css/[name].[contenthash].css'), 45 | // set the following option to `true` if you want to extract CSS from 46 | // codesplit chunks into this main css file as well. 47 | // This will result in *all* of your app's CSS being loaded upfront. 48 | allChunks: false, 49 | }), 50 | // Compress extracted CSS. We are using this plugin so that possible 51 | // duplicated CSS from different components can be deduped. 52 | new OptimizeCSSPlugin({ 53 | cssProcessorOptions: config.build.productionSourceMap 54 | ? { safe: true, map: { inline: false } } 55 | : { safe: true } 56 | }), 57 | // generate dist index.html with correct asset hash for caching. 58 | // you can customize output by editing /index.html 59 | // see https://github.com/ampedandwired/html-webpack-plugin 60 | new HtmlWebpackPlugin({ 61 | filename: config.build.index, 62 | template: 'index.html', 63 | inject: true, 64 | minify: { 65 | removeComments: true, 66 | collapseWhitespace: true, 67 | removeAttributeQuotes: true 68 | // more options: 69 | // https://github.com/kangax/html-minifier#options-quick-reference 70 | }, 71 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 72 | chunksSortMode: 'dependency' 73 | }), 74 | // keep module.id stable when vender modules does not change 75 | new webpack.HashedModuleIdsPlugin(), 76 | // enable scope hoisting 77 | new webpack.optimize.ModuleConcatenationPlugin(), 78 | // split vendor js into its own file 79 | new webpack.optimize.CommonsChunkPlugin({ 80 | name: 'vendor', 81 | minChunks: function (module) { 82 | // any required modules inside node_modules are extracted to vendor 83 | return ( 84 | module.resource && 85 | /\.js$/.test(module.resource) && 86 | module.resource.indexOf( 87 | path.join(__dirname, '../node_modules') 88 | ) === 0 89 | ) 90 | } 91 | }), 92 | // extract webpack runtime and module manifest to its own file in order to 93 | // prevent vendor hash from being updated whenever app bundle is updated 94 | new webpack.optimize.CommonsChunkPlugin({ 95 | name: 'manifest', 96 | minChunks: Infinity 97 | }), 98 | // This instance extracts shared chunks from code splitted chunks and bundles them 99 | // in a separate chunk, similar to the vendor chunk 100 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 101 | new webpack.optimize.CommonsChunkPlugin({ 102 | name: 'app', 103 | async: 'vendor-async', 104 | children: true, 105 | minChunks: 3 106 | }), 107 | 108 | // copy custom static assets 109 | new CopyWebpackPlugin([ 110 | { 111 | from: path.resolve(__dirname, '../static'), 112 | to: config.build.assetsSubDirectory, 113 | ignore: ['.*'] 114 | } 115 | ]) 116 | ] 117 | }) 118 | 119 | if (config.build.productionGzip) { 120 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 121 | 122 | webpackConfig.plugins.push( 123 | new CompressionWebpackPlugin({ 124 | asset: '[path].gz[query]', 125 | algorithm: 'gzip', 126 | test: new RegExp( 127 | '\\.(' + 128 | config.build.productionGzipExtensions.join('|') + 129 | ')$' 130 | ), 131 | threshold: 10240, 132 | minRatio: 0.8 133 | }) 134 | ) 135 | } 136 | 137 | if (config.build.bundleAnalyzerReport) { 138 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 139 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 140 | } 141 | 142 | module.exports = webpackConfig 143 | -------------------------------------------------------------------------------- /front2/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 | 13 | const env = require('../config/prod.env') 14 | 15 | const webpackConfig = merge(baseWebpackConfig, { 16 | module: { 17 | rules: utils.styleLoaders({ 18 | sourceMap: config.build.productionSourceMap, 19 | extract: true, 20 | usePostCSS: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify 35 | new webpack.optimize.UglifyJsPlugin({ 36 | compress: { 37 | warnings: false 38 | }, 39 | sourceMap: config.build.productionSourceMap, 40 | parallel: true 41 | }), 42 | // extract css into its own file 43 | new ExtractTextPlugin({ 44 | filename: utils.assetsPath('css/[name].[contenthash].css'), 45 | // set the following option to `true` if you want to extract CSS from 46 | // codesplit chunks into this main css file as well. 47 | // This will result in *all* of your app's CSS being loaded upfront. 48 | allChunks: false, 49 | }), 50 | // Compress extracted CSS. We are using this plugin so that possible 51 | // duplicated CSS from different components can be deduped. 52 | new OptimizeCSSPlugin({ 53 | cssProcessorOptions: config.build.productionSourceMap 54 | ? { safe: true, map: { inline: false } } 55 | : { safe: true } 56 | }), 57 | // generate dist index.html with correct asset hash for caching. 58 | // you can customize output by editing /index.html 59 | // see https://github.com/ampedandwired/html-webpack-plugin 60 | new HtmlWebpackPlugin({ 61 | filename: config.build.index, 62 | template: 'index.html', 63 | inject: true, 64 | minify: { 65 | removeComments: true, 66 | collapseWhitespace: true, 67 | removeAttributeQuotes: true 68 | // more options: 69 | // https://github.com/kangax/html-minifier#options-quick-reference 70 | }, 71 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 72 | chunksSortMode: 'dependency' 73 | }), 74 | // keep module.id stable when vender modules does not change 75 | new webpack.HashedModuleIdsPlugin(), 76 | // enable scope hoisting 77 | new webpack.optimize.ModuleConcatenationPlugin(), 78 | // split vendor js into its own file 79 | new webpack.optimize.CommonsChunkPlugin({ 80 | name: 'vendor', 81 | minChunks: function (module) { 82 | // any required modules inside node_modules are extracted to vendor 83 | return ( 84 | module.resource && 85 | /\.js$/.test(module.resource) && 86 | module.resource.indexOf( 87 | path.join(__dirname, '../node_modules') 88 | ) === 0 89 | ) 90 | } 91 | }), 92 | // extract webpack runtime and module manifest to its own file in order to 93 | // prevent vendor hash from being updated whenever app bundle is updated 94 | new webpack.optimize.CommonsChunkPlugin({ 95 | name: 'manifest', 96 | minChunks: Infinity 97 | }), 98 | // This instance extracts shared chunks from code splitted chunks and bundles them 99 | // in a separate chunk, similar to the vendor chunk 100 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 101 | new webpack.optimize.CommonsChunkPlugin({ 102 | name: 'app', 103 | async: 'vendor-async', 104 | children: true, 105 | minChunks: 3 106 | }), 107 | 108 | // copy custom static assets 109 | new CopyWebpackPlugin([ 110 | { 111 | from: path.resolve(__dirname, '../static'), 112 | to: config.build.assetsSubDirectory, 113 | ignore: ['.*'] 114 | } 115 | ]) 116 | ] 117 | }) 118 | 119 | if (config.build.productionGzip) { 120 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 121 | 122 | webpackConfig.plugins.push( 123 | new CompressionWebpackPlugin({ 124 | asset: '[path].gz[query]', 125 | algorithm: 'gzip', 126 | test: new RegExp( 127 | '\\.(' + 128 | config.build.productionGzipExtensions.join('|') + 129 | ')$' 130 | ), 131 | threshold: 10240, 132 | minRatio: 0.8 133 | }) 134 | ) 135 | } 136 | 137 | if (config.build.bundleAnalyzerReport) { 138 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 139 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 140 | } 141 | 142 | module.exports = webpackConfig 143 | -------------------------------------------------------------------------------- /admin/src/components/EditArticle.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 160 | 161 | 170 | -------------------------------------------------------------------------------- /front2/src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 91 | 123 | 126 | -------------------------------------------------------------------------------- /server/lib/mysql.js: -------------------------------------------------------------------------------- 1 | var mysql = require('mysql'); 2 | var config = require('../config/index.js') 3 | 4 | //定义pool池 5 | var pool = mysql.createPool({ 6 | host : config.dev.database.HOST, 7 | user : config.dev.database.USERNAME, 8 | password : config.dev.database.PASSWORD, 9 | database : config.dev.database.DATABASE 10 | }); 11 | 12 | let query = function( sql, values ) { 13 | 14 | return new Promise(( resolve, reject ) => { 15 | pool.getConnection(function(err, connection) { 16 | if (err) { 17 | resolve( err ) 18 | } else { 19 | connection.query(sql, values, ( err, rows) => { 20 | if ( err ) { 21 | reject( err ) 22 | } else { 23 | resolve( rows ) 24 | } 25 | //回收pool 26 | connection.release() 27 | }) 28 | } 29 | }) 30 | }) 31 | 32 | } 33 | //登录查询用户 34 | let dologin = function(username,password){ 35 | let _sql = `select * from user where username = "${username}" and password = "${password}"`; 36 | return query(_sql) 37 | } 38 | //登录更新token 39 | let updateUserToken = function(value){ 40 | let _sql = "update user set token = ? where id = ?;" 41 | return query(_sql,value) 42 | } 43 | 44 | //添加文章 45 | let addArticlModel = function(value){ 46 | let _sql = "insert into article (typeid,title,tags,thumb,content,time) values(?,?,?,?,?,?)" 47 | return query(_sql,value) 48 | } 49 | //查询栏目下的文章是否存在 50 | let selectTitleById = function(title,type){ 51 | let _sql = `select * from article where title = "${title}" and typeid = "${type}" `; 52 | return query(_sql) 53 | } 54 | //添加栏目 55 | let addTypeModel = function(value){ 56 | let _sql = 'insert into types (type_name,time) values(?,?)' 57 | return query(_sql,value) 58 | } 59 | //根据栏目名称查询 60 | let selectTypeModelByType = function(value){ 61 | let _sql = `select * from types where type_name ="${value}" `; 62 | return query(_sql) 63 | } 64 | 65 | //查询所有的栏目 66 | let selectTypeAllModel = function(){ 67 | let _sql = `select * from types`; 68 | return query(_sql) 69 | } 70 | //删除栏目根据栏目id 71 | let delTypeByIdModel = function(id){ 72 | let _sql = `delete from types where id = "${id}"`; 73 | return query(_sql) 74 | } 75 | let editTypeByIdModel = function(value){ 76 | let _sql = "update types set type_name = ?,time = ? where id = ?"; 77 | return query(_sql,value) 78 | } 79 | //获取所有文章 80 | let selectAllArticleModel = function(value){ 81 | let _sql = `select * from article`; 82 | return query(_sql) 83 | } 84 | 85 | //根据id查找文章 86 | let selectArticleByIdModel = function(id){ 87 | let _sql = `select * from article where id = "${id}"`; 88 | return query(_sql); 89 | } 90 | 91 | //文章编辑 92 | let updateArticleModel = function(value){ 93 | let _sql = "update article set typeid = ?,title = ?,tags = ?,thumb = ? ,content = ?,time = ? where id =?" 94 | return query(_sql,value) 95 | } 96 | //文章公开和禁止切换 97 | let updateArticleStateModel = function(value){ 98 | let _sql = "update article set is_public = ? where id = ?" 99 | return query(_sql,value) 100 | } 101 | 102 | //删除文章 103 | let delArticleByIdModel = function(id){ 104 | let _sql = `delete from article where id = "${id}"`; 105 | return query(_sql) 106 | } 107 | 108 | //推荐文章 109 | let articleRecommendModel = function(data){ 110 | let _sql = "update article set recommend = ? where id = ?" 111 | return query(_sql,data) 112 | } 113 | //设置轮播 114 | let setSliderModel = function(data){ 115 | let _sql = "update article set is_slider = ? where id =?" 116 | return query(_sql,data) 117 | } 118 | 119 | 120 | 121 | //前台 122 | let selectArticleByTypeIdModel = function(tid,page){ 123 | let _sql = `select * from article where typeid = "${tid}" limit ${(page-1)*8},8;` 124 | return query(_sql); 125 | } 126 | 127 | //根据tid 查询热门文章 128 | let selectArticleByTypeHotModel = function(id){ 129 | let _sql = ''; 130 | if(id){ 131 | _sql = `SELECT * FROM article where typeid = "${id}" ORDER BY consult DESC limit 0,4` 132 | }else{ 133 | _sql = `SELECT * FROM article ORDER BY consult DESC limit 0,4` 134 | } 135 | return query(_sql) 136 | } 137 | 138 | //查找推荐文章一条 139 | let getRecommendModel = function(id){ 140 | let _sql = ''; 141 | if(id){ 142 | _sql = `SELECT * FROM article where typeid = "${id}" and recommend = 1 ORDER BY recommend DESC limit 0,1` 143 | }else{ 144 | _sql = `SELECT * FROM article where recommend = 1 ORDER BY recommend DESC limit 0,1` 145 | } 146 | return query(_sql) 147 | } 148 | 149 | let updateConsultModel = function(data){ 150 | let _sql = 'update article set consult = ? where id = ?' 151 | return query(_sql,data) 152 | } 153 | 154 | //查询轮播 155 | let selectSliderModel = function(){ 156 | let _sql = `SELECT * FROM article where is_slider = 1 ORDER BY is_slider DESC limit 0,4` 157 | return query(_sql) 158 | } 159 | //首页列表(不包括 热门和轮播 ) 160 | let homeListModel = function(page){ 161 | let _sql = `SELECT * FROM article where is_slider = 0 and recommend = 0 ORDER BY time DESC limit ${(page-1)*4},4;` 162 | return query(_sql) 163 | } 164 | 165 | //首页类型 (3条输出) 166 | let homeTypeListModel = function(){ 167 | let _sql = `SELECT * FROM article where is_public = 1 ORDER BY time DESC`; 168 | return query(_sql) 169 | } 170 | 171 | //首页最新文章 172 | let newArticleListModel = function(){ 173 | let _sql = `SELECT * FROM article where is_public = 1 ORDER BY time DESC limit 0,3`; 174 | return query(_sql) 175 | } 176 | 177 | //搜索 178 | let searchArticleListModel = function(title,page){ 179 | let _sql = `SELECT * FROM article where is_public = 1 and title like "%${title}%" ORDER BY time DESC limit ${(page-1)*12},12`; 180 | return query(_sql) 181 | } 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | module.exports = { 194 | dologin, 195 | updateUserToken, 196 | addArticlModel, 197 | addTypeModel, 198 | selectTypeModelByType, 199 | selectTypeAllModel, 200 | delTypeByIdModel, 201 | editTypeByIdModel, 202 | selectTitleById, 203 | selectAllArticleModel, 204 | selectArticleByIdModel, 205 | updateArticleModel, 206 | updateArticleStateModel, 207 | delArticleByIdModel, 208 | selectArticleByTypeIdModel, 209 | selectArticleByTypeHotModel, 210 | articleRecommendModel, 211 | getRecommendModel, 212 | updateConsultModel, 213 | setSliderModel, 214 | selectSliderModel, 215 | homeListModel, 216 | homeTypeListModel, 217 | newArticleListModel, 218 | searchArticleListModel 219 | 220 | } -------------------------------------------------------------------------------- /admin/src/components/Types.vue: -------------------------------------------------------------------------------- 1 | 58 | 191 | 206 | -------------------------------------------------------------------------------- /admin/src/components/List.vue: -------------------------------------------------------------------------------- 1 | 66 | 203 | 213 | -------------------------------------------------------------------------------- /front2/src/components/Detail.vue: -------------------------------------------------------------------------------- 1 | 95 | 151 | 154 | -------------------------------------------------------------------------------- /front2/src/assets/css/typography.css: -------------------------------------------------------------------------------- 1 | /*********************************** 2 | /* GUI */ 3 | /***********************************/ 4 | /* extend class */ 5 | .font, .headline__category, .article__text, .article__time, .projects__title__h2, .block-title__view-all, .projects__title__view-all, .twitter__header__name, .twitter__header__text, .message__time, .message__text, .message__link, .title__h1, .redaction__category, .redaction__title, .redaction__text, .redaction__time, .comments .media-middle, .comments .media-body, .current__text, .weather__temperature em, .weather__city__list li a, .exchange__name, .exchange__course, .exchange__course span { 6 | font-size: 14px; 7 | line-height: 1.42857143; 8 | font-weight: normal; 9 | font-family: 'Roboto', sans-serif; 10 | color: #4e4e4e; 11 | } 12 | 13 | .transition, .wsmenu-list > li > a::before, .thumbnail__link img, .banner__link, .nav-tabs > li > a, .share .bg, .share .bg_facebook, .share .bg_twitter, .share .bg_google, .share__link, .tags ul li a, .message__link, .btn-comment, .error__right .error__list li .link::after, .social__item .facebook, .social__item .twitter, .social__item .gplus, .weather__city__list li, .weather__city__list li a { 14 | -webkit-transition-delay: 0s; 15 | transition-delay: 0s; 16 | -webkit-transition-duration: .2s; 17 | transition-duration: .2s; 18 | -webkit-transition-timing-function: ease-in-out; 19 | transition-timing-function: ease-in-out; 20 | } 21 | 22 | .absolute-arrow, .weather__city em::before, .exchange__course span::before { 23 | position: absolute; 24 | content: ''; 25 | top: 50%; 26 | right: -15px; 27 | width: 9px; 28 | height: 9px; 29 | margin-top: -7px; 30 | border-width: 2px; 31 | border-style: solid; 32 | border-color: transparent; 33 | -webkit-transform: rotate(-45deg); 34 | transform: rotate(-45deg); 35 | background-color: transparent; 36 | } 37 | 38 | /* end extend class */ 39 | /* button ajax_load*/ 40 | .ajax_load { 41 | position: relative; 42 | width: 128px; 43 | height: 40px; 44 | margin: 0 auto; 45 | font-family: 'Open Sans', sans-serif; 46 | font-size: 14px; 47 | line-height: 40px; 48 | text-align: center; 49 | cursor: pointer; 50 | background: #ffffff; 51 | color: #4e4e4e; 52 | } 53 | 54 | .ajax_load i { 55 | display: inline-block; 56 | -webkit-transition-delay: 0s; 57 | transition-delay: 0s; 58 | -webkit-transition-timing-function: ease-out; 59 | transition-timing-function: ease-out; 60 | -webkit-transition-property: -webkit-transform; 61 | transition-property: -webkit-transform; 62 | transition-property: transform; 63 | transition-property: transform, -webkit-transform; 64 | -webkit-transition-duration: 1.2s; 65 | transition-duration: 1.2s; 66 | } 67 | 68 | .ajax_load:hover i { 69 | -webkit-transform: rotate(720deg); 70 | transform: rotate(720deg); 71 | } 72 | 73 | svg { 74 | position: absolute; 75 | top: 0; 76 | left: 0; 77 | } 78 | 79 | svg rect, svg path, svg polyline { 80 | fill: none; 81 | stroke: #4e4e4e; 82 | stroke-width: 1; 83 | } 84 | 85 | .ajax_load:hover svg rect { 86 | stroke: #4e4e4e; 87 | } 88 | 89 | /* end button ajax_load*/ 90 | /* Basic Box */ 91 | svg rect { 92 | stroke-dasharray: 400, 0; 93 | -webkit-transition: all 0.8s ease-in-out; 94 | -moz-transition: all 0.8s ease-in-out; 95 | -ms-transition: all 0.8s ease-in-out; 96 | -o-transition: all 0.8s ease-in-out; 97 | } 98 | 99 | .ajax_load:hover svg rect { 100 | stroke-width: 3; 101 | stroke-dasharray: 35, 245; 102 | stroke-dashoffset: 64; 103 | -webkit-transition: all 0.8s ease-in-out; 104 | -moz-transition: all 0.8s ease-in-out; 105 | -ms-transition: all 0.8s ease-in-out; 106 | -o-transition: all 0.8s ease-in-out; 107 | } 108 | 109 | /* form hover effect */ 110 | .form-control::-webkit-input-placeholder { 111 | text-indent: 10px; 112 | -webkit-transition: text-indent 0.4s ease-in; 113 | transition: text-indent 0.4s ease-in; 114 | } 115 | 116 | .form-control:focus::-webkit-input-placeholder { 117 | text-indent: 999px; 118 | } 119 | 120 | /* end form hover effect */ 121 | /* yellow line in title word*/ 122 | .yel_line, .yellow-line, .underscore { 123 | position: relative; 124 | } 125 | 126 | .yel_line::after, .yellow-line::after, .underscore::after { 127 | position: absolute; 128 | content: ''; 129 | bottom: -2px; 130 | left: 0; 131 | background: #f38844; 132 | height: 2px; 133 | width: 30px; 134 | } 135 | 136 | /* end yellow line in title word*/ 137 | /* thumbnail image hover */ 138 | .thumbnail__link { 139 | position: relative; 140 | } 141 | 142 | .thumbnail__link::before { 143 | position: absolute; 144 | top: 0; 145 | left: -75%; 146 | z-index: 2; 147 | display: block; 148 | content: ''; 149 | width: 50%; 150 | height: 100%; 151 | background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 100%); 152 | background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.3) 100%); 153 | -webkit-transform: skewX(-25deg); 154 | transform: skewX(-25deg); 155 | } 156 | 157 | .thumbnail__link:hover::before { 158 | -webkit-animation: shine .75s; 159 | animation: shine .75s; 160 | } 161 | 162 | @-webkit-keyframes shine { 163 | 100% { 164 | left: 125%; 165 | } 166 | } 167 | 168 | @keyframes shine { 169 | 100% { 170 | left: 125%; 171 | } 172 | } 173 | /* end thumbnail image hover */ 174 | .container { 175 | padding-right: 10px !important; 176 | padding-left: 10px !important; 177 | } 178 | 179 | .row { 180 | margin-right: -10px !important; 181 | margin-left: -10px !important; 182 | } 183 | 184 | .col-lg-1, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-md-1, .col-md-10, .col-md-11, .col-md-12, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-sm-1, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-xs-1, .col-xs-10, .col-xs-11, .col-xs-12, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9 { 185 | padding-right: 10px !important; 186 | padding-left: 10px !important; 187 | } 188 | /*********************************** 189 | /* END GUI */ 190 | /***********************************/ 191 | 192 | /*********************************** 193 | /* FONTS */ 194 | /***********************************/ 195 | /* SVG - fonts */ 196 | @font-face { 197 | font-family: 'fontello'; 198 | src: url("../fonts/fontello.eot?44020949"); 199 | src: url("../fonts/fontello.eot?44020949#iefix") format("embedded-opentype"), url("../fonts/fontello.woff?44020949") format("woff"), url("../fonts/fontello.ttf?44020949") format("truetype"); 200 | font-weight: normal; 201 | font-style: normal; 202 | } 203 | 204 | [class^="icon-"]:before, [class*=" icon-"]:before { 205 | font-family: "fontello"; 206 | font-style: normal; 207 | font-weight: normal; 208 | speak: none; 209 | display: inline-block; 210 | text-decoration: inherit; 211 | width: 1em; 212 | margin-right: .2em; 213 | text-align: center; 214 | /* opacity: .8; */ 215 | /* For safety - reset parent styles, that can break glyph codes*/ 216 | font-variant: normal; 217 | text-transform: none; 218 | /* fix buttons height, for twitter bootstrap */ 219 | line-height: 1em; 220 | /* Animation center compensation - margins should be symmetric */ 221 | /* remove if not needed */ 222 | margin-left: .2em; 223 | /* you can be more comfortable with increased icons size */ 224 | /* font-size: 120%; */ 225 | /* Font smoothing. That was taken from TWBS */ 226 | -webkit-font-smoothing: antialiased; 227 | -moz-osx-font-smoothing: grayscale; 228 | /* Uncomment for 3D effect */ 229 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 230 | } 231 | 232 | .icon-sun:before { 233 | content: '\e800'; 234 | } 235 | 236 | .icon-euro:before { 237 | content: '\e801'; 238 | } 239 | 240 | .icon-search:before { 241 | content: '\e802'; 242 | } 243 | 244 | .icon-down-open:before { 245 | content: '\e803'; 246 | } 247 | 248 | .icon-up-open:before { 249 | content: '\e804'; 250 | } 251 | 252 | .icon-play:before { 253 | content: '\e805'; 254 | } 255 | 256 | .icon-facebook:before { 257 | content: '\e806'; 258 | } 259 | 260 | .icon-gplus:before { 261 | content: '\e807'; 262 | } 263 | 264 | .icon-twitter:before { 265 | content: '\e808'; 266 | } 267 | 268 | .icon-eye:before { 269 | content: '\e809'; 270 | } 271 | 272 | .icon-comment-empty:before { 273 | content: '\e80a'; 274 | } 275 | 276 | .icon-reply:before { 277 | content: '\e80b'; 278 | } 279 | 280 | .icon-arrows-cw:before { 281 | content: '\e80c'; 282 | } 283 | 284 | /* end SVG - fonts */ 285 | /* fonts (в head подключен шрифт с Google-fonts) */ 286 | 287 | /*********************************** 288 | /* END FONTS */ 289 | /***********************************/ 290 | 291 | -------------------------------------------------------------------------------- /server/controllers/front.js: -------------------------------------------------------------------------------- 1 | const Model = require('../lib/mysql.js'); 2 | const moment = require('moment') 3 | class FrontController{ 4 | //文章添加 5 | static async articleList(ctx){ 6 | //es6 解构 7 | //这里解构需要和 ctx.request.body 的对象属性相同 8 | const { tid,page } = ctx.request.body; 9 | let time = moment().format('YYYY-MM-DD HH:mm') 10 | if(!tid || isNaN(tid)){ 11 | ctx.jsonReturn({ 12 | code:1, 13 | msg:'id err' 14 | }) 15 | } 16 | if(!page || isNaN(page)){ 17 | page = 1 18 | } 19 | 20 | await Model.selectArticleByTypeIdModel(tid,page) 21 | .then(res=>{ 22 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 23 | if(selectInfoRes.length <= 0){ 24 | ctx.jsonReturn({ 25 | code:1, 26 | msg:'无数据' 27 | }) 28 | }else{ 29 | ctx.jsonReturn({ 30 | code:2, 31 | data:{ 32 | list:selectInfoRes 33 | }, 34 | msg:'ok' 35 | }) 36 | 37 | } 38 | }) 39 | .catch(err=>{ 40 | ctx.jsonReturn({ 41 | code:1, 42 | msg:'添加失败,服务器异常' 43 | }) 44 | }) 45 | 46 | } 47 | //查询阅读数前4的文章 48 | static async getMostConstuList(ctx){ 49 | const {pid} = ctx.request.body; 50 | if(!pid || isNaN(pid)){ 51 | ctx.jsonReturn({ 52 | code:1, 53 | msg:'pid err' 54 | }) 55 | } 56 | await Model.selectArticleByTypeHotModel(pid) 57 | .then(res=>{ 58 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 59 | if(selectInfoRes.length <= 0){ 60 | ctx.jsonReturn({ 61 | code:2, 62 | data:{ 63 | list:[] 64 | }, 65 | msg:'ok' 66 | }) 67 | }else{ 68 | ctx.jsonReturn({ 69 | code:2, 70 | data:{ 71 | list:selectInfoRes 72 | }, 73 | msg:'ok' 74 | }) 75 | 76 | } 77 | }) 78 | } 79 | static async getRecommend(ctx){ 80 | const {pid} = ctx.request.body; 81 | await Model.getRecommendModel(pid) 82 | .then(res=>{ 83 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 84 | if(selectInfoRes.length <= 0){ 85 | ctx.jsonReturn({ 86 | code:2, 87 | data:{ 88 | list:[] 89 | }, 90 | msg:'ok' 91 | }) 92 | }else{ 93 | ctx.jsonReturn({ 94 | code:2, 95 | data:{ 96 | list:selectInfoRes 97 | }, 98 | msg:'ok' 99 | }) 100 | 101 | } 102 | }) 103 | } 104 | 105 | //查找文章 106 | static async findArticleById(ctx){ 107 | const {id} = ctx.request.body; 108 | await Model.selectArticleByIdModel(id) 109 | .then(res=>{ 110 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 111 | if(selectInfoRes.length <= 0){ 112 | ctx.jsonReturn({ 113 | code:2, 114 | data:{ 115 | list:[] 116 | }, 117 | msg:'ok' 118 | }) 119 | }else{ 120 | ctx.jsonReturn({ 121 | code:2, 122 | data:{ 123 | list:selectInfoRes 124 | }, 125 | msg:'ok' 126 | }) 127 | 128 | } 129 | }) 130 | } 131 | static async updateConsult(ctx){ 132 | const{id,consult} = ctx.request.body; 133 | 134 | if(!id || isNaN(id)){ 135 | ctx.jsonReturn({ 136 | code:1, 137 | msg:'err' 138 | }) 139 | } 140 | await Model.updateConsultModel([consult,id]) 141 | .then(res=>{ 142 | if(res){ 143 | ctx.jsonReturn({ 144 | code:2, 145 | msg:'ok' 146 | }) 147 | }else{ 148 | ctx.jsonReturn({ 149 | code:1, 150 | msg:'err' 151 | }) 152 | } 153 | }) 154 | } 155 | //轮播查询 156 | static async selectSlider(ctx){ 157 | await Model.selectSliderModel() 158 | .then(res=>{ 159 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 160 | if(selectInfoRes.length <= 0){ 161 | ctx.jsonReturn({ 162 | code:2, 163 | data:{ 164 | list:[] 165 | }, 166 | msg:'ok' 167 | }) 168 | }else{ 169 | ctx.jsonReturn({ 170 | code:2, 171 | data:{ 172 | list:selectInfoRes 173 | }, 174 | msg:'ok' 175 | }) 176 | 177 | } 178 | }) 179 | } 180 | 181 | static async homeList(ctx){ 182 | const {page} = ctx.request.body; 183 | if(!page || isNaN(page)){ 184 | page = 1 185 | } 186 | await Model.homeListModel(page) 187 | .then(res=>{ 188 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 189 | if(selectInfoRes.length <= 0){ 190 | ctx.jsonReturn({ 191 | code:1, 192 | 193 | msg:'无数据' 194 | }) 195 | }else{ 196 | ctx.jsonReturn({ 197 | code:2, 198 | data:{ 199 | list:selectInfoRes 200 | }, 201 | msg:'ok' 202 | }) 203 | 204 | } 205 | }) 206 | } 207 | //最新文章 208 | static async newList(ctx){ 209 | await Model.newArticleListModel() 210 | .then(res=>{ 211 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 212 | if(selectInfoRes.length <= 0){ 213 | ctx.jsonReturn({ 214 | code:1, 215 | msg:'无数据' 216 | }) 217 | }else{ 218 | ctx.jsonReturn({ 219 | code:2, 220 | data:{ 221 | list:selectInfoRes 222 | }, 223 | msg:'ok' 224 | }) 225 | 226 | } 227 | }) 228 | } 229 | static async getTypeList(ctx){ 230 | let typeListArr = await Model.selectTypeAllModel() 231 | .then(typeRes=>{ 232 | var typeList = JSON.parse(JSON.stringify(typeRes)) 233 | return typeList 234 | }) 235 | await Model.homeTypeListModel() 236 | .then(res=>{ 237 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 238 | if(selectInfoRes.length <= 0){ 239 | ctx.jsonReturn({ 240 | code:2, 241 | data:{ 242 | list:[] 243 | }, 244 | msg:'无数据' 245 | }) 246 | }else{ 247 | 248 | // [ 249 | // { 250 | // type:'nodejs', 251 | // list:[] 252 | // } 253 | // ] 254 | 255 | let newList = []; 256 | typeListArr.forEach(type=>{ 257 | let itemArr = {type:type.type_name,list:[],typeid:type.id}; 258 | selectInfoRes.forEach(item=>{ 259 | if(type.id == item.typeid){ 260 | item['type_name'] = type.type_name 261 | itemArr.list.push(item) 262 | } 263 | }) 264 | newList.push(itemArr) 265 | }) 266 | ctx.jsonReturn({ 267 | code:2, 268 | data:{ 269 | list:newList 270 | }, 271 | msg:'ok' 272 | }) 273 | 274 | } 275 | }) 276 | } 277 | static async searchArticleList(ctx){ 278 | const {page,title} = ctx.request.body; 279 | 280 | if(!page || isNaN(page)){ 281 | ctx.jsonReturn({ 282 | code:1, 283 | msg:'err' 284 | }) 285 | } 286 | await Model.searchArticleListModel(title,page) 287 | .then(res=>{ 288 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 289 | if(selectInfoRes.length <= 0){ 290 | ctx.jsonReturn({ 291 | code:1, 292 | msg:'err2' 293 | }) 294 | }else{ 295 | ctx.jsonReturn({ 296 | code:2, 297 | data:{ 298 | list:selectInfoRes 299 | }, 300 | msg:'ok' 301 | }) 302 | 303 | } 304 | }) 305 | 306 | } 307 | 308 | 309 | } 310 | module.exports = FrontController; -------------------------------------------------------------------------------- /front2/src/components/List.vue: -------------------------------------------------------------------------------- 1 | 152 | 232 | 264 | -------------------------------------------------------------------------------- /server/controllers/article.js: -------------------------------------------------------------------------------- 1 | const Model = require('../lib/mysql.js'); 2 | const moment = require('moment') 3 | class ArticleController{ 4 | //文章添加 5 | static async createArticle(ctx){ 6 | //es6 解构 7 | //这里解构需要和 ctx.request.body 的对象属性相同 8 | const { title,content,tags,type,thumb } = ctx.request.body; 9 | let time = moment().format('YYYY-MM-DD HH:mm') 10 | if(!title){ 11 | ctx.jsonReturn({ 12 | code:1, 13 | msg:'标题不能为空' 14 | }) 15 | } 16 | if(!content){ 17 | ctx.jsonReturn({ 18 | code:1, 19 | msg:'内容不能为空' 20 | }) 21 | } 22 | if(!type){ 23 | ctx.jsonReturn({ 24 | code:1, 25 | msg:'类型不能为空' 26 | }) 27 | } 28 | await Model.selectTypeAllModel() 29 | .then(res=>{ 30 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 31 | if(selectInfoRes.length <= 0){ 32 | ctx.jsonReturn({ 33 | code:1, 34 | msg:'暂时无栏目,先添加栏目' 35 | }) 36 | }else{ 37 | var typeArr = []; 38 | selectInfoRes.forEach(function(item){ 39 | typeArr.push(item.id) 40 | }); 41 | function in_array(needle, haystack) { 42 | for(var i in haystack) { 43 | if(haystack[i] == needle) { 44 | return true; 45 | } 46 | } 47 | return false; 48 | } 49 | if(!in_array(type,typeArr)){ 50 | ctx.jsonReturn({ 51 | code:1, 52 | msg:'栏目id不合法' 53 | }) 54 | } 55 | let seleInfo = Model.selectTitleById(title,type); 56 | return seleInfo; 57 | } 58 | }) 59 | .then(seleInfo=>{ 60 | var seIn = JSON.parse(JSON.stringify(seleInfo)) 61 | if(seIn.length){ 62 | ctx.jsonReturn({ 63 | code:1, 64 | msg:'该栏目下已存在此文章' 65 | }) 66 | }else{ 67 | let addRes = Model.addArticlModel([type,title,tags,thumb,content,time]) 68 | if(addRes){ 69 | ctx.jsonReturn({ 70 | code:2, 71 | msg:'添加文章成功' 72 | }) 73 | }else{ 74 | ctx.jsonReturn({ 75 | code:1, 76 | msg:'失败' 77 | }) 78 | } 79 | } 80 | }) 81 | .catch(err=>{ 82 | ctx.jsonReturn({ 83 | code:1, 84 | msg:'添加失败,服务器异常' 85 | }) 86 | }) 87 | 88 | } 89 | static async getArticleList(ctx){ 90 | let resList = await Model.selectAllArticleModel() 91 | .then(res=>{ 92 | var resList = JSON.parse(JSON.stringify(res)) 93 | if(resList.length == 0){ 94 | ctx.jsonReturn({ 95 | code:1, 96 | data:{ 97 | list:[] 98 | }, 99 | msg:'err' 100 | }) 101 | } 102 | return resList; 103 | }) 104 | 105 | await Model.selectTypeAllModel() 106 | .then(typeListres=>{ 107 | var typeList = JSON.parse(JSON.stringify(typeListres)) 108 | let newList = resList.map(function(item,index){ 109 | typeList.forEach(function(item2,index){ 110 | if(item['typeid'] == item2['id']){ 111 | item['type_name'] = item2['type_name'] 112 | } 113 | if(item['is_public'] == 1){ 114 | item['public_state'] = '发布' 115 | }else{ 116 | item['public_state'] = '未发布' 117 | } 118 | }) 119 | return item; 120 | }) 121 | ctx.jsonReturn({ 122 | code:2, 123 | data:{ 124 | list:newList 125 | }, 126 | msg:'ok' 127 | }) 128 | }).catch(err=>{ 129 | ctx.jsonReturn({ 130 | code:1, 131 | data:{ 132 | list:[] 133 | }, 134 | msg:'err' 135 | }) 136 | }) 137 | 138 | } 139 | static async getArticle(ctx){ 140 | const{ id } = ctx.request.body; 141 | console.log( id ); 142 | if(!id || isNaN(id)){ 143 | ctx.jsonReturn({ 144 | code:1, 145 | msg:'id err' 146 | }) 147 | } 148 | await Model.selectArticleByIdModel(id) 149 | .then(res=>{ 150 | var resList = JSON.parse(JSON.stringify(res)) 151 | if(resList.length){ 152 | ctx.jsonReturn({ 153 | code:2, 154 | data:resList[0], 155 | msg:'ok' 156 | }) 157 | }else{ 158 | ctx.jsonReturn({ 159 | code:1, 160 | msg:'err' 161 | }) 162 | } 163 | 164 | }).catch(err=>{ 165 | ctx.jsonReturn({ 166 | code:1, 167 | msg:'err' 168 | }) 169 | }) 170 | } 171 | static async editArticle(ctx){ 172 | //es6 解构 173 | //这里解构需要和 ctx.request.body 的对象属性相同 174 | const { title,content,tags,type,id,thumb } = ctx.request.body; 175 | let time = moment().format('YYYY-MM-DD HH:mm') 176 | if(!title){ 177 | ctx.jsonReturn({ 178 | code:1, 179 | msg:'标题不能为空' 180 | }) 181 | } 182 | if(!content){ 183 | ctx.jsonReturn({ 184 | code:1, 185 | msg:'内容不能为空' 186 | }) 187 | } 188 | if(!type){ 189 | ctx.jsonReturn({ 190 | code:1, 191 | msg:'类型不能为空' 192 | }) 193 | } 194 | await Model.selectTypeAllModel() 195 | .then(res=>{ 196 | var selectInfoRes = JSON.parse(JSON.stringify(res)) 197 | if(selectInfoRes.length <= 0){ 198 | ctx.jsonReturn({ 199 | code:1, 200 | msg:'暂时无栏目,先添加栏目' 201 | }) 202 | }else{ 203 | var typeArr = []; 204 | selectInfoRes.forEach(function(item){ 205 | typeArr.push(item.id) 206 | }); 207 | function in_array(needle, haystack) { 208 | for(var i in haystack) { 209 | if(haystack[i] == needle) { 210 | return true; 211 | } 212 | } 213 | return false; 214 | } 215 | if(!in_array(type,typeArr)){ 216 | ctx.jsonReturn({ 217 | code:1, 218 | msg:'栏目id不合法' 219 | }) 220 | } 221 | let seleInfo = Model.selectTitleById(title,type); 222 | return seleInfo; 223 | } 224 | }) 225 | .then(seleInfo=>{ 226 | var seIn = JSON.parse(JSON.stringify(seleInfo)) 227 | if(seIn.length){ 228 | ctx.jsonReturn({ 229 | code:1, 230 | msg:'改栏目下已存在此文章' 231 | }) 232 | }else{ 233 | let addRes = Model.updateArticleModel([type,title,tags,thumb,content,time,id]) 234 | if(addRes){ 235 | ctx.jsonReturn({ 236 | code:2, 237 | msg:'修改文章成功' 238 | }) 239 | }else{ 240 | ctx.jsonReturn({ 241 | code:1, 242 | msg:'修改文章失败' 243 | }) 244 | } 245 | } 246 | }) 247 | .catch(err=>{ 248 | ctx.jsonReturn({ 249 | code:1, 250 | msg:'修改失败,服务器异常' 251 | }) 252 | }) 253 | 254 | 255 | } 256 | static async updateState(ctx){ 257 | const{state,id} = ctx.request.body; 258 | if(!state || !id){ 259 | ctx.jsonReturn({ 260 | code:1, 261 | msg:'err' 262 | }) 263 | } 264 | await Model.updateArticleStateModel([state,id]) 265 | .then(res=>{ 266 | if(res){ 267 | ctx.jsonReturn({ 268 | code:2, 269 | msg:'操作成功' 270 | }) 271 | }else{ 272 | ctx.jsonReturn({ 273 | code:1, 274 | msg:'操作失败' 275 | }) 276 | } 277 | }).catch(err=>{ 278 | ctx.jsonReturn({ 279 | code:1, 280 | msg:'操作失败:'+err 281 | }) 282 | }) 283 | 284 | } 285 | static async delArticleById(ctx){ 286 | const{ id } = ctx.request.body; 287 | if(!id || isNaN(id)){ 288 | ctx.jsonReturn({ 289 | code:1, 290 | msg:'id err' 291 | }) 292 | } 293 | await Model.delArticleByIdModel(id) 294 | .then(res=>{ 295 | if(res){ 296 | ctx.jsonReturn({ 297 | code:2, 298 | msg:'删除成功' 299 | }) 300 | }else{ 301 | ctx.jsonReturn({ 302 | code:1, 303 | msg:'删除失败' 304 | }) 305 | } 306 | }) 307 | } 308 | //推荐 309 | static async setRecommend(ctx){ 310 | const { id,recommedid } = ctx.request.body; 311 | if(!id || isNaN(id)){ 312 | ctx.jsonReturn({ 313 | code:1, 314 | msg:'id err' 315 | }) 316 | } 317 | await Model.articleRecommendModel([recommedid,id]) 318 | .then(res=>{ 319 | if(res){ 320 | ctx.jsonReturn({ 321 | code:2, 322 | msg:'操作成功' 323 | }) 324 | }else{ 325 | ctx.jsonReturn({ 326 | code:1, 327 | msg:'操作失败' 328 | }) 329 | } 330 | }) 331 | } 332 | static async setSliderCon(ctx){ 333 | const { id,slider_state } = ctx.request.body; 334 | if(!id || isNaN(id)){ 335 | ctx.jsonReturn({ 336 | code:1, 337 | msg:'id err' 338 | }) 339 | } 340 | await Model.setSliderModel([slider_state,id]) 341 | .then(res=>{ 342 | if(res){ 343 | ctx.jsonReturn({ 344 | code:2, 345 | msg:'操作成功' 346 | }) 347 | }else{ 348 | ctx.jsonReturn({ 349 | code:1, 350 | msg:'操作失败' 351 | }) 352 | } 353 | }) 354 | } 355 | } 356 | module.exports = ArticleController; --------------------------------------------------------------------------------