Browse Source

后台管理系统

panchao 5 years ago
commit
6ea758996f
52 changed files with 3589 additions and 0 deletions
  1. 3 0
      .browserslistrc
  2. 22 0
      .gitignore
  3. 21 0
      LICENSE
  4. 181 0
      README.md
  5. 164 0
      README_EN.md
  6. 5 0
      babel.config.js
  7. 28 0
      package.json
  8. 5 0
      postcss.config.js
  9. 17 0
      public/index.html
  10. BIN
      screenshots/wms1.png
  11. BIN
      screenshots/wms2.png
  12. BIN
      screenshots/wms3.png
  13. 10 0
      src/App.vue
  14. 9 0
      src/api/index.js
  15. 28 0
      src/assets/css/color-dark.css
  16. 4 0
      src/assets/css/icon.css
  17. 173 0
      src/assets/css/main.css
  18. 29 0
      src/assets/css/theme-green/color-green.css
  19. BIN
      src/assets/css/theme-green/fonts/element-icons.ttf
  20. BIN
      src/assets/css/theme-green/fonts/element-icons.woff
  21. 1 0
      src/assets/css/theme-green/index.css
  22. BIN
      src/assets/img/img.jpg
  23. BIN
      src/assets/img/login-bg.jpg
  24. BIN
      src/assets/logo.png
  25. 185 0
      src/components/common/Header.vue
  26. 48 0
      src/components/common/Home.vue
  27. 171 0
      src/components/common/Sidebar.vue
  28. 186 0
      src/components/common/Tags.vue
  29. 6 0
      src/components/common/bus.js
  30. 80 0
      src/components/common/directives.js
  31. 30 0
      src/components/common/i18n.js
  32. 56 0
      src/components/page/403.vue
  33. 56 0
      src/components/page/404.vue
  34. 109 0
      src/components/page/BaseCharts.vue
  35. 141 0
      src/components/page/BaseForm.vue
  36. 232 0
      src/components/page/BaseTable.vue
  37. 343 0
      src/components/page/Dashboard.vue
  38. 26 0
      src/components/page/Donate.vue
  39. 36 0
      src/components/page/DragDialog.vue
  40. 174 0
      src/components/page/DragList.vue
  41. 46 0
      src/components/page/I18n.vue
  42. 225 0
      src/components/page/Icon.vue
  43. 101 0
      src/components/page/Login.vue
  44. 67 0
      src/components/page/Markdown.vue
  45. 38 0
      src/components/page/Permission.vue
  46. 129 0
      src/components/page/Tabs.vue
  47. 141 0
      src/components/page/Upload.vue
  48. 53 0
      src/components/page/VueEditor.vue
  49. 51 0
      src/main.js
  50. 116 0
      src/router/index.js
  51. 27 0
      src/utils/request.js
  52. 16 0
      vue.config.js

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not ie <= 8

+ 22 - 0
.gitignore

@@ -0,0 +1,22 @@
+.DS_Store
+node_modules
+/dist
+example.html
+favicon.ico
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 vue-manage-system
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

File diff suppressed because it is too large
+ 181 - 0
README.md


+ 164 - 0
README_EN.md

