├── src ├── components │ ├── Layout │ │ ├── Layout.scss │ │ ├── index.vue │ │ └── Layout.vue │ ├── App │ │ ├── images │ │ │ ├── bg.jpg │ │ │ ├── bg2.png │ │ │ └── bg3.jpg │ │ ├── index.vue │ │ └── App.scss │ ├── Banner │ │ ├── images │ │ │ ├── bg.jpg │ │ │ ├── bg2.png │ │ │ ├── bg3.jpg │ │ │ ├── img01.png │ │ │ ├── img02.jpg │ │ │ └── img02.png │ │ ├── Banner.scss │ │ └── index.vue │ ├── common │ │ └── Toast │ │ │ └── index.js │ └── ProgressBar.vue ├── public │ ├── logo-48.png │ ├── logo-120.png │ ├── logo-144.png │ ├── logo-152.png │ ├── logo-192.png │ ├── logo-256.png │ ├── logo-384.png │ └── logo-512.png ├── pages │ ├── Home │ │ ├── Index │ │ │ ├── images │ │ │ │ └── wx.png │ │ │ ├── Home.scss │ │ │ └── index.vue │ │ ├── List │ │ │ ├── images │ │ │ │ ├── a1.jpg │ │ │ │ ├── a10.jpg │ │ │ │ ├── a2.jpg │ │ │ │ ├── a3.jpg │ │ │ │ ├── a4.jpg │ │ │ │ ├── a5.jpg │ │ │ │ ├── a6.jpg │ │ │ │ ├── a7.jpg │ │ │ │ ├── a8.jpg │ │ │ │ ├── a9.jpg │ │ │ │ └── wx.png │ │ │ ├── List.scss │ │ │ └── index.vue │ │ ├── Details │ │ │ ├── Details.scss │ │ │ └── index.vue │ │ └── route.js │ ├── Login │ │ ├── images │ │ │ └── notFound.jpg │ │ ├── Login.scss │ │ └── index.vue │ ├── NotFoundPage │ │ ├── images │ │ │ ├── 404.png │ │ │ └── notFound.jpg │ │ ├── NotFoundPage.scss │ │ └── index.vue │ └── routes.js ├── store │ ├── index.js │ ├── login │ │ └── index.js │ └── home │ │ └── index.js ├── utils │ ├── filters.js │ ├── title.js │ ├── bridge.js │ ├── util.js │ ├── format.js │ ├── fetchApi │ │ ├── fetch.js │ │ ├── fetch-client-(备份20160720).js │ │ ├── fetch-client.js │ │ └── fetch-server.js │ ├── validate.js │ └── fetch.js └── views │ └── index.template.html ├── server ├── env │ ├── prd.js │ ├── pre.js │ ├── test.js │ └── dev.js ├── config.js ├── error.js ├── logger.js ├── express.js └── server.js ├── assets ├── images │ ├── a4.jpg │ ├── a5.jpg │ ├── a7.jpg │ ├── a8.jpg │ ├── a9.jpg │ ├── 404.png │ ├── a10.jpg │ ├── bg2.png │ ├── img01.png │ └── img02.png ├── fonts │ ├── ionicons.eot │ ├── ionicons.ttf │ ├── ionicons.woff │ └── ionicons.svg ├── public │ ├── logo-48.png │ ├── logo-120.png │ ├── logo-144.png │ ├── logo-152.png │ ├── logo-192.png │ ├── logo-256.png │ ├── logo-384.png │ └── logo-512.png ├── js │ ├── 2.18e5bac323c205796d49.js │ ├── manifest.6a5fd1fc2b981fe18281.js │ ├── 4.2ba8b0ea69ab323082d9.js │ ├── 5.95f3fcb64e74ab55624c.js │ ├── 3.c299a5f8bda21fc21fdd.js │ ├── 1.6e3d48efa6a1fec884fd.js │ ├── app.387598ae874dd5716761.js │ └── 0.d2daa19751f85de12e1d.js ├── service-worker.js └── vue-ssr-client-manifest.json ├── .gitignore ├── .babelrc ├── .npmrc ├── app.js ├── devServer.js ├── manifest.json ├── README.md ├── bin └── www └── package.json /src/components/Layout/Layout.scss: -------------------------------------------------------------------------------- 1 | 2 | .layout{ 3 | max-width: 100% 4 | } -------------------------------------------------------------------------------- /server/env/prd.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PORT: 80, 3 | API : { 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /server/env/pre.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PORT: 80, 3 | API : { 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /server/env/test.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PORT: 80, 3 | API : { 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /assets/images/a4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a4.jpg -------------------------------------------------------------------------------- /assets/images/a5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a5.jpg -------------------------------------------------------------------------------- /assets/images/a7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a7.jpg -------------------------------------------------------------------------------- /assets/images/a8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a8.jpg -------------------------------------------------------------------------------- /assets/images/a9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a9.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | yarn-error.log 6 | .idea 7 | *.iml -------------------------------------------------------------------------------- /assets/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/404.png -------------------------------------------------------------------------------- /assets/images/a10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/a10.jpg -------------------------------------------------------------------------------- /assets/images/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/bg2.png -------------------------------------------------------------------------------- /src/public/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-48.png -------------------------------------------------------------------------------- /assets/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/fonts/ionicons.eot -------------------------------------------------------------------------------- /assets/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/fonts/ionicons.ttf -------------------------------------------------------------------------------- /assets/images/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/img01.png -------------------------------------------------------------------------------- /assets/images/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/images/img02.png -------------------------------------------------------------------------------- /assets/public/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-48.png -------------------------------------------------------------------------------- /src/public/logo-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-120.png -------------------------------------------------------------------------------- /src/public/logo-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-144.png -------------------------------------------------------------------------------- /src/public/logo-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-152.png -------------------------------------------------------------------------------- /src/public/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-192.png -------------------------------------------------------------------------------- /src/public/logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-256.png -------------------------------------------------------------------------------- /src/public/logo-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-384.png -------------------------------------------------------------------------------- /src/public/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/public/logo-512.png -------------------------------------------------------------------------------- /assets/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/fonts/ionicons.woff -------------------------------------------------------------------------------- /assets/public/logo-120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-120.png -------------------------------------------------------------------------------- /assets/public/logo-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-144.png -------------------------------------------------------------------------------- /assets/public/logo-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-152.png -------------------------------------------------------------------------------- /assets/public/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-192.png -------------------------------------------------------------------------------- /assets/public/logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-256.png -------------------------------------------------------------------------------- /assets/public/logo-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-384.png -------------------------------------------------------------------------------- /assets/public/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/assets/public/logo-512.png -------------------------------------------------------------------------------- /assets/fonts/ionicons.svg: -------------------------------------------------------------------------------- 1 | module.exports = __webpack_public_path__ + "images/ionicons.svg?621bd386841f74e0053cb8e67f8a0604"; -------------------------------------------------------------------------------- /src/components/App/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/App/images/bg.jpg -------------------------------------------------------------------------------- /src/components/App/images/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/App/images/bg2.png -------------------------------------------------------------------------------- /src/components/App/images/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/App/images/bg3.jpg -------------------------------------------------------------------------------- /src/components/Banner/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/bg.jpg -------------------------------------------------------------------------------- /src/pages/Home/Index/images/wx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/Index/images/wx.png -------------------------------------------------------------------------------- /src/pages/Home/List/images/a1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a1.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a10.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a2.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a3.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a4.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a5.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a6.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a7.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a8.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/a9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/a9.jpg -------------------------------------------------------------------------------- /src/pages/Home/List/images/wx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Home/List/images/wx.png -------------------------------------------------------------------------------- /src/pages/Login/images/notFound.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/Login/images/notFound.jpg -------------------------------------------------------------------------------- /src/components/Banner/images/bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/bg2.png -------------------------------------------------------------------------------- /src/components/Banner/images/bg3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/bg3.jpg -------------------------------------------------------------------------------- /src/pages/NotFoundPage/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/NotFoundPage/images/404.png -------------------------------------------------------------------------------- /src/components/Banner/images/img01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/img01.png -------------------------------------------------------------------------------- /src/components/Banner/images/img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/img02.jpg -------------------------------------------------------------------------------- /src/components/Banner/images/img02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/components/Banner/images/img02.png -------------------------------------------------------------------------------- /src/pages/NotFoundPage/images/notFound.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meibin08/Vue-Vuex-axios-SSR/HEAD/src/pages/NotFoundPage/images/notFound.jpg -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { "modules": false }] 4 | ], 5 | "plugins": [ 6 | "syntax-dynamic-import","transform-object-rest-spread", 7 | "transform-runtime" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /server/env/dev.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | PORT: 8081, 3 | API : { 4 | 5 | easy: { 6 | host: 'easy-mock.com/mock/59294d8e91470c0ac1fe8a4c', 7 | port: 80 8 | }, 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/pages/Home/Index/Home.scss: -------------------------------------------------------------------------------- 1 | 2 | .home{ 3 | max-width:100%; 4 | .ivu-col{ 5 | margin-bottom:10px; 6 | } 7 | .mt-10{margin-top:10px;text-align:right} 8 | .img{width:50px;height:30px;} 9 | } 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ 2 | phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/ 3 | electron_mirror=https://npm.taobao.org/mirrors/electron/ 4 | registry=https://registry.npm.taobao.org -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('express'); 3 | var path = require('path'); 4 | var config = require('./server/config'); 5 | 6 | var app = express(); 7 | require('./server/express')(app); 8 | require('./server/server')(app); 9 | require('./server/error')(app); 10 | require('./bin/www')(app); 11 | -------------------------------------------------------------------------------- /src/components/App/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | -------------------------------------------------------------------------------- /devServer.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require('express'); 3 | var webpack = require('webpack'); 4 | 5 | const path = require('path') 6 | 7 | var app = express(); 8 | 9 | require('./server/express')(app); 10 | require('./server/server')(app); 11 | require('./server/error')(app); 12 | require('./bin/www')(app); 13 | -------------------------------------------------------------------------------- /src/pages/Login/Login.scss: -------------------------------------------------------------------------------- 1 | 2 | .login{ 3 | margin:15% auto 0; 4 | text-align: center; 5 | width:320px; 6 | padding:5px; 7 | border-radius:5px; 8 | background-color:rgba(0,0,0,.5); 9 | .ivu-alert{ 10 | padding:8px 16px; 11 | margin:0; 12 | } 13 | .tit{ 14 | padding:10px 0 20px; 15 | text-align:center; 16 | font-size:14px; 17 | } 18 | } -------------------------------------------------------------------------------- /src/components/Banner/Banner.scss: -------------------------------------------------------------------------------- 1 | .app-banner{ 2 | width:100%; 3 | overflow:hidden; 4 | .demo-carousel{ 5 | width:100%; 6 | text-align: center; 7 | background-color: #eadcc7; 8 | height: 300px; 9 | img{ 10 | max-width:100%; 11 | } 12 | &.c3{ 13 | background-color: #1b9bb5; 14 | } 15 | &.c4{ 16 | background-color: #b30038; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/pages/NotFoundPage/NotFoundPage.scss: -------------------------------------------------------------------------------- 1 | 2 | .error-tips{ 3 | width: 666px; 4 | margin: 15% auto 0; 5 | text-align:center; 6 | .err-inner{ 7 | width:550px; 8 | height:420px; 9 | padding:60% 0 0; 10 | background: url(./images/404.png) center center no-repeat; 11 | margin: 0 auto ; 12 | font-size:15px; 13 | } 14 | .a-block{ 15 | display: inline-block;margin-right:30px; 16 | text-decoration: underline; 17 | } 18 | } -------------------------------------------------------------------------------- /src/pages/Home/List/List.scss: -------------------------------------------------------------------------------- 1 | 2 | .list{ 3 | .name{ 4 | color:#06c; 5 | } 6 | .a-block{ 7 | display:block; 8 | color: #34495e; 9 | } 10 | .ivu-card{ 11 | margin-bottom:15px; 12 | } 13 | .user-icon{ 14 | width:50px; 15 | height:50px; 16 | border-radius:4px; 17 | border:1px solid #999; 18 | } 19 | .row{ 20 | background:#fff; 21 | border-radius:5px; 22 | padding:5px 10px; 23 | } 24 | } 25 | .preview-img{ 26 | max-width:400px; 27 | max-height: 400px; 28 | } 29 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @authors :Bin Mei 3 | * @email : meibin08@163.com 4 | * @QQ群 : 386485473 -- 技术交流群 5 | * @date :2017-07-20 6 | * @description:pc官网 -> store - store 模块 7 | */ 8 | 9 | import Vue from 'vue' 10 | import Vuex from 'vuex' 11 | import home from './home/index' 12 | import login from './login/index' 13 | 14 | Vue.use(Vuex) 15 | 16 | export function createStore (){ 17 | return new Vuex.Store({ 18 | modules: { 19 | home, 20 | login 21 | } 22 | }); 23 | } ; -------------------------------------------------------------------------------- /server/config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var extend = require('util')._extend; 3 | 4 | var dev = require('./env/dev'), 5 | test = require('./env/test'), 6 | pre = require('./env/pre'), 7 | prd = require('./env/prd'); 8 | 9 | var defaults = { 10 | root: path.normalize(__dirname + '/..') 11 | }; 12 | 13 | module.exports = { 14 | dev: extend(dev, defaults), 15 | test: extend(test, defaults), 16 | pre: extend(pre, defaults), 17 | prd: extend(prd, defaults) 18 | }[process.env.DEPLOY_ENV || 'dev']; 19 | -------------------------------------------------------------------------------- /src/pages/Home/Details/Details.scss: -------------------------------------------------------------------------------- 1 | 2 | .list{ 3 | .name{ 4 | color:#06c; 5 | } 6 | .label{color:#666} 7 | .a-block{ 8 | display:block; 9 | color: #34495e; 10 | } 11 | .ivu-card{ 12 | margin-bottom:15px; 13 | } 14 | .user-icon{ 15 | width:50px; 16 | height:50px; 17 | border-radius:4px; 18 | border:1px solid #999; 19 | } 20 | .row{ 21 | background:#fff; 22 | border-radius:5px; 23 | padding:5px 10px; 24 | } 25 | } 26 | .preview-img{ 27 | max-width:400px; 28 | max-height: 400px; 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/filters.js: -------------------------------------------------------------------------------- 1 | export function host (url) { 2 | const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '') 3 | const parts = host.split('.').slice(-3) 4 | if (parts[0] === 'www') parts.shift() 5 | return parts.join('.') 6 | } 7 | 8 | export function timeAgo (time) { 9 | const between = Date.now() / 1000 - Number(time) 10 | if (between < 3600) { 11 | return pluralize(~~(between / 60), ' minute') 12 | } else if (between < 86400) { 13 | return pluralize(~~(between / 3600), ' hour') 14 | } else { 15 | return pluralize(~~(between / 86400), ' day') 16 | } 17 | } 18 | 19 | function pluralize (time, label) { 20 | if (time === 1) { 21 | return time + label 22 | } 23 | return time + label + 's' 24 | } 25 | -------------------------------------------------------------------------------- /server/error.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (app) { 3 | 4 | // catch 404 and forward to error handler 5 | app.use(function(req, res, next) { 6 | var err = new Error('Not Found'); 7 | err.status = 404; 8 | next(err); 9 | }); 10 | 11 | // error handlers 12 | 13 | // development error handler 14 | // will print stacktrace 15 | if (app.get('env') === 'dev') { 16 | app.use(function(err, req, res, next) { 17 | res.status(err.status || 500); 18 | res.render('error', { 19 | message: err.message, 20 | error: err 21 | }); 22 | }); 23 | } 24 | 25 | // production error handler 26 | // no stacktraces leaked to user 27 | app.use(function(err, req, res, next) { 28 | res.status(err.status || 500); 29 | res.render('error', { 30 | message: err.message, 31 | error: {} 32 | }); 33 | }); 34 | 35 | }; -------------------------------------------------------------------------------- /src/utils/title.js: -------------------------------------------------------------------------------- 1 | function getTitle (vm) { 2 | 3 | const { meta } = (vm&&vm.$route&&vm.$route||{}); 4 | const title = meta&&meta.title; 5 | return title; 6 | /*console.log() 7 | if (title) { 8 | return typeof title === 'function' 9 | ? title.call(vm) 10 | : title 11 | }*/ 12 | } 13 | 14 | const serverTitleMixin = { 15 | created () { 16 | const title = getTitle(this) 17 | if (title&&this&&this.$ssrContext) { 18 | console.log(18,title,this.$ssrContext) 19 | //this.$ssrContext.title = `Vue HN 2.0 | ${title}` 20 | } 21 | } 22 | } 23 | 24 | const clientTitleMixin = { 25 | mounted () { 26 | const title = getTitle(this) 27 | if (title) { 28 | document.title = `Vue HN 2.0 | ${title}` 29 | } 30 | } 31 | } 32 | 33 | export default clientTitleMixin 34 | /*export default process.env.VUE_ENV === 'server' 35 | ? serverTitleMixin 36 | : clientTitleMixin*/ 37 | -------------------------------------------------------------------------------- /src/components/Banner/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue demo", 3 | "short_name": "Vue HN", 4 | "icons": [{ 5 | "src": "/static/logo-120.png", 6 | "sizes": "120x120", 7 | "type": "image/png" 8 | }, { 9 | "src": "/static/logo-144.png", 10 | "sizes": "144x144", 11 | "type": "image/png" 12 | }, { 13 | "src": "/static/logo-152.png", 14 | "sizes": "152x152", 15 | "type": "image/png" 16 | }, { 17 | "src": "/static/logo-192.png", 18 | "sizes": "192x192", 19 | "type": "image/png" 20 | }, { 21 | "src": "/static/logo-256.png", 22 | "sizes": "256x256", 23 | "type": "image/png" 24 | }, { 25 | "src": "/static/logo-384.png", 26 | "sizes": "384x384", 27 | "type": "image/png" 28 | }, { 29 | "src": "/static/logo-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png" 32 | }], 33 | "start_url": "/", 34 | "background_color": "#f2f3f5", 35 | "display": "standalone", 36 | "theme_color": "#f60" 37 | } 38 | -------------------------------------------------------------------------------- /src/components/common/Toast/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import Vue from 'vue' 4 | 5 | var StaticToast = new Vue({ 6 | methods: { 7 | default(type,content,duration,closable){ 8 | this.$Message.config({ 9 | top: 180 10 | }); 11 | this.$Message[type||"info"]({ 12 | content: content||"提示", 13 | duration: (duration||3), 14 | closable: (closable||false) 15 | }); 16 | }, 17 | info (content,duration,closable) { 18 | StaticToast.default("info",content,duration,closable); 19 | }, 20 | success:(content,duration,closable)=>{ 21 | StaticToast.default("success",content,duration,closable); 22 | }, 23 | warning:(content,duration,closable)=>{ 24 | StaticToast.default("warning",content,duration,closable); 25 | }, 26 | error:(content,duration,closable)=>{ 27 | StaticToast.default("error",content,duration,closable); 28 | }, 29 | destroy(){ 30 | this.$Message.destroy(); 31 | } 32 | } 33 | }) 34 | /*const StaticToast= { 35 | 36 | }*/ 37 | export default StaticToast; 38 | 39 | -------------------------------------------------------------------------------- /src/pages/NotFoundPage/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 46 | 47 | 51 | -------------------------------------------------------------------------------- /src/utils/bridge.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @authors :Bin Mei 4 | * @date :2017-07-20 5 | * @description:pc官网 -> bridge 设置标题 6 | */ 7 | 8 | 9 | 10 | function getTitle (vm) { 11 | 12 | const { meta } = (vm&&vm.$route&&vm.$route||{}); 13 | const title = meta&&meta.title; 14 | return title; 15 | /*console.log() 16 | if (title) { 17 | return typeof title === 'function' 18 | ? title.call(vm) 19 | : title 20 | }*/ 21 | } 22 | 23 | const serverTitleMixin = { 24 | created () { 25 | const title = getTitle(this) 26 | if (title) { 27 | //this.$ssrContext.title = `Vue HN 2.0 | ${title}` 28 | } 29 | } 30 | } 31 | /*设置页面title*/ 32 | export const clientTitleMixin = { 33 | mounted () { 34 | const title = getTitle(this) 35 | if (title) { 36 | document.title = title||"vue服务端渲染示例"; 37 | } 38 | } 39 | }; 40 | 41 | /* 42 | 控制每个页面进入的权限、及页面的title设置 43 | @requireAuth 页面是否需要登录进入 44 | @title 当前页面的title; 45 | */ 46 | export const Bridges=(title,auth)=>{ 47 | return { 48 | meta:{ 49 | requireAuth: auth||false, 50 | title:title||"vue服务端渲染示例" 51 | } 52 | }; 53 | } 54 | 55 | -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author monkeywang 3 | * Date: 17/3/14 4 | */ 5 | import Vue from 'vue' 6 | import $http from 'vue-resource' 7 | // import { createAPI } from 'create-api' 8 | Vue.use($http) 9 | 10 | const logRequests = !!process.env.DEBUG_API 11 | const client = process.env.VUE_ENV == "client"; 12 | 13 | /*const api = createAPI({ 14 | version: '/v0', 15 | config: { 16 | databaseURL: 'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c' 17 | } 18 | })*/ 19 | let vm = new Vue() 20 | 21 | const configPath = client?"/zaApi/easy":'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c'; 22 | // const configPath = 'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c'; 23 | 24 | export class Utils { 25 | get (url, data = {}) { 26 | url = configPath + url; 27 | 28 | return new Promise((resolve, reject) => { 29 | vm.$http.get(url, {params: data, credentials: true}).then((response) => { 30 | //console.log(18,response); 31 | resolve(response.body) 32 | }, function (error) { 33 | console.log('接口异常') 34 | }).catch(function () { 35 | console.log("Promise Rejected"); 36 | }); 37 | 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/logger.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var log4js = require('log4js'); 3 | var env = process.env.DEPLOY_ENV || 'dev'; 4 | var package = require('../package.json'); 5 | 6 | if (env != 'dev') { 7 | // 日志存放位置 8 | var log_dir = (env != 'dev') 9 | ? '/alidata1/admin/' + package.name 10 | : './'; 11 | 12 | log4js.loadAppender('file'); 13 | log4js.configure({ 14 | appenders: [ 15 | { 16 | type: 'file', 17 | filename: path.join(log_dir, 'logs/'+process.env.HOSTNAME+'_'+package.name+'.log'), 18 | // pattern: '_yyyy-MM-dd.log', 19 | // alwaysIncludePattern: true, 20 | category: 'info', 21 | }, 22 | { 23 | type: 'file', 24 | filename: path.join(log_dir, 'logs/'+process.env.HOSTNAME+'_'+package.name+'_error.log'), 25 | // pattern: '_yyyy-MM-dd.log', 26 | // alwaysIncludePattern: true, 27 | category: 'error', 28 | } 29 | ] 30 | }); 31 | } 32 | 33 | var logger_info = log4js.getLogger('info'); 34 | logger_info.setLevel('INFO'); 35 | 36 | var logger_error = log4js.getLogger('error'); 37 | logger_error.setLevel('ERROR'); 38 | 39 | module.exports = { 40 | info: function(msg) { 41 | logger_info.info(msg) 42 | }, 43 | error: function(msg) { 44 | logger_error.error(msg) 45 | } 46 | }; -------------------------------------------------------------------------------- /src/views/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /server/express.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs') 3 | const path = require('path') 4 | const express = require('express') 5 | const favicon = require('serve-favicon') 6 | const resolve = file => path.resolve(__dirname, file) 7 | 8 | var cookieParser = require('cookie-parser') 9 | var compression = require('compression') 10 | var config = require('./config') 11 | module.exports = function (app) { 12 | // app.set('views', config.root + '/src/views');//设置模板文件路径 13 | app.set('views', config.root + '/src/views');//设置模板文件路径 14 | // app.engine('.html', require('ejs').__express) 15 | // app.set('view engine', 'html'); 16 | const isProd = process.env.NODE_ENV === 'production' 17 | 18 | /* 19 | @ maxAge 最大缓存30天 20 | */ 21 | const serve = (path, cache) => express.static(resolve(path), { 22 | maxAge: cache && isProd ? 1000 * 60 * 60 * 24 * 30 : 0 23 | }) 24 | app.use(cookieParser()) 25 | app.use(compression({ threshold: 0 })) 26 | /* 27 | @ 设置全部静态资源访问路径 28 | @ assets 编译后的文件目录 29 | */ 30 | let static =isProd?"assets":"src"; 31 | app.use('/assets', serve(config.root + '/assets', true)) // 32 | app.use('/static', serve(config.root + '/'+static+'/public', true)) 33 | app.use('/manifest.json', serve(config.root + '/manifest.json', true)) 34 | app.use('/service-worker.js', serve(config.root + '/assets/service-worker.js')) 35 | app.use(favicon(config.root + '/'+static+'/public/logo-48.png')) 36 | 37 | 38 | 39 | 40 | 41 | 42 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue-Vuex-axios-SRR 示例 2 | 3 | ### 示例说明 4 | 5 | - 1、该示例由官方 vue-hackernews-2.0 示例根据公司实际项目需要进行优化改进过,非原创, 6 | - 2、实现的功能点列表: 7 | - 1)、项目目录结构进行了变更,让整个项目分类看起来更一目了然, 8 | - 2)、处理了500、404等页面的跳转逻辑, 9 | - 3)、融入了axios,进行多队列请求、单个请求的回调封装逻辑处理 10 | - 4)、引入了scss的支持,页面title的修改优化,无需在每个组件内部设置,直接在路由配置页配置添加即可, 11 | - 5)、路由划分为按功能模块分类,便于维护 12 | - 6)、store的维护,变更,不同模块写在不同文件内,分开处理 13 | - 7)、引入了mock.js数据…… 14 | - 8)、其他还有很多细节,有兴趣的同学自己去慢慢发现吧…… 15 | - 3、如果觉得该示例不错,对您有一定的帮助,记得帮 star、star、star 哦 ^_^ ^_^ ^_^ ^_^~~ 16 | - 想了解更多?——[@IT·平头哥联盟](https://honeybadger8.github.io/blog/ "@IT·平头哥联盟-首席填坑官∙苏南") 17 | 18 | **** 19 | 20 | ## 技术交流 21 | - 公众号:`honeyBadger8` 下方可扫码👇 22 | - 群:912594095、[386485473](https://shang.qq.com/wpa/qunwpa?idkey=d44baf17512787eb0e4f268849a3239d6b9675145a606e21b9a055176bd1c0e2 "技术交流群 @IT·平头哥联盟") 23 | - 博客:[@IT·平头哥联盟](https://honeybadger8.github.io/blog/ "@IT·平头哥联盟-首席填坑官∙苏南") 24 | 25 | ## 安装依赖包 26 | - npm install 27 | - 建议使用 cnpm 速度更快一些,[淘宝 NPM 镜像](https://npm.taobao.org/) 28 | 29 | ## 开发环境 30 | - npm run dev 31 | - 访问http://localhost:80/home 32 | 33 | ## 生成环境 34 | - npm run build 35 | - npm start 36 | 37 | ## 关于我 38 | - 最近在写博客了,有兴趣的同学可以关注一下我和朋友一起整的公众号,专注于分享前端/测试的一些心得总结👇👇, 39 | - 公众号:`honeyBadger8`。 40 | 41 | ![@IT·平头哥联盟-首席填坑官∙苏南,公众号:honeyBadger8,宝剑锋从磨砺出 梅花香自苦寒来,用心分享 做有温度的攻城狮,专注于分享前端、测试 等领域的积累,文章来源于(自己/群友)工作中积累的经验、填过的坑,希望能尽绵薄之力 助其他同学少走一些弯路](https://honeybadger8.github.io/blog/frontends/_banner/card.gif "@IT·平头哥联盟-首席填坑官∙苏南,公众号:honeyBadger8") 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /assets/js/2.18e5bac323c205796d49.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([2],{151:function(t,n,e){"use strict";function r(t){e(189)}Object.defineProperty(n,"__esModule",{value:!0});var i=e(192),a=e(193),o=e(60),s=r,c=o(i.a,a.a,s,null,null);n.default=c.exports},189:function(t,n,e){var r=e(190);"string"==typeof r&&(r=[[t.i,r,""]]),r.locals&&(t.exports=r.locals);e(146)("681d7cb6",r,!0)},190:function(t,n,e){n=t.exports=e(145)(void 0),n.push([t.i,".error-tips{width:666px;margin:15% auto 0;text-align:center}.error-tips .err-inner{width:550px;height:420px;padding:60% 0 0;background:url("+e(191)+") 50% no-repeat;margin:0 auto;font-size:15px}.error-tips .a-block{display:inline-block;margin-right:30px;text-decoration:underline}",""])},191:function(t,n,e){t.exports=e.p+"images/404.png?d022af973da8a895df602a976f5159cc"},192:function(t,n,e){"use strict";var r=null;n.a={name:"noFound-view",mounted:function(){this.down()},data:function(){return{second:5}},methods:{down:function(){var t=this;r=setInterval(function(){if(t.second<=0)return clearInterval(r),t.$router.replace({path:"/home"}),!1;t.second--},1e3)}}}},193:function(t,n,e){"use strict";var r=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",[e("div",{staticClass:"error-tips"},[e("Card",[e("div",{staticClass:"err-inner"},[e("p",[e("router-link",{staticClass:"a-block",attrs:{to:{path:"/home"}}},[t._v("返回首页")]),e("span",[e("strong",{staticClass:"red"},[t._v(t._s(t.second))]),t._v("秒后自动回到首页")])],1)])])],1)])},i=[],a={render:r,staticRenderFns:i};n.a=a}}); -------------------------------------------------------------------------------- /assets/js/manifest.6a5fd1fc2b981fe18281.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,a,c){for(var f,u,i,d=0,s=[];d store - login 模块 7 | */ 8 | import StaticToast from 'src/components/common/Toast/index'; 9 | import {fetchSingle,fetchGroup} from "src/utils/fetch"; 10 | 11 | /* 12 | * @ 登录模块 type 常量; 用于更新不同的操作数据 13 | */ 14 | export const LOGIN = 'LOGIN'; 15 | 16 | 17 | /* 18 | * @ 登录模块 initState; 19 | */ 20 | const state = { 21 | userToken:"" 22 | }; 23 | 24 | /* 25 | * @ 登录模块 getters - 对外数据接收器 26 | */ 27 | const getters = { 28 | _Token (state) { 29 | return state.userToken 30 | } 31 | 32 | }; 33 | 34 | /* 35 | * @ 登录模块 actions 36 | */ 37 | const actions = { 38 | 39 | submit({commit, state}, options){ 40 | 41 | const {data,success,error} = options||{}; 42 | // 登录成功后更新userToken 43 | return fetchSingle({url:'/api/test/userToken',type:"POST",data}).then(res=>{ 44 | if(res.code == 0){ 45 | commit(LOGIN, {userToken: res.data.userToken}); 46 | }else{ 47 | StaticToast.warning(res.message); 48 | }; 49 | success&&success(res); 50 | },error=>{ 51 | error&&error(error); 52 | console.log("1555login",error) 53 | }); 54 | } 55 | 56 | }; 57 | 58 | /* 59 | * @ 登录模块 mutations - 数据更新 60 | */ 61 | const mutations = { 62 | 63 | [LOGIN](state, { userToken }){ 64 | 65 | // 登录成功后更新userToken 66 | state.userToken = userToken 67 | } 68 | 69 | }; 70 | 71 | 72 | 73 | export default { 74 | state, 75 | getters, 76 | actions, 77 | mutations 78 | } 79 | -------------------------------------------------------------------------------- /src/pages/routes.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @authors :Bin Mei 4 | * @date :2017-07-20 5 | * @description:pc官网 -> 路由对统一外出口 6 | */ 7 | 8 | 9 | import Vue from 'vue' 10 | import Router from 'vue-router' 11 | import HomeRoute from "./Home/route.js" //首页模块router 12 | const NotFoundPage = () => import('./NotFoundPage/index.vue') //详情 13 | const Login = () => import('./Login/index.vue') //详情 14 | import iView from 'iview'; 15 | import { createStore } from 'src/store/index.js' 16 | import format from 'src/utils/format' 17 | import 'iview/dist/styles/iview.css'; // 使用 CSS 18 | 19 | const store = createStore() 20 | 21 | Vue.use(iView); 22 | Vue.use(Router); 23 | 24 | var router = new Router({ 25 | mode: 'history', 26 | scrollBehavior: () => ({ y: 0 }), 27 | routes: [ 28 | ...HomeRoute, 29 | 30 | 31 | { path: '/login/:page(\\d+)?', component: Login,meta: { 32 | title:"登录页" 33 | } }, 34 | { path: '/', redirect: '/home' }, 35 | { 36 | path: '*', component:NotFoundPage , 37 | meta: { 38 | requireAuth: false,title:"页面不存在" 39 | } 40 | }, 41 | 42 | ] 43 | }); 44 | // store.state.cookies 45 | 46 | //已在 build => entry => client 页面处理 47 | /*router.beforeEach((to, from, next) => { 48 | //页面是是否要登录进入,在该处统一处理 49 | if (to.matched.some(r => r.meta.requireAuth)) { 50 | // let format 51 | 52 | if (true) { 53 | next(); 54 | } 55 | else { 56 | 57 | next({ 58 | path: '/login', 59 | query: {redirect: to.fullPath} 60 | }) 61 | } 62 | } 63 | else { 64 | next(); 65 | } 66 | });*/ 67 | 68 | export function createRouter () { 69 | return router; 70 | } 71 | -------------------------------------------------------------------------------- /assets/js/4.2ba8b0ea69ab323082d9.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([4],{150:function(t,e,s){"use strict";function a(t){s(185)}Object.defineProperty(e,"__esModule",{value:!0});var i=s(187),r=s(188),n=s(60),o=a,l=n(i.a,r.a,o,null,null);e.default=l.exports},185:function(t,e,s){var a=s(186);"string"==typeof a&&(a=[[t.i,a,""]]),a.locals&&(t.exports=a.locals);s(146)("5c1e8767",a,!0)},186:function(t,e,s){e=t.exports=s(145)(void 0),e.push([t.i,".list .name{color:#06c}.list .label{color:#666}.list .a-block{display:block;color:#34495e}.list .ivu-card{margin-bottom:15px}.list .user-icon{width:50px;height:50px;border-radius:4px;border:1px solid #999}.list .row{background:#fff;border-radius:5px;padding:5px 10px}.preview-img{max-width:400px;max-height:400px}",""])},187:function(t,e,s){"use strict";var a=s(32),i=s.n(a),r=s(61);e.a={name:"detail-view",computed:i()({},Object(r.c)({_details:"_details"})),methods:i()({},Object(r.b)({details:"details"})),data:function(){return{currentName:this.$route.query.name}},asyncData:function(t){var e=t.store,s=t.route.params.id,a=e.state.cookies.userToken;return e.dispatch("details",{id:s,userToken:a})}}},188:function(t,e,s){"use strict";var a=function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"list main"},[t._details&&Object.keys(t._details).length?s("section",[s("Alert",[s("p",[t._v("作者:"),s("strong",{staticClass:"name"},[t._v(t._s(t.currentName))]),t._v("老师 ")]),s("p",[t._v("发表时间:"+t._s(t._details.date)+" ")])]),s("Card",{attrs:{bordered:!0}},[s("p",{slot:"title"},[s("span",{staticClass:"label"},[t._v("文章标题:")]),t._v(t._s(t._details.title))]),s("section",[t._v("\n "+t._s(t._details.content)+"\n ")])])],1):s("h1",[t._v("请求出错")])])},i=[],r={render:a,staticRenderFns:i};e.a=r}}); -------------------------------------------------------------------------------- /src/pages/Home/route.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Bridges} from 'src/utils/bridge.js' 4 | const Layout = () => import('src/components/Layout/index.vue') //共用部分 5 | const Home = () => import('./Index/index.vue') //首页 6 | const List = () => import('./List/index.vue') //列表 7 | const Details = () => import('./Details/index.vue') //详情 8 | 9 | 10 | // export default [ 11 | // { 12 | // path:'/home', 13 | // component:Home, 14 | // name:"home", 15 | // ...Bridges("首 页",true), 16 | // //children 使用注意,内容渲染在home 内的route-view入口 内; 17 | // children:[ 18 | 19 | // ], 20 | 21 | // },{ 22 | // path:'/list/:page(\\d+)?', 23 | // name:"list", 24 | // component:List, 25 | // ...Bridges("列表页",true), 26 | // },{ 27 | // path:'/details/:id', 28 | // name:"details", 29 | // component:Details, 30 | // ...Bridges("文章详情页",true), 31 | // } 32 | // ]; 33 | // import {Bridges} from 'src/utils/bridge.js' 34 | // import Layout from "src/components/Layout/index.vue" //首页 35 | // import Home from "src/pages/Home/Index/index.vue" //首页 36 | // import List from "src/pages/Home/List/index.vue" //列表 37 | // import Details from "src/pages/Home/Details/index.vue" //详情 38 | 39 | export default [ 40 | { 41 | path:'/home', 42 | component:Layout, 43 | // name:"layout", 44 | //children 使用注意,内容渲染在home 内的route-view入口 内; 45 | children:[ 46 | { 47 | // name:"", 48 | path: '/', 49 | component: Home, 50 | ...Bridges("首 页"), 51 | },{ 52 | path:'list', 53 | // name:"list", 54 | component:List, 55 | ...Bridges("列表页",true), 56 | },{ 57 | path:'details/:id', 58 | // name:"details", 59 | component:Details, 60 | ...Bridges("文章详情页",true), 61 | } 62 | ], 63 | } 64 | ]; -------------------------------------------------------------------------------- /src/pages/Home/Details/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 63 | 64 | 68 | -------------------------------------------------------------------------------- /src/components/Layout/index.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | -------------------------------------------------------------------------------- /src/components/Layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | -------------------------------------------------------------------------------- /src/pages/Home/Index/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 66 | 70 | 71 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | var config = require('../server/config'); 7 | 8 | var debug = require('debug')('todo:server'); 9 | var http = require('http'); 10 | 11 | module.exports = function (app) { 12 | var port = normalizePort(config.PORT || '8080'); 13 | app.set('port', port); 14 | 15 | var server = http.createServer(app); 16 | server.listen(port); 17 | console.log('Express app http started on port ' + port); 18 | server.on('error', onError); 19 | server.on('listening', onListening); 20 | 21 | /** 22 | * Normalize a port into a number, string, or false. 23 | */ 24 | function normalizePort(val) { 25 | var port = parseInt(val, 10); 26 | if (isNaN(port)) { 27 | // named pipe 28 | return val; 29 | } 30 | if (port >= 0) { 31 | // port number 32 | return port; 33 | } 34 | return false; 35 | } 36 | 37 | /** 38 | * Event listener for HTTP server "error" event. 39 | */ 40 | function onError(error) { 41 | if (error.syscall !== 'listen') { 42 | throw error; 43 | } 44 | var bind = typeof port === 'string' 45 | ? 'Pipe ' + port 46 | : 'Port ' + port; 47 | // handle specific listen errors with friendly messages 48 | switch (error.code) { 49 | case 'EACCES': 50 | console.error(bind + ' requires elevated privileges'); 51 | process.exit(1); 52 | break; 53 | case 'EADDRINUSE': 54 | console.error(bind + ' is already in use'); 55 | process.exit(1); 56 | break; 57 | default: 58 | throw error; 59 | } 60 | } 61 | 62 | /** 63 | * Event listener for HTTP server "listening" event. 64 | */ 65 | function onListening() { 66 | var addr = server.address(); 67 | var bind = typeof addr === 'string' 68 | ? 'pipe ' + addr 69 | : 'port ' + addr.port; 70 | debug('Listening on ' + bind); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /assets/js/5.95f3fcb64e74ab55624c.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([5],{147:function(t,s,a){"use strict";function e(t){a(157)}Object.defineProperty(s,"__esModule",{value:!0});var r=a(159),i=a(60),o=e,n=i(null,r.a,o,null,null);s.default=n.exports},157:function(t,s,a){var e=a(158);"string"==typeof e&&(e=[[t.i,e,""]]),e.locals&&(t.exports=e.locals);a(146)("42e93068",e,!0)},158:function(t,s,a){s=t.exports=a(145)(void 0),s.push([t.i,".layout{max-width:100%}",""])},159:function(t,s,a){"use strict";var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"layout"},[a("header",{staticClass:"header"},[a("nav",{staticClass:"inner"},[a("router-link",{attrs:{to:"/",exact:""}},[a("img",{staticClass:"logo",attrs:{src:"/static/logo-48.png",alt:"logo"}})]),a("router-link",{attrs:{to:"/login"}},[t._v("登录页")]),a("router-link",{attrs:{to:"/details"}},[t._v("详情页")]),a("router-link",{attrs:{to:"/noFound"}},[t._v("错误404页")]),a("a",{staticClass:"github",attrs:{href:"https://github.com/meibin08",target:"_blank",title:"GitHub",rel:"noopener"}},[a("Icon",{attrs:{type:"social-github",size:"26",color:"#fff"}})],1)],1)]),a("transition",{attrs:{name:"fade",mode:"out-in"}},[a("router-view",{staticClass:"view"})],1),a("Alert",{staticClass:"main",attrs:{"show-icon":""}},[a("h3",[t._v("该项目示例说明:")]),a("template",{slot:"desc"},[a("p",[t._v("1、该示例由官方 vue-hackernews-2.0 示例根据公司实际项目需要进行优化改进过,非原创,")]),a("p",[t._v("2、实现的功能点列表:")]),a("p",[t._v(" 1)、项目目录结构进行了变更,让整个项目分类看起来更一目了然,")]),a("p",[t._v(" 2)、处理了500、404等页面的跳转逻辑,")]),a("p",[t._v(" 3)、融入了axios,进行多队列请求、单个请求的回调封装逻辑处理")]),a("p",[t._v(" 4)、引入了scss的支持,页面title的修改优化,无需在每个组件内部设置,直接在路由配置页配置添加即可,")]),a("p",[t._v(" 5)、路由划分为按功能模块分类,便于维护")]),a("p",[t._v(" 6)、store的维护,变更,不同模块写在不同文件内,分开处理")]),a("p",[t._v(" 7)、引入了mock.js数据……")]),a("p",[t._v(" 8)、其他还有很多细节,有兴趣的同学自己去慢慢发现吧……")]),a("p",[t._v("3、如果觉得该示例不错,对您有一定的帮助,记得帮 "),a("span",{staticClass:"red"},[t._v("star、star、star")]),t._v(" 哦 ^_^ ^_^ ^_^ ^_^~~")]),a("br"),a("h6",[t._v("想了解更多吗?"),a("strong",[a("a",{attrs:{href:"https://github.com/meibin08"}},[t._v("请猛戳我吧!")])])])])],2)],1)},r=[],i={render:e,staticRenderFns:r};s.a=i}}); -------------------------------------------------------------------------------- /assets/js/3.c299a5f8bda21fc21fdd.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([3],{152:function(t,e,n){"use strict";function o(t){n(194)}Object.defineProperty(e,"__esModule",{value:!0});var r=n(196),s=n(197),a=n(60),i=o,p=a(r.a,s.a,i,null,null);e.default=p.exports},194:function(t,e,n){var o=n(195);"string"==typeof o&&(o=[[t.i,o,""]]),o.locals&&(t.exports=o.locals);n(146)("d9bda868",o,!0)},195:function(t,e,n){e=t.exports=n(145)(void 0),e.push([t.i,".login{margin:15% auto 0;text-align:center;width:320px;padding:5px;border-radius:5px;background-color:rgba(0,0,0,.5)}.login .ivu-alert{padding:8px 16px;margin:0}.login .tit{padding:10px 0 20px;text-align:center;font-size:14px}",""])},196:function(t,e,n){"use strict";var o=n(32),r=n.n(o),s=n(61),a=n(33),i=(n(18),n(19)),p=!1;e.a={name:"login-view",mounted:function(){},computed:r()({},Object(s.c)({_Token:"_Token"})),methods:r()({},Object(s.b)({submit:"submit"}),{login:function(){var t=this,e=this.$route.query.redirect;return this.name?this.password?!p&&(p=!0,void this.submit({data:{name:this.name,password:this.password},success:function(n){i.a.setCookie("userToken",t._Token),t.$router.replace({path:e}),p=!1}})):void a.a.warning("请输入您的密码"):void a.a.warning("请输入您的用户名")}}),data:function(){this.$route.query.name;return{modal:!1,name:"",password:""}}}},197:function(t,e,n){"use strict";var o=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"login"},[n("Alert",[n("h4",{staticClass:"tit"},[t._v("登录")]),n("Form",{ref:"formInline"},[n("Form-item",{attrs:{prop:"user"}},[n("Input",{attrs:{type:"text",placeholder:"Username"},model:{value:t.name,callback:function(e){t.name=e},expression:"name"}},[n("Icon",{attrs:{type:"ios-person-outline"},slot:"prepend"})],1)],1),n("Form-item",{attrs:{prop:"password"}},[n("Input",{attrs:{type:"password",placeholder:"Password"},model:{value:t.password,callback:function(e){t.password=e},expression:"password"}},[n("Icon",{attrs:{type:"ios-locked-outline"},slot:"prepend"})],1)],1),n("p",[t._v(t._s(t._Token))]),n("Form-item",[n("Button",{attrs:{type:"primary"},on:{click:t.login}},[t._v("登录")])],1)],1)],1)],1)},r=[],s={render:o,staticRenderFns:r};e.a=s}}); -------------------------------------------------------------------------------- /src/pages/Login/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 85 | 86 | 90 | -------------------------------------------------------------------------------- /src/components/App/App.scss: -------------------------------------------------------------------------------- 1 | html,body{ 2 | width:100%; 3 | height:100%; 4 | } 5 | body{ 6 | background: url(./images/bg2.png) center center repeat; 7 | background-color:#f5f5f5; 8 | 9 | } 10 | .app-content{ 11 | 12 | .red{ 13 | color:#e42323 14 | } 15 | .main{ 16 | width:1000px; 17 | max-width:1000px; 18 | margin:20px auto; 19 | overflow: hidden; 20 | } 21 | } 22 | 23 | 24 | body{ 25 | 26 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; 27 | font-size :15px; 28 | background-color: lighten(#eceef1, 30%); 29 | margin :0; 30 | padding-top :55px; 31 | color: #34495e; 32 | overflow-y :scroll 33 | } 34 | 35 | a{ 36 | color :#34495e; 37 | text-decoration :none 38 | } 39 | .header{ 40 | background-color :#333; 41 | position: fixed; 42 | z-index: 999; 43 | height :55px; 44 | top: 0; 45 | left: 0; 46 | right: 0} 47 | .inner{ 48 | max-width: 800px; 49 | box-sizing: border-box; 50 | margin :0px auto; 51 | padding :15px 5px; 52 | a{ 53 | color: rgba(255, 255, 255, .8); 54 | line-height: 24px; 55 | transition :color .15s ease; 56 | display :inline-block; 57 | vertical-align: middle; 58 | font-weight :300; 59 | letter-spacing :.075em; 60 | margin-right: 1.8em; 61 | &:hover{ 62 | color :#fff 63 | } 64 | &.router-link-active{ 65 | color :#fff; 66 | font-weight :400 67 | } 68 | &:nth-child(6){ 69 | margin-right: 0 70 | } 71 | } 72 | } 73 | .github{ 74 | color: #fff; 75 | font-size :.9em; 76 | margin: 0; 77 | float: right 78 | } 79 | 80 | .logo{ 81 | width: 24px; 82 | margin-right: 10px; 83 | display: inline-block; 84 | vertical-align: middle 85 | } 86 | 87 | .view{ 88 | max-width: 800px; 89 | margin: 0 auto; 90 | position :relative 91 | } 92 | 93 | .fade-enter-active, .fade-leave-active{ 94 | transition: all .2s ease 95 | } 96 | 97 | .fade-enter, .fade-leave-active{ 98 | opacity: 0 99 | } 100 | 101 | @media (max-width: 860px){ 102 | .header .inner{ 103 | padding :15px 30px 104 | } 105 | } 106 | 107 | @media (max-width: 600px){ 108 | .header{ 109 | .inner{ 110 | padding: 15px} 111 | a{ 112 | margin-right: 1em 113 | } 114 | .github{ 115 | display: none 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /src/components/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 88 | 89 | 103 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ssr", 3 | "description": "A Vue.js project", 4 | "author": "Bin.Mei ", 5 | "private": true, 6 | "scripts": { 7 | "dev": "node devServer", 8 | "start": "cross-env NODE_ENV=production node app", 9 | "build": "rimraf assets && npm run build:client && npm run build:server", 10 | "build:client": "cross-env NODE_ENV=production webpack --config build/client/webpack.client.prd.config.js --progress --hide-modules", 11 | "build:server": "cross-env NODE_ENV=production webpack --config build/server/webpack.server.prd.config.js --progress --hide-modules" 12 | }, 13 | "engines": { 14 | "node": ">=6.0", 15 | "npm": ">=3.0" 16 | }, 17 | "dependencies": { 18 | "compression": "^1.6.2", 19 | "cookie-parser": "^1.4.3", 20 | "debug": "^2.2.0", 21 | "log4js": "^0.6.38", 22 | "morgan": "^1.7.0", 23 | "cross-env": "^4.0.0", 24 | "es6-promise": "^4.1.0", 25 | "express": "^4.15.2", 26 | "extract-text-webpack-plugin": "^2.1.0", 27 | "firebase": "^3.7.2", 28 | "lru-cache": "^4.0.2", 29 | "serve-favicon": "^2.4.1", 30 | "http-proxy": "^1.14.0", 31 | "vue": "^2.4.1", 32 | "vue-router": "^2.7.0", 33 | "vue-resource": "^1.2.1", 34 | "vue-server-renderer": "^2.4.1", 35 | "vuex": "^2.3.1", 36 | "vuex-router-sync": "^4.1.2" 37 | }, 38 | "devDependencies": { 39 | "autoprefixer": "^6.7.7", 40 | "axios": "^0.16.2", 41 | "babel-core": "^6.24.1", 42 | "babel-loader": "^7.1.1", 43 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 44 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 45 | "babel-plugin-transform-runtime": "^6.3.13", 46 | "babel-polyfill": "^6.22.0", 47 | "babel-preset-env": "^1.4.0", 48 | "babel-preset-es2015": "^6.0.0", 49 | "babel-preset-stage-0": "^6.5.0", 50 | "copy-webpack-plugin": "^4.0.1", 51 | "css-loader": "^0.28.0", 52 | "file-loader": "^0.11.1", 53 | "friendly-errors-webpack-plugin": "^1.6.1", 54 | "glob": "^7.1.1", 55 | "html5-history-api": "^4.2.8", 56 | "iview": "^2.0.0-rc.19", 57 | "node-sass": "^3.7.0", 58 | "rimraf": "^2.6.1", 59 | "sass-loader": "^4.1.1", 60 | "style-loader": "^0.18.2", 61 | "stylus": "^0.54.5", 62 | "stylus-loader": "^3.0.1", 63 | "sw-precache-webpack-plugin": "^0.10.1", 64 | "url-loader": "^0.5.8", 65 | "vue-loader": "^13.0.1", 66 | "vue-style-loader": "^3.0.0", 67 | "vue-template-compiler": "^2.4.1", 68 | "webpack": "^3.2.0", 69 | "webpack-dev-middleware": "^1.10.1", 70 | "webpack-hot-middleware": "^2.17.1", 71 | "webpack-merge": "^4.0.0", 72 | "webpack-node-externals": "^1.5.4" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/pages/Home/List/index.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 99 | 100 | 104 | -------------------------------------------------------------------------------- /assets/js/1.6e3d48efa6a1fec884fd.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([1],{148:function(t,e,a){"use strict";function o(t){a(160)}Object.defineProperty(e,"__esModule",{value:!0});var s=a(162),n=a(169),r=a(60),i=o,c=r(s.a,n.a,i,null,null);e.default=c.exports},160:function(t,e,a){var o=a(161);"string"==typeof o&&(o=[[t.i,o,""]]),o.locals&&(t.exports=o.locals);a(146)("fe28d920",o,!0)},161:function(t,e,a){e=t.exports=a(145)(void 0),e.push([t.i,".home{max-width:100%}.home .ivu-col{margin-bottom:10px}.home .mt-10{margin-top:10px;text-align:right}.home .img{width:50px;height:30px}",""])},162:function(t,e,a){"use strict";var o=a(32),s=a.n(o),n=a(61),r=a(163);e.a={name:"home-view",components:{Banner:r.a},computed:s()({},Object(n.c)({_home:"_home"}),{home_error:function(){return this.$store.state.home.home_error}}),methods:s()({},Object(n.b)({home:"home"}),{goToList:function(t){return this.$router.push({path:"/home/list",query:{name:t}})}}),asyncData:function(t){var e=t.store,a=t.route.params.id;return e.dispatch("home",{id:a})}}},163:function(t,e,a){"use strict";function o(t){a(164)}var s=a(166),n=a(60),r=o,i=n(null,s.a,r,null,null);e.a=i.exports},164:function(t,e,a){var o=a(165);"string"==typeof o&&(o=[[t.i,o,""]]),o.locals&&(t.exports=o.locals);a(146)("18c02a52",o,!0)},165:function(t,e,a){e=t.exports=a(145)(void 0),e.push([t.i,".app-banner{width:100%;overflow:hidden}.app-banner .demo-carousel{width:100%;text-align:center;background-color:#eadcc7;height:300px}.app-banner .demo-carousel img{max-width:100%}.app-banner .demo-carousel.c3{background-color:#1b9bb5}.app-banner .demo-carousel.c4{background-color:#b30038}",""])},166:function(t,e,a){"use strict";var o=function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"app-banner"},[o("Carousel",{attrs:{autoplay:"",height:300,arrow:"never"}},[o("Carousel-item",[o("div",{staticClass:"demo-carousel"},[o("img",{attrs:{src:a(167),alt:""}})])]),o("Carousel-item",[o("div",{staticClass:"demo-carousel"},[o("img",{attrs:{src:a(168),alt:""}})])]),o("Carousel-item",[o("div",{staticClass:"demo-carousel c3"},[o("img",{attrs:{src:"http://img3.job1001.com/zhuanti/android/images/zsj/banner3.png",alt:""}})])]),o("Carousel-item",[o("div",{staticClass:"demo-carousel c4"},[o("img",{attrs:{src:"http://img3.job1001.com/zhuanti/android/images/zsj/banner1.png",alt:""}})])])],1)],1)},s=[],n={render:o,staticRenderFns:s};e.a=n},167:function(t,e,a){t.exports=a.p+"images/img01.png?96d6f287b3c6d23d6e7aebdd273ff7a8"},168:function(t,e,a){t.exports=a.p+"images/img02.png?2113fefce936df86c4ca7d27fae8b0ce"},169:function(t,e,a){"use strict";var o=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"home"},[a("banner"),a("div",{staticClass:"content main"},[t._home&&t._home.length?a("Row",{attrs:{type:"flex",gutter:10}},t._l(t._home,function(e){return a("Col",{key:e.id,attrs:{span:"8"}},[a("Card",[a("p",{slot:"title"},[t._v("作者:"+t._s(e.title))]),a("p",[t._v("时间:"+t._s(e.date))]),a("p",[t._v("签名:"+t._s(e.content))]),a("p",[t._v("共发表过"),a("span",{staticClass:"red"},[t._v(t._s(e.sum))]),t._v("文章")]),a("p",{staticClass:"mt-10"},[a("Button",{attrs:{type:"info",size:"small"},on:{click:function(a){t.goToList(e.title)}}},[t._v("查看列表")])],1)])],1)})):a("h1",[t._v(t._s(t.home_error))])],1)],1)},s=[],n={render:o,staticRenderFns:s};e.a=n}}); -------------------------------------------------------------------------------- /src/utils/format.js: -------------------------------------------------------------------------------- 1 | import { isNotEmpty, isIdCard } from './validate'; 2 | 3 | const client = process.env.VUE_ENV == "client"; 4 | let format = { 5 | 6 | // 格式化日期 7 | date: function (date, fmt) { 8 | if (!date || !fmt) { 9 | return date; 10 | } 11 | if (date.length == 8) { 12 | date = date.substr(0, 4) + '-' + date.substr(4, 2) + '-' + date.substr(6, 2) 13 | } 14 | date = new Date(date.toString().replace(/-/g, "/")); 15 | var o = { 16 | "M+": date.getMonth() + 1, //月份 17 | "d+": date.getDate(), //日 18 | "h+": date.getHours(), //小时 19 | "m+": date.getMinutes(), //分 20 | "s+": date.getSeconds(), //秒 21 | "q+": Math.floor((date.getMonth() + 3) / 3), //季度 22 | "S": date.getMilliseconds() //毫秒 23 | }; 24 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); 25 | for (var k in o) 26 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 27 | return fmt; 28 | }, 29 | 30 | // 根据身份证获取出生年月 31 | getBirthdayByIdCard(idCard) { 32 | if (!isIdCard(idCard)) { 33 | return; 34 | } 35 | let tmpStr; 36 | if (idCard.length == 15) { 37 | tmpStr = idCard.substring(6, 12); 38 | tmpStr = "19" + tmpStr; 39 | tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6) 40 | return tmpStr; 41 | } else { 42 | tmpStr = idCard.substring(6, 14); 43 | tmpStr = tmpStr.substring(0, 4) + "-" + tmpStr.substring(4, 6) + "-" + tmpStr.substring(6) 44 | return tmpStr; 45 | } 46 | }, 47 | 48 | // 根据身份证获取性别 49 | getSexByIdCard(idCard) { 50 | if (!isIdCard(idCard)) { 51 | return; 52 | } 53 | return (parseInt(idCard.substr(16, 1)) % 2) 54 | }, 55 | setCookie(name,value) 56 | { 57 | var Days = 7; 58 | var exp = new Date(); 59 | exp.setTime(exp.getTime() + Days*24*60*60*1000); 60 | document.cookie = name + "="+ escape (value) + ";expires=" + exp.toGMTString() + ";path=/"; 61 | }, 62 | 63 | //读取cookies 64 | getCookie(name) 65 | { 66 | var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)"); 67 | 68 | if(arr=document.cookie.match(reg)) 69 | 70 | return unescape(arr[2]); 71 | else 72 | return null; 73 | } , 74 | //删除cookies 75 | removeCookie(name) 76 | { 77 | 78 | var exp = new Date(); 79 | exp.setTime(exp.getTime() - 1); 80 | var cval=getCookie(name); 81 | if(cval!=null) document.cookie= name + "="+cval+";expires="+exp.toGMTString() + ";path=/"; 82 | } , 83 | clientCookie(){ 84 | let result = {}; 85 | let cookies = document.cookie; 86 | if(!cookies){ 87 | return result; 88 | }; 89 | let str = cookies.split(";"); 90 | for(var i=0;i store - home 模块 7 | */ 8 | import StaticToast from 'src/components/common/Toast/index'; 9 | import {fetchSingle,fetchGroup} from "src/utils/fetch"; 10 | 11 | /* 12 | * @ home 模块 type 常量; 用于更新不同的操作数据 13 | */ 14 | export const LOGIN = 'LOGIN'; 15 | export const SET_HOME = 'SET_HOME'; 16 | export const SET_LIST = 'SET_LIST'; 17 | export const SET_DETAILS = 'SET_DETAILS'; 18 | export const SET_ERROR = 'SET_ERROR'; 19 | 20 | 21 | /* 22 | * @ home 模块 initState; 23 | */ 24 | const state = { 25 | home:[], 26 | home_error:"请求出错啦", 27 | list:[], 28 | details:{} 29 | }; 30 | 31 | /* 32 | * @ home 模块 getters - 对外数据接收器 function 格式 以 _ 开头 33 | */ 34 | const getters = { 35 | _home (state) { 36 | /* 37 | * @ home页面数据 38 | */ 39 | return state.home 40 | }, 41 | _list(state){ 42 | /* 43 | * @ home -> list 页面数据 44 | */ 45 | return state.list; 46 | }, 47 | _details(state){ 48 | /* 49 | * @ home -> list -> detail 页面数据 50 | */ 51 | return state.details; 52 | } 53 | 54 | }; 55 | 56 | /* 57 | * @ home 模块 actions 58 | */ 59 | const actions = { 60 | 61 | 62 | home: ({commit, state}) =>{ 63 | /* 64 | * @ 首页初始数据 65 | */ 66 | return fetchSingle({url:'/api/test/homeList', data:{city: state.city}}).then(res=>{ 67 | // console.log(32,'首页初始数据',res); 68 | if(res.code == 0){ 69 | commit(SET_HOME, {home: res.data}); 70 | }else{ 71 | commit(SET_ERROR, {message: res.message,"key":"home_error"}); 72 | }; 73 | },error=>{ 74 | console.log(1555,error) 75 | }); 76 | }, 77 | 78 | list:({commit, state},{name,userToken})=>{ 79 | /* 80 | * @ 文章列表页初始数据 81 | */ 82 | return fetchSingle({url:'/api/test/sectionList',data:{name,userToken},type:"POST"}).then(res=>{ 83 | // console.log(32,res); 84 | if(res.code == 0){ 85 | commit(SET_LIST, {list: res.data}); 86 | }else{ 87 | commit(SET_ERROR, {message: res.message,"key":"list_error"}); 88 | }; 89 | },error=>{ 90 | console.log(35,error) 91 | }); 92 | return ; 93 | return fetchGroup([ 94 | {url:'/api/test/section_list', data:{name},type:"POST"}, 95 | {url:'/api/test/list', data:{city: state.city}}, 96 | ]).then(res=>{ 97 | if(res[0].code == 0){ 98 | commit(SET_LIST, {list: res[0].data.list}); 99 | }else{ 100 | 101 | commit(SET_LIST, {list: res.data,"key":"list_error"}); 102 | } 103 | },error=>{ 104 | console.log(35,error); 105 | }); 106 | }, 107 | 108 | details: ({commit, state},{id,userToken}) =>{ 109 | /* 110 | * @ 文章详情页数据 111 | */ 112 | return fetchSingle({url:`/api/test/details/${id}`,type:"POST",data:{id,userToken}}).then(res=>{ 113 | if(res.code == 0){ 114 | // console.log(48,res) 115 | commit(SET_DETAILS, {details: res.data}); 116 | }else{ 117 | commit(SET_ERROR, {message: res.message,"key":"home_error"}); 118 | }; 119 | },error=>{ 120 | console.log(1555,error) 121 | });; 122 | } 123 | 124 | }; 125 | 126 | /* 127 | * @ home 模块 mutations - 数据更新 128 | */ 129 | const mutations = { 130 | [SET_HOME](state, { home }) { 131 | state.home = home 132 | }, 133 | [SET_LIST](state, { list }) {//console.log("setHome",home); 134 | state.list = list 135 | }, 136 | [SET_DETAILS](state, { details }) {//console.log("setHome",home); 137 | state.details = details 138 | }, 139 | [SET_ERROR](state, { message,key }) { 140 | state.home_error = message; 141 | console.log("sssetHome",state); 142 | } 143 | 144 | }; 145 | 146 | 147 | export default { 148 | state, 149 | getters, 150 | actions, 151 | mutations 152 | } 153 | -------------------------------------------------------------------------------- /src/utils/fetchApi/fetch.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author monkeywang 3 | * Date: 17/3/14 4 | */ 5 | import Vue from 'vue' 6 | import axios from 'axios' 7 | // import { createApp } from 'src/app' 8 | import { createRouter } from 'src/pages/routes' 9 | const router = createRouter() 10 | // const { app, router, store } = createApp(); 11 | 12 | const client = process.env.VUE_ENV == "client"; 13 | 14 | axios.defaults.timeout = 5000; 15 | axios.defaults.baseURL = client?"/zaApi/easy":'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c';; 16 | 17 | // http request 拦截器 18 | axios.interceptors.request.use( 19 | config => { 20 | let token = localStorage.getItem("token"); 21 | if (token) { 22 | config.headers.Authorization = `token ${token}`; 23 | } 24 | return config; 25 | }, 26 | err => { 27 | return Promise.reject(err); 28 | }); 29 | 30 | // http response 拦截器 31 | axios.interceptors.response.use( 32 | response => { 33 | return response; 34 | }, 35 | error => { 36 | if (error.response) { 37 | switch (error.response.status) { 38 | case 401: 39 | console.log(error); 40 | router.replace({ 41 | path: 'login', 42 | query: {redirect: router.currentRoute.fullPath} 43 | }) 44 | break; 45 | case 404: 46 | case 500: 47 | router.replace({ 48 | path: 'notFound', 49 | query: {redirect: router.currentRoute.fullPath} 50 | }) 51 | break; 52 | 53 | } 54 | }; 55 | 56 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 57 | return Promise.reject(error.response.data) 58 | }); 59 | 60 | const fetch =(options)=>{ 61 | let { url, type, data, ...others } = options; 62 | let opts = { 63 | ...others, 64 | method: (type || 'get').toUpperCase(), 65 | // credentials: 'include', 66 | data, 67 | url, 68 | headers: { 69 | 'Accept': 'application/json', 70 | 'Content-Type': 'application/json' 71 | } 72 | } 73 | return axios(opts); 74 | } 75 | 76 | export const fetchJson = (options)=>{ 77 | 78 | fetch(options).then(resData => toJson(resData, options)) 79 | .then(resData => resHandler(resData, options)) 80 | }; 81 | export const fetchJsonAll = (queue)=>{ 82 | var promises = (queue||[]).map(function(item){ 83 | return fetch(item); 84 | }); 85 | axios.all(promises) 86 | .then(axios.spread((res1, res2)=>{ 87 | console.log(res1) 88 | console.log(res2) 89 | })).catch(err=>{ console.log(err) }) 90 | 91 | 92 | }; 93 | 94 | 95 | 96 | 97 | function toJson(resp, options) { 98 | if (resp.status >= 400) { 99 | return errorHandler(null, options, resp.status) 100 | } 101 | return resp; 102 | } 103 | 104 | // 请求成功处理 105 | function resHandler(resData, options) { 106 | // Loading(false); 107 | if (resData.status && resData.status != 200) { 108 | return errorHandler(resData.error, options, resData.status); 109 | } 110 | if (!resData || (resData.data&& resData.data.code > 20000)) { 111 | // options.error && options.error(resData.data); 112 | /*router.replace({ 113 | path: 'login', 114 | query: {redirect: router.currentRoute.fullPath} 115 | });*/ 116 | return ; 117 | // StaticToast.error(resData.message); 118 | } else { 119 | options.success && options.success(resData.data); 120 | }; 121 | 122 | } 123 | 124 | // 异常处理 125 | function errorHandler(error, options, status) { 126 | // Loading(false); 127 | options.error && options.error(error); 128 | console.log(`网络异常,请稍后重试(${status})`); 129 | // ErrorLogs(options._url,{status,error}); 130 | } 131 | 132 | 133 | 134 | 135 | export default axios; 136 | -------------------------------------------------------------------------------- /src/utils/fetchApi/fetch-client-(备份20160720).js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @authors :Bin Mei 4 | * @date :2017-07-20 5 | * @description:pc官网 -> api请求 -客户端 6 | */ 7 | 8 | import Vue from 'vue' 9 | import axios from 'axios' 10 | // import { createApp } from 'src/app' 11 | import { createRouter } from 'src/pages/routes' 12 | const router = createRouter() 13 | // const { app, router, store } = createApp(); 14 | 15 | const client = process.env.VUE_ENV == "client"; 16 | 17 | axios.defaults.timeout = 5000; 18 | axios.defaults.baseURL = client?"/zaApi/easy":'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c';; 19 | 20 | // http request 拦截器 21 | axios.interceptors.request.use( 22 | config => { 23 | let token = "dsfdsagfdsgfdsgfdsgfdg";//localStorage.getItem("token"); 24 | if (token) { 25 | config.headers.Authorization = `token ${token}`; 26 | } 27 | return config; 28 | }, 29 | err => { 30 | return Promise.reject(err); 31 | }); 32 | 33 | // http response 拦截器 34 | axios.interceptors.response.use( 35 | response => { 36 | 37 | if (response.status >= 200 && response.status < 300) { 38 | return response; 39 | } 40 | return Promise.reject(response); 41 | }, 42 | error => { 43 | 44 | if (error.response) { 45 | switch (error.response.status) { 46 | case 401: 47 | console.log(error); 48 | router.replace({ 49 | path: 'login', 50 | query: {redirect: router.currentRoute.fullPath} 51 | }) 52 | break; 53 | case 404: 54 | case 500: 55 | //以提示的方式处理 56 | 57 | /*router.replace({ 58 | path: 'notFound', 59 | query: {redirect: router.currentRoute.fullPath} 60 | })*/ 61 | break; 62 | 63 | } 64 | }; 65 | 66 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 67 | return Promise.reject(error.response) 68 | }); 69 | 70 | const fetch =(options)=>{ 71 | let { url, type, data, ...others } = options; 72 | let opts = { 73 | ...others, 74 | method: (type || 'get').toUpperCase(), 75 | // credentials: 'include', 76 | data, 77 | url, 78 | headers: { 79 | 'Accept': 'application/json', 80 | 'Content-Type': 'application/json' 81 | } 82 | } 83 | 84 | return new Promise((resolve, reject)=>{ 85 | axios(opts) 86 | .then(resData =>{ 87 | 88 | resolve(resData) 89 | }).catch((error) => { 90 | reject(error); 91 | }); 92 | }); 93 | // return axios(opts).then(resData => resData); 94 | } 95 | 96 | export const fetchJson = (options)=>{ 97 | return fetch(options).then(res=>{ 98 | 99 | return resHandler(res, options); 100 | },(err) =>{ 101 | console.log(121,err) 102 | return errorHandler(err, options, err.status); 103 | }); 104 | // return fetch(options).then(res=>resHandler(res, options),(err) =>resHandler(err, options)); 105 | // fetch(options).then(resData => toJson(resData, options)).then(resData => resHandler(resData, options)) 106 | }; 107 | export const fetchJsonAll = (queue)=>{ 108 | var promises = (queue||[]).map(function(item){ 109 | return fetch(item); 110 | }); 111 | axios.all(promises) 112 | .then(axios.spread((res1, res2)=>{ 113 | console.log(res1) 114 | console.log(res2) 115 | })).catch(err=>{ console.log(err) }) 116 | 117 | 118 | }; 119 | 120 | 121 | 122 | 123 | function toJson(resp, options) { 124 | if (resp.status >= 400) { 125 | return errorHandler(null, options, resp.status) 126 | } 127 | return resp; 128 | } 129 | 130 | // 请求成功处理 131 | function resHandler(resData, options) { 132 | 133 | // Loading(false); 134 | if (resData.status && resData.status != 200) { 135 | return errorHandler(resData.data, options, resData.status); 136 | } 137 | if (!resData || (resData.data&& resData.data.code > 20000)) { 138 | // options.error && options.error(resData.data); 139 | router.replace({ 140 | path: 'login', 141 | query: {redirect: router.currentRoute.fullPath} 142 | }); 143 | return ; 144 | // StaticToast.error(resData.message); 145 | } else { 146 | options.success && options.success(resData.data); 147 | }; 148 | 149 | } 150 | 151 | // 异常处理 152 | function errorHandler(error, options, status) { 153 | // Loading(false); 154 | options.error && options.error(error.data); 155 | console.log(`网络异常,请稍后重试(${status})`); 156 | // ErrorLogs(options._url,{status,error}); 157 | } 158 | 159 | 160 | 161 | 162 | export default axios; 163 | -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | 2 | const MOBILE_REG = /^1[3|4|5|8|7|9][0-9]\d{8}$/, 3 | EMAIL_REG = /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/, 4 | MONEY_REG = /^([1-9][\d]{0,7}|0)(\.[\d]{1,2})?$/, 5 | NAME_REG = /^([\u4e00-\u9fa5]+|[a-zA-Z0-9]+)$/, 6 | CHINESE_REG = /^[\u4e00-\u9fa5]+$/, 7 | BANKNO_REG=/^\d{16,19}$/, 8 | PWD_REG = /(\d(?!\d{5})|[A-Za-z](?![A-Za-z]{5})){6}/; 9 | 10 | function isRule(regText, value) { 11 | if (!value || value.length == 0) 12 | return true 13 | 14 | const reg = new RegExp(regText) 15 | if (!reg.test(value)) { 16 | return false 17 | } 18 | return true 19 | } 20 | 21 | module.exports = { 22 | 23 | isFromWeixin: () => { 24 | let ua = window.navigator.userAgent.toLowerCase(); 25 | if (ua.match(/MicroMessenger/i) == "micromessenger") { 26 | return true; 27 | } else { 28 | return false; 29 | } 30 | }, 31 | isIos(){ 32 | let userAgent= navigator.userAgent; 33 | let IsiOS = !!userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端 34 | return IsiOS; 35 | }, 36 | isMobile: (mobile) => { 37 | return isRule(MOBILE_REG, mobile) 38 | }, 39 | 40 | isEmail: (email) => { 41 | return isRule(EMAIL_REG, email) 42 | }, 43 | 44 | isMoney: (money) => { 45 | return isRule(MONEY_REG, money) 46 | }, 47 | 48 | isUsername: (name) => { 49 | return isRule(NAME_REG, name) 50 | }, 51 | isChinese: (name) => { 52 | return isRule(CHINESE_REG, name) 53 | }, 54 | isBankNo: (name) => { 55 | return isRule(BANKNO_REG, name) 56 | }, 57 | 58 | isNotEmpty: (data) => { 59 | return data && (data.length > 0) 60 | }, 61 | 62 | isPwd: (pwd) => { 63 | return isRule(PWD_REG, pwd) 64 | }, 65 | isSame: (data1, data2) => { 66 | return data1 === data2 67 | }, 68 | isIdCard: (card) => { 69 | if (!card) return true; 70 | var num = card.toUpperCase(); 71 | //身份证号码为15位或者18位,15位时全为数字,18位前17位为数字,最后一位是校验位,可能为数字或字符X。 72 | if (!(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(num))) { 73 | return false; 74 | } 75 | //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。 76 | //下面分别分析出生日期和校验位 77 | var len, re; 78 | var birthday,sex; 79 | len = num.length; 80 | if (len == 15) { 81 | 82 | //获取出生日期 83 | birthday = '19' + card.substring(6, 8) + "-" + card.substring(8, 10) + "-" + card.substring(10, 12); 84 | //获取性别 85 | sex = parseInt(card.substr(14, 1)) % 2 == 1 ? 'M' : 'F'; 86 | 87 | re = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/); 88 | var arrSplit = num.match(re); 89 | 90 | //检查生日日期是否正确 91 | var dtmBirth = new Date('19' + arrSplit[2] + '/' + arrSplit[3] + '/' + arrSplit[4]); 92 | var bGoodDay; 93 | bGoodDay = (dtmBirth.getYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4])); 94 | if (!bGoodDay) { 95 | return false; 96 | } else { 97 | //将15位身份证转成18位 98 | //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。 99 | var valnum; 100 | var arrInt = new Array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); 101 | var arrCh = new Array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); 102 | var nTemp = 0, 103 | i; 104 | 105 | num = num.substr(0, 6) + '19' + num.substr(6, num.length - 6); 106 | for (i = 0; i < 17; i++) { 107 | nTemp += num.substr(i, 1) * arrInt[i]; 108 | } 109 | num += arrCh[nTemp % 11]; 110 | } 111 | } else if (len == 18) { 112 | 113 | //获取出生日期 114 | birthday = card.substring(6, 10) + "-" + card.substring(10, 12) + "-" + card.substring(12, 14); 115 | //获取性别 116 | sex = parseInt(card.substr(16, 1)) % 2 == 1 ? 'M' : 'F'; 117 | 118 | re = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/); 119 | var arrSplit = num.match(re); 120 | 121 | //检查生日日期是否正确 122 | var dtmBirth = new Date(arrSplit[2] + "/" + arrSplit[3] + "/" + arrSplit[4]); 123 | var bGoodDay; 124 | bGoodDay = (dtmBirth.getFullYear() == Number(arrSplit[2])) && ((dtmBirth.getMonth() + 1) == Number(arrSplit[3])) && (dtmBirth.getDate() == Number(arrSplit[4])); 125 | if (!bGoodDay) { 126 | return false; 127 | } else { 128 | //检验18位身份证的校验码是否正确。 129 | //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。 130 | var valnum; 131 | var arrInt = new Array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2); 132 | var arrCh = new Array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'); 133 | var nTemp = 0, 134 | i; 135 | for (i = 0; i < 17; i++) { 136 | nTemp += num.substr(i, 1) * arrInt[i]; 137 | } 138 | valnum = arrCh[nTemp % 11]; 139 | if (valnum != num.substr(17, 1)) { 140 | return false; 141 | } 142 | } 143 | } 144 | return { 145 | birthday: birthday, 146 | sex: sex 147 | } 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/utils/fetchApi/fetch-client.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @authors :Bin Mei 4 | * @date :2017-07-20 5 | * @description:pc官网 -> api请求 -客户端 6 | */ 7 | 8 | import Vue from 'vue' 9 | import axios from 'axios' 10 | // import { createApp } from 'src/app' 11 | import { createRouter } from 'src/pages/routes' 12 | import { createStore } from 'src/store' 13 | const router = createRouter() 14 | const store = createStore() 15 | 16 | const client = process.env.VUE_ENV == "client"; 17 | 18 | //取客户端cookie; 19 | const getCookie = cookies => { 20 | let cookie = '' 21 | Object.keys(cookies).forEach(item => { 22 | cookie+= item + '=' + cookies[item] + '; ' 23 | }) 24 | return cookie; 25 | } 26 | 27 | 28 | 29 | axios.defaults.timeout = 5000; 30 | axios.defaults.baseURL = client?"/zaApi/easy":'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c'; 31 | 32 | // http request 拦截器 33 | axios.interceptors.request.use( 34 | config => { 35 | let token = "dsfdsagfdsgfdsgfdsgfdg";//localStorage.getItem("token"); 36 | 37 | //把取到的cookie设置在请求头部; 38 | if (store.state.cookies &&!client) { 39 | config.headers.cookie =getCookie(store.state.cookies); 40 | }; 41 | return config; 42 | }, 43 | err => { 44 | return Promise.reject(err); 45 | }); 46 | 47 | // http response 拦截器 48 | axios.interceptors.response.use( 49 | response => { 50 | 51 | if (response.status >= 200 && response.status < 300) { 52 | return response; 53 | } 54 | return Promise.reject(response); 55 | }, 56 | error => { 57 | // console.log("57======================",error); 58 | 59 | 60 | //单个、多个队列请求可统一在这里处理 登录失效等逻辑 61 | if (error.response) { 62 | switch (error.response.status) { 63 | case 401: 64 | console.log(error); 65 | router.replace({ 66 | path: 'login', 67 | query: {redirect: router.currentRoute.fullPath} 68 | }) 69 | break; 70 | case 404: 71 | case 500: 72 | //以提示的方式处理 73 | 74 | /*router.replace({ 75 | path: 'notFound', 76 | query: {redirect: router.currentRoute.fullPath} 77 | })*/ 78 | break; 79 | 80 | } 81 | }; 82 | 83 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 84 | return Promise.reject(error) 85 | }); 86 | 87 | const fetch =(options)=>{ 88 | let { url, type, data, ...others } = options; 89 | let opts = { 90 | ...others, 91 | method: (type || 'get').toUpperCase(), 92 | data, 93 | url, 94 | headers: { 95 | 'Accept': 'application/json', 96 | 'Content-Type': 'application/json' 97 | } 98 | } 99 | 100 | return new Promise((resolve, reject)=>{ 101 | axios(opts) 102 | .then(resData =>{ 103 | resolve(resData) 104 | }).catch((error) => { 105 | reject((error||{message: '网络异常,请刷新重试', type: 1})); 106 | }); 107 | }); 108 | } 109 | 110 | export const fetchJson = (options)=>{ 111 | return fetch(options).then(res=>resHandler(res),(err) =>errorHandler(err, err.status)); 112 | // return fetch(options).then(res=>resHandler(res, options),(err) =>resHandler(err, options)); 113 | // fetch(options).then(resData => toJson(resData, options)).then(resData => resHandler(resData, options)) 114 | }; 115 | export const fetchJsonAll = (queue)=>{ 116 | var promises = (queue||[]).map(function(item){ 117 | return fetch(item); 118 | }); 119 | return axios.all(promises) 120 | .then(res=>{ 121 | //多个队列请求,只安第一个返回的结果处理 未登录状态; 122 | let firstItem = res[0]; 123 | if (firstItem && firstItem.data&& firstItem.data.code > 20000) { 124 | router.replace({ 125 | path: 'login', 126 | query: {redirect: router.currentRoute.fullPath} 127 | }); 128 | return ; 129 | } ; 130 | let result = (res||[]).map((item)=>{ 131 | return item.data; 132 | }) 133 | return Promise.resolve(result); 134 | },(err) =>errorHandler(err.data||err, err.status)) 135 | }; 136 | 137 | 138 | 139 | 140 | function toJson(resp, options) { 141 | if (resp.status >= 400) { 142 | return errorHandler(null, options, resp.status) 143 | } 144 | return resp; 145 | } 146 | 147 | // 请求成功处理 148 | function resHandler(resData, options) { 149 | 150 | if (resData.status && resData.status != 200) { 151 | return errorHandler(resData.data||resData, resData.status); 152 | } 153 | if (!resData || (resData.data&& resData.data.code > 20000)) { 154 | router.replace({ 155 | path: 'login', 156 | query: {redirect: router.currentRoute.fullPath} 157 | }); 158 | return ; 159 | } else { 160 | return Promise.resolve(resData.data); 161 | }; 162 | } 163 | // 异常处理 164 | function errorHandler(error, status) { 165 | console.log(`网络异常,请稍后重试(${status})`); 166 | return Promise.reject(error.data||error); 167 | } 168 | 169 | 170 | 171 | 172 | 173 | export default axios; 174 | -------------------------------------------------------------------------------- /src/utils/fetch.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * @authors :Bin Mei 4 | * @date :2017-07-20 5 | * @description:pc官网 -> api请求 -客户、服务端都共用引起页面逻辑 6 | */ 7 | 8 | import Vue from 'vue' 9 | import axios from 'axios' 10 | import format from 'src/utils/format' 11 | import { createRouter } from 'src/pages/routes' 12 | import { createStore } from 'src/store/index.js' 13 | const router = createRouter() 14 | const store = createStore() 15 | 16 | const client = process.env.VUE_ENV == "client"; 17 | 18 | 19 | 20 | axios.defaults.timeout = 5000; 21 | axios.defaults.baseURL = client?"/zaApi/easy":'https://easy-mock.com/mock/59294d8e91470c0ac1fe8a4c'; 22 | 23 | // http request 拦截器 24 | axios.interceptors.request.use( 25 | config => { 26 | 27 | if (client) { 28 | //config.headers.cookie =getCookie(format.getAllCookies()); 29 | }; 30 | 31 | return config; 32 | }, 33 | err => { 34 | return Promise.reject(err); 35 | }); 36 | 37 | // http response 拦截器 38 | axios.interceptors.response.use( 39 | response => { 40 | 41 | if (response.status >= 200 && response.status < 300) { 42 | return response; 43 | } 44 | return Promise.reject(response); 45 | }, 46 | error => { 47 | console.log("57======================",error); 48 | 49 | 50 | //单个、多个队列请求可统一在这里处理 登录失效等逻辑 51 | if (error.response) { 52 | switch (error.response.status) { 53 | case 401: 54 | console.log(error); 55 | router.replace({ 56 | path: 'login', 57 | query: {redirect: router.currentRoute.fullPath} 58 | }) 59 | break; 60 | case 404: 61 | case 500: 62 | //以提示的方式处理 63 | 64 | /*router.replace({ 65 | path: 'notFound', 66 | query: {redirect: router.currentRoute.fullPath} 67 | })*/ 68 | break; 69 | 70 | } 71 | }; 72 | 73 | // console.log(JSON.stringify(error));//console : Error: Request failed with status code 402 74 | return Promise.reject(error) 75 | }); 76 | 77 | const fetch =(options)=>{ 78 | /* 79 | 80 | * @ 数据请求 81 | * @ options - url 接口url 结果== baseUrl + url; 82 | * @ options - type 请求类型 post\get等; 83 | * @ options - data 请求数据 object类型; 84 | * @ options - others 其他参数; 85 | @ options - userToken 用户token 服务端 初始化数据方法,请手动传入,客户端会自动带上; 86 | */ 87 | let { url, type, data,userToken, ...others } = options; 88 | let opts = { 89 | ...others, 90 | method: (type || 'get').toUpperCase(), 91 | data, 92 | url, 93 | headers: { 94 | 'Accept': 'application/json', 95 | 'Content-Type': 'application/json' 96 | 97 | } 98 | }; 99 | 100 | return new Promise((resolve, reject)=>{ 101 | axios(opts) 102 | .then(resData =>{ 103 | resolve(resData) 104 | }).catch((error) => { 105 | reject((error||{message: '网络异常,请刷新重试', type: 1})); 106 | }); 107 | }); 108 | } 109 | 110 | 111 | 112 | export const fetchSingle = (options)=>{ 113 | 114 | /* 115 | * @ 单个数据请求 116 | * @ options -- 请参考 fetch 方法 117 | */ 118 | return fetch(options).then(res=>resHandler(res),(err) =>errorHandler(err, err.status)); 119 | }; 120 | export const fetchGroup = (queue)=>{ 121 | var promises = (queue||[]).map(function(item){ 122 | return fetch(item); 123 | }); 124 | return axios.all(promises) 125 | .then(res=>{ 126 | //多个队列请求,只安第一个返回的结果处理 未登录状态; 127 | let firstItem = res[0]; 128 | if (firstItem && firstItem.data&& firstItem.data.code > 20000) { 129 | router.push({ 130 | path: '/login', 131 | query: {redirect: router.currentRoute.fullPath} 132 | }); 133 | return ; 134 | } ; 135 | let result = (res||[]).map((item)=>{ 136 | return item.data; 137 | }) 138 | return Promise.resolve(result); 139 | },(err) =>errorHandler(err.data||err, err.status)) 140 | }; 141 | 142 | 143 | 144 | 145 | function toJson(resp, options) { 146 | if (resp.status >= 400) { 147 | return errorHandler(resp, resp.status) 148 | } 149 | return resp; 150 | } 151 | 152 | function resHandler(resData, options) { 153 | 154 | /* 155 | 156 | * @ 请求成功处理 157 | * @ status -- 当前请求的状态 158 | * @ data -- 业务接口返回的数据 159 | * @ code -- 业务接口状态码 160 | 161 | */ 162 | if (resData.status && resData.status != 200) { 163 | return errorHandler(resData.data||resData, resData.status); 164 | } 165 | if (!resData || (resData.data&& resData.data.code > 20000)) { 166 | router.push({ 167 | path: '/login', 168 | query: {redirect: router.currentRoute.fullPath} 169 | }); 170 | return ; 171 | } else { 172 | return Promise.resolve(resData.data); 173 | }; 174 | } 175 | 176 | function errorHandler(error, status) { 177 | 178 | /* 179 | * @ 异常处理 180 | * @ status -- 当前请求的状态 181 | * @ error||error.data -- 业务接口返回的数据 182 | 183 | */ 184 | console.log(`网络异常,请稍后重试(${status})`); 185 | return Promise.reject(error.data||error); 186 | } 187 | 188 | 189 | axios.prototype.fetchSingle = fetchSingle; 190 | axios.prototype.fetchGroup = fetchGroup; 191 | 192 | export default axios; 193 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | 2 | const fs = require('fs') 3 | const path = require('path') 4 | const resolve = file => path.resolve(__dirname, file) 5 | const httpProxy = require('http-proxy'); 6 | const LRU = require('lru-cache') 7 | const config = require('./config'); 8 | const logger = require('./logger'); 9 | 10 | module.exports = function (app) { 11 | 12 | var insuranceServers = config.INSURANCE_SERVER; 13 | var apiServers = config.API; 14 | 15 | var proxy = httpProxy.createProxyServer(); 16 | 17 | proxy.on('error', function(e) { 18 | logger.error(e); 19 | }); 20 | 21 | /* 22 | *接口代理 23 | */ 24 | 25 | app.all('/zaApi/*', function (req, res) {//接口API代理 26 | var url = req.url; 27 | console.log('req:', url) 28 | var regExp = /\/zaApi\/(.*?)\//, 29 | hostkey = req.url.match(regExp)[1], 30 | target = ''; 31 | req.url = req.url.replace(regExp, '/'); 32 | target = 'http://' + apiServers[hostkey].host; 33 | console.log('zaApi:', target + req.url); 34 | console.log('-------------------------'); 35 | proxy.web(req, res, { 36 | target: target, 37 | changeOrigin: true 38 | }); 39 | }); 40 | 41 | 42 | /** 43 | *html输出 44 | * 45 | */ 46 | const { createBundleRenderer } = require('vue-server-renderer') 47 | const isProd = process.env.NODE_ENV === 'production' 48 | const useMicroCache = process.env.MICRO_CACHE !== 'false' 49 | const serverInfo = 50 | `express/${require('express/package.json').version} ` + 51 | `vue-server-renderer/${require('vue-server-renderer/package.json').version}` 52 | 53 | const template = fs.readFileSync(resolve('../src/views/index.template.html'), 'utf-8') 54 | 55 | function createRenderer (bundle, options) { 56 | 57 | return createBundleRenderer(bundle, Object.assign(options, { 58 | template, 59 | // for component caching 60 | cache: LRU({ 61 | max: 1000, 62 | maxAge: 1000 * 60 * 15 63 | }), 64 | // this is only needed when vue-server-renderer is npm-linked 65 | basedir: resolve('./assets'), 66 | // recommended for performance 67 | runInNewContext: false 68 | })) 69 | } 70 | 71 | let renderer 72 | let readyPromise 73 | //线上环境 74 | if (isProd) { 75 | // In production: create server renderer using built server bundle. 76 | // The server bundle is generated by vue-ssr-webpack-plugin. 77 | const bundle = require('../assets/vue-ssr-server-bundle.json') 78 | // The client manifests are optional, but it allows the renderer 79 | // to automatically infer preload/prefetch links and directly add