├── .babelrc ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── build ├── dev-server.js ├── webpack.config.js └── webpack.dist.config.js ├── index.html ├── package.json ├── postcss.config.js └── src ├── components ├── common.scss ├── layout.vue ├── layout1.vue └── modal.js ├── main.js ├── pages ├── item1 │ └── item1.vue ├── item2 │ └── item2.vue └── item3 │ ├── echarts │ └── line.vue │ ├── option1.vue │ └── option2.vue ├── store ├── index.js └── modules │ └── menu.js ├── testdata └── localdata │ ├── linelist.json │ └── menulist.json └── utils ├── axios.js ├── errorcode.js └── localdata.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["env", { 3 | "targets": { 4 | "browsers": ["last 2 versions"] 5 | } 6 | }]], 7 | "plugins": [ 8 | ["transform-object-rest-spread"] 9 | ] 10 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labnize/vue-vuex-router-element-webpack/665984fab646906002404f6fba82f724c01e79b6/.eslintignore -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // parser: 'babel-eslint', 3 | env: { 4 | browser: true, 5 | commonjs: true, 6 | es6: true 7 | }, 8 | // extends: ['airbnb-base'], 9 | extends: ['plugin:vue/recommended', 'airbnb-base'], 10 | plugins: [ 11 | 'vue' 12 | ], 13 | rules: { 14 | quotes: [2, 'single'], 15 | semi: 2, 16 | 'max-len': [1, 120, 2], 17 | 'arrow-body-style': [1, 'as-needed'], 18 | 'comma-dangle': [2, 'never'], 19 | 'no-debugger': 2, 20 | 'no-console': 2, 21 | 'object-curly-spacing': [2, 'always'], 22 | 'no-undef': [1], 23 | 'new-cap': 1, 24 | 'no-param-reassign': 0, 25 | 'global-require': 0, 26 | 'no-mixed-operators': 0, 27 | "import/no-extraneous-dependencies": 0, 28 | "import/no-unresolved": 0, 29 | "import/extensions": 0, 30 | "import/no-dynamic-require": 0, 31 | "import/no-duplicates": 0, 32 | "vue/max-attributes-per-line": 0 33 | } 34 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | dist/ 4 | 5 | .DS_Store 6 | *.log 7 | *.log.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue基础MVVM框架 2 | 3 | ## 技术及框架 4 | 5 | 包括但不限于: 6 | 7 | - ES6/7 JS 8 | - ESLint (代码规范) 9 | - Vue (MVVM框架) 10 | - Vuex (统一状态管理) 11 | - Vue Router (路由) 12 | - axios (http库) 13 | - Element UI (UI框架) 14 | - Babel (ES6/7代码转译浏览器可执行) 15 | - Webpack (打包工具) 16 | 17 | ## 安装调试 18 | 19 | ```bash 20 | npm i 21 | # 或 yarn 22 | npm run start 23 | # 或 yarn start 24 | ``` 25 | ## 打包 26 | 27 | ```bash 28 | npm run build 29 | # or 30 | yarn build 31 | ``` 32 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const WebpackDevServer = require('webpack-dev-server'); 3 | const webpackDevConfig = require('./webpack.config.js'); 4 | const open = require('open'); 5 | 6 | const compiler = webpack(webpackDevConfig); 7 | new WebpackDevServer(compiler, { 8 | contentBase: './', 9 | historyApiFallback: true, 10 | hot: true, 11 | noInfo: false, 12 | publicPath: '/', 13 | stats: { colors: true } 14 | }).listen(3001, 'localhost', () => { 15 | console.log('http://127.0.0.1:3001'); 16 | console.log('Opening your system browser...'); 17 | open('http://127.0.0.1:3001'); 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /build/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const { VueLoaderPlugin } = require('vue-loader'); 5 | 6 | const webpackConfig = { 7 | entry: { 8 | app: path.resolve(__dirname, '../src/main.js') 9 | }, 10 | output: { 11 | filename: '[name].js', 12 | publicPath: '/' 13 | }, 14 | cache: true, 15 | devtool: 'inline-source-map', 16 | resolve: { 17 | extensions: ['.js', '.vue'], 18 | alias: { 19 | vue$: 'vue/dist/vue.esm.js', 20 | components: path.join(__dirname, '../src/components'), 21 | pages: path.join(__dirname, '../src/pages'), 22 | localData: path.join(__dirname, '../src/testdata/localdata'), 23 | util: path.join(__dirname, '../src/utils') 24 | // jquery: path.join(__dirname, '../node_modules/jquery/dist/jquery.min.js') 25 | } 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | test: /\.vue$/, 31 | loader: 'vue-loader', 32 | options: { 33 | loaders: { 34 | scss: 'vue-style-loader!css-loader!sass-loader', // 135 | -------------------------------------------------------------------------------- /src/components/layout1.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 100 | 101 | 128 | -------------------------------------------------------------------------------- /src/components/modal.js: -------------------------------------------------------------------------------- 1 | import { MessageBox, Message } from 'element-ui'; 2 | import 'components/common.scss'; 3 | 4 | const Modal = { 5 | confirmModal(data, ok) { 6 | MessageBox.confirm(data, { 7 | confirmButtonText: '确定', 8 | customClass: 'confirm-dialog', 9 | center: true, 10 | type: 'warning', 11 | showCancelButton: false 12 | }).then(() => { 13 | // ok确认回调可放到调用的地方,单独定义 14 | Message({ 15 | type: 'success', 16 | message: '全部开启成功!' 17 | }); 18 | }).catch(() => { 19 | Message({ 20 | type: 'info', 21 | message: '已取消开启操作' 22 | }); 23 | }); 24 | } 25 | }; 26 | 27 | export default Modal; 28 | 29 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import ElementUI from 'element-ui'; 2 | import 'normalize.css'; 3 | import 'element-ui/lib/theme-chalk/index.css'; 4 | import Vue from 'vue'; 5 | import VueRouter from 'vue-router'; 6 | 7 | import item1 from 'pages/item1/item1'; 8 | import item2 from 'pages/item2/item2'; 9 | import item31 from 'pages/item3/option1'; 10 | import item32 from 'pages/item3/option2'; 11 | 12 | import store from './store'; 13 | 14 | Vue.use(ElementUI); 15 | Vue.use(VueRouter); 16 | /* eslint-disable no-new */ 17 | 18 | const router = new VueRouter({ 19 | mode: 'history', 20 | base: __dirname, 21 | routes: [ 22 | { path: '/', component: item1 }, 23 | { path: '/item1', component: item1 }, 24 | { path: '/item2', component: item2 }, 25 | { path: '/item3-1', component: item31 }, 26 | { path: '/item3-2', component: item32 } 27 | ] 28 | }); 29 | new Vue({ 30 | el: '#app', 31 | router, 32 | store, 33 | template: ` 34 |
35 | 36 |
37 | ` 38 | }); 39 | -------------------------------------------------------------------------------- /src/pages/item1/item1.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /src/pages/item2/item2.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 53 | -------------------------------------------------------------------------------- /src/pages/item3/echarts/line.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 121 | -------------------------------------------------------------------------------- /src/pages/item3/option1.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 58 | 59 | 65 | -------------------------------------------------------------------------------- /src/pages/item3/option2.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import menu from './modules/menu'; 4 | 5 | Vue.use(Vuex); 6 | 7 | const store = new Vuex.Store({ 8 | modules: { 9 | menu 10 | } 11 | }); 12 | 13 | export default store; 14 | -------------------------------------------------------------------------------- /src/store/modules/menu.js: -------------------------------------------------------------------------------- 1 | import Axios from 'util/axios'; 2 | 3 | const menu = { 4 | state: { 5 | menuList: [] 6 | }, 7 | mutations: { 8 | CHANGE_MENULIST: (state, list) => { 9 | state.menuList = list; 10 | } 11 | }, 12 | actions: { 13 | fetch({ commit }, param) { 14 | const params = { 15 | ...param, 16 | successFn(data) { 17 | if (data.list && data.list.length) { 18 | commit('CHANGE_MENULIST', data.list); 19 | } 20 | } 21 | }; 22 | console.log(params); 23 | Axios.fetch(params); 24 | } 25 | } 26 | }; 27 | 28 | export default menu; 29 | -------------------------------------------------------------------------------- /src/testdata/localdata/linelist.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": 0, 3 | "list": [ 4 | { 5 | "id": "1", 6 | "name": "平台", 7 | "counttime": "2016-11-08 16:19", 8 | "successrate": 22, 9 | "maxsuccessimagerate": 82, 10 | "maxsuccessregionrate": 56, 11 | "maxfailimagerate": 92, 12 | "maxfailregionrate": 33 13 | },{ 14 | "id": "2", 15 | "name": "平台", 16 | "counttime": "2016-11-08 17:19", 17 | "successrate": 32, 18 | "maxsuccessimagerate": 52, 19 | "maxsuccessregionrate": 66, 20 | "maxfailimagerate": 22, 21 | "maxfailregionrate": 23 22 | },{ 23 | "id": "3", 24 | "name": "平台", 25 | "counttime": "2016-11-08 18:19", 26 | "successrate": 42, 27 | "maxsuccessimagerate": 32, 28 | "maxsuccessregionrate": 76, 29 | "maxfailimagerate": 32, 30 | "maxfailregionrate": 43 31 | },{ 32 | "id": "4", 33 | "name": "平台", 34 | "counttime": "2016-11-08 19:19", 35 | "successrate": 22, 36 | "maxsuccessimagerate": 42, 37 | "maxsuccessregionrate": 6, 38 | "maxfailimagerate": 142, 39 | "maxfailregionrate": 13 40 | },{ 41 | "id": "5", 42 | "name": "平台", 43 | "counttime": "2016-11-08 20:19", 44 | "successrate": 62, 45 | "maxsuccessimagerate": 52, 46 | "maxsuccessregionrate": 96, 47 | "maxfailimagerate": 52, 48 | "maxfailregionrate": 73 49 | } 50 | ], 51 | "message": "error" 52 | } -------------------------------------------------------------------------------- /src/testdata/localdata/menulist.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": 0, 3 | "list": [ 4 | { 5 | "key": "item1", 6 | "name": "导航一" 7 | }, 8 | { 9 | "key": "item2", 10 | "name": "导航二" 11 | }, 12 | { 13 | "key": "item3", 14 | "name": "导航三", 15 | "sub": [ 16 | { 17 | "key": "item3-1", 18 | "name": "选项1" 19 | }, { 20 | "key": "item3-2", 21 | "name": "选项2" 22 | } 23 | ] 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /src/utils/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Loading } from 'element-ui'; 3 | import { MessageBox } from 'element-ui'; 4 | import 'components/common.scss'; 5 | import ErrorCode from './errorcode'; 6 | 7 | const localDatas = require('util/localdata'); 8 | 9 | export default class AXIOS { 10 | constructor() { 11 | this.loading = ''; 12 | } 13 | 14 | static getEnvPrefix() { 15 | if (window.isDebug) { 16 | return ''; 17 | } 18 | return window.apiUrl; 19 | } 20 | 21 | static fetch(fetchObj) { 22 | const { 23 | loadingFlag, 24 | method, 25 | successFn, 26 | errorFn 27 | } = fetchObj; 28 | let { 29 | url, 30 | data = {} 31 | } = fetchObj; 32 | 33 | if (loadingFlag) { 34 | this.loading = Loading.service({ 35 | lock: true, 36 | text: '加载中...', 37 | spinner: 'el-icon-loading', 38 | customClass: 'axios-loading', 39 | background: 'rgba(0, 0, 0, 0.7)' 40 | }); 41 | } 42 | if (window.isDebug) { 43 | setTimeout(() => { 44 | if (loadingFlag) { 45 | this.loading.close(); 46 | } 47 | const localData = localDatas[url]; 48 | if (localData.result === 0) { 49 | successFn(localData); 50 | } else { 51 | const errorMsg = ErrorCode(localData.code) || '服务器异常,请联系运维人员!'; 52 | if (errorFn) { 53 | errorFn(errorMsg); 54 | } else { 55 | AXIOS.modalError(errorMsg); 56 | } 57 | } 58 | }, 500); 59 | return; 60 | } 61 | 62 | url = this.getEnvPrefix() + url; 63 | if (method.toLowerCase() === 'get') { 64 | data = null; 65 | } else { 66 | data = JSON.stringify(data); 67 | } 68 | 69 | axios({ 70 | method, 71 | url, 72 | data: { 73 | req: AXIOS.isExisty(data) ? data : {} 74 | }, 75 | responseType: 'json' 76 | }) 77 | .then((res) => { 78 | if (loadingFlag) { 79 | this.loading.close(); 80 | } 81 | if (res.result === 0) { 82 | successFn(res); 83 | } else { 84 | const errorMsg = ErrorCode(res.result) || '服务器异常,请联系运维人员!'; 85 | if (errorFn) { 86 | errorFn(errorMsg); 87 | } else { 88 | AXIOS.modalError(errorMsg); 89 | } 90 | } 91 | }) 92 | .catch((err) => { 93 | if (errorFn) { 94 | errorFn(err); 95 | } else { 96 | AXIOS.modalError(err); 97 | } 98 | }); 99 | } 100 | 101 | static isExisty(obj) { 102 | return obj !== null; 103 | } 104 | 105 | static modalError(message) { 106 | MessageBox.alert(message, { 107 | showConfirmButton: false, 108 | customClass: 'axios-error', 109 | center: true, 110 | type: 'error', 111 | callback: (action) => {} 112 | }); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/utils/errorcode.js: -------------------------------------------------------------------------------- 1 | const map = { 2 | 4: '服务器异常' 3 | }; 4 | 5 | module.exports = function (code) { 6 | return map[code]; 7 | }; 8 | -------------------------------------------------------------------------------- /src/utils/localdata.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This module is used for mock testing 3 | * add new mock data if backend haven't implement the logic yet 4 | */ 5 | 6 | const localData = { 7 | 'claa/menulist': require('localData/menulist.json'), 8 | 'claa/linelist': require('localData/linelist.json') 9 | }; 10 | 11 | module.exports = localData; 12 | --------------------------------------------------------------------------------