@@ -0,0 +1,164 @@
+# vue-manage-system #
+The web management system solution based on Vue2 and Element-UI。[live demo](http://blog.gdfengshuo.com/example/work/)
+
+## Donation
+![WeChat](http://blog.gdfengshuo.com/images/weixin.jpg)
+
+## Preface ##
+The scheme as a set of multi-function background frame templates, suitable for most of the WEB management system development. Convenient development fast simple good components based on Vue2 and Element-UI. Color separation of color style, support manual switch themes, and it is convenient to use a custom theme color.
+
+## Function ##
+- [x] Element-UI
+- [x] Login/Logout
+- [x] Dashboard
+- [x] Table
+- [x] Tabs
+- [x] From
+- [x] Chart :bar_chart:
+- [x] Editor
+- [x] Markdown
+- [x] Upload pictures by clipping or dragging
+- [x] Support manual switch themes :sparkles:
+- [x] List drag sort
+- [x] Permission
+- [x] 404 / 403
+- [x] Three level menu
+- [x] Custom icon
+
+
+## Installation steps ##
+
+	git clone https://github.com/lin-xin/vue-manage-system.git		// Clone templates
+	cd vue-manage-system											// Enter template directory
+	npm install													// Installation dependency
+
+## Local development ##
+
+	// Open server and access http://localhost:8080 in browser
+	npm run serve
+
+## Constructing production ##
+
+	// Constructing project
+	npm run build
+
+## Component description and presentation ##
+
+### vue-schart ###
+Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/linxin/vue-schart)
+
+```JavaScript
+<template>
+    <div>
+        <schart :canvasId="canvasId"
+				:type="type"
+				:width="width"
+				:height="height"
+				:data="data"
+				:options="options"
+		></schart>
+    </div>
+</template>
+	
+<script>
+    import Schart from 'vue-schart';
+    export default {
+        data: function(){
+            return {
+                canvasId: 'myCanvas',
+                type: 'bar',
+                width: 500,
+                height: 400,
+                data: [
+                    {name: '2014', value: 1342},
+                    {name: '2015', value: 2123},
+                    {name: '2016', value: 1654},
+                    {name: '2017', value: 1795},
+                ],
+                options: {
+                    title: 'Total sales of stores in recent years'
+                }
+            }
+        },
+        components: {
+            Schart
+        }
+    }
+</script>
+```
+
+### element-ui ###
+A desktop component library based on vue.js2.0 . Github : [element](http://element.eleme.io/#/zh-CN/component/layout)
+
+### Vue-Quill-Editor ###
+Quill editor component for Vue2. Github : [vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
+
+### mavonEditor ###
+A markdown editor based on Vue that supports a variety of personalized features. Github: [mavonEditor](https://github.com/hinesboy/mavonEditor)
+
+### vue-cropperjs ###
+A Vue wrapper component for cropperjs. Github: [vue-cropperjs](https://github.com/Agontuk/vue-cropperjs)
+
+
+## Notice ##
+### 一、If I don't want to use some components, how can I delete it? ###
+
+For example, I don't want to use the Vue-Quill-Editor component, I need to take four steps.
+
+The first step to remove the component of the routing. Enter 'src/router/index.js' and delete the code below.
+
+```JavaScript
+{
+    path: '/editor',
+    component: resolve => require(['../components/page/VueEditor.vue'], resolve) 
+},
+```
+
+Second,delete the component files. Enter 'src/components/page/' and delete 'VueEditor.vue' file.
+
+The third step is to delete the entry. Enter 'src/components/common/Sidebar.vue' and delete the code below.
+	
+```js
+{
+	index: 'editor',
+	title: '富文本编辑器'
+},
+```
+
+Finally, uninstall this component.
+	
+	npm un vue-quill-editor -S
+
+Complete!
+
+### 二、How to switch themes? ###
+
+The first step to enter 'src/main.js' and change into green theme.
+
+```javascript
+import 'element-ui/lib/theme-default/index.css';    // default theme
+// import '../static/css/theme-green/index.css';       // green theme
+```
+
+The second step to enter 'src/App.vue' and change into green theme.
+
+```javascript
+@import "../static/css/main.css";
+@import "../static/css/color-dark.css";     /*深色主题*/
+/*@import "../static/css/theme-green/color-green.css";   !*浅绿色主题*!*/
+```
+
+Finally,enter 'src/components/common/Sidebar.vue' and find el-menu Tags,delete 'background-color/text-color/active-text-color'。
+
+## Screenshot ##
+### Default theme ###
+
+![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms1.png)
+
+### Green theme ###
+
+![Image text](https://github.com/lin-xin/manage-system/raw/master/screenshots/wms2.png)
+
+## License
+
+[MIT](https://github.com/lin-xin/vue-manage-system/blob/master/LICENSE)

+ 5 - 0
babel.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  presets: [
+    '@vue/app'
+  ]
+}

+ 28 - 0
package.json

@@ -0,0 +1,28 @@
+{
+  "name": "vue-manage-system",
+  "version": "4.0.1",
+  "private": true,
+  "scripts": {
+    "dev": "npm run serve",
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "babel-polyfill": "^6.26.0",
+    "element-ui": "^2.9.2",
+    "mavon-editor": "^2.6.17",
+    "vue": "^2.6.10",
+    "vue-cropperjs": "^3.0.0",
+    "vue-i18n": "^8.10.0",
+    "vue-quill-editor": "^3.0.6",
+    "vue-router": "^3.0.1",
+    "vue-schart": "^1.0.0",
+    "vuedraggable": "^2.17.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^3.2.0",
+    "@vue/cli-service": "^3.2.0",
+    "vue-template-compiler": "^2.5.21"
+  }
+}

+ 5 - 0
postcss.config.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {}
+  }
+}

+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
+    <link rel="stylesheet" href="//at.alicdn.com/t/font_830376_qzecyukz0s.css">
+    <title>vue-manage-system</title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but vms doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

BIN
screenshots/wms1.png


BIN
screenshots/wms2.png


BIN
screenshots/wms3.png


+ 10 - 0
src/App.vue

@@ -0,0 +1,10 @@
+<template>
+    <div id="app">
+        <router-view></router-view>
+    </div>
+</template>
+<style>
+    @import "./assets/css/main.css";
+    @import "./assets/css/color-dark.css";     /*深色主题*/
+    /*@import "./assets/css/theme-green/color-green.css";   浅绿色主题*/
+</style>

+ 9 - 0
src/api/index.js

@@ -0,0 +1,9 @@
+import request from '../utils/request';
+
+export const fetchData = (query) => {
+    return request({
+        url: '/ms/table/list',
+        method: 'post',
+        data: query
+    })
+}

+ 28 - 0
src/assets/css/color-dark.css

@@ -0,0 +1,28 @@
+.header{
+    background-color: #242f42;
+}
+.login-wrap{
+    background: #324157;
+}
+.plugins-tips{
+    background: #eef1f6;
+}
+.plugins-tips a{
+    color: #20a0ff;
+}
+.el-upload--text em {
+    color: #20a0ff;
+}
+.pure-button{
+    background: #20a0ff;
+}
+.tags-li.active {
+    border: 1px solid #409EFF;
+    background-color: #409EFF;
+}
+.message-title{
+    color: #20a0ff;
+}
+.collapse-btn:hover{
+    background: rgb(40,52,70);
+}

+ 4 - 0
src/assets/css/icon.css

@@ -0,0 +1,4 @@
+
+    [class*=" el-icon-lx"], [class^=el-icon-lx] {
+        font-family: lx-iconfont!important;
+    }

+ 173 - 0
src/assets/css/main.css

@@ -0,0 +1,173 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+html,
+body,
+#app,
+.wrapper {
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+body {
+    font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
+}
+
+a {
+    text-decoration: none
+}
+
+
+.content-box {
+    position: absolute;
+    left: 250px;
+    right: 0;
+    top: 70px;
+    bottom: 0;
+    padding-bottom: 30px;
+    -webkit-transition: left .3s ease-in-out;
+    transition: left .3s ease-in-out;
+    background: #f0f0f0;
+}
+
+.content {
+    width: auto;
+    height: 100%;
+    padding: 10px;
+    overflow-y: scroll;
+    box-sizing: border-box;
+}
+
+.content-collapse {
+    left: 65px;
+}
+
+.container {
+    padding: 30px;
+    background: #fff;
+    border: 1px solid #ddd;
+    border-radius: 5px;
+}
+
+.crumbs {
+    margin: 10px 0;
+}
+
+.pagination {
+    margin: 20px 0;
+    text-align: right;
+}
+
+.plugins-tips {
+    padding: 20px 10px;
+    margin-bottom: 20px;
+}
+
+.el-button+.el-tooltip {
+    margin-left: 10px;
+}
+
+.el-table tr:hover {
+    background: #f6faff;
+}
+
+.mgb20 {
+    margin-bottom: 20px;
+}
+
+.move-enter-active,
+.move-leave-active {
+    transition: opacity .5s;
+}
+
+.move-enter,
+.move-leave {
+    opacity: 0;
+}
+
+/*BaseForm*/
+
+.form-box {
+    width: 600px;
+}
+
+.form-box .line {
+    text-align: center;
+}
+
+.el-time-panel__content::after,
+.el-time-panel__content::before {
+    margin-top: -7px;
+}
+
+.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
+    padding-bottom: 0;
+}
+
+/*Upload*/
+
+.pure-button {
+    width: 150px;
+    height: 40px;
+    line-height: 40px;
+    text-align: center;
+    color: #fff;
+    border-radius: 3px;
+}
+
+.g-core-image-corp-container .info-aside {
+    height: 45px;
+}
+
+.el-upload--text {
+    background-color: #fff;
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    box-sizing: border-box;
+    width: 360px;
+    height: 180px;
+    text-align: center;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+}
+
+.el-upload--text .el-icon-upload {
+    font-size: 67px;
+    color: #97a8be;
+    margin: 40px 0 16px;
+    line-height: 50px;
+}
+
+.el-upload--text {
+    color: #97a8be;
+    font-size: 14px;
+    text-align: center;
+}
+
+.el-upload--text em {
+    font-style: normal;
+}
+
+/*VueEditor*/
+
+.ql-container {
+    min-height: 400px;
+}
+
+.ql-snow .ql-tooltip {
+    transform: translateX(117.5px) translateY(10px) !important;
+}
+
+.editor-btn {
+    margin-top: 20px;
+}
+
+/*markdown*/
+
+.v-note-wrapper .v-note-panel {
+    min-height: 500px;
+}

+ 29 - 0
src/assets/css/theme-green/color-green.css

@@ -0,0 +1,29 @@
+.header{
+    background-color: #07c4a8;
+}
+.login-wrap{
+    background: rgba(56, 157, 170, 0.82);;
+}
+.plugins-tips{
+    background: #f2f2f2;
+}
+.plugins-tips a{
+    color: #00d1b2;
+}
+.el-upload--text em {
+    color: #00d1b2;
+}
+.pure-button{
+    background: #00d1b2;
+}
+.pagination > .active > a, .pagination > .active > a:hover, .pagination > .active > a:focus, .pagination > .active > span, .pagination > .active > span:hover, .pagination > .active > span:focus {
+    background-color: #00d1b2 !important;
+    border-color: #00d1b2 !important;
+}
+.tags-li.active {
+    border: 1px solid #00d1b2;
+    background-color: #00d1b2;
+}
+.collapse-btn:hover{
+    background: #00d1b2;
+}

BIN
src/assets/css/theme-green/fonts/element-icons.ttf


BIN
src/assets/css/theme-green/fonts/element-icons.woff


File diff suppressed because it is too large
+ 1 - 0
src/assets/css/theme-green/index.css


BIN
src/assets/img/img.jpg


BIN
src/assets/img/login-bg.jpg


BIN
src/assets/logo.png


+ 185 - 0
src/components/common/Header.vue

@@ -0,0 +1,185 @@
+<template>
+    <div class="header">
+        <!-- 折叠按钮 -->
+        <div class="collapse-btn" @click="collapseChage">
+            <i class="el-icon-menu"></i>
+        </div>
+        <div class="logo">后台管理系统</div>
+        <div class="header-right">
+            <div class="header-user-con">
+                <!-- 全屏显示 -->
+                <div class="btn-fullscreen" @click="handleFullScreen">
+                    <el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom">
+                        <i class="el-icon-rank"></i>
+                    </el-tooltip>
+                </div>
+                <!-- 消息中心 -->
+                <div class="btn-bell">
+                    <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
+                        <router-link to="/tabs">
+                            <i class="el-icon-bell"></i>
+                        </router-link>
+                    </el-tooltip>
+                    <span class="btn-bell-badge" v-if="message"></span>
+                </div>
+                <!-- 用户头像 -->
+                <div class="user-avator"><img src="../../assets/img/img.jpg"></div>
+                <!-- 用户名下拉菜单 -->
+                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
+                    <span class="el-dropdown-link">
+                        {{username}} <i class="el-icon-caret-bottom"></i>
+                    </span>
+                    <el-dropdown-menu slot="dropdown">
+                        <a href="http://blog.gdfengshuo.com/about/" target="_blank">
+                            <el-dropdown-item>关于作者</el-dropdown-item>
+                        </a>
+                        <a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
+                            <el-dropdown-item>项目仓库</el-dropdown-item>
+                        </a>
+                        <el-dropdown-item divided  command="loginout">退出登录</el-dropdown-item>
+                    </el-dropdown-menu>
+                </el-dropdown>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    import bus from '../common/bus';
+    export default {
+        data() {
+            return {
+                collapse: false,
+                fullscreen: false,
+                name: 'linxin',
+                message: 2
+            }
+        },
+        computed:{
+            username(){
+                let username = localStorage.getItem('ms_username');
+                return username ? username : this.name;
+            }
+        },
+        methods:{
+            // 用户名下拉菜单选择事件
+            handleCommand(command) {
+                if(command == 'loginout'){
+                    localStorage.removeItem('ms_username')
+                    this.$router.push('/login');
+                }
+            },
+            // 侧边栏折叠
+            collapseChage(){
+                this.collapse = !this.collapse;
+                bus.$emit('collapse', this.collapse);
+            },
+            // 全屏事件
+            handleFullScreen(){
+                let element = document.documentElement;
+                if (this.fullscreen) {
+                    if (document.exitFullscreen) {
+                        document.exitFullscreen();
+                    } else if (document.webkitCancelFullScreen) {
+                        document.webkitCancelFullScreen();
+                    } else if (document.mozCancelFullScreen) {
+                        document.mozCancelFullScreen();
+                    } else if (document.msExitFullscreen) {
+                        document.msExitFullscreen();
+                    }
+                } else {
+                    if (element.requestFullscreen) {
+                        element.requestFullscreen();
+                    } else if (element.webkitRequestFullScreen) {
+                        element.webkitRequestFullScreen();
+                    } else if (element.mozRequestFullScreen) {
+                        element.mozRequestFullScreen();
+                    } else if (element.msRequestFullscreen) {
+                        // IE11
+                        element.msRequestFullscreen();
+                    }
+                }
+                this.fullscreen = !this.fullscreen;
+            }
+        },
+        mounted(){
+            if(document.body.clientWidth < 1500){
+                this.collapseChage();
+            }
+        }
+    }
+</script>
+<style scoped>
+    .header {
+        position: relative;
+        box-sizing: border-box;
+        width: 100%;
+        height: 70px;
+        font-size: 22px;
+        color: #fff;
+    }
+    .collapse-btn{
+        float: left;
+        padding: 0 21px;
+        cursor: pointer;
+        line-height: 70px;
+    }
+    .header .logo{
+        float: left;
+        width:250px;
+        line-height: 70px;
+    }
+    .header-right{
+        float: right;
+        padding-right: 50px;
+    }
+    .header-user-con{
+        display: flex;
+        height: 70px;
+        align-items: center;
+    }
+    .btn-fullscreen{
+        transform: rotate(45deg);
+        margin-right: 5px;
+        font-size: 24px;
+    }
+    .btn-bell, .btn-fullscreen{
+        position: relative;
+        width: 30px;
+        height: 30px;
+        text-align: center;
+        border-radius: 15px;
+        cursor: pointer;
+    }
+    .btn-bell-badge{
+        position: absolute;
+        right: 0;
+        top: -2px;
+        width: 8px;
+        height: 8px;
+        border-radius: 4px;
+        background: #f56c6c;
+        color: #fff;
+    }
+    .btn-bell .el-icon-bell{
+        color: #fff;
+    }
+    .user-name{
+        margin-left: 10px;
+    }
+    .user-avator{
+        margin-left: 20px;
+    }
+    .user-avator img{
+        display: block;
+        width:40px;
+        height:40px;
+        border-radius: 50%;
+    }
+    .el-dropdown-link{
+        color: #fff;
+        cursor: pointer;
+    }
+    .el-dropdown-menu__item{
+        text-align: center;
+    }
+</style>

+ 48 - 0
src/components/common/Home.vue

@@ -0,0 +1,48 @@
+<template>
+    <div class="wrapper">
+        <v-head></v-head>
+        <v-sidebar></v-sidebar>
+        <div class="content-box" :class="{'content-collapse':collapse}">
+            <v-tags></v-tags>
+            <div class="content">
+                <transition name="move" mode="out-in">
+                    <keep-alive :include="tagsList">
+                        <router-view></router-view>
+                    </keep-alive>
+                </transition>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+    import vHead from './Header.vue';
+    import vSidebar from './Sidebar.vue';
+    import vTags from './Tags.vue';
+    import bus from './bus';
+    export default {
+        data(){
+            return {
+                tagsList: [],
+                collapse: false
+            }
+        },
+        components:{
+            vHead, vSidebar, vTags
+        },
+        created(){
+            bus.$on('collapse', msg => {
+                this.collapse = msg;
+            })
+
+            // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
+            bus.$on('tags', msg => {
+                let arr = [];
+                for(let i = 0, len = msg.length; i < len; i ++){
+                    msg[i].name && arr.push(msg[i].name);
+                }
+                this.tagsList = arr;
+            })
+        }
+    }
+</script>

+ 171 - 0
src/components/common/Sidebar.vue

@@ -0,0 +1,171 @@
+<template>
+    <div class="sidebar">
+        <el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157"
+            text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
+            <template v-for="item in items">
+                <template v-if="item.subs">
+                    <el-submenu :index="item.index" :key="item.index">
+                        <template slot="title">
+                            <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
+                        </template>
+                        <template v-for="subItem in item.subs">
+                            <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
+                                <template slot="title">{{ subItem.title }}</template>
+                                <el-menu-item v-for="(threeItem,i) in subItem.subs" :key="i" :index="threeItem.index">
+                                    {{ threeItem.title }}
+                                </el-menu-item>
+                            </el-submenu>
+                            <el-menu-item v-else :index="subItem.index" :key="subItem.index">
+                                {{ subItem.title }}
+                            </el-menu-item>
+                        </template>
+                    </el-submenu>
+                </template>
+                <template v-else>
+                    <el-menu-item :index="item.index" :key="item.index">
+                        <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
+                    </el-menu-item>
+                </template>
+            </template>
+        </el-menu>
+    </div>
+</template>
+
+<script>
+    import bus from '../common/bus';
+    export default {
+        data() {
+            return {
+                collapse: false,
+                items: [
+                    {
+                        icon: 'el-icon-lx-home',
+                        index: 'dashboard',
+                        title: '系统首页'
+                    },
+                    {
+                        icon: 'el-icon-lx-cascades',
+                        index: 'table',
+                        title: '基础表格'
+                    },
+                    {
+                        icon: 'el-icon-lx-copy',
+                        index: 'tabs',
+                        title: 'tab选项卡'
+                    },
+                    {
+                        icon: 'el-icon-lx-calendar',
+                        index: '3',
+                        title: '表单相关',
+                        subs: [
+                            {
+                                index: 'form',
+                                title: '基本表单'
+                            },
+                            {
+                                index: '3-2',
+                                title: '三级菜单',
+                                subs: [
+                                    {
+                                        index: 'editor',
+                                        title: '富文本编辑器'
+                                    },
+                                    {
+                                        index: 'markdown',
+                                        title: 'markdown编辑器'
+                                    },
+                                ]
+                            },
+                            {
+                                index: 'upload',
+                                title: '文件上传'
+                            }
+                        ]
+                    },
+                    {
+                        icon: 'el-icon-lx-emoji',
+                        index: 'icon',
+                        title: '自定义图标'
+                    },
+                    {
+                        icon: 'el-icon-pie-chart',
+                        index: 'charts',
+                        title: 'schart图表'
+                    },
+                    {
+                        icon: 'el-icon-rank',
+                        index: '6',
+                        title: '拖拽组件',
+                        subs: [
+                            {
+                                index: 'drag',
+                                title: '拖拽列表',
+                            },
+                            {
+                                index: 'dialog',
+                                title: '拖拽弹框',
+                            }
+                        ]
+                    },
+                    {
+                        icon: 'el-icon-lx-global',
+                        index: 'i18n',
+                        title: '国际化功能'
+                    },
+                    {
+                        icon: 'el-icon-lx-warn',
+                        index: '7',
+                        title: '错误处理',
+                        subs: [
+                            {
+                                index: 'permission',
+                                title: '权限测试'
+                            },
+                            {
+                                index: '404',
+                                title: '404页面'
+                            }
+                        ]
+                    }
+                    ,
+                    {
+                        icon: 'el-icon-lx-redpacket_fill',
+                        index: '/donate',
+                        title: '支持作者'
+                    }
+                ]
+            }
+        },
+        computed:{
+            onRoutes(){
+                return this.$route.path.replace('/','');
+            }
+        },
+        created(){
+            // 通过 Event Bus 进行组件间通信,来折叠侧边栏
+            bus.$on('collapse', msg => {
+                this.collapse = msg;
+            })
+        }
+    }
+</script>
+
+<style scoped>
+    .sidebar{
+        display: block;
+        position: absolute;
+        left: 0;
+        top: 70px;
+        bottom:0;
+        overflow-y: scroll;
+    }
+    .sidebar::-webkit-scrollbar{
+        width: 0;
+    }
+    .sidebar-el-menu:not(.el-menu--collapse){
+        width: 250px;
+    }
+    .sidebar > ul {
+        height:100%;
+    }
+</style>

+ 186 - 0
src/components/common/Tags.vue

@@ -0,0 +1,186 @@
+<template>
+    <div class="tags" v-if="showTags">
+        <ul>
+            <li class="tags-li" v-for="(item,index) in tagsList" :class="{'active': isActive(item.path)}" :key="index">
+                <router-link :to="item.path" class="tags-li-title">
+                    {{item.title}}
+                </router-link>
+                <span class="tags-li-icon" @click="closeTags(index)"><i class="el-icon-close"></i></span>
+            </li>
+        </ul>
+        <div class="tags-close-box">
+            <el-dropdown @command="handleTags">
+                <el-button size="mini" type="primary">
+                    标签选项<i class="el-icon-arrow-down el-icon--right"></i>
+                </el-button>
+                <el-dropdown-menu size="small" slot="dropdown">
+                    <el-dropdown-item command="other">关闭其他</el-dropdown-item>
+                    <el-dropdown-item command="all">关闭所有</el-dropdown-item>
+                </el-dropdown-menu>
+            </el-dropdown>
+        </div>
+    </div>
+</template>
+
+<script>
+    import bus from './bus';
+    export default {
+        data() {
+            return {
+                tagsList: []
+            }
+        },
+        methods: {
+            isActive(path) {
+                return path === this.$route.fullPath;
+            },
+            // 关闭单个标签
+            closeTags(index) {
+                const delItem = this.tagsList.splice(index, 1)[0];
+                const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
+                if (item) {
+                    delItem.path === this.$route.fullPath && this.$router.push(item.path);
+                }else{
+                    this.$router.push('/');
+                }
+            },
+            // 关闭全部标签
+            closeAll(){
+                this.tagsList = [];
+                this.$router.push('/');
+            },
+            // 关闭其他标签
+            closeOther(){
+                const curItem = this.tagsList.filter(item => {
+                    return item.path === this.$route.fullPath;
+                })
+                this.tagsList = curItem;
+            },
+            // 设置标签
+            setTags(route){
+                const isExist = this.tagsList.some(item => {
+                    return item.path === route.fullPath;
+                })
+                if(!isExist){
+                    if(this.tagsList.length >= 8){
+                        this.tagsList.shift();
+                    }
+                    this.tagsList.push({
+                        title: route.meta.title,
+                        path: route.fullPath,
+                        name: route.matched[1].components.default.name
+                    })
+                }
+                bus.$emit('tags', this.tagsList);
+            },
+            handleTags(command){
+                command === 'other' ? this.closeOther() : this.closeAll();
+            }
+        },
+        computed: {
+            showTags() {
+                return this.tagsList.length > 0;
+            }
+        },
+        watch:{
+            $route(newValue, oldValue){
+                this.setTags(newValue);
+            }
+        },
+        created(){
+            this.setTags(this.$route);
+            // 监听关闭当前页面的标签页
+            bus.$on('close_current_tags', () => {
+                for (let i = 0, len = this.tagsList.length; i < len; i++) {
+                    const item = this.tagsList[i];
+                    if(item.path === this.$route.fullPath){
+                        if(i < len - 1){
+                            this.$router.push(this.tagsList[i+1].path);
+                        }else if(i > 0){
+                            this.$router.push(this.tagsList[i-1].path);
+                        }else{
+                            this.$router.push('/');
+                        }
+                        this.tagsList.splice(i, 1);
+                        break;
+                    }
+                }
+            })
+        }
+    }
+
+</script>
+
+
+<style>
+    .tags {
+        position: relative;
+        height: 30px;
+        overflow: hidden;
+        background: #fff;
+        padding-right: 120px;
+        box-shadow: 0 5px 10px #ddd;
+    }
+
+    .tags ul {
+        box-sizing: border-box;
+        width: 100%;
+        height: 100%;
+    }
+
+    .tags-li {
+        float: left;
+        margin: 3px 5px 2px 3px;
+        border-radius: 3px;
+        font-size: 12px;
+        overflow: hidden;
+        cursor: pointer;
+        height: 23px;
+        line-height: 23px;
+        border: 1px solid #e9eaec;
+        background: #fff;
+        padding: 0 5px 0 12px;
+        vertical-align: middle;
+        color: #666;
+        -webkit-transition: all .3s ease-in;
+        -moz-transition: all .3s ease-in;
+        transition: all .3s ease-in;
+    }
+
+    .tags-li:not(.active):hover {
+        background: #f8f8f8;
+    }
+
+    .tags-li.active {
+        color: #fff;
+    }
+
+    .tags-li-title {
+        float: left;
+        max-width: 80px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-right: 5px;
+        color: #666;
+    }
+
+    .tags-li.active .tags-li-title {
+        color: #fff;
+    }
+
+    .tags-close-box {
+        position: absolute;
+        right: 0;
+        top: 0;
+        box-sizing: border-box;
+        padding-top: 1px;
+        text-align: center;
+        width: 110px;
+        height: 30px;
+        background: #fff;
+        box-shadow: -3px 0 15px 3px rgba(0, 0, 0, .1);
+        z-index: 10;
+    }
+
+</style>

+ 6 - 0
src/components/common/bus.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+
+// 使用 Event Bus
+const bus = new Vue();
+
+export default bus;

+ 80 - 0
src/components/common/directives.js

@@ -0,0 +1,80 @@
+import Vue from 'vue';
+
+// v-dialogDrag: 弹窗拖拽属性
+Vue.directive('dialogDrag', {
+    bind(el, binding, vnode, oldVnode) {
+        const dialogHeaderEl = el.querySelector('.el-dialog__header');
+        const dragDom = el.querySelector('.el-dialog');
+
+        dialogHeaderEl.style.cssText += ';cursor:move;'
+        dragDom.style.cssText += ';top:0px;'
+
+        // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+        const sty = (() => {
+            if (window.document.currentStyle) {
+                return (dom, attr) => dom.currentStyle[attr];
+            } else {
+                return (dom, attr) => getComputedStyle(dom, false)[attr];
+            }
+        })()
+
+        dialogHeaderEl.onmousedown = (e) => {
+            // 鼠标按下,计算当前元素距离可视区的距离
+            const disX = e.clientX - dialogHeaderEl.offsetLeft;
+            const disY = e.clientY - dialogHeaderEl.offsetTop;
+
+            const screenWidth = document.body.clientWidth; // body当前宽度
+            const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取) 
+
+            const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
+            const dragDomheight = dragDom.offsetHeight; // 对话框高度
+
+            const minDragDomLeft = dragDom.offsetLeft;
+            const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
+
+            const minDragDomTop = dragDom.offsetTop;
+            const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+
+
+            // 获取到的值带px 正则匹配替换
+            let styL = sty(dragDom, 'left');
+            let styT = sty(dragDom, 'top');
+
+            // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+            if (styL.includes('%')) {
+                styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
+                styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
+            } else {
+                styL = +styL.replace(/\px/g, '');
+                styT = +styT.replace(/\px/g, '');
+            };
+
+            document.onmousemove = function (e) {
+                // 通过事件委托,计算移动的距离 
+                let left = e.clientX - disX;
+                let top = e.clientY - disY;
+
+                // 边界处理
+                if (-(left) > minDragDomLeft) {
+                    left = -(minDragDomLeft);
+                } else if (left > maxDragDomLeft) {
+                    left = maxDragDomLeft;
+                }
+
+                if (-(top) > minDragDomTop) {
+                    top = -(minDragDomTop);
+                } else if (top > maxDragDomTop) {
+                    top = maxDragDomTop;
+                }
+
+                // 移动当前元素  
+                dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
+            };
+
+            document.onmouseup = function (e) {
+                document.onmousemove = null;
+                document.onmouseup = null;
+            };
+        }
+    }
+})

+ 30 - 0
src/components/common/i18n.js

@@ -0,0 +1,30 @@
+export const messages = {
+    'zh': {
+        i18n: {
+            breadcrumb: '国际化产品',
+            tips: '通过切换语言按钮,来改变当前内容的语言。',
+            btn: '切换英文',
+            title1: '常用用法',
+            p1: '要是你把你的秘密告诉了风,那就别怪风把它带给树。',
+            p2: '没有什么比信念更能支撑我们度过艰难的时光了。',
+            p3: '只要能把自己的事做好,并让自己快乐,你就领先于大多数人了。',
+            title2: '组件插值',
+            info: 'Element组件需要国际化,请参考 {action}。',
+            value: '文档'
+        }
+    },
+    'en': {
+        i18n: {
+            breadcrumb: 'International Products',
+            tips: 'Click on the button to change the current language. ',
+            btn: 'Switch Chinese',
+            title1: 'Common usage',
+            p1: "If you reveal your secrets to the wind you should not blame the wind for  revealing them to the trees.",
+            p2: "Nothing can help us endure dark times better than our faith. ",
+            p3: "If you can do what you do best and be happy, you're further along in life  than most people.",
+            title2: 'Component interpolation',
+            info: 'The default language of Element is Chinese. If you wish to use another language, please refer to the {action}.',
+            value: 'documentation'
+        }
+    }
+}

+ 56 - 0
src/components/page/403.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="error-page">
+      <div class="error-code">4<span>0</span>3</div>
+      <div class="error-desc">啊哦~ 你没有权限访问该页面哦</div>
+      <div class="error-handle">
+          <router-link to="/">
+            <el-button type="primary" size="large">返回首页</el-button>
+          </router-link>
+          <el-button class="error-btn" type="primary" size="large" @click="goBack">返回上一页</el-button>
+      </div>
+  </div>
+</template>
+
+<script>
+export default {
+  methods: {
+      goBack(){
+          this.$router.go(-1);
+      }
+  }
+}
+</script>
+
+
+<style scoped>
+    .error-page{
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-direction: column;
+        width: 100%;
+        height: 100%;
+        background: #f3f3f3;
+        box-sizing: border-box;
+    }
+    .error-code{
+        line-height: 1;
+        font-size: 250px;
+        font-weight: bolder;
+        color: #f02d2d;
+    }
+    .error-code span{
+        color: #00a854;
+    }
+    .error-desc{
+        font-size: 30px;
+        color: #777;
+    }
+    .error-handle{
+        margin-top: 30px;
+        padding-bottom: 200px;
+    }
+    .error-btn{
+        margin-left: 100px;
+    }
+</style>

+ 56 - 0
src/components/page/404.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="error-page">
+      <div class="error-code">4<span>0</span>4</div>
+      <div class="error-desc">啊哦~ 你所访问的页面不存在</div>
+      <div class="error-handle">
+          <router-link to="/">
+            <el-button type="primary" size="large">返回首页</el-button>
+          </router-link>
+          <el-button class="error-btn" type="primary" size="large" @click="goBack">返回上一页</el-button>
+      </div>
+  </div>
+</template>
+
+<script>
+export default {
+  methods: {
+      goBack(){
+          this.$router.go(-1);
+      }
+  }
+}
+</script>
+
+
+<style scoped>
+    .error-page{
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        flex-direction: column;
+        width: 100%;
+        height: 100%;
+        background: #f3f3f3;
+        box-sizing: border-box;
+    }
+    .error-code{
+        line-height: 1;
+        font-size: 250px;
+        font-weight: bolder;
+        color: #2d8cf0;
+    }
+    .error-code span{
+        color: #00a854;
+    }
+    .error-desc{
+        font-size: 30px;
+        color: #777;
+    }
+    .error-handle{
+        margin-top: 30px;
+        padding-bottom: 200px;
+    }
+    .error-btn{
+        margin-left: 100px;
+    }
+</style>

+ 109 - 0
src/components/page/BaseCharts.vue

@@ -0,0 +1,109 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-pie-chart"></i> schart图表</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                vue-schart:vue.js封装sChart.js的图表组件。
+                访问地址:<a href="https://github.com/lin-xin/vue-schart" target="_blank">vue-schart</a>
+            </div>
+            <div class="schart-box">
+                <div class="content-title">柱状图</div>
+                <schart class="schart" canvasId="bar" :data="data1" type="bar" :options="options1"></schart>
+            </div>
+            <div class="schart-box">
+            <div class="content-title">折线图</div>
+            <schart class="schart" canvasId="line" :data="data1" type="line" :options="options2"></schart>
+            </div>
+            <div class="schart-box">
+            <div class="content-title">饼状图</div>
+            <schart class="schart" canvasId="pie" :data="data2" type="pie" :options="options3"></schart>
+            </div>
+            <div class="schart-box">
+            <div class="content-title">环形图</div>
+            <schart class="schart" canvasId="ring" :data="data2" type="ring" :options="options4"></schart>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+    import Schart from 'vue-schart';
+    export default {
+        name: 'basecharts',
+        components: {
+            Schart
+        },
+        data: () => ({
+            data1:[
+                {name:'2012',value:1141},
+                {name:'2013',value:1499},
+                {name:'2014',value:2260},
+                {name:'2015',value:1170},
+                {name:'2016',value:970},
+                {name:'2017',value:1450}
+            ],
+            data2 : [
+                {name:'短袖',value:1200},
+                {name:'休闲裤',value:1222},
+                {name:'连衣裙',value:1283},
+                {name:'外套',value:1314},
+                {name:'羽绒服',value:2314}
+            ],
+            options1: {
+                title: '某商店近年营业总额',
+                autoWidth: true,   // 设置宽高自适应
+                showValue: false,
+                bgColor: '#F9EFCC',
+                fillColor: '#00887C',
+                contentColor: 'rgba(46,199,201,0.3)',
+                yEqual: 7
+            },
+            options2: {
+                title: '某商店近年营业总额',
+                bgColor: '#D5E4EB',
+                titleColor: '#00887C',
+                fillColor: 'red',
+                contentColor: 'rgba(46,199,201,0.3)'
+            },
+            options3: {
+                title: '某商店各商品年度销量',
+                bgColor: '#829dca',
+                titleColor: '#ffffff',
+                legendColor: '#ffffff',
+                radius: 120
+            },
+            options4: {
+                title: '某商店各商品年度销量',
+                bgColor: '#829daa',
+                titleColor: '#ffffff',
+                legendColor: '#ffffff',
+                radius: 120,
+                innerRadius:80
+            }
+        })
+    }
+</script>
+
+<style scoped>
+.schart-box{
+    display: inline-block;
+    margin: 20px;
+}
+    .schart{
+        width: 500px;
+        height: 400px;
+    }
+    .content-title{
+        clear: both;
+        font-weight: 400;
+        line-height: 50px;
+        margin: 10px 0;
+        font-size: 22px;
+        color: #1f2f3d;
+    }
+    
+</style>

+ 141 - 0
src/components/page/BaseForm.vue

@@ -0,0 +1,141 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
+                <el-breadcrumb-item>基本表单</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="form-box">
+                <el-form ref="form" :model="form" label-width="80px">
+                    <el-form-item label="表单名称">
+                        <el-input v-model="form.name"></el-input>
+                    </el-form-item>
+                    <el-form-item label="选择器">
+                        <el-select v-model="form.region" placeholder="请选择">
+                            <el-option key="bbk" label="步步高" value="bbk"></el-option>
+                            <el-option key="xtc" label="小天才" value="xtc"></el-option>
+                            <el-option key="imoo" label="imoo" value="imoo"></el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item label="日期时间">
+                        <el-col :span="11">
+                            <el-date-picker type="date" placeholder="选择日期" v-model="form.date1" style="width: 100%;"></el-date-picker>
+                        </el-col>
+                        <el-col class="line" :span="2">-</el-col>
+                        <el-col :span="11">
+                            <el-time-picker placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
+                        </el-col>
+                    </el-form-item>
+                    <el-form-item label="城市级联">
+                        <el-cascader :options="options" v-model="form.options"></el-cascader>
+                    </el-form-item>
+                    <el-form-item label="选择开关">
+                        <el-switch v-model="form.delivery"></el-switch>
+                    </el-form-item>
+                    <el-form-item label="多选框">
+                        <el-checkbox-group v-model="form.type">
+                            <el-checkbox label="步步高" name="type"></el-checkbox>
+                            <el-checkbox label="小天才" name="type"></el-checkbox>
+                            <el-checkbox label="imoo" name="type"></el-checkbox>
+                        </el-checkbox-group>
+                    </el-form-item>
+                    <el-form-item label="单选框">
+                        <el-radio-group v-model="form.resource">
+                            <el-radio label="步步高"></el-radio>
+                            <el-radio label="小天才"></el-radio>
+                            <el-radio label="imoo"></el-radio>
+                        </el-radio-group>
+                    </el-form-item>
+                    <el-form-item label="文本框">
+                        <el-input type="textarea" rows="5" v-model="form.desc"></el-input>
+                    </el-form-item>
+                    <el-form-item>
+                        <el-button type="primary" @click="onSubmit">表单提交</el-button>
+                        <el-button>取消</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+
+    </div>
+</template>
+
+<script>
+    export default {
+        name: 'baseform',
+        data: function(){
+            return {
+                options:[
+                    {
+                        value: 'guangdong',
+                        label: '广东省',
+                        children: [
+                            {
+                                value: 'guangzhou',
+                                label: '广州市',
+                                children: [
+                                    {
+                                        value: 'tianhe',
+                                        label: '天河区'
+                                    },
+                                    {
+                                        value: 'haizhu',
+                                        label: '海珠区'
+                                    }
+                                ]
+                            },
+                            {
+                                value: 'dongguan',
+                                label: '东莞市',
+                                children: [
+                                    {
+                                        value: 'changan',
+                                        label: '长安镇'
+                                    },
+                                    {
+                                        value: 'humen',
+                                        label: '虎门镇'
+                                    }
+                                ]
+                            }
+                        ]
+                    },
+                    {
+                        value: 'hunan',
+                        label: '湖南省',
+                        children: [
+                            {
+                                value: 'changsha',
+                                label: '长沙市',
+                                children: [
+                                    {
+                                        value: 'yuelu',
+                                        label: '岳麓区'
+                                    }
+                                ]
+                            }
+                        ]
+                    }
+                ],
+                form: {
+                    name: '',
+                    region: '',
+                    date1: '',
+                    date2: '',
+                    delivery: true,
+                    type: ['步步高'],
+                    resource: '小天才',
+                    desc: '',
+                    options: []
+                }
+            }
+        },
+        methods: {
+            onSubmit() {
+                this.$message.success('提交成功!');
+            }
+        }
+    }
+</script>

+ 232 - 0
src/components/page/BaseTable.vue

@@ -0,0 +1,232 @@
+<template>
+    <div class="table">
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-cascades"></i> 基础表格</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="handle-box">
+                <el-button type="primary" icon="el-icon-delete" class="handle-del mr10" @click="delAll">批量删除</el-button>
+                <el-select v-model="select_cate" placeholder="筛选省份" class="handle-select mr10">
+                    <el-option key="1" label="广东省" value="广东省"></el-option>
+                    <el-option key="2" label="湖南省" value="湖南省"></el-option>
+                </el-select>
+                <el-input v-model="select_word" placeholder="筛选关键词" class="handle-input mr10"></el-input>
+                <el-button type="primary" icon="el-icon-search" @click="search">搜索</el-button>
+            </div>
+            <el-table :data="data" border class="table" ref="multipleTable" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55" align="center"></el-table-column>
+                <el-table-column prop="date" label="日期" sortable width="150">
+                </el-table-column>
+                <el-table-column prop="name" label="姓名" width="120">
+                </el-table-column>
+                <el-table-column prop="address" label="地址" :formatter="formatter">
+                </el-table-column>
+                <el-table-column label="操作" width="180" align="center">
+                    <template slot-scope="scope">
+                        <el-button type="text" icon="el-icon-edit" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+                        <el-button type="text" icon="el-icon-delete" class="red" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="pagination">
+                <el-pagination background @current-change="handleCurrentChange" layout="prev, pager, next" :total="1000">
+                </el-pagination>
+            </div>
+        </div>
+
+        <!-- 编辑弹出框 -->
+        <el-dialog title="编辑" :visible.sync="editVisible" width="30%">
+            <el-form ref="form" :model="form" label-width="50px">
+                <el-form-item label="日期">
+                    <el-date-picker type="date" placeholder="选择日期" v-model="form.date" value-format="yyyy-MM-dd" style="width: 100%;"></el-date-picker>
+                </el-form-item>
+                <el-form-item label="姓名">
+                    <el-input v-model="form.name"></el-input>
+                </el-form-item>
+                <el-form-item label="地址">
+                    <el-input v-model="form.address"></el-input>
+                </el-form-item>
+
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="editVisible = false">取 消</el-button>
+                <el-button type="primary" @click="saveEdit">确 定</el-button>
+            </span>
+        </el-dialog>
+
+        <!-- 删除提示框 -->
+        <el-dialog title="提示" :visible.sync="delVisible" width="300px" center>
+            <div class="del-dialog-cnt">删除不可恢复,是否确定删除?</div>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="delVisible = false">取 消</el-button>
+                <el-button type="primary" @click="deleteRow">确 定</el-button>
+            </span>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+    import { fetchData } from '../../api/index';
+    export default {
+        name: 'basetable',
+        data() {
+            return {
+                tableData: [],
+                cur_page: 1,
+                multipleSelection: [],
+                select_cate: '',
+                select_word: '',
+                del_list: [],
+                is_search: false,
+                editVisible: false,
+                delVisible: false,
+                form: {
+                    name: '',
+                    date: '',
+                    address: ''
+                },
+                idx: -1,
+                id: -1
+            }
+        },
+        created() {
+            this.getData();
+        },
+        computed: {
+            data() {
+                return this.tableData.filter((d) => {
+                    let is_del = false;
+                    for (let i = 0; i < this.del_list.length; i++) {
+                        if (d.name === this.del_list[i].name) {
+                            is_del = true;
+                            break;
+                        }
+                    }
+                    if (!is_del) {
+                        if (d.address.indexOf(this.select_cate) > -1 &&
+                            (d.name.indexOf(this.select_word) > -1 ||
+                                d.address.indexOf(this.select_word) > -1)
+                        ) {
+                            return d;
+                        }
+                    }
+                })
+            }
+        },
+        methods: {
+            // 分页导航
+            handleCurrentChange(val) {
+                this.cur_page = val;
+                this.getData();
+            },
+            // 获取 easy-mock 的模拟数据
+            getData() {
+                fetchData({
+                    page: this.cur_page
+                }).then((res) => {
+                    this.tableData = res.list;
+                })
+            },
+            search() {
+                this.is_search = true;
+            },
+            formatter(row, column) {
+                return row.address;
+            },
+            filterTag(value, row) {
+                return row.tag === value;
+            },
+            handleEdit(index, row) {
+                this.idx = index;
+                this.id = row.id;
+                this.form = {
+                    id: row.id,
+                    name: row.name,
+                    date: row.date,
+                    address: row.address
+                }
+                this.editVisible = true;
+            },
+            handleDelete(index, row) {
+                this.idx = index;
+                this.id = row.id;
+                this.delVisible = true;
+            },
+            delAll() {
+                const length = this.multipleSelection.length;
+                let str = '';
+                this.del_list = this.del_list.concat(this.multipleSelection);
+                for (let i = 0; i < length; i++) {
+                    str += this.multipleSelection[i].name + ' ';
+                }
+                this.$message.error('删除了' + str);
+                this.multipleSelection = [];
+            },
+            handleSelectionChange(val) {
+                this.multipleSelection = val;
+            },
+            // 保存编辑
+            saveEdit() {
+                this.editVisible = false;
+                this.$message.success(`修改第 ${this.idx+1} 行成功`);
+                if(this.tableData[this.idx].id === this.id){
+                    this.$set(this.tableData, this.idx, this.form);
+                }else{
+                    for(let i = 0; i < this.tableData.length; i++){
+                        if(this.tableData[i].id === this.id){
+                            this.$set(this.tableData, i, this.form);
+                            return ;
+                        }
+                    }
+                }
+            },
+            // 确定删除
+            deleteRow(){
+                this.$message.success('删除成功');
+                this.delVisible = false;
+                if(this.tableData[this.idx].id === this.id){
+                    this.tableData.splice(this.idx, 1);
+                }else{
+                    for(let i = 0; i < this.tableData.length; i++){
+                        if(this.tableData[i].id === this.id){
+                            this.tableData.splice(i, 1);
+                            return ;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+</script>
+
+<style scoped>
+    .handle-box {
+        margin-bottom: 20px;
+    }
+
+    .handle-select {
+        width: 120px;
+    }
+
+    .handle-input {
+        width: 300px;
+        display: inline-block;
+    }
+    .del-dialog-cnt{
+        font-size: 16px;
+        text-align: center
+    }
+    .table{
+        width: 100%;
+        font-size: 14px;
+    }
+    .red{
+        color: #ff0000;
+    }
+    .mr10{
+        margin-right: 10px;
+    }
+</style>

+ 343 - 0
src/components/page/Dashboard.vue

@@ -0,0 +1,343 @@
+<template>
+    <div>
+        <el-row :gutter="20">
+            <el-col :span="8">
+                <el-card shadow="hover" class="mgb20" style="height:252px;">
+                    <div class="user-info">
+                        <img src="../../assets/img/img.jpg" class="user-avator" alt="">
+                        <div class="user-info-cont">
+                            <div class="user-info-name">{{name}}</div>
+                            <div>{{role}}</div>
+                        </div>
+                    </div>
+                    <div class="user-info-list">上次登录时间:<span>2018-01-01</span></div>
+                    <div class="user-info-list">上次登录地点:<span>东莞</span></div>
+                </el-card>
+                <el-card shadow="hover" style="height:252px;">
+                    <div slot="header" class="clearfix">
+                        <span>语言详情</span>
+                    </div>
+                    Vue
+                    <el-progress :percentage="71.3" color="#42b983"></el-progress>
+                    JavaScript
+                    <el-progress :percentage="24.1" color="#f1e05a"></el-progress>
+                    CSS
+                    <el-progress :percentage="3.7"></el-progress>
+                    HTML
+                    <el-progress :percentage="0.9" color="#f56c6c"></el-progress>
+                </el-card>
+            </el-col>
+            <el-col :span="16">
+                <el-row :gutter="20" class="mgb20">
+                    <el-col :span="8">
+                        <el-card shadow="hover" :body-style="{padding: '0px'}">
+                            <div class="grid-content grid-con-1">
+                                <i class="el-icon-lx-people grid-con-icon"></i>
+                                <div class="grid-cont-right">
+                                    <div class="grid-num">1234</div>
+                                    <div>用户访问量</div>
+                                </div>
+                            </div>
+                        </el-card>
+                    </el-col>
+                    <el-col :span="8">
+                        <el-card shadow="hover" :body-style="{padding: '0px'}">
+                            <div class="grid-content grid-con-2">
+                                <i class="el-icon-lx-notice grid-con-icon"></i>
+                                <div class="grid-cont-right">
+                                    <div class="grid-num">321</div>
+                                    <div>系统消息</div>
+                                </div>
+                            </div>
+                        </el-card>
+                    </el-col>
+                    <el-col :span="8">
+                        <el-card shadow="hover" :body-style="{padding: '0px'}">
+                            <div class="grid-content grid-con-3">
+                                <i class="el-icon-lx-goods grid-con-icon"></i>
+                                <div class="grid-cont-right">
+                                    <div class="grid-num">5000</div>
+                                    <div>数量</div>
+                                </div>
+                            </div>
+                        </el-card>
+                    </el-col>
+                </el-row>
+                <el-card shadow="hover" style="height:403px;">
+                    <div slot="header" class="clearfix">
+                        <span>待办事项</span>
+                        <el-button style="float: right; padding: 3px 0" type="text">添加</el-button>
+                    </div>
+                    <el-table :data="todoList" :show-header="false" height="304" style="width: 100%;font-size:14px;">
+                        <el-table-column width="40">
+                            <template slot-scope="scope">
+                                <el-checkbox v-model="scope.row.status"></el-checkbox>
+                            </template>
+                        </el-table-column>
+                        <el-table-column>
+                            <template slot-scope="scope">
+                                <div class="todo-item" :class="{'todo-item-del': scope.row.status}">{{scope.row.title}}</div>
+                            </template>
+                        </el-table-column>
+                        <el-table-column width="60">
+                            <template slot-scope="scope">
+                                <i class="el-icon-edit"></i>
+                                <i class="el-icon-delete"></i>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                </el-card>
+            </el-col>
+        </el-row>
+        <el-row :gutter="20">
+            <el-col :span="12">
+                <el-card shadow="hover">
+                    <schart ref="bar" class="schart" canvasId="bar" :data="data" type="bar" :options="options"></schart>
+                </el-card>
+            </el-col>
+            <el-col :span="12">
+                <el-card shadow="hover">
+                    <schart ref="line" class="schart" canvasId="line" :data="data" type="line" :options="options2"></schart>
+                </el-card>
+            </el-col>
+        </el-row>
+    </div>
+</template>
+
+<script>
+    import Schart from 'vue-schart';
+    import bus from '../common/bus';
+    export default {
+        name: 'dashboard',
+        data() {
+            return {
+                name: localStorage.getItem('ms_username'),
+                todoList: [{
+                        title: '今天要修复100个bug',
+                        status: false,
+                    },
+                    {
+                        title: '今天要修复100个bug',
+                        status: false,
+                    },
+                    {
+                        title: '今天要写100行代码加几个bug吧',
+                        status: false,
+                    }, {
+                        title: '今天要修复100个bug',
+                        status: false,
+                    },
+                    {
+                        title: '今天要修复100个bug',
+                        status: true,
+                    },
+                    {
+                        title: '今天要写100行代码加几个bug吧',
+                        status: true,
+                    }
+                ],
+                data: [{
+                        name: '2018/09/04',
+                        value: 1083
+                    },
+                    {
+                        name: '2018/09/05',
+                        value: 941
+                    },
+                    {
+                        name: '2018/09/06',
+                        value: 1139
+                    },
+                    {
+                        name: '2018/09/07',
+                        value: 816
+                    },
+                    {
+                        name: '2018/09/08',
+                        value: 327
+                    },
+                    {
+                        name: '2018/09/09',
+                        value: 228
+                    },
+                    {
+                        name: '2018/09/10',
+                        value: 1065
+                    }
+                ],
+                options: {
+                    title: '最近七天每天的用户访问量',
+                    showValue: false,
+                    fillColor: 'rgb(45, 140, 240)',
+                    bottomPadding: 30,
+                    topPadding: 30
+                },
+                options2: {
+                    title: '最近七天用户访问趋势',
+                    fillColor: '#FC6FA1',
+                    axisColor: '#008ACD',
+                    contentColor: '#EEEEEE',
+                    bgColor: '#F5F8FD',
+                    bottomPadding: 30,
+                    topPadding: 30
+                }
+            }
+        },
+        components: {
+            Schart
+        },
+        computed: {
+            role() {
+                return this.name === 'admin' ? '超级管理员' : '普通用户';
+            }
+        },
+        created(){
+            this.handleListener();
+            this.changeDate();
+        },
+        activated(){
+            this.handleListener();
+        },
+        deactivated(){
+            window.removeEventListener('resize', this.renderChart);
+            bus.$off('collapse', this.handleBus);
+        },
+        methods: {
+            changeDate(){
+                const now = new Date().getTime();
+                this.data.forEach((item, index) => {
+                    const date = new Date(now - (6 - index) * 86400000);
+                    item.name = `${date.getFullYear()}/${date.getMonth()+1}/${date.getDate()}`
+                })
+            },
+            handleListener(){
+                bus.$on('collapse', this.handleBus);
+                // 调用renderChart方法对图表进行重新渲染
+                window.addEventListener('resize', this.renderChart)
+            },
+            handleBus(msg){
+                setTimeout(() => {
+                    this.renderChart()
+                }, 300);
+            },
+            renderChart(){
+                this.$refs.bar.renderChart();
+                this.$refs.line.renderChart();
+            }
+        }
+    }
+
+</script>
+
+
+<style scoped>
+    .el-row {
+        margin-bottom: 20px;
+    }
+
+    .grid-content {
+        display: flex;
+        align-items: center;
+        height: 100px;
+    }
+
+    .grid-cont-right {
+        flex: 1;
+        text-align: center;
+        font-size: 14px;
+        color: #999;
+    }
+
+    .grid-num {
+        font-size: 30px;
+        font-weight: bold;
+    }
+
+    .grid-con-icon {
+        font-size: 50px;
+        width: 100px;
+        height: 100px;
+        text-align: center;
+        line-height: 100px;
+        color: #fff;
+    }
+
+    .grid-con-1 .grid-con-icon {
+        background: rgb(45, 140, 240);
+    }
+
+    .grid-con-1 .grid-num {
+        color: rgb(45, 140, 240);
+    }
+
+    .grid-con-2 .grid-con-icon {
+        background: rgb(100, 213, 114);
+    }
+
+    .grid-con-2 .grid-num {
+        color: rgb(45, 140, 240);
+    }
+
+    .grid-con-3 .grid-con-icon {
+        background: rgb(242, 94, 67);
+    }
+
+    .grid-con-3 .grid-num {
+        color: rgb(242, 94, 67);
+    }
+
+    .user-info {
+        display: flex;
+        align-items: center;
+        padding-bottom: 20px;
+        border-bottom: 2px solid #ccc;
+        margin-bottom: 20px;
+    }
+
+    .user-avator {
+        width: 120px;
+        height: 120px;
+        border-radius: 50%;
+    }
+
+    .user-info-cont {
+        padding-left: 50px;
+        flex: 1;
+        font-size: 14px;
+        color: #999;
+    }
+
+    .user-info-cont div:first-child {
+        font-size: 30px;
+        color: #222;
+    }
+
+    .user-info-list {
+        font-size: 14px;
+        color: #999;
+        line-height: 25px;
+    }
+
+    .user-info-list span {
+        margin-left: 70px;
+    }
+
+    .mgb20 {
+        margin-bottom: 20px;
+    }
+
+    .todo-item {
+        font-size: 14px;
+    }
+
+    .todo-item-del {
+        text-decoration: line-through;
+        color: #999;
+    }
+
+    .schart {
+        width: 100%;
+        height: 300px;
+    }
+
+</style>

+ 26 - 0
src/components/page/Donate.vue

@@ -0,0 +1,26 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-redpacket_fill"></i> 支持作者</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                如果该框架对你有帮助,那就请作者喝杯饮料吧!加微信号linxin_20探讨问题。
+            </div>
+            <div><img src="http://blog.gdfengshuo.com/images/weixin.jpg"></div>
+        </div>
+
+    </div>
+</template>
+
+<script>
+    export default {
+        
+    }
+</script>
+
+<style>
+
+</style>

+ 36 - 0
src/components/page/DragDialog.vue

@@ -0,0 +1,36 @@
+<template>
+    <section class="main">
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-rank"></i> 拖拽组件</el-breadcrumb-item>
+                <el-breadcrumb-item>拖拽弹框</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <p>通过指令 v-dialogDrag 使 Dialog 对话框具有可拖拽的功能。</p>
+            <br>
+            <el-button type="primary" @click="visible = true;">点我弹框</el-button>
+        </div>
+        <el-dialog v-dialogDrag title="拖拽弹框" center :visible.sync="visible" width="30%">
+            我是一个可以拖拽的对话框!
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="visible = false">取 消</el-button>
+                <el-button type="primary" @click="visible = false">确 定</el-button>
+            </span>
+        </el-dialog>
+    </section>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+            visible: false
+        }
+    }
+}
+</script>
+
+<style>
+
+</style>

+ 174 - 0
src/components/page/DragList.vue

@@ -0,0 +1,174 @@
+<template>
+    <section class="main">
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-rank"></i> 拖拽组件</el-breadcrumb-item>
+                <el-breadcrumb-item>拖拽排序</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                Vue.Draggable:基于 Sortable.js 的 Vue 拖拽组件。
+                访问地址:<a href="https://github.com/SortableJS/Vue.Draggable" target="_blank">Vue.Draggable</a>
+            </div>
+            <div class="drag-box">
+                <div class="drag-box-item">
+                    <div class="item-title">todo</div>
+                    <draggable v-model="todo" @remove="removeHandle" :options="dragOptions">
+                        <transition-group tag="div" id="todo" class="item-ul">
+                            <div v-for="item in todo" class="drag-list" :key="item.id">
+                                {{item.content}}
+                            </div>
+                        </transition-group>
+                    </draggable>
+                </div>
+                <div class="drag-box-item">
+                    <div class="item-title">doing</div>
+                    <draggable v-model="doing" @remove="removeHandle" :options="dragOptions">
+                        <transition-group tag="div" id="doing" class="item-ul">
+                            <div v-for="item in doing" class="drag-list" :key="item.id">
+                                {{item.content}}
+                            </div>
+                        </transition-group>
+                    </draggable>
+                </div>
+                <div class="drag-box-item">
+                    <div class="item-title">done</div>
+                    <draggable v-model="done" @remove="removeHandle" :options="dragOptions">
+                        <transition-group tag="div" id="done" class="item-ul">
+                            <div v-for="item in done" class="drag-list" :key="item.id">
+                                {{item.content}}
+                            </div>
+                        </transition-group>
+                    </draggable>
+                </div>
+            </div>
+        </div>
+    </section>
+</template>
+
+<script>
+    import draggable from 'vuedraggable'
+    export default {
+        name: 'draglist',
+        data() {
+            return {
+                dragOptions:{
+                    animation: 120,
+                    scroll: true,
+                    group: 'sortlist',
+                    ghostClass: 'ghost-style'
+                },
+                todo: [
+                    {
+                        id: 1,
+                        content: '开发图表组件'
+                    },
+                    {
+                        id: 2,
+                        content: '开发拖拽组件'
+                    },
+                    {
+                        id: 3,
+                        content: '开发权限测试组件'
+                    }
+                ],
+                doing: [
+                    {
+                        id: 1,
+                        content: '开发登录注册页面'
+                    },
+                    {
+                        id: 2,
+                        content: '开发头部组件'
+                    },
+                    {
+                        id: 3,
+                        content: '开发表格相关组件'
+                    },
+                    {
+                        id: 4,
+                        content: '开发表单相关组件'
+                    }
+                ],
+                done:[
+                    {
+                        id: 1,
+                        content: '初始化项目,生成工程目录,完成相关配置'
+                    },
+                    {
+                        id: 2,
+                        content: '开发项目整体框架'
+                    }
+                ]
+            }
+        },
+        components:{
+            draggable
+        },
+        methods: {
+            removeHandle(event){
+                console.log(event);
+                this.$message.success(`从 ${event.from.id} 移动到 ${event.to.id} `);
+            }
+        }
+    }
+
+</script>
+
+<style scoped>
+    .drag-box{
+        display: flex;
+        user-select: none;
+    }
+    .drag-box-item {
+        flex: 1;
+        max-width: 330px;
+        min-width: 300px;
+        background-color: #eff1f5;
+        margin-right: 16px;
+        border-radius: 6px;
+        border: 1px #e1e4e8 solid;
+    }
+    .item-title{
+        padding: 8px 8px 8px 12px;
+        font-size: 14px;
+        line-height: 1.5;
+        color: #24292e;
+        font-weight: 600;
+    }
+    .item-ul{
+        padding: 0 8px 8px;
+        height: 500px;
+        overflow-y: scroll;
+    }
+    .item-ul::-webkit-scrollbar{
+        width: 0;
+    }
+    .drag-list {
+        border: 1px #e1e4e8 solid;
+        padding: 10px;
+        margin: 5px 0 10px;
+        list-style: none;
+        background-color: #fff;
+        border-radius: 6px;
+        cursor: pointer;
+        -webkit-transition: border .3s ease-in;
+        transition: border .3s ease-in;
+    }
+    .drag-list:hover {
+        border: 1px solid #20a0ff;
+    }
+    .drag-title {
+        font-weight: 400;
+        line-height: 25px;
+        margin: 10px 0;
+        font-size: 22px;
+        color: #1f2f3d;
+    }
+    .ghost-style{
+        display: block;
+        color: transparent;
+        border-style: dashed
+    }
+</style>

+ 46 - 0
src/components/page/I18n.vue

@@ -0,0 +1,46 @@
+<template>
+    <section class="main">
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-global"></i> {{$t('i18n.breadcrumb')}}</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <span>{{$t('i18n.tips')}}</span>
+            <el-button type="primary" @click="$i18n.locale = $i18n.locale === 'zh'?'en':'zh';">{{$t('i18n.btn')}}</el-button>
+            <div class="list">
+            <h2>{{$t('i18n.title1')}}</h2>
+                <p>{{$t('i18n.p1')}}</p>
+                <p>{{$t('i18n.p2')}}</p>
+                <p>{{$t('i18n.p3')}}</p>
+            </div>
+            <h2>{{$t('i18n.title2')}}</h2>
+            <div>
+                <i18n path="i18n.info" tag="p">
+                    <a place="action" href="https://element.eleme.cn/2.0/#/zh-CN/component/i18n">{{ $t('i18n.value') }}</a>
+                </i18n>
+            </div>
+        </div>
+    </section>
+</template>
+
+<script>
+export default {
+    data(){
+        return {
+        }
+    }
+}
+</script>
+
+<style scoped>
+.list{
+    padding: 30px 0;
+}
+.list p{
+    margin-bottom: 20px;
+}
+a{
+    color: #409eff;
+}
+</style>

+ 225 - 0
src/components/page/Icon.vue

@@ -0,0 +1,225 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-emoji"></i> 自定义图标</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <h2>使用方法</h2>
+            <p style="line-height: 50px;">
+                直接通过设置类名为 el-icon-lx-iconName 来使用即可。例如:(共{{iconList.length}}个图标)
+            </p>
+            <p class="example-p">
+                <i class="el-icon-lx-redpacket_fill" style="font-size: 30px;color: #ff5900"></i>
+                <span>&lt;i class=&quot;el-icon-lx-redpacket_fill&quot;&gt;&lt;/i&gt;</span>
+            </p>
+            <p class="example-p">
+                <i class="el-icon-lx-weibo" style="font-size: 30px;color:#fd5656"></i>
+                <span>&lt;i class=&quot;el-icon-lx-weibo&quot;&gt;&lt;/i&gt;</span>
+            </p>
+            <p class="example-p">
+                <i class="el-icon-lx-emojifill" style="font-size: 30px;color: #ffc300"></i>
+                <span>&lt;i class=&quot;el-icon-lx-emojifill&quot;&gt;&lt;/i&gt;</span>
+            </p>
+            <br>
+            <h2>图标</h2>
+            <div class="search-box">
+                <el-input class="search" size="large" v-model="keyword" clearable placeholder="请输入图标名称"></el-input>
+            </div>
+            <ul>
+                <li class="icon-li" v-for="(item,index) in list" :key="index">
+                    <div class="icon-li-content">
+                        <i :class="`el-icon-lx-${item}`"></i>
+                        <span>{{item}}</span>
+                    </div>
+                </li>
+            </ul>
+        </div>
+
+    </div>
+</template>
+
+<script>
+    export default {
+        data: function(){
+            return {
+                keyword: '',
+                iconList: [
+                    'attentionforbid',
+                    'attentionforbidfill',
+                    'attention',
+                    'attentionfill',
+                    'tag',
+                    'tagfill',
+                    'people',
+                    'peoplefill',
+                    'notice',
+                    'noticefill',
+                    'mobile',
+                    'mobilefill',
+                    'voice',
+                    'voicefill',
+                    'unlock',
+                    'lock',
+                    'home',
+                    'homefill',
+                    'delete',
+                    'deletefill',
+                    'notification',
+                    'notificationfill',
+                    'notificationforbidfill',
+                    'like',
+                    'likefill',
+                    'comment',
+                    'commentfill',
+                    'camera',
+                    'camerafill',
+                    'warn',
+                    'warnfill',
+                    'time',
+                    'timefill',
+                    'location',
+                    'locationfill',
+                    'favor',
+                    'favorfill',
+                    'skin',
+                    'skinfill',
+                    'news',
+                    'newsfill',
+                    'record',
+                    'recordfill',
+                    'emoji',
+                    'emojifill',
+                    'message',
+                    'messagefill',
+                    'goods',
+                    'goodsfill',
+                    'crown',
+                    'crownfill',
+                    'move',
+                    'add',
+                    'hot',
+                    'hotfill',
+                    'service',
+                    'servicefill',
+                    'present',
+                    'presentfill',
+                    'pic',
+                    'picfill',
+                    'rank',
+                    'rankfill',
+                    'male',
+                    'female',
+                    'down',
+                    'top',
+                    'recharge',
+                    'rechargefill',
+                    'forward',
+                    'forwardfill',
+                    'info',
+                    'infofill',
+                    'redpacket',
+                    'redpacket_fill',
+                    'roundadd',
+                    'roundaddfill',
+                    'friendadd',
+                    'friendaddfill',
+                    'cart',
+                    'cartfill',
+                    'more',
+                    'moreandroid',
+                    'back',
+                    'right',
+                    'shop',
+                    'shopfill',
+                    'question',
+                    'questionfill',
+                    'roundclose',
+                    'roundclosefill',
+                    'roundcheck',
+                    'roundcheckfill',
+                    'global',
+                    'mail',
+                    'punch',
+                    'exit',
+                    'upload',
+                    'read',
+                    'file',
+                    'link',
+                    'full',
+                    'group',
+                    'friend',
+                    'profile',
+                    'addressbook',
+                    'calendar',
+                    'text',
+                    'copy',
+                    'share',
+                    'wifi',
+                    'vipcard',
+                    'weibo',
+                    'remind',
+                    'refresh',
+                    'filter',
+                    'settings',
+                    'scan',
+                    'qrcode',
+                    'cascades',
+                    'apps',
+                    'sort',
+                    'searchlist',
+                    'search',
+                    'edit'
+                ]
+            }
+        },
+        computed: {
+            list(){
+                return this.iconList.filter((item) => {
+                    return item.indexOf(this.keyword) !== -1;
+                })
+            }
+        }
+    }
+</script>
+
+<style scoped>
+.example-p{
+    height: 45px;
+    display: flex;
+    align-items: center;
+}
+.search-box{
+    text-align: center;
+    margin-top: 10px;
+}
+.search{
+    width: 300px;
+}
+ul,li{
+    list-style: none;
+}
+.icon-li{
+    display: inline-block;
+    padding: 10px;
+    width: 120px;
+    height: 120px;
+}
+.icon-li-content{
+    display: flex;
+    height: 100%;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+}
+.icon-li-content i{
+    font-size: 36px;
+    color: #606266;
+}
+.icon-li-content span{
+    margin-top: 10px;
+    color: #787878;
+}
+</style>

+ 101 - 0
src/components/page/Login.vue

@@ -0,0 +1,101 @@
+<template>
+    <div class="login-wrap">
+        <div class="ms-login">
+            <div class="ms-title">后台管理系统</div>
+            <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="0px" class="ms-content">
+                <el-form-item prop="username">
+                    <el-input v-model="ruleForm.username" placeholder="username">
+                        <el-button slot="prepend" icon="el-icon-lx-people"></el-button>
+                    </el-input>
+                </el-form-item>
+                <el-form-item prop="password">
+                    <el-input type="password" placeholder="password" v-model="ruleForm.password" @keyup.enter.native="submitForm('ruleForm')">
+                        <el-button slot="prepend" icon="el-icon-lx-lock"></el-button>
+                    </el-input>
+                </el-form-item>
+                <div class="login-btn">
+                    <el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
+                </div>
+                <p class="login-tips">Tips : 用户名和密码随便填。</p>
+            </el-form>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        data: function(){
+            return {
+                ruleForm: {
+                    username: 'admin',
+                    password: '123123'
+                },
+                rules: {
+                    username: [
+                        { required: true, message: '请输入用户名', trigger: 'blur' }
+                    ],
+                    password: [
+                        { required: true, message: '请输入密码', trigger: 'blur' }
+                    ]
+                }
+            }
+        },
+        methods: {
+            submitForm(formName) {
+                this.$refs[formName].validate((valid) => {
+                    if (valid) {
+                        localStorage.setItem('ms_username',this.ruleForm.username);
+                        this.$router.push('/');
+                    } else {
+                        console.log('error submit!!');
+                        return false;
+                    }
+                });
+            }
+        }
+    }
+</script>
+
+<style scoped>
+    .login-wrap{
+        position: relative;
+        width:100%;
+        height:100%;
+        background-image: url(../../assets/img/login-bg.jpg);
+        background-size: 100%;
+    }
+    .ms-title{
+        width:100%;
+        line-height: 50px;
+        text-align: center;
+        font-size:20px;
+        color: #fff;
+        border-bottom: 1px solid #ddd;
+    }
+    .ms-login{
+        position: absolute;
+        left:50%;
+        top:50%;
+        width:350px;
+        margin:-190px 0 0 -175px;
+        border-radius: 5px;
+        background: rgba(255,255,255, 0.3);
+        overflow: hidden;
+    }
+    .ms-content{
+        padding: 30px 30px;
+    }
+    .login-btn{
+        text-align: center;
+    }
+    .login-btn button{
+        width:100%;
+        height:36px;
+        margin-bottom: 10px;
+    }
+    .login-tips{
+        font-size:12px;
+        line-height:30px;
+        color:#fff;
+    }
+</style>

+ 67 - 0
src/components/page/Markdown.vue

@@ -0,0 +1,67 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
+                <el-breadcrumb-item>markdown编辑器</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                mavonEditor:基于Vue的markdown编辑器。
+                访问地址:<a href="https://github.com/hinesboy/mavonEditor" target="_blank">mavonEditor</a>
+            </div>
+            <mavon-editor v-model="content" ref="md" @imgAdd="$imgAdd" @change="change" style="min-height: 600px"/>
+            <el-button class="editor-btn" type="primary" @click="submit">提交</el-button>
+        </div>
+    </div>
+</template>
+
+<script>
+    import { mavonEditor } from 'mavon-editor'
+    import 'mavon-editor/dist/css/index.css'
+    export default {
+        name: 'markdown',
+        data: function(){
+            return {
+                content:'',
+                html:'',
+                configs: {
+                }
+            }
+        },
+        components: {
+            mavonEditor
+        },
+        methods: {
+            // 将图片上传到服务器,返回地址替换到md中
+            $imgAdd(pos, $file){
+                var formdata = new FormData();
+                formdata.append('file', $file);
+                // 这里没有服务器供大家尝试,可将下面上传接口替换为你自己的服务器接口
+                this.$axios({
+                    url: '/common/upload',
+                    method: 'post',
+                    data: formdata,
+                    headers: { 'Content-Type': 'multipart/form-data' },
+                }).then((url) => {
+                    this.$refs.md.$img2Url(pos, url);
+                })
+            },
+            change(value, render){
+                // render 为 markdown 解析后的结果
+                this.html = render;
+            },
+            submit(){
+                console.log(this.content);
+                console.log(this.html);
+                this.$message.success('提交成功!');
+            }
+        }
+    }
+</script>
+<style scoped>
+    .editor-btn{
+        margin-top: 20px;
+    }
+</style>

+ 38 - 0
src/components/page/Permission.vue

@@ -0,0 +1,38 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-warn"></i> 权限测试</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <h1>管理员权限页面</h1>
+            <p>只有用 admin 账号登录的才拥有管理员权限,才能进到这个页面,其他账号想进来都会跳到403页面,重新用管理员账号登录才有权限。</p>
+            <p>想尝试一下,请<router-link to="/login" class="logout">退出登录</router-link>,随便输入个账号名,再进来试试看。</p>
+        </div>
+
+    </div>
+</template>
+
+<script>
+    export default {
+        data: function(){
+            return {}
+        }
+    }
+</script>
+
+<style scoped>
+h1{
+    text-align: center;
+    margin: 30px 0;
+}
+p{
+    line-height: 30px;
+    margin-bottom: 10px;
+    text-indent: 2em;
+}
+.logout{
+    color: #409EFF;
+}
+</style>

+ 129 - 0
src/components/page/Tabs.vue

@@ -0,0 +1,129 @@
+<template>
+    <div class="">
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-copy"></i> tab选项卡</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <el-tabs v-model="message">
+                <el-tab-pane :label="`未读消息(${unread.length})`" name="first">
+                    <el-table :data="unread" :show-header="false" style="width: 100%">
+                        <el-table-column>
+                            <template slot-scope="scope">
+                                <span class="message-title">{{scope.row.title}}</span>
+                            </template>
+                        </el-table-column>
+                        <el-table-column prop="date" width="180"></el-table-column>
+                        <el-table-column width="120">
+                            <template slot-scope="scope">
+                                <el-button size="small" @click="handleRead(scope.$index)">标为已读</el-button>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    <div class="handle-row">
+                        <el-button type="primary">全部标为已读</el-button>
+                    </div>
+                </el-tab-pane>
+                <el-tab-pane :label="`已读消息(${read.length})`" name="second">
+                    <template v-if="message === 'second'">
+                        <el-table :data="read" :show-header="false" style="width: 100%">
+                            <el-table-column>
+                                <template slot-scope="scope">
+                                    <span class="message-title">{{scope.row.title}}</span>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="date" width="150"></el-table-column>
+                            <el-table-column width="120">
+                                <template slot-scope="scope">
+                                    <el-button type="danger" @click="handleDel(scope.$index)">删除</el-button>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                        <div class="handle-row">
+                            <el-button type="danger">删除全部</el-button>
+                        </div>
+                    </template>
+                </el-tab-pane>
+                <el-tab-pane :label="`回收站(${recycle.length})`" name="third">
+                    <template v-if="message === 'third'">
+                        <el-table :data="recycle" :show-header="false" style="width: 100%">
+                            <el-table-column>
+                                <template slot-scope="scope">
+                                    <span class="message-title">{{scope.row.title}}</span>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="date" width="150"></el-table-column>
+                            <el-table-column width="120">
+                                <template slot-scope="scope">
+                                    <el-button @click="handleRestore(scope.$index)">还原</el-button>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                        <div class="handle-row">
+                            <el-button type="danger">清空回收站</el-button>
+                        </div>
+                    </template>
+                </el-tab-pane>
+            </el-tabs>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        name: 'tabs',
+        data() {
+            return {
+                message: 'first',
+                showHeader: false,
+                unread: [{
+                    date: '2018-04-19 20:00:00',
+                    title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护',
+                },{
+                    date: '2018-04-19 21:00:00',
+                    title: '今晚12点整发大红包,先到先得',
+                }],
+                read: [{
+                    date: '2018-04-19 20:00:00',
+                    title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
+                }],
+                recycle: [{
+                    date: '2018-04-19 20:00:00',
+                    title: '【系统通知】该系统将于今晚凌晨2点到5点进行升级维护'
+                }]
+            }
+        },
+        methods: {
+            handleRead(index) {
+                const item = this.unread.splice(index, 1);
+                console.log(item);
+                this.read = item.concat(this.read);
+            },
+            handleDel(index) {
+                const item = this.read.splice(index, 1);
+                this.recycle = item.concat(this.recycle);
+            },
+            handleRestore(index) {
+                const item = this.recycle.splice(index, 1);
+                this.read = item.concat(this.read);
+            }
+        },
+        computed: {
+            unreadNum(){
+                return this.unread.length;
+            }
+        }
+    }
+
+</script>
+
+<style>
+.message-title{
+    cursor: pointer;
+}
+.handle-row{
+    margin-top: 30px;
+}
+</style>
+

+ 141 - 0
src/components/page/Upload.vue

@@ -0,0 +1,141 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
+                <el-breadcrumb-item>图片上传</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="content-title">支持拖拽</div>
+            <div class="plugins-tips">
+                Element UI自带上传组件。
+                访问地址:<a href="http://element.eleme.io/#/zh-CN/component/upload" target="_blank">Element UI Upload</a>
+            </div>
+            <el-upload
+                class="upload-demo"
+                drag
+                action="http://jsonplaceholder.typicode.com/api/posts/"
+                multiple>
+                <i class="el-icon-upload"></i>
+                <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+                <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
+            </el-upload>
+            <div class="content-title">支持裁剪</div>
+            <div class="plugins-tips">
+                vue-cropperjs:一个封装了 cropperjs 的 Vue 组件。
+                访问地址:<a href="https://github.com/Agontuk/vue-cropperjs" target="_blank">vue-cropperjs</a>
+            </div>
+            <div class="crop-demo">
+                <img :src="cropImg" class="pre-img">
+                <div class="crop-demo-btn">选择图片
+                    <input class="crop-input" type="file" name="image" accept="image/*" @change="setImage"/>
+                </div>
+            </div>
+        
+            <el-dialog title="裁剪图片" :visible.sync="dialogVisible" width="30%">
+                <vue-cropper ref='cropper' :src="imgSrc" :ready="cropImage" :zoom="cropImage" :cropmove="cropImage" style="width:100%;height:300px;"></vue-cropper>
+                <span slot="footer" class="dialog-footer">
+                    <el-button @click="cancelCrop">取 消</el-button>
+                    <el-button type="primary" @click="dialogVisible = false">确 定</el-button>
+                </span>
+            </el-dialog>
+        </div>
+    </div>
+</template>
+
+<script>
+    import VueCropper  from 'vue-cropperjs';
+    export default {
+        name: 'upload',
+        data: function(){
+            return {
+                defaultSrc: require('../../assets/img/img.jpg'),
+                fileList: [],
+                imgSrc: '',
+                cropImg: '',
+                dialogVisible: false,
+            }
+        },
+        components: {
+            VueCropper
+        },
+        methods:{
+            setImage(e){
+                const file = e.target.files[0];
+                if (!file.type.includes('image/')) {
+                    return;
+                }
+                const reader = new FileReader();
+                reader.onload = (event) => {
+                    this.dialogVisible = true;
+                    this.imgSrc = event.target.result;
+                    this.$refs.cropper && this.$refs.cropper.replace(event.target.result);
+                };
+                reader.readAsDataURL(file);
+            },
+            cropImage () {
+                this.cropImg = this.$refs.cropper.getCroppedCanvas().toDataURL();
+            },
+            cancelCrop(){
+                this.dialogVisible = false;
+                this.cropImg = this.defaultSrc;
+            },
+            imageuploaded(res) {
+                console.log(res)
+            },
+            handleError(){
+                this.$notify.error({
+                    title: '上传失败',
+                    message: '图片上传接口上传失败,可更改为自己的服务器接口'
+                });
+            }
+        },
+        created(){
+            this.cropImg = this.defaultSrc;
+        }
+    }
+</script>
+
+<style scoped>
+    .content-title{
+        font-weight: 400;
+        line-height: 50px;
+        margin: 10px 0;
+        font-size: 22px;
+        color: #1f2f3d;
+    }
+    .pre-img{   
+        width: 100px;
+        height: 100px;
+        background: #f8f8f8;
+        border: 1px solid #eee;
+        border-radius: 5px;
+    }
+    .crop-demo{
+        display: flex;
+        align-items: flex-end;
+    }
+    .crop-demo-btn{
+        position: relative;
+        width: 100px;
+        height: 40px;
+        line-height: 40px;
+        padding: 0 20px;
+        margin-left: 30px;
+        background-color: #409eff;
+        color: #fff;
+        font-size: 14px;
+        border-radius: 4px;
+        box-sizing: border-box;
+    }
+    .crop-input{
+        position: absolute;
+        width: 100px;
+        height: 40px;
+        left: 0;
+        top: 0;
+        opacity: 0;
+        cursor: pointer;
+    }
+</style>

+ 53 - 0
src/components/page/VueEditor.vue

@@ -0,0 +1,53 @@
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-lx-calendar"></i> 表单</el-breadcrumb-item>
+                <el-breadcrumb-item>编辑器</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <div class="plugins-tips">
+                Vue-Quill-Editor:基于Quill、适用于Vue2的富文本编辑器。
+                访问地址:<a href="https://github.com/surmon-china/vue-quill-editor" target="_blank">vue-quill-editor</a>
+            </div>
+            <quill-editor ref="myTextEditor" v-model="content" :options="editorOption"></quill-editor>
+            <el-button class="editor-btn" type="primary" @click="submit">提交</el-button>
+        </div>
+    </div>
+</template>
+
+<script>
+    import 'quill/dist/quill.core.css';
+    import 'quill/dist/quill.snow.css';
+    import 'quill/dist/quill.bubble.css';
+    import { quillEditor } from 'vue-quill-editor';
+    export default {
+        name: 'editor',
+        data: function(){
+            return {
+                content: '',
+                editorOption: {
+                    placeholder: 'Hello World'
+                }
+            }
+        },
+        components: {
+            quillEditor
+        },
+        methods: {
+            onEditorChange({ editor, html, text }) {
+                this.content = html;
+            },
+            submit(){
+                console.log(this.content);
+                this.$message.success('提交成功!');
+            }
+        }
+    }
+</script>
+<style scoped>
+    .editor-btn{
+        margin-top: 20px;
+    }
+</style>

+ 51 - 0
src/main.js

@@ -0,0 +1,51 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from './router'
+import axios from 'axios';
+import ElementUI from 'element-ui';
+import VueI18n from 'vue-i18n';
+import { messages } from './components/common/i18n';
+import 'element-ui/lib/theme-chalk/index.css'; // 默认主题
+// import '../static/css/theme-green/index.css';       // 浅绿色主题
+import './assets/css/icon.css';
+import './components/common/directives';
+import "babel-polyfill";
+
+Vue.config.productionTip = false
+Vue.use(VueI18n);
+Vue.use(ElementUI, {
+    size: 'small'
+});
+Vue.prototype.$axios = axios;
+
+const i18n = new VueI18n({
+    locale: 'zh',
+    messages
+})
+
+//使用钩子函数对路由进行权限跳转
+router.beforeEach((to, from, next) => {
+    const role = localStorage.getItem('ms_username');
+    if (!role && to.path !== '/login') {
+        next('/login');
+    } else if (to.meta.permission) {
+        // 如果是管理员权限则可进入,这里只是简单的模拟管理员权限而已
+        role === 'admin' ? next() : next('/403');
+    } else {
+        // 简单的判断IE10及以下不进入富文本编辑器,该组件不兼容
+        if (navigator.userAgent.indexOf('MSIE') > -1 && to.path === '/editor') {
+            Vue.prototype.$alert('vue-quill-editor组件不兼容IE10及以下浏览器,请使用更高版本的浏览器查看', '浏览器不兼容通知', {
+                confirmButtonText: '确定'
+            });
+        } else {
+            next();
+        }
+    }
+})
+
+
+new Vue({
+    router,
+    i18n,
+    render: h => h(App)
+}).$mount('#app')

+ 116 - 0
src/router/index.js

@@ -0,0 +1,116 @@
+import Vue from 'vue';
+import Router from 'vue-router';
+
+Vue.use(Router);
+
+export default new Router({
+    routes: [
+        {
+            path: '/',
+            redirect: '/dashboard'
+        },
+        {
+            path: '/',
+            component: resolve => require(['../components/common/Home.vue'], resolve),
+            meta: { title: '自述文件' },
+            children:[
+                {
+                    path: '/dashboard',
+                    component: resolve => require(['../components/page/Dashboard.vue'], resolve),
+                    meta: { title: '系统首页' }
+                },
+                {
+                    path: '/icon',
+                    component: resolve => require(['../components/page/Icon.vue'], resolve),
+                    meta: { title: '自定义图标' }
+                },
+                {
+                    path: '/table',
+                    component: resolve => require(['../components/page/BaseTable.vue'], resolve),
+                    meta: { title: '基础表格' }
+                },
+                {
+                    path: '/tabs',
+                    component: resolve => require(['../components/page/Tabs.vue'], resolve),
+                    meta: { title: 'tab选项卡' }
+                },
+                {
+                    path: '/form',
+                    component: resolve => require(['../components/page/BaseForm.vue'], resolve),
+                    meta: { title: '基本表单' }
+                },
+                {
+                    // 富文本编辑器组件
+                    path: '/editor',
+                    component: resolve => require(['../components/page/VueEditor.vue'], resolve),
+                    meta: { title: '富文本编辑器' }
+                },
+                {
+                    // markdown组件
+                    path: '/markdown',
+                    component: resolve => require(['../components/page/Markdown.vue'], resolve),
+                    meta: { title: 'markdown编辑器' }    
+                },
+                {
+                    // 图片上传组件
+                    path: '/upload',
+                    component: resolve => require(['../components/page/Upload.vue'], resolve),
+                    meta: { title: '文件上传' }   
+                },
+                {
+                    // vue-schart组件
+                    path: '/charts',
+                    component: resolve => require(['../components/page/BaseCharts.vue'], resolve),
+                    meta: { title: 'schart图表' }
+                },
+                {
+                    // 拖拽列表组件
+                    path: '/drag',
+                    component: resolve => require(['../components/page/DragList.vue'], resolve),
+                    meta: { title: '拖拽列表' }
+                },
+                {
+                    // 拖拽Dialog组件
+                    path: '/dialog',
+                    component: resolve => require(['../components/page/DragDialog.vue'], resolve),
+                    meta: { title: '拖拽弹框' }
+                },
+                {
+                    // 国际化组件
+                    path: '/i18n',
+                    component: resolve => require(['../components/page/I18n.vue'], resolve),
+                    meta: { title: '国际化' }
+                },
+                {
+                    // 权限页面
+                    path: '/permission',
+                    component: resolve => require(['../components/page/Permission.vue'], resolve),
+                    meta: { title: '权限测试', permission: true }
+                },
+                {
+                    path: '/404',
+                    component: resolve => require(['../components/page/404.vue'], resolve),
+                    meta: { title: '404' }
+                },
+                {
+                    path: '/403',
+                    component: resolve => require(['../components/page/403.vue'], resolve),
+                    meta: { title: '403' }
+                },
+                {
+                    path: '/donate',
+                    component: resolve => require(['../components/page/Donate.vue'], resolve),
+                    meta: { title: '支持作者' }
+                }
+            ]
+        },
+        {
+            path: '/login',
+            component: resolve => require(['../components/page/Login.vue'], resolve)
+        },
+        {
+            path: '*',
+            redirect: '/404'
+        }
+    ]
+})

+ 27 - 0
src/utils/request.js

@@ -0,0 +1,27 @@
+import axios from 'axios';
+
+const service = axios.create({
+    // process.env.NODE_ENV === 'development' 来判断是否开发环境
+    baseURL: 'https://www.easy-mock.com/mock/592501a391470c0ac1fab128',
+    timeout: 5000
+})
+
+service.interceptors.request.use( config => {
+    return config;
+}, error => {
+    console.log(error);
+    return Promise.reject();
+})
+
+service.interceptors.response.use(response => {
+    if(response.status === 200){
+        return response.data;
+    }else{
+        Promise.reject();
+    }
+}, error => {
+    console.log(error);
+    return Promise.reject();
+})
+
+export default service;

+ 16 - 0
vue.config.js

@@ -0,0 +1,16 @@
+module.exports = {
+    baseUrl: './',
+    assetsDir: 'static',
+    productionSourceMap: false,
+    // devServer: {
+    //     proxy: {
+    //         '/api':{
+    //             target:'http://jsonplaceholder.typicode.com',
+    //             changeOrigin:true,
+    //             pathRewrite:{
+    //                 '/api':''
+    //             }
+    //         }
+    //     }
+    // }
+}