Переглянути джерело

Merge pull request #78 from lin-xin/dev

vue-manage-system 升级改版
林鑫 7 роки тому
батько
коміт
847fbe0d70
44 змінених файлів з 1451 додано та 1273 видалено
  1. 7 8
      .babelrc
  2. 9 0
      .postcssrc.js
  3. 12 14
      README.md
  4. 24 147
      README_EN.md
  5. 31 30
      build/build.js
  6. 22 13
      build/check-versions.js
  7. 0 9
      build/dev-client.js
  8. 0 81
      build/dev-server.js
  9. BIN
      build/logo.png
  10. 68 31
      build/utils.js
  11. 0 0
      build/vendor-manifest.json
  12. 16 11
      build/vue-loader.conf.js
  13. 36 23
      build/webpack.base.conf.js
  14. 62 17
      build/webpack.dev.conf.js
  15. 65 22
      build/webpack.prod.conf.js
  16. 3 2
      config/dev.env.js
  17. 80 45
      config/index.js
  18. 1 0
      config/prod.env.js
  19. 39 34
      package.json
  20. BIN
      screenshots/wms1.png
  21. BIN
      screenshots/wms2.png
  22. 19 2
      src/components/common/Header.vue
  23. 30 19
      src/components/common/Home.vue
  24. 41 23
      src/components/common/Sidebar.vue
  25. 6 0
      src/components/common/bus.js
  26. 21 19
      src/components/page/BaseCharts.vue
  27. 139 81
      src/components/page/BaseForm.vue
  28. 52 50
      src/components/page/BaseTable.vue
  29. 127 69
      src/components/page/DragList.vue
  30. 5 6
      src/components/page/Login.vue
  31. 68 42
      src/components/page/Markdown.vue
  32. 0 216
      src/components/page/OldCharts.vue
  33. 38 0
      src/components/page/Permission.vue
  34. 87 95
      src/components/page/Readme.vue
  35. 96 34
      src/components/page/Upload.vue
  36. 51 51
      src/components/page/VueEditor.vue
  37. 18 16
      src/components/page/VueTable.vue
  38. 21 2
      src/main.js
  39. 71 59
      src/router/index.js
  40. 16 2
      static/css/main.css
  41. 70 0
      static/css/mavon-flex.css
  42. BIN
      static/css/theme-green/fonts/element-icons.ttf
  43. BIN
      static/css/theme-green/fonts/element-icons.woff
  44. 0 0
      static/css/theme-green/index.css

+ 7 - 8
.babelrc

@@ -1,13 +1,12 @@
 {
   "presets": [
-    ["es2015", { "modules": false }],
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
     "stage-2"
   ],
-  "plugins": ["transform-runtime"],
-  "comments": false,
-  "env": {
-    "test": {
-      "plugins": [ "istanbul" ]
-    }
-  }
+  "plugins": ["transform-vue-jsx", "transform-runtime"]
 }

+ 9 - 0
.postcssrc.js

@@ -0,0 +1,9 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    // to edit target browsers: use "browserslist" field in package.json
+    "postcss-import": {},
+    "autoprefixer": {}
+  }
+}

+ 12 - 14
README.md

@@ -31,6 +31,7 @@
 	|-- src                              // 源码目录
 	|   |-- components                   // 组件
 	|       |-- common                   // 公共组件
+	|           |-- bus.js           	 // Event Bus
 	|           |-- Header.vue           // 公共头部
 	|           |-- Home.vue           	 // 公共路由入口
 	|           |-- Sidebar.vue          // 公共左边栏
@@ -38,12 +39,14 @@
 	|           |-- BaseCharts.vue       // 基础图表
 	|           |-- BaseForm.vue         // 基础表单
 	|           |-- BaseTable.vue        // 基础表格
+	|           |-- DragList.vue         // 拖拽列表组件
 	|           |-- Login.vue          	 // 登录
 	|           |-- Markdown.vue         // markdown组件
+	|           |-- Premission.vue       // 权限测试组件
 	|           |-- Readme.vue           // 自述组件
 	|           |-- Upload.vue           // 图片上传
 	|           |-- VueEditor.vue        // 富文本编辑器
-	|           |-- VueTable.vue         // vue表格组件
+	|           |-- VueTable.vue         // datasource表格组件
 	|   |-- App.vue                      // 页面入口文件
 	|   |-- main.js                      // 程序入口文件,加载各种公共组件
 	|-- .babelrc                         // ES6语法编译配置
@@ -56,8 +59,8 @@
 
 ## 安装步骤 ##
 
-	git clone https://github.com/lin-xin/manage-system.git      // 把模板下载到本地
-	cd manage-system    // 进入模板目录
+	git clone https://github.com/lin-xin/vue-manage-system.git      // 把模板下载到本地
+	cd vue-manage-system    // 进入模板目录
 	npm install         // 安装项目依赖,等待安装完成之后
 
 ## 本地开发 ##
@@ -122,21 +125,16 @@ vue.js封装sChart.js的图表组件。访问地址:[vue-schart](https://githu
 ### vue-datasource ###
 一个用于动态创建表格的vue.js服务端组件。访问地址:[vue-datasource](https://github.com/coderdiaz/vue-datasource)
 
-
-
 ### Vue-Quill-Editor ###
 基于Quill、适用于Vue2的富文本编辑器。访问地址:[vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
 
-### Vue-SimpleMDE ###
-Vue.js的Markdown Editor组件。访问地址:[Vue-SimpleMDE](https://github.com/F-loat/vue-simplemde)
-
-
-
-### Vue-Core-Image-Upload ###
-一款轻量级的vue上传插件,支持裁剪。访问地址:[Vue-Core-Image-Upload](https://github.com/Vanthink-UED/vue-core-image-upload)
-
+(IE10及以下不支持)
 
+### mavonEditor ###
+基于Vue的markdown编辑器。访问地址:[mavonEditor](https://github.com/hinesboy/mavonEditor)
 
+### vue-cropperjs ###
+一个封装了 cropperjs 的 Vue 组件,用于裁剪图片。访问地址:[vue-cropperjs](https://github.com/Agontuk/vue-cropperjs)
 
 ## 其他注意事项 ##
 ### 一、如果我不想用到上面的某些组件呢,那我怎么在模板中删除掉不影响到其他功能呢? ###
@@ -183,7 +181,7 @@ import 'element-ui/lib/theme-default/index.css';    // 默认主题
 /*@import "../static/css/theme-green/color-green.css";   !*浅绿色主题*!*/
 ```
 
-第三步:打开 src/components/common/Sidebar.vue 文件,找到 el-menu 标签,把 theme="dark" 去掉即可。
+第三步:打开 src/components/common/Sidebar.vue 文件,找到 el-menu 标签,把 background-color/text-color/active-text-color 属性去掉即可。
 
 ## 项目截图 ##
 ### 默认皮肤 ###

+ 24 - 147
README_EN.md

@@ -1,6 +1,8 @@
 # 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.
@@ -25,6 +27,7 @@ The scheme as a set of multi-function background frame templates, suitable for m
 	|-- src                              // Source directory
 	|   |-- components                   // Components
 	|       |-- common                   // Common component
+	|           |-- bus.js           	 // Event Bus
 	|           |-- Header.vue           // Header component
 	|           |-- Home.vue           	 // Home component
 	|           |-- Sidebar.vue          // Sidebar component
@@ -33,8 +36,9 @@ The scheme as a set of multi-function background frame templates, suitable for m
 	|           |-- BaseForm.vue         // BaseForm
 	|           |-- BaseTable.vue        // BaseTable
 	|           |-- Login.vue          	 // Login
+	|           |-- DragList.vue
 	|           |-- Markdown.vue         // Markdown
-	|           |-- MixCharts.vue        // MixCharts
+	|           |-- Premission.vue
 	|           |-- Readme.vue           // Readme
 	|           |-- Upload.vue           // Upload
 	|           |-- VueEditor.vue        // VueEditor
@@ -51,8 +55,8 @@ The scheme as a set of multi-function background frame templates, suitable for m
 
 ## Installation steps ##
 
-	git clone https://github.com/lin-xin/manage-system.git		// Clone templates
-	cd manage-system											// Enter template directory
+	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 ##
@@ -67,149 +71,6 @@ The scheme as a set of multi-function background frame templates, suitable for m
 
 ## Component description and presentation ##
 
-### element-ui ###
-A desktop component library based on vue.js2.0 . Github : [element](http://element.eleme.io/#/zh-CN/component/layout)
-
-### vue-datasource ###
-A Vue.js server side component to create dynamic tables. Github : [vue-datasource](https://github.com/coderdiaz/vue-datasource)
-
-```JavaScript
-<template>
-	<div>
-		<datasource language="en" :table-data="information.data"
-	        :columns="columns"
-	        :pagination="information.pagination"
-	        :actions="actions"
-	        v-on:change="changePage"
-	        v-on:searching="onSearch"></datasource>
-	</div>
-</template>
-
-<script>
-	import Datasource from 'vue-datasource';        // import Datasource component
-    export default {
-        data: function(){
-            return {
-                information: {
-	                pagination: {...},              // pagination config
-	                data: [...]
-	            },
-	            columns: [...],                     // col config
-	            actions: [...]                      // function config
-            }
-        },
-        components: {
-            Datasource										
-        },
-	    methods: {
-	        changePage(values) {...},
-	        onSearch(searchQuery) {...}
-	    }
-	}
-</script>
-```
-
-
-### Vue-Quill-Editor ###
-Quill editor component for Vue2. Github : [vue-quill-editor](https://github.com/surmon-china/vue-quill-editor)
-
-```JavaScript
-<template>
-	<div>
-		<quill-editor ref="myTextEditor" v-model="content" :config="editorOption"></quill-editor>
-	</div>
-</template>
-
-<script>
-	import { quillEditor } from 'vue-quill-editor';     // import quillEditor component
-    export default {
-        data: function(){
-            return {
-                content: '',								
-                editorOption: {								
-                    // something config
-                }
-            }
-        },
-        components: {
-            quillEditor										
-        }
-	}
-</script>
-```
-
-### Vue-SimpleMDE ###
-Markdown Editor component for Vue.js. Github : [Vue-SimpleMDE](https://github.com/F-loat/vue-simplemde)
-
-```JavaScript
-<template>
-    <div>
-        <markdown-editor v-model="content" :configs="configs" ref="markdownEditor"></markdown-editor>
-    </div>
-</template>
-
-<script>
-    import { markdownEditor } from 'vue-simplemde';			
-    export default {
-        data: function(){
-            return {
-                content:'',									
-                configs: {									
-                    status: false,							
-                    initialValue: 'Hello BBK',				
-                    renderingConfig: {
-                        codeSyntaxHighlighting: true,		
-                        highlightingTheme: 'atom-one-light' 
-                    }
-                }
-            }
-        },
-        components: {
-            markdownEditor									
-        }
-    }
-</script>
-```
-
-### Vue-Core-Image-Upload ###
-a vue plugin for image upload and crop. Github : [Vue-Core-Image-Upload](https://github.com/Vanthink-UED/vue-core-image-upload)
-
-```JavaScript
-
-<template>
-    <div>
-		<img :src="src">									
-        <vue-core-image-upload :class="['pure-button','pure-button-primary','js-btn-crop']"
-           :crop="true"										
-           text="上传图片"
-           url=""											
-           extensions="png,gif,jpeg,jpg"					
-           @:imageuploaded="imageuploaded">					
-		</vue-core-image-upload>
-    </div>
-</template>
-
-<script>
-    import VueCoreImageUpload  from 'vue-core-image-upload';	
-    export default {
-        data: function(){
-            return {
-                src:'../img/1.jpg'							
-            }
-        },
-        components: {
-            VueCoreImageUpload								
-        },
-        methods:{
-            imageuploaded(res) {							
-                console.log(res)
-            }
-        }
-    }
-</script>
-
-```
-
 ### vue-schart ###
 Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/linxin/vue-schart)
 
@@ -253,6 +114,22 @@ Vue.js wrapper for sChart.js. Github : [vue-schart](https://github.com/linxin/vu
 </script>
 ```
 
+### element-ui ###
+A desktop component library based on vue.js2.0 . Github : [element](http://element.eleme.io/#/zh-CN/component/layout)
+
+### vue-datasource ###
+A Vue.js server side component to create dynamic tables. Github : [vue-datasource](https://github.com/coderdiaz/vue-datasource)
+
+### 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? ###
 
@@ -298,7 +175,7 @@ The second step to enter 'src/App.vue' and change into green theme.
 /*@import "../static/css/theme-green/color-green.css";   !*浅绿色主题*!*/
 ```
 
-Finally,enter 'src/components/common/Sidebar.vue' and find el-menu Tags,delete 'theme="dark"'。
+Finally,enter 'src/components/common/Sidebar.vue' and find el-menu Tags,delete 'background-color/text-color/active-text-color'。
 
 ## Screenshot ##
 ### Default theme ###

+ 31 - 30
build/build.js

@@ -1,40 +1,41 @@
-// https://github.com/shelljs/shelljs
+'use strict'
 require('./check-versions')()
 
 process.env.NODE_ENV = 'production'
 
-var ora = require('ora')
-var path = require('path')
-var chalk = require('chalk')
-var shell = require('shelljs')
-var webpack = require('webpack')
-var config = require('../config')
-var webpackConfig = require('./webpack.prod.conf')
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
 
-var spinner = ora('building for production...')
+const spinner = ora('building for production...')
 spinner.start()
 
-var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
-shell.rm('-rf', assetsPath)
-shell.mkdir('-p', assetsPath)
-shell.config.silent = true
-shell.cp('-R', 'static/*', assetsPath)
-shell.config.silent = false
-
-webpack(webpackConfig, function (err, stats) {
-  spinner.stop()
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
   if (err) throw err
-  process.stdout.write(stats.toString({
-    colors: true,
-    modules: false,
-    children: false,
-    chunks: false,
-    chunkModules: false
-  }) + '\n\n')
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
 
-  console.log(chalk.cyan('  Build complete.\n'))
-  console.log(chalk.yellow(
-    '  Tip: built files are meant to be served over an HTTP server.\n' +
-    '  Opening index.html over file:// won\'t work.\n'
-  ))
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
 })

+ 22 - 13
build/check-versions.js

@@ -1,28 +1,35 @@
-var chalk = require('chalk')
-var semver = require('semver')
-var packageConfig = require('../package.json')
+'use strict'
+const chalk = require('chalk')
+const semver = require('semver')
+const packageConfig = require('../package.json')
+const shell = require('shelljs')
 
 function exec (cmd) {
   return require('child_process').execSync(cmd).toString().trim()
 }
 
-var versionRequirements = [
+const versionRequirements = [
   {
     name: 'node',
     currentVersion: semver.clean(process.version),
     versionRequirement: packageConfig.engines.node
-  },
-  {
+  }
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
     name: 'npm',
     currentVersion: exec('npm --version'),
     versionRequirement: packageConfig.engines.npm
-  }
-]
+  })
+}
 
 module.exports = function () {
-  var warnings = []
-  for (var i = 0; i < versionRequirements.length; i++) {
-    var mod = versionRequirements[i]
+  const warnings = []
+
+  for (let i = 0; i < versionRequirements.length; i++) {
+    const mod = versionRequirements[i]
+
     if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
       warnings.push(mod.name + ': ' +
         chalk.red(mod.currentVersion) + ' should be ' +
@@ -35,10 +42,12 @@ module.exports = function () {
     console.log('')
     console.log(chalk.yellow('To use this template, you must update following to modules:'))
     console.log()
-    for (var i = 0; i < warnings.length; i++) {
-      var warning = warnings[i]
+
+    for (let i = 0; i < warnings.length; i++) {
+      const warning = warnings[i]
       console.log('  ' + warning)
     }
+
     console.log()
     process.exit(1)
   }

+ 0 - 9
build/dev-client.js

@@ -1,9 +0,0 @@
-/* eslint-disable */
-require('eventsource-polyfill')
-var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
-
-hotClient.subscribe(function (event) {
-  if (event.action === 'reload') {
-    window.location.reload()
-  }
-})

+ 0 - 81
build/dev-server.js

@@ -1,81 +0,0 @@
-require('./check-versions')()
-
-var config = require('../config')
-if (!process.env.NODE_ENV) {
-  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
-}
-
-var opn = require('opn')
-var path = require('path')
-var express = require('express')
-var webpack = require('webpack')
-var proxyMiddleware = require('http-proxy-middleware')
-var webpackConfig = require('./webpack.dev.conf')
-
-// default port where dev server listens for incoming traffic
-var port = process.env.PORT || config.dev.port
-// automatically open browser, if not set will be false
-var autoOpenBrowser = !!config.dev.autoOpenBrowser
-// Define HTTP proxies to your custom API backend
-// https://github.com/chimurai/http-proxy-middleware
-var proxyTable = config.dev.proxyTable
-
-var app = express()
-var compiler = webpack(webpackConfig)
-
-var devMiddleware = require('webpack-dev-middleware')(compiler, {
-  publicPath: webpackConfig.output.publicPath,
-  quiet: true
-})
-
-var hotMiddleware = require('webpack-hot-middleware')(compiler, {
-  log: () => {}
-})
-// force page reload when html-webpack-plugin template changes
-compiler.plugin('compilation', function (compilation) {
-  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
-    hotMiddleware.publish({ action: 'reload' })
-    cb()
-  })
-})
-
-// proxy api requests
-Object.keys(proxyTable).forEach(function (context) {
-  var options = proxyTable[context]
-  if (typeof options === 'string') {
-    options = { target: options }
-  }
-  app.use(proxyMiddleware(options.filter || context, options))
-})
-
-// handle fallback for HTML5 history API
-app.use(require('connect-history-api-fallback')())
-
-// serve webpack bundle output
-app.use(devMiddleware)
-
-// enable hot-reload and state-preserving
-// compilation error display
-app.use(hotMiddleware)
-
-// serve pure static assets
-var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
-app.use(staticPath, express.static('./static'))
-
-var uri = 'http://localhost:' + port
-
-devMiddleware.waitUntilValid(function () {
-  console.log('> Listening at ' + uri + '\n')
-})
-
-module.exports = app.listen(port, function (err) {
-  if (err) {
-    console.log(err)
-    return
-  }
-
-  // when env is testing, don't need open it
-  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
-    opn(uri)
-  }
-})

BIN
build/logo.png


+ 68 - 31
build/utils.js

@@ -1,64 +1,101 @@
-var path = require('path')
-var config = require('../config')
-var ExtractTextPlugin = require('extract-text-webpack-plugin')
+'use strict'
+const path = require('path')
+const config = require('../config')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const packageConfig = require('../package.json')
 
 exports.assetsPath = function (_path) {
-  var assetsSubDirectory = process.env.NODE_ENV === 'production'
+  const assetsSubDirectory = process.env.NODE_ENV === 'production'
     ? config.build.assetsSubDirectory
     : config.dev.assetsSubDirectory
+
   return path.posix.join(assetsSubDirectory, _path)
 }
 
 exports.cssLoaders = function (options) {
   options = options || {}
+
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  const postcssLoader = {
+    loader: 'postcss-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
   // generate loader string to be used with extract text plugin
-  function generateLoaders (loaders) {
-    var sourceLoader = loaders.map(function (loader) {
-      var extraParamChar
-      if (/\?/.test(loader)) {
-        loader = loader.replace(/\?/, '-loader?')
-        extraParamChar = '&'
-      } else {
-        loader = loader + '-loader'
-        extraParamChar = '?'
-      }
-      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
-    }).join('!')
+  function generateLoaders (loader, loaderOptions) {
+    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
+
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
 
     // Extract CSS when that option is specified
     // (which is the case during production build)
     if (options.extract) {
       return ExtractTextPlugin.extract({
-        use: sourceLoader,
+        use: loaders,
         fallback: 'vue-style-loader'
       })
     } else {
-      return ['vue-style-loader', sourceLoader].join('!')
+      return ['vue-style-loader'].concat(loaders)
     }
   }
 
-  // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
   return {
-    css: generateLoaders(['css']),
-    postcss: generateLoaders(['css']),
-    less: generateLoaders(['css', 'less']),
-    sass: generateLoaders(['css', 'sass?indentedSyntax']),
-    scss: generateLoaders(['css', 'sass']),
-    stylus: generateLoaders(['css', 'stylus']),
-    styl: generateLoaders(['css', 'stylus'])
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
   }
 }
 
 // Generate loaders for standalone style files (outside of .vue)
 exports.styleLoaders = function (options) {
-  var output = []
-  var loaders = exports.cssLoaders(options)
-  for (var extension in loaders) {
-    var loader = loaders[extension]
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
     output.push({
       test: new RegExp('\\.' + extension + '$'),
-      loader: loader
+      use: loader
     })
   }
+
   return output
 }
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
build/vendor-manifest.json


+ 16 - 11
build/vue-loader.conf.js

@@ -1,17 +1,22 @@
-var utils = require('./utils')
-var config = require('../config')
-var isProduction = process.env.NODE_ENV === 'production'
+'use strict'
+const utils = require('./utils')
+const config = require('../config')
+const isProduction = process.env.NODE_ENV === 'production'
+const sourceMapEnabled = isProduction
+  ? config.build.productionSourceMap
+  : config.dev.cssSourceMap
 
 module.exports = {
   loaders: utils.cssLoaders({
-    sourceMap: isProduction
-      ? config.build.productionSourceMap
-      : config.dev.cssSourceMap,
+    sourceMap: sourceMapEnabled,
     extract: isProduction
   }),
-  postcss: [
-    require('autoprefixer')({
-      browsers: ['last 2 versions']
-    })
-  ]
+  cssSourceMap: sourceMapEnabled,
+  cacheBusting: config.dev.cacheBusting,
+  transformToRequire: {
+    video: ['src', 'poster'],
+    source: 'src',
+    img: 'src',
+    image: 'xlink:href'
+  }
 }

+ 36 - 23
build/webpack.base.conf.js

@@ -1,16 +1,19 @@
-var path = require('path')
-var utils = require('./utils')
-var webpack = require('webpack')
-var config = require('../config')
-var vueLoaderConfig = require('./vue-loader.conf')
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
 
 function resolve (dir) {
   return path.join(__dirname, '..', dir)
 }
 
+
+
 module.exports = {
+  context: path.resolve(__dirname, '../'),
   entry: {
-    app: ['babel-polyfill','./src/main.js']
+    app: './src/main.js'
   },
   output: {
     path: config.build.assetsRoot,
@@ -21,22 +24,18 @@ module.exports = {
   },
   resolve: {
     extensions: ['.js', '.vue', '.json'],
-    modules: [
-      resolve('src'),
-      resolve('node_modules')
-    ],
     alias: {
-      'vue$': 'vue/dist/vue.common.js',
-      'src': resolve('src'),
-      'assets': resolve('src/assets'),
-      'components': resolve('src/components')
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src'),
+      'static': path.resolve(__dirname, '../static'),
     }
   },
   module: {
     rules: [
       {
         test: /\.vue$/,
-        loader: 'vue-loader'
+        loader: 'vue-loader',
+        options: vueLoaderConfig
       },
       {
         test: /\.js$/,
@@ -46,25 +45,39 @@ module.exports = {
       {
         test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
         loader: 'url-loader',
-        query: {
+        options: {
           limit: 10000,
           name: utils.assetsPath('img/[name].[hash:7].[ext]')
         }
       },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
       {
         test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
         loader: 'url-loader',
-        query: {
+        options: {
           limit: 10000,
           name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
         }
       }
     ]
   },
-    // plugins: [
-    //     new webpack.DllReferencePlugin({
-    //       context: path.resolve(__dirname, '..'),
-    //       manifest: require('./vendor-manifest.json')
-    //     })
-    // ]
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
 }

+ 62 - 17
build/webpack.dev.conf.js

@@ -1,28 +1,48 @@
-var utils = require('./utils')
-var webpack = require('webpack')
-var config = require('../config')
-var merge = require('webpack-merge')
-var baseWebpackConfig = require('./webpack.base.conf')
-var HtmlWebpackPlugin = require('html-webpack-plugin')
-var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
 
-// add hot-reload related code to entry chunks
-Object.keys(baseWebpackConfig.entry).forEach(function (name) {
-  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
-})
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
 
-module.exports = merge(baseWebpackConfig, {
+const devWebpackConfig = merge(baseWebpackConfig, {
   module: {
-    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
   },
   // cheap-module-eval-source-map is faster for development
-  devtool: '#cheap-module-eval-source-map',
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: true,
+    hot: true,
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    }
+  },
   plugins: [
     new webpack.DefinePlugin({
-      'process.env': config.dev.env
+      'process.env': require('../config/dev.env')
     }),
-    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
     new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
     new webpack.NoEmitOnErrorsPlugin(),
     // https://github.com/ampedandwired/html-webpack-plugin
     new HtmlWebpackPlugin({
@@ -30,6 +50,31 @@ module.exports = merge(baseWebpackConfig, {
       template: 'index.html',
       inject: true
     }),
-    new FriendlyErrorsPlugin()
   ]
 })
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 65 - 22
build/webpack.prod.conf.js

@@ -1,21 +1,27 @@
-var path = require('path')
-var utils = require('./utils')
-var webpack = require('webpack')
-var config = require('../config')
-var merge = require('webpack-merge')
-var baseWebpackConfig = require('./webpack.base.conf')
-var HtmlWebpackPlugin = require('html-webpack-plugin')
-var ExtractTextPlugin = require('extract-text-webpack-plugin')
-var env = config.build.env
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
 
-var webpackConfig = merge(baseWebpackConfig, {
+const env = require('../config/prod.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
   module: {
     rules: utils.styleLoaders({
       sourceMap: config.build.productionSourceMap,
-      extract: true
+      extract: true,
+      usePostCSS: true
     })
   },
-  devtool: config.build.productionSourceMap ? '#source-map' : false,
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
   output: {
     path: config.build.assetsRoot,
     filename: utils.assetsPath('js/[name].[chunkhash].js'),
@@ -26,15 +32,30 @@ var webpackConfig = merge(baseWebpackConfig, {
     new webpack.DefinePlugin({
       'process.env': env
     }),
-    new webpack.optimize.UglifyJsPlugin({
-      compress: {
-        warnings: false
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
       },
-      sourceMap: true
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
     }),
     // extract css into its own file
     new ExtractTextPlugin({
-      filename: utils.assetsPath('css/[name].[contenthash].css')
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
     }),
     // generate dist index.html with correct asset hash for caching.
     // you can customize output by editing /index.html
@@ -53,10 +74,14 @@ var webpackConfig = merge(baseWebpackConfig, {
       // necessary to consistently work with multiple chunks via CommonsChunkPlugin
       chunksSortMode: 'dependency'
     }),
+    // keep module.id stable when vender modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
     // split vendor js into its own file
     new webpack.optimize.CommonsChunkPlugin({
       name: 'vendor',
-      minChunks: function (module, count) {
+      minChunks (module) {
         // any required modules inside node_modules are extracted to vendor
         return (
           module.resource &&
@@ -71,13 +96,31 @@ var webpackConfig = merge(baseWebpackConfig, {
     // prevent vendor hash from being updated whenever app bundle is updated
     new webpack.optimize.CommonsChunkPlugin({
       name: 'manifest',
-      chunks: ['vendor']
-    })
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
   ]
 })
 
 if (config.build.productionGzip) {
-  var CompressionWebpackPlugin = require('compression-webpack-plugin')
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
 
   webpackConfig.plugins.push(
     new CompressionWebpackPlugin({
@@ -95,7 +138,7 @@ if (config.build.productionGzip) {
 }
 
 if (config.build.bundleAnalyzerReport) {
-  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
   webpackConfig.plugins.push(new BundleAnalyzerPlugin())
 }
 

+ 3 - 2
config/dev.env.js

@@ -1,5 +1,6 @@
-var merge = require('webpack-merge')
-var prodEnv = require('./prod.env')
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
 
 module.exports = merge(prodEnv, {
   NODE_ENV: '"development"'

+ 80 - 45
config/index.js

@@ -1,50 +1,85 @@
+'use strict'
+// Template version: 1.2.7
 // see http://vuejs-templates.github.io/webpack for documentation.
-var path = require('path')
+
+const path = require('path')
 
 module.exports = {
-    build: {
-        env: require('./prod.env'),
-        index: path.resolve(__dirname, '../dist/index.html'),
-        assetsRoot: path.resolve(__dirname, '../dist'),
-        assetsSubDirectory: 'static',
-        assetsPublicPath: './',
-        productionSourceMap: false,
-        // Gzip off by default as many popular static hosts such as
-        // Surge or Netlify already gzip all static assets for you.
-        // Before setting to `true`, make sure to:
-        // npm install --save-dev compression-webpack-plugin
-        productionGzip: false,
-        productionGzipExtensions: ['js', 'css'],
-        // Run the build command with an extra argument to
-        // View the bundle analyzer report after build finishes:
-        // `npm run build --report`
-        // Set to `true` or `false` to always turn it on or off
-        bundleAnalyzerReport: process.env.npm_config_report
+  dev: {
+
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {
+      '/api':{
+          target:'http://jsonplaceholder.typicode.com',
+          changeOrigin:true,
+          pathRewrite:{
+              '/api':''
+          }
+      },
+      '/ms':{
+          target: 'https://www.easy-mock.com/mock/592501a391470c0ac1fab128',
+          changeOrigin: true
+      }
     },
-    dev: {
-        env: require('./dev.env'),
-        port: 8080,
-        autoOpenBrowser: true,
-        assetsSubDirectory: 'static',
-        assetsPublicPath: '/',
-        proxyTable: {
-            '/api':{
-                target:'http://jsonplaceholder.typicode.com',
-                changeOrigin:true,
-                pathRewrite:{
-                    '/api':''
-                }
-            },
-            '/ms':{
-                target: 'https://www.easy-mock.com/mock/592501a391470c0ac1fab128',
-                changeOrigin: true
-            }
-        },
-        // CSS Sourcemaps off by default because relative paths are "buggy"
-        // with this option, according to the CSS-Loader README
-        // (https://github.com/webpack/css-loader#sourcemaps)
-        // In our experience, they generally work as expected,
-        // just be aware of this issue when enabling this option.
-        cssSourceMap: false
-    }
+    // Various Dev Server settings
+    host: 'localhost', // can be overwritten by process.env.HOST
+    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: false,
+    errorOverlay: true,
+    notifyOnErrors: true,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'eval-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    // CSS Sourcemaps off by default because relative paths are "buggy"
+    // with this option, according to the CSS-Loader README
+    // (https://github.com/webpack/css-loader#sourcemaps)
+    // In our experience, they generally work as expected,
+    // just be aware of this issue when enabling this option.
+    cssSourceMap: false,
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: './',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: false,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
 }

+ 1 - 0
config/prod.env.js

@@ -1,3 +1,4 @@
+'use strict'
 module.exports = {
   NODE_ENV: '"production"'
 }

+ 39 - 34
package.json

@@ -1,57 +1,62 @@
 {
-  "name": "manage-system",
-  "version": "2.1.0",
+  "name": "vue-manage-system",
+  "version": "3.0.0",
   "description": "基于Vue.js 2.x系列 + element-ui 内容管理系统解决方案",
   "author": "lin-xin <2981207131@qq.com>",
   "private": true,
   "scripts": {
-    "dev": "node build/dev-server.js",
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
     "build": "node build/build.js",
     "build:dll": "webpack --config build/webpack.dll.conf.js"
   },
   "dependencies": {
     "axios": "^0.15.3",
     "babel-polyfill": "^6.23.0",
-    "element-ui": "1.3.1",
-    "vue": "^2.3.2",
-    "vue-core-image-upload": "2.1.11",
-    "vue-datasource": "1.0.9",
-    "vue-quill-editor": "2.1.6",
-    "vue-router": "^2.3.1",
-    "vue-schart": "^0.1.2",
-    "vue-simplemde": "0.3.8"
+    "element-ui": "2.3.3",
+    "mavon-editor": "^2.5.2",
+    "vue": "^2.5.16",
+    "vue-cropperjs": "^2.2.0",
+    "vue-datasource": "1.0.12",
+    "vue-quill-editor": "3.0.6",
+    "vue-router": "^3.0.1",
+    "vue-schart": "^0.1.4",
+    "vuedraggable": "^2.16.0"
   },
   "devDependencies": {
-    "autoprefixer": "^6.7.2",
+    "autoprefixer": "^7.1.2",
     "babel-core": "^6.22.1",
-    "babel-loader": "^6.2.10",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-syntax-jsx": "^6.18.0",
     "babel-plugin-transform-runtime": "^6.22.0",
-    "babel-preset-es2015": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.5.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-es2015": "^6.24.1",
     "babel-preset-stage-2": "^6.22.0",
-    "babel-register": "^6.22.0",
-    "chalk": "^1.1.3",
-    "connect-history-api-fallback": "^1.3.0",
+    "chalk": "^2.0.1",
+    "copy-webpack-plugin": "^4.0.1",
     "css-loader": "^0.28.0",
-    "eventsource-polyfill": "^0.9.6",
-    "express": "^4.14.1",
-    "extract-text-webpack-plugin": "^2.0.0",
-    "file-loader": "^0.11.1",
-    "friendly-errors-webpack-plugin": "^1.1.3",
-    "function-bind": "^1.1.0",
-    "html-webpack-plugin": "^2.28.0",
-    "http-proxy-middleware": "^0.17.3",
-    "opn": "^4.0.2",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^1.1.4",
+    "friendly-errors-webpack-plugin": "^1.6.1",
+    "html-webpack-plugin": "^2.30.1",
+    "node-notifier": "^5.1.2",
+    "optimize-css-assets-webpack-plugin": "^3.2.0",
     "ora": "^1.2.0",
+    "portfinder": "^1.0.13",
+    "postcss-import": "^11.0.0",
+    "postcss-loader": "^2.0.8",
+    "rimraf": "^2.6.0",
     "semver": "^5.3.0",
     "shelljs": "^0.7.6",
+    "uglifyjs-webpack-plugin": "^1.1.1",
     "url-loader": "^0.5.8",
-    "vue-loader": "^11.3.4",
-    "vue-style-loader": "^2.0.5",
-    "vue-template-compiler": "^2.2.6",
-    "webpack": "^2.3.3",
-    "webpack-bundle-analyzer": "^2.2.1",
-    "webpack-dev-middleware": "^1.10.0",
-    "webpack-hot-middleware": "^2.18.0",
+    "vue-loader": "^13.3.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^3.6.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-dev-server": "^2.9.1",
     "webpack-merge": "^4.1.0"
   },
   "engines": {
@@ -60,7 +65,7 @@
   },
   "browserslist": [
     "> 1%",
-    "last 2 versions",
+    "last 5 versions",
     "not ie <= 8"
   ]
 }

BIN
screenshots/wms1.png


BIN
screenshots/wms2.png


+ 19 - 2
src/components/common/Header.vue

@@ -1,5 +1,8 @@
 <template>
     <div class="header">
+        <div class="collapse-btn" @click="collapseChage">
+            <i class="el-icon-menu"></i>
+        </div>
         <div class="logo">后台管理系统</div>
         <div class="user-info">
             <el-dropdown trigger="click" @command="handleCommand">
@@ -8,16 +11,18 @@
                     {{username}}
                 </span>
                 <el-dropdown-menu slot="dropdown">
-                    <el-dropdown-item command="loginout">退出</el-dropdown-item>
+                    <el-dropdown-item command="loginout">退出登录</el-dropdown-item>
                 </el-dropdown-menu>
             </el-dropdown>
         </div>
     </div>
 </template>
 <script>
+    import bus from '../common/bus';
     export default {
         data() {
             return {
+                collapse: false,
                 name: 'linxin'
             }
         },
@@ -33,6 +38,10 @@
                     localStorage.removeItem('ms_username')
                     this.$router.push('/login');
                 }
+            },
+            collapseChage(){
+                this.collapse = !this.collapse;
+                bus.$emit('collapse', this.collapse);
             }
         }
     }
@@ -47,10 +56,18 @@
         line-height: 70px;
         color: #fff;
     }
+    .collapse-btn{
+        float: left;
+        padding: 0 21px;
+        cursor: pointer;
+    }
+    .collapse-btn:hover{
+        background: rgb(40,52,70);
+    }
     .header .logo{
         float: left;
         width:250px;
-        text-align: center;
+        /* text-align: center; */
     }
     .user-info {
         float: right;

+ 30 - 19
src/components/common/Home.vue

@@ -1,19 +1,30 @@
-<template>
-    <div class="wrapper">
-        <v-head></v-head>
-        <v-sidebar></v-sidebar>
-        <div class="content">
-            <transition name="move" mode="out-in"><router-view></router-view></transition>
-        </div>
-    </div>
-</template>
-
-<script>
-    import vHead from './Header.vue';
-    import vSidebar from './Sidebar.vue';
-    export default {
-        components:{
-            vHead, vSidebar
-        }
-    }
-</script>
+<template>
+    <div class="wrapper">
+        <v-head></v-head>
+        <v-sidebar></v-sidebar>
+        <div class="content" :class="{'content-collapse':collapse}">
+            <transition name="move" mode="out-in"><router-view></router-view></transition>
+        </div>
+    </div>
+</template>
+
+<script>
+    import vHead from './Header.vue';
+    import vSidebar from './Sidebar.vue';
+    import bus from '../common/bus';
+    export default {
+        data(){
+            return {
+                collapse: false
+            }
+        },
+        components:{
+            vHead, vSidebar
+        },
+        created(){
+            bus.$on('collapse', msg => {
+                this.collapse = msg;
+            })
+        }
+    }
+</script>

+ 41 - 23
src/components/common/Sidebar.vue

@@ -1,17 +1,21 @@
 <template>
     <div class="sidebar">
-        <el-menu :default-active="onRoutes" class="el-menu-vertical-demo" theme="dark" unique-opened router>
+        <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">
-                        <template slot="title"><i :class="item.icon"></i>{{ item.title }}</template>
-                        <el-menu-item v-for="(subItem,i) in item.subs" :key="i" :index="subItem.index">{{ subItem.title }}
+                    <el-submenu :index="item.index" :key="item.index">
+                        <template slot="title">
+                            <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
+                        </template>
+                        <el-menu-item v-for="(subItem,i) in item.subs" :key="i" :index="subItem.index">
+                            {{ subItem.title }}
                         </el-menu-item>
                     </el-submenu>
                 </template>
                 <template v-else>
-                    <el-menu-item :index="item.index">
-                        <i :class="item.icon"></i>{{ item.title }}
+                    <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>
@@ -20,46 +24,48 @@
 </template>
 
 <script>
+    import bus from '../common/bus';
     export default {
         data() {
             return {
+                collapse: false,
                 items: [
                     {
                         icon: 'el-icon-setting',
                         index: 'readme',
-                        title: '自述'
+                        title: '自述文件'
                     },
                     {
-                        icon: 'el-icon-menu',
+                        icon: 'el-icon-tickets',
                         index: '2',
-                        title: '表格',
+                        title: '常用表格',
                         subs: [
                             {
-                                index: 'basetable',
+                                index: 'table',
                                 title: '基础表格'
                             },
                             {
-                                index: 'vuetable',
-                                title: 'Vue表格组件'
+                                index: 'datasource',
+                                title: 'datasource表格'
                             }
                         ]
                     },
                     {
                         icon: 'el-icon-date',
                         index: '3',
-                        title: '表单',
+                        title: '表单相关',
                         subs: [
                             {
-                                index: 'baseform',
+                                index: 'form',
                                 title: '基本表单'
                             },
                             {
-                                index: 'vueeditor',
-                                title: '编辑器'
+                                index: 'editor',
+                                title: '富文本编辑器'
                             },
                             {
                                 index: 'markdown',
-                                title: 'markdown'
+                                title: 'markdown编辑器'
                             },
                             {
                                 index: 'upload',
@@ -69,13 +75,18 @@
                     },
                     {
                         icon: 'el-icon-star-on',
-                        index: 'basecharts',
-                        title: '图表'
+                        index: 'charts',
+                        title: 'schart图表'
                     },
                     {
-                        icon: 'el-icon-upload2',
+                        icon: 'el-icon-rank',
                         index: 'drag',
-                        title: '拖拽'
+                        title: '拖拽列表'
+                    },
+                    {
+                        icon: 'el-icon-warning',
+                        index: 'permission',
+                        title: '权限测试'
                     }
                 ]
             }
@@ -84,6 +95,12 @@
             onRoutes(){
                 return this.$route.path.replace('/','');
             }
+        },
+        created(){
+            // 通过 Event Bus 进行组件间通信,来折叠侧边栏
+            bus.$on('collapse', msg => {
+                this.collapse = msg;
+            })
         }
     }
 </script>
@@ -92,11 +109,12 @@
     .sidebar{
         display: block;
         position: absolute;
-        width: 250px;
         left: 0;
         top: 70px;
         bottom:0;
-        background: #2E363F;
+    }
+    .sidebar-el-menu:not(.el-menu--collapse){
+        width: 250px;
     }
     .sidebar > ul {
         height:100%;

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

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

+ 21 - 19
src/components/page/BaseCharts.vue

@@ -6,25 +6,27 @@
                 <el-breadcrumb-item>基础图表</el-breadcrumb-item>
             </el-breadcrumb>
         </div>
-        <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">
-            <div class="content-title">柱状图</div>
-            <schart canvasId="bar" width="500" height="400" :data="data1" type="bar" :options="options1"></schart>
-        </div>
-        <div class="schart">
-        <div class="content-title">折线图</div>
-        <schart canvasId="line" width="500" height="400" :data="data1" type="line" :options="options1"></schart>
-        </div>
-        <div class="schart">
-        <div class="content-title">饼状图</div>
-        <schart canvasId="pie" width="500" height="400" :data="data2" type="pie" :options="options2"></schart>
-        </div>
-        <div class="schart">
-        <div class="content-title">环形图</div>
-        <schart canvasId="ring" width="500" height="400" :data="data2" type="ring" :options="options2"></schart>
+        <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">
+                <div class="content-title">柱状图</div>
+                <schart canvasId="bar" width="500" height="400" :data="data1" type="bar" :options="options1"></schart>
+            </div>
+            <div class="schart">
+            <div class="content-title">折线图</div>
+            <schart canvasId="line" width="500" height="400" :data="data1" type="line" :options="options1"></schart>
+            </div>
+            <div class="schart">
+            <div class="content-title">饼状图</div>
+            <schart canvasId="pie" width="500" height="400" :data="data2" type="pie" :options="options2"></schart>
+            </div>
+            <div class="schart">
+            <div class="content-title">环形图</div>
+            <schart canvasId="ring" width="500" height="400" :data="data2" type="ring" :options="options2"></schart>
+            </div>
         </div>
     </div>
 </template>

+ 139 - 81
src/components/page/BaseForm.vue

@@ -1,82 +1,140 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-date"></i> 表单</el-breadcrumb-item>
-                <el-breadcrumb-item>基本表单</el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-        <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 type="fixed-time" placeholder="选择时间" v-model="form.date2" style="width: 100%;"></el-time-picker>
-                    </el-col>
-                </el-form-item>
-                <el-form-item label="选择开关">
-                    <el-switch on-text="" off-text="" 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" 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>
-</template>
-
-<script>
-    export default {
-        data: function(){
-            return {
-                form: {
-                    name: '',
-                    region: '',
-                    date1: '',
-                    date2: '',
-                    delivery: true,
-                    type: ['步步高'],
-                    resource: '小天才',
-                    desc: ''
-                }
-            }
-        },
-        methods: {
-            onSubmit() {
-                this.$message.success('提交成功!');
-            }
-        }
-    }
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-date"></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 {
+        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>

+ 52 - 50
src/components/page/BaseTable.vue

@@ -2,42 +2,44 @@
     <div class="table">
         <div class="crumbs">
             <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-menu"></i> 表格</el-breadcrumb-item>
+                <el-breadcrumb-item><i class="el-icon-tickets"></i> 表格</el-breadcrumb-item>
                 <el-breadcrumb-item>基础表格</el-breadcrumb-item>
             </el-breadcrumb>
         </div>
-        <div class="handle-box">
-            <el-button type="primary" 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="search" @click="search">搜索</el-button>
-        </div>
-        <el-table :data="data" border style="width: 100%" ref="multipleTable" @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55"></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">
-                <template scope="scope">
-                    <el-button size="small"
-                            @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
-                    <el-button size="small" type="danger"
-                            @click="handleDelete(scope.$index, scope.row)">删除</el-button>
-                </template>
-            </el-table-column>
-        </el-table>
-        <div class="pagination">
-            <el-pagination
-                    @current-change ="handleCurrentChange"
-                    layout="prev, pager, next"
-                    :total="1000">
-            </el-pagination>
+        <div class="container">
+            <div class="handle-box">
+                <el-button type="primary" 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="search" @click="search">搜索</el-button>
+            </div>
+            <el-table :data="data" border style="width: 100%" ref="multipleTable" @selection-change="handleSelectionChange">
+                <el-table-column type="selection" width="55"></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">
+                    <template slot-scope="scope">
+                        <el-button size="small"
+                                @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
+                        <el-button size="small" type="danger"
+                                @click="handleDelete(scope.$index, scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div class="pagination">
+                <el-pagination
+                        @current-change ="handleCurrentChange"
+                        layout="prev, pager, next"
+                        :total="1000">
+                </el-pagination>
+            </div>
         </div>
     </div>
 </template>
@@ -61,19 +63,18 @@
         },
         computed: {
             data(){
-                const self = this;
-                return self.tableData.filter(function(d){
+                return this.tableData.filter((d) => {
                     let is_del = false;
-                    for (let i = 0; i < self.del_list.length; i++) {
-                        if(d.name === self.del_list[i].name){
+                    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(self.select_cate) > -1 && 
-                            (d.name.indexOf(self.select_word) > -1 ||
-                            d.address.indexOf(self.select_word) > -1)
+                        if(d.address.indexOf(this.select_cate) > -1 && 
+                            (d.name.indexOf(this.select_word) > -1 ||
+                            d.address.indexOf(this.select_word) > -1)
                         ){
                             return d;
                         }
@@ -82,17 +83,19 @@
             }
         },
         methods: {
+            // 分页导航
             handleCurrentChange(val){
                 this.cur_page = val;
                 this.getData();
             },
+            // 获取 easy-mock 的模拟数据
             getData(){
-                let self = this;
+                // 开发环境使用 easy-mock 数据,正式环境使用 json 文件
                 if(process.env.NODE_ENV === 'development'){
-                    self.url = '/ms/table/list';
+                    this.url = '/ms/table/list';
                 };
-                self.$axios.post(self.url, {page:self.cur_page}).then((res) => {
-                    self.tableData = res.data.list;
+                this.$axios.post(this.url, {page:this.cur_page}).then((res) => {
+                    this.tableData = res.data.list;
                 })
             },
             search(){
@@ -111,15 +114,14 @@
                 this.$message.error('删除第'+(index+1)+'行');
             },
             delAll(){
-                const self = this,
-                    length = self.multipleSelection.length;
+                const length = this.multipleSelection.length;
                 let str = '';
-                self.del_list = self.del_list.concat(self.multipleSelection);
+                this.del_list = this.del_list.concat(this.multipleSelection);
                 for (let i = 0; i < length; i++) {
-                    str += self.multipleSelection[i].name + ' ';
+                    str += this.multipleSelection[i].name + ' ';
                 }
-                self.$message.error('删除了'+str);
-                self.multipleSelection = [];
+                this.$message.error('删除了'+str);
+                this.multipleSelection = [];
             },
             handleSelectionChange(val) {
                 this.multipleSelection = val;

+ 127 - 69
src/components/page/DragList.vue

@@ -2,104 +2,162 @@
     <section class="main">
         <div class="crumbs">
             <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-upload2"></i> 拖拽排序</el-breadcrumb-item>
+                <el-breadcrumb-item><i class="el-icon-rank"></i> 拖拽排序</el-breadcrumb-item>
             </el-breadcrumb>
         </div>
-        <div class="drag-box-left">
-            <div class="drag-title">拖动排序</div>
-            <div class="drag-list" draggable="true" 
-                v-for="list in data1" 
-                :data-id="list.id" 
-                @dragstart="dragstartEvent"
-                @dragend="dragendEvent"
-                @dragenter="dragenterEvent"
-                @dragleave="dragleaveEvent"
-                @dragover="dragoverEvent"
-            >{{list.title}}</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,index) in todo" class="drag-list" :key="index">
+                                {{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,index) in doing" class="drag-list" :key="index">
+                                {{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,index) in done" class="drag-list" :key="index">
+                                {{item.content}}
+                            </div>
+                        </transition-group>
+                    </draggable>
+                </div>
+            </div>
         </div>
     </section>
 </template>
 
 <script>
+    import draggable from 'vuedraggable'
     export default {
         data() {
             return {
-                dragElement: null,
-                lock: true,
-                data1: [
-                    {id: 1, title: '这里是列表1的标题'},
-                    {id: 2, title: '这里是列表2的标题'},
-                    {id: 3, title: '这里是列表3的标题'},
-                    {id: 4, title: '这里是列表4的标题'},
-                    {id: 5, title: '这里是列表5的标题'},
-                    {id: 6, title: '这里是列表6的标题'},
-                    {id: 7, title: '这里是列表7的标题'}
+                dragOptions:{
+                    animation: 120,
+                    scroll: true,
+                    group: 'sortlist',
+                    ghostClass: 'ghost-style'
+                },
+                todo: [
+                    {
+                        content: '开发图表组件'
+                    },
+                    {
+                        content: '开发拖拽组件'
+                    },
+                    {
+                        content: '开发权限测试组件'
+                    }
+                ],
+                doing: [
+                    {
+                        content: '开发登录注册页面'
+                    },
+                    {
+                        content: '开发头部组件'
+                    },
+                    {
+                        content: '开发表格相关组件'
+                    },
+                    {
+                        content: '开发表单相关组件'
+                    }
                 ],
-                data2: [
-                    {id: 1, title: '这里是列表11的标题'},
-                    {id: 2, title: '这里是列表12的标题'},
-                    {id: 3, title: '这里是列表13的标题'},
-                    {id: 4, title: '这里是列表14的标题'}
+                done:[
+                    {
+                        content: '初始化项目,生成工程目录,完成相关配置'
+                    },
+                    {
+                        content: '开发项目整体框架'
+                    }
                 ]
             }
         },
+        components:{
+            draggable
+        },
         methods: {
-            dragstartEvent(ev) {
-                const self = this;
-                self.dragElement = ev.target;
-                ev.target.style.backgroundColor = '#f8f8f8';
-            },
-            dragendEvent(ev) {
-                ev.target.style.backgroundColor = '#fff';
-                ev.preventDefault();
-            },
-            dragenterEvent(ev) {
-                const self = this;
-                if(self.dragElement != ev.target){
-                    ev.target.parentNode.insertBefore(self.dragElement, ev.target);
-                }
-            },
-            dragleaveEvent(ev) {
-                const self = this;
-                if(self.dragElement != ev.target){
-                    if(self.lock && (ev.target == ev.target.parentNode.lastElementChild || ev.target == ev.target.parentNode.lastChild)){
-                        ev.target.parentNode.appendChild(self.dragElement);
-                        self.lock = false;
-                    }else{
-                        self.lock = true;
-                    }
-                }
-            },
-            dragoverEvent(ev) {
-                ev.preventDefault();
+            removeHandle(event){
+                console.log(event);
+                this.$message.success(`从 ${event.from.id} 移动到 ${event.to.id} `);
             }
         }
     }
+
 </script>
 
 <style scoped>
-    .drag-box-left{
-        float: left;
-        width: 45%;
+    .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;
     }
-    .drag-box-right{
-        float: right;
-        width: 45%;
+    .item-ul{
+        padding: 0 8px 8px;
+        height: 500px;
+        overflow-y: scroll;
     }
-    .drag-list{
-        border: 1px solid #ddd;
-        padding:10px;
-        margin-bottom: 20px;
-        transition: border .3s;
+    .item-ul::-webkit-scrollbar{
+        width: 0;
     }
-    .drag-list:hover{
+    .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{
+    .drag-title {
         font-weight: 400;
         line-height: 25px;
         margin: 10px 0;
         font-size: 22px;
         color: #1f2f3d;
     }
-</style>
+    .ghost-style{
+        display: block;
+        color: transparent;
+        border-style: dashed
+    }
+</style>

+ 5 - 6
src/components/page/Login.vue

@@ -23,8 +23,8 @@
         data: function(){
             return {
                 ruleForm: {
-                    username: '',
-                    password: ''
+                    username: 'admin',
+                    password: '123123'
                 },
                 rules: {
                     username: [
@@ -38,11 +38,10 @@
         },
         methods: {
             submitForm(formName) {
-                const self = this;
-                self.$refs[formName].validate((valid) => {
+                this.$refs[formName].validate((valid) => {
                     if (valid) {
-                        localStorage.setItem('ms_username',self.ruleForm.username);
-                        self.$router.push('/readme');
+                        localStorage.setItem('ms_username',this.ruleForm.username);
+                        this.$router.push('/readme');
                     } else {
                         console.log('error submit!!');
                         return false;

+ 68 - 42
src/components/page/Markdown.vue

@@ -1,42 +1,68 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-date"></i> 表单</el-breadcrumb-item>
-                <el-breadcrumb-item>markdown</el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-        <div class="plugins-tips">
-            Vue-SimpleMDE:Vue.js的Markdown Editor组件。
-            访问地址:<a href="https://github.com/F-loat/vue-simplemde" target="_blank">Vue-SimpleMDE</a>
-        </div>
-        <markdown-editor v-model="content" :configs="configs" ref="markdownEditor"></markdown-editor>
-        <div class="plugins-tips">
-            <p>既然用了markdown语法了,那么就有一个很实际的问题了。要怎么在前台展示数据呢?</p>
-            <br>
-            <p>这个时候就需要解析markdown语法了。可以使用 <a href="https://github.com/miaolz123/vue-markdown" target="_blank">vue-markdown</a>:一个基于vue.js的markdown语法解析插件。(这里不作展开,有需要自行了解)</p>
-        </div>
-    </div>
-</template>
-
-<script>
-    import { markdownEditor } from 'vue-simplemde';
-    export default {
-        data: function(){
-            return {
-                content:'',
-                configs: {
-                    status: true,
-                    initialValue: 'Hello BBK',
-                    renderingConfig: {
-                        codeSyntaxHighlighting: true,
-                        highlightingTheme: 'atom-one-light'
-                    }
-                }
-            }
-        },
-        components: {
-            markdownEditor
-        }
-    }
-</script>
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-date"></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'
+    // 重写mavon的flex样式来兼容IE
+    import 'static/css/mavon-flex.css'
+    export default {
+        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>

+ 0 - 216
src/components/page/OldCharts.vue

@@ -1,216 +0,0 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-date"></i> 图表</el-breadcrumb-item>
-                <el-breadcrumb-item>基础图表</el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-        <div class="plugins-tips">
-            vue-echarts-v3:基于vue2和eCharts.js3的图表组件。
-            访问地址:<a href="https://github.com/xlsdg/vue-echarts-v3" target="_blank">vue-echarts-v3</a>
-        </div>
-        <div class="content-title">基础图表</div>
-        <div class="echarts">
-            <IEcharts :option="line" ></IEcharts>
-        </div>
-        <div class="echarts">
-            <IEcharts :option="bar" ></IEcharts>
-        </div>
-        <div class="echarts">
-            <IEcharts :option="pie" ></IEcharts>
-        </div>
-        <div class="echarts">
-            <IEcharts :option="pie_radius" ></IEcharts>
-        </div>
-        <div class="content-title">混合图表</div>
-        <div class="mix-echarts">
-            <IEcharts :option="mix" ></IEcharts>
-        </div>
-        <v-schart canvasId="canvas" width="500" height="400" :data="data" type="bar"></v-schart>
-    </div>
-</template>
-
-<script>
-    // import IEcharts from 'vue-echarts-v3';
-    import vSchart from 'vue-schart';
-    import IEcharts from 'vue-echarts-v3/src/lite.vue';
-  
-    import 'echarts/lib/chart/bar';
-    import 'echarts/lib/chart/line';
-    import 'echarts/lib/chart/pie';
-    import 'echarts/lib/component/legend';
-    import 'echarts/lib/component/tooltip';
-    import 'echarts/lib/component/title';
-    export default {
-        components: {
-            IEcharts,vSchart
-        },
-        data: () => ({
-            data:[
-            {name:'2012',value:1141},
-            {name:'2013',value:1499},
-            {name:'2014',value:2260},
-            {name:'2015',value:1170},
-            {name:'2016',value:970},
-            {name:'2017',value:1450}
-        ],
-            line: {
-                color:["#20a0ff","#13CE66","#F7BA2A","#FF4949"],
-                title: {
-                    text: '曲线图'
-                },
-                xAxis: {
-                    data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
-                },
-                yAxis:{},
-                series: [
-                    {
-                        name: "销量",
-                        type: "line",
-                        data: [5, 20, 36, 10, 10, 20]
-                    }
-                ]
-            },
-            bar: {
-                color:["#20a0ff","#13CE66","#F7BA2A","#FF4949"],
-                title: {
-                    text: '柱状图'
-                },
-                xAxis: {
-                    data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
-                },
-                yAxis:{},
-                series: [
-                    {
-                        name: "销量",
-                        type: "bar",
-                        data: [5, 20, 36, 10, 10, 20]
-                    }
-                ]
-            },
-            pie: {
-                color:["#20a0ff","#13CE66","#F7BA2A","#FF4949","#61a0a8"],
-                title : {
-                    text: '饼状图',
-                    x:'center'
-                },
-                tooltip : {
-                    trigger: 'item',
-                    formatter: "{a} <br/>{b} : {c} ({d}%)"
-                },
-                legend: {
-                    orient: 'vertical',
-                    left: 'left',
-                    data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋"]
-                },
-                series : [
-                    {
-                        name: '销量',
-                        type: 'pie',
-                        radius : '55%',
-                        center: ['50%', '50%'],
-                        data:[
-                            {value:335, name:'衬衫'},
-                            {value:310, name:'羊毛衫'},
-                            {value:234, name:'雪纺衫'},
-                            {value:135, name:'裤子'},
-                            {value:548, name:'高跟鞋'}
-                        ],
-                        itemStyle: {
-                            emphasis: {
-                                shadowBlur: 10,
-                                shadowOffsetX: 0,
-                                shadowColor: 'rgba(0, 0, 0, 0.5)'
-                            }
-                        }
-                    }
-                ]
-            },
-            pie_radius:{
-                color:["#20a0ff","#13CE66","#F7BA2A","#FF4949","#61a0a8"],
-                title : {
-                    text: '环形图',
-                    x:'center'
-                },
-                tooltip : {
-                    trigger: 'item',
-                    formatter: "{a} <br/>{b} : {c} ({d}%)"
-                },
-                legend: {
-                    orient: 'vertical',
-                    left: 'left',
-                    data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋"]
-                },
-                series : [
-                    {
-                        name: '销量',
-                        type: 'pie',
-                        radius : ['40%','60%'],
-                        data:[
-                            {value:335, name:'衬衫'},
-                            {value:310, name:'羊毛衫'},
-                            {value:234, name:'雪纺衫'},
-                            {value:135, name:'裤子'},
-                            {value:548, name:'高跟鞋'}
-                        ],
-                        itemStyle: {
-                            emphasis: {
-                                shadowBlur: 10,
-                                shadowOffsetX: 0,
-                                shadowColor: 'rgba(0, 0, 0, 0.5)'
-                            }
-                        }
-                    }
-                ]
-            },
-            mix:{
-                    color:["#20a0ff","#13CE66","#F7BA2A","#FF4949","#61a0a8"],
-                    legend: {
-                        data:['步步高','小天才','imoo']
-                    },
-                    xAxis: {
-                        data: ['周一','周二','周三','周四','周五','周末']
-                    },
-                    yAxis:{},
-                    series: [
-                        {
-                            name: "步步高",
-                            type: "line",
-                            data: [15, 20, 26, 30, 40, 27]
-                        },
-                        {
-                            name: "小天才",
-                            type: "bar",
-                            data: [5, 30, 36, 10, 34, 20]
-                        },
-                        {
-                            name: "imoo",
-                            type: "bar",
-                            data: [35, 40, 30, 50, 60, 40]
-                        }
-                    ]
-                }
-        })
-    }
-</script>
-
-<style scoped>
-    .echarts {
-        float: left;
-        width: 500px;
-        height: 400px;
-    }
-    .content-title{
-        clear: both;
-        font-weight: 400;
-        line-height: 50px;
-        margin: 10px 0;
-        font-size: 22px;
-        color: #1f2f3d;
-    }
-    .mix-echarts{
-        width:900px;
-        height:600px;
-    }
-</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-warning"></i> 权限测试</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="container">
+            <h1>管理员权限页面</h1>
+            <p>只有用 admin 账号登录的才拥有管理员权限,才能进到这个页面,其他账号想进来都会跳到登录页面,重新用管理员账号登录才有权限。</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>

+ 87 - 95
src/components/page/Readme.vue

@@ -1,96 +1,88 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-setting"></i> 自述</el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-        <div class="ms-doc">
-            <h3>README.md</h3>
-            <article>
-                <h1>manage-system</h1>
-                <p>基于Vue.js 2.x系列 + Element UI 的后台管理系统解决方案</p>
-                <h2>前言</h2>
-                <p>之前在公司用了Vue + Element组件库做了个后台管理系统,基本很多组件可以直接引用组件库的,但是也有一些需求无法满足。像图片裁剪上传、富文本编辑器、图表等这些在后台管理系统中很常见的功能,就需要引用其他的组件才能完成。从寻找组件,到使用组件的过程中,遇到了很多问题,也积累了宝贵的经验。所以我就把开发这个后台管理系统的经验,总结成这个后台管理系统解决方案。</p>
-                <p>该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于vue.js,使用vue-cli脚手架快速生成项目目录,引用Element UI组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。</p>
-                <h2>功能</h2>
-                <el-checkbox disabled checked>Element UI</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>登录/注销</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>表格</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>表单</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>图表</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>富文本编辑器</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>markdown编辑器</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>图片拖拽/裁剪上传</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>支持切换主题色</el-checkbox>
-                <br>
-                <el-checkbox disabled checked>列表拖拽排序</el-checkbox>
-                <br>
-            </article>
-        </div>
-
-    </div>
-</template>
-
-<script>
-    export default {
-        data: function(){
-            return {}
-        }
-    }
-</script>
-
-<style scoped>
-    .ms-doc{
-        width:100%;
-        max-width: 980px;
-        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
-    }
-    .ms-doc h3{
-        padding: 9px 10px 10px;
-        margin: 0;
-        font-size: 14px;
-        line-height: 17px;
-        background-color: #f5f5f5;
-        border: 1px solid #d8d8d8;
-        border-bottom: 0;
-        border-radius: 3px 3px 0 0;
-    }
-    .ms-doc article{
-        padding: 45px;
-        word-wrap: break-word;
-        background-color: #fff;
-        border: 1px solid #ddd;
-        border-bottom-right-radius: 3px;
-        border-bottom-left-radius: 3px;
-    }
-    .ms-doc article h1{
-        font-size:32px;
-        padding-bottom: 10px;
-        margin-bottom: 15px;
-        border-bottom: 1px solid #ddd;
-    }
-    .ms-doc article h2 {
-        margin: 24px 0 16px;
-        font-weight: 600;
-        line-height: 1.25;
-        padding-bottom: 7px;
-        font-size: 24px;
-        border-bottom: 1px solid #eee;
-    }
-    .ms-doc article p{
-        margin-bottom: 15px;
-        line-height: 1.5;
-    }
-    .ms-doc article .el-checkbox{
-        margin-bottom: 5px;
-    }
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-setting"></i> 自述</el-breadcrumb-item>
+            </el-breadcrumb>
+        </div>
+        <div class="ms-doc">
+            <h3>README.md</h3>
+            <article>
+                <h1>manage-system</h1>
+                <p>基于Vue.js 2.x系列 + Element UI 的后台管理系统解决方案</p>
+                <h2>前言</h2>
+                <p>之前在公司用了Vue + Element组件库做了个后台管理系统,基本很多组件可以直接引用组件库的,但是也有一些需求无法满足。像图片裁剪上传、富文本编辑器、图表等这些在后台管理系统中很常见的功能,就需要引用其他的组件才能完成。从寻找组件,到使用组件的过程中,遇到了很多问题,也积累了宝贵的经验。所以我就把开发这个后台管理系统的经验,总结成这个后台管理系统解决方案。</p>
+                <p>该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(Web Management System)开发。基于vue.js,使用vue-cli脚手架快速生成项目目录,引用Element UI组件库,方便开发快速简洁好看的组件。分离颜色样式,支持手动切换主题色,而且很方便使用自定义主题色。</p>
+                <h2>功能</h2>
+                <el-checkbox disabled checked>Element UI</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>登录/注销</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>表格</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>表单</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>图表</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>富文本编辑器</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>markdown编辑器</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>图片拖拽/裁剪上传</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>支持切换主题色</el-checkbox>
+                <br>
+                <el-checkbox disabled checked>列表拖拽排序</el-checkbox>
+                <br>
+            </article>
+        </div>
+
+    </div>
+</template>
+
+<style scoped>
+    .ms-doc{
+        width:100%;
+        max-width: 980px;
+        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
+    }
+    .ms-doc h3{
+        padding: 9px 10px 10px;
+        margin: 0;
+        font-size: 14px;
+        line-height: 17px;
+        background-color: #f5f5f5;
+        border: 1px solid #d8d8d8;
+        border-bottom: 0;
+        border-radius: 3px 3px 0 0;
+    }
+    .ms-doc article{
+        padding: 45px;
+        word-wrap: break-word;
+        background-color: #fff;
+        border: 1px solid #ddd;
+        border-bottom-right-radius: 3px;
+        border-bottom-left-radius: 3px;
+    }
+    .ms-doc article h1{
+        font-size:32px;
+        padding-bottom: 10px;
+        margin-bottom: 15px;
+        border-bottom: 1px solid #ddd;
+    }
+    .ms-doc article h2 {
+        margin: 24px 0 16px;
+        font-weight: 600;
+        line-height: 1.25;
+        padding-bottom: 7px;
+        font-size: 24px;
+        border-bottom: 1px solid #eee;
+    }
+    .ms-doc article p{
+        margin-bottom: 15px;
+        line-height: 1.5;
+    }
+    .ms-doc article .el-checkbox{
+        margin-bottom: 5px;
+    }
 </style>

+ 96 - 34
src/components/page/Upload.vue

@@ -6,49 +6,80 @@
                 <el-breadcrumb-item>图片上传</el-breadcrumb-item>
             </el-breadcrumb>
         </div>
-        <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 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="/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>
-        <el-upload
-            class="upload-demo"
-            drag
-            action="/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-Core-Image-Upload:一款轻量级的vue上传插件,支持裁剪。
-            访问地址:<a href="https://github.com/Vanthink-UED/vue-core-image-upload" target="_blank">Vue-Core-Image-Upload</a>
-        </div>
-        <img class="pre-img" :src="src" alt="">
-        <vue-core-image-upload :class="['pure-button','pure-button-primary','js-btn-crop']"
-                               :crop="true"
-                               text="上传图片"
-                               url="/api/posts/"
-                               extensions="png,gif,jpeg,jpg"
-                               @:imageuploaded="imageuploaded"
-                               @:errorhandle="handleError"></vue-core-image-upload>
     </div>
 </template>
 
 <script>
-    import VueCoreImageUpload  from 'vue-core-image-upload';
+    import VueCropper  from 'vue-cropperjs';
     export default {
         data: function(){
             return {
-                src: './static/img/img.jpg',
-                fileList: []
+                defaultSrc: './static/img/img.jpg',
+                fileList: [],
+                imgSrc: '',
+                cropImg: '',
+                dialogVisible: false,
             }
         },
         components: {
-                VueCoreImageUpload
+            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)
             },
@@ -58,6 +89,9 @@
                     message: '图片上传接口上传失败,可更改为自己的服务器接口'
                 });
             }
+        },
+        created(){
+            this.cropImg = this.defaultSrc;
         }
     }
 </script>
@@ -70,9 +104,37 @@
         font-size: 22px;
         color: #1f2f3d;
     }
-    .pre-img{
-        width:250px;
-        height: 250px;
-        margin-bottom: 20px;
+    .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>

+ 51 - 51
src/components/page/VueEditor.vue

@@ -1,52 +1,52 @@
-<template>
-    <div>
-        <div class="crumbs">
-            <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-date"></i> 表单</el-breadcrumb-item>
-                <el-breadcrumb-item>编辑器</el-breadcrumb-item>
-            </el-breadcrumb>
-        </div>
-        <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" :config="editorOption"></quill-editor>
-        <el-button class="editor-btn" type="primary" @click="submit">提交</el-button>
-    </div>
-</template>
-
-<script>
-    import { quillEditor } from 'vue-quill-editor';
-    export default {
-        data: function(){
-            return {
-                content: '<p>Hello BBK</p>',
-                editorOption: {
-                    // something config
-                }
-            }
-        },
-        components: {
-            quillEditor
-        },
-        methods: {
-            onEditorChange({ editor, html, text }) {
-                this.content = html;
-            },
-            submit(){
-                console.log(this.content);
-                this.$message.success('提交成功!');
-            }
-        },
-        computed: {
-            editor() {
-                return this.$refs.myTextEditor.quillEditor;
-            }
-        }
-    }
-</script>
-<style scoped>
-    .editor-btn{
-        margin-top: 20px;
-    }
+<template>
+    <div>
+        <div class="crumbs">
+            <el-breadcrumb separator="/">
+                <el-breadcrumb-item><i class="el-icon-date"></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 {
+        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>

+ 18 - 16
src/components/page/VueTable.vue

@@ -2,18 +2,21 @@
     <div class="table">
         <div class="crumbs">
             <el-breadcrumb separator="/">
-                <el-breadcrumb-item><i class="el-icon-menu"></i> 表格</el-breadcrumb-item>
-                <el-breadcrumb-item>Vue表格组件</el-breadcrumb-item>
+                <el-breadcrumb-item><i class="el-icon-tickets"></i> 表格</el-breadcrumb-item>
+                <el-breadcrumb-item>VueDatasource</el-breadcrumb-item>
             </el-breadcrumb>
         </div>
-        <div class="plugins-tips">
-            vue-datasource:一个用于动态创建表格的vue.js服务端组件。
-            访问地址:<a href="https://github.com/coderdiaz/vue-datasource" target="_blank">vue-datasource</a>
+        <div class="container">
+            <div class="plugins-tips">
+                vue-datasource:一个用于动态创建表格的vue.js服务端组件。
+                访问地址:<a href="https://github.com/coderdiaz/vue-datasource" target="_blank">vue-datasource</a>
+            </div>
+            <datasource language="en" :table-data="getData" :columns="columns" :pagination="information.pagination"
+                    :actions="actions"
+                    v-on:change="changePage"
+                    v-on:searching="onSearch"
+            ></datasource>
         </div>
-        <datasource language="en" :table-data="getData" :columns="columns" :pagination="information.pagination"
-                :actions="actions"
-                v-on:change="changePage"
-                v-on:searching="onSearch"></datasource>
     </div>
 </template>
 
@@ -21,8 +24,7 @@
     import axios from 'axios';
     import Datasource from 'vue-datasource';
     export default {
-        data: function(){
-            const self = this;
+        data(){
             return {
                 url: './static/datasource.json',
                 information: {
@@ -51,8 +53,8 @@
                     {
                         text: 'Click',
                         class: 'btn-primary',
-                        event(e, row) {
-                            self.$message('选中的行数: ' + row.row.id);
+                        event: (e, row)=>{
+                            row && this.$message('选中的行数: ' + row.row.id);
                         }
                     }
                 ],
@@ -73,15 +75,15 @@
         },
         computed:{
             getData(){
-                const self = this;
-                return self.information.data.filter(function (d) {
-                    if(d.name.indexOf(self.query) > -1){
+                return this.information.data.filter((d) =>{
+                    if(d.name.indexOf(this.query) > -1){
                         return d;
                     }
                 })
             }
         },
         beforeMount(){
+            // 开发环境使用 easy-mock 数据,正式环境使用 json 文件
             if(process.env.NODE_ENV === 'development'){
                 this.url = '/ms/table/source';
             };

+ 21 - 2
src/main.js

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

+ 71 - 59
src/router/index.js

@@ -1,59 +1,71 @@
-import Vue from 'vue';
-import Router from 'vue-router';
-
-Vue.use(Router);
-
-export default new Router({
-    routes: [
-        {
-            path: '/',
-            redirect: '/login'
-        },
-        {
-            path: '/readme',
-            component: resolve => require(['../components/common/Home.vue'], resolve),
-            children:[
-                {
-                    path: '/',
-                    component: resolve => require(['../components/page/Readme.vue'], resolve)
-                },
-                {
-                    path: '/basetable',
-                    component: resolve => require(['../components/page/BaseTable.vue'], resolve)
-                },
-                {
-                    path: '/vuetable',
-                    component: resolve => require(['../components/page/VueTable.vue'], resolve)     // vue-datasource组件
-                },
-                {
-                    path: '/baseform',
-                    component: resolve => require(['../components/page/BaseForm.vue'], resolve)
-                },
-                {
-                    path: '/vueeditor',
-                    component: resolve => require(['../components/page/VueEditor.vue'], resolve)    // Vue-Quill-Editor组件
-                },
-                {
-                    path: '/markdown',
-                    component: resolve => require(['../components/page/Markdown.vue'], resolve)     // Vue-Quill-Editor组件
-                },
-                {
-                    path: '/upload',
-                    component: resolve => require(['../components/page/Upload.vue'], resolve)       // Vue-Core-Image-Upload组件
-                },
-                {
-                    path: '/basecharts',
-                    component: resolve => require(['../components/page/BaseCharts.vue'], resolve)   // vue-schart组件
-                },
-                {
-                    path: '/drag',
-                    component: resolve => require(['../components/page/DragList.vue'], resolve)    // 拖拽列表组件
-                }
-            ]
-        },
-        {
-            path: '/login',
-            component: resolve => require(['../components/page/Login.vue'], resolve)
-        },
-    ]
-})
+import Vue from 'vue';
+import Router from 'vue-router';
+
+Vue.use(Router);
+
+export default new Router({
+    routes: [
+        {
+            path: '/',
+            redirect: '/login'
+        },
+        {
+            path: '/readme',
+            component: resolve => require(['../components/common/Home.vue'], resolve),
+            children:[
+                {
+                    path: '/',
+                    component: resolve => require(['../components/page/Readme.vue'], resolve)
+                },
+                {
+                    path: '/table',
+                    component: resolve => require(['../components/page/BaseTable.vue'], resolve)
+                },
+                {
+                    // vue-datasource组件
+                    path: '/datasource',
+                    component: resolve => require(['../components/page/VueTable.vue'], resolve)     
+                },
+                {
+                    path: '/form',
+                    component: resolve => require(['../components/page/BaseForm.vue'], resolve)
+                },
+                {
+                    // 富文本编辑器组件
+                    path: '/editor',
+                    component: resolve => require(['../components/page/VueEditor.vue'], resolve)    
+                },
+                {
+                    // markdown组件
+                    path: '/markdown',
+                    component: resolve => require(['../components/page/Markdown.vue'], resolve)     
+                },
+                {
+                    // 图片上传组件
+                    path: '/upload',
+                    component: resolve => require(['../components/page/Upload.vue'], resolve)       
+                },
+                {
+                    // vue-schart组件
+                    path: '/charts',
+                    component: resolve => require(['../components/page/BaseCharts.vue'], resolve)   
+                },
+                {
+                    // 拖拽列表组件
+                    path: '/drag',
+                    component: resolve => require(['../components/page/DragList.vue'], resolve)
+                },
+                {
+                    // 权限页面
+                    path: '/permission',
+                    component: resolve => require(['../components/page/Permission.vue'], resolve),
+                    meta: {permission: true}
+                }
+            ]
+        },
+        {
+            path: '/login',
+            component: resolve => require(['../components/page/Login.vue'], resolve)
+        },
+    ]
+})

+ 16 - 2
static/css/main.css

@@ -5,11 +5,11 @@ html,body,#app,.wrapper{
     overflow: hidden;
 }
 body{
-    font-family:"Helvetica Neue",Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
+    font-family:'PingFang SC', "Helvetica Neue",Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
 }
 a{text-decoration: none}
 .content{
-    background: none repeat scroll 0 0 #fff;
+    background: none repeat scroll 0 0 #f0f0f0;
     position: absolute;
     left: 250px;
     right: 0;
@@ -19,6 +19,17 @@ a{text-decoration: none}
     padding:40px;
     box-sizing: border-box;
     overflow-y: scroll;
+    -webkit-transition: left .3s ease-in-out;
+    transition: left .3s ease-in-out;
+}
+.content-collapse{
+    left: 65px;
+}
+.container{
+    padding: 30px;
+    background: #fff;
+    border: 1px solid #ddd;
+    border-radius: 5px;
 }
 .crumbs{
     margin-bottom: 20px;
@@ -58,6 +69,9 @@ a{text-decoration: none}
 .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;
+}
 /*Readme*/
 .ms-doc .el-checkbox__input.is-disabled+.el-checkbox__label{
     color: #333;

+ 70 - 0
static/css/mavon-flex.css

@@ -0,0 +1,70 @@
+.v-note-wrapper{
+    display: flex;
+    flex-direction: column;
+}
+.v-note-wrapper .v-note-op{
+    display: flex;
+    flex: none;
+}
+.v-note-wrapper .v-note-op .left,
+.v-note-wrapper .v-note-op .right{
+    flex: 1;
+}
+.v-note-wrapper {
+  display: flex;
+  flex-direction: column;
+}
+.v-note-wrapper .v-note-op {
+  display: flex;
+  flex: none;
+}
+.v-note-wrapper .v-note-op .left,
+.v-note-wrapper .v-note-op .right {
+  flex: 1;
+}
+
+.v-note-wrapper .v-note-panel {
+  display: flex;
+  flex: 1;
+  min-height: 500px;
+}
+.v-note-wrapper .v-note-panel .v-note-edit.divarea-wrapper {
+  flex: 0 0 50%;
+}
+
+.v-note-wrapper .v-note-panel .v-note-edit.divarea-wrapper.single-edit {
+  flex: 0 0 100%;
+}
+.v-note-wrapper .v-note-panel .v-note-edit.divarea-wrapper.single-show {
+  width: 0;
+  flex: 0 0 0;
+  display: none;
+}
+
+.v-note-wrapper .v-note-panel .v-note-show {
+  flex: 0 0 50%;
+}
+.v-note-wrapper .v-note-panel .v-note-show.single-show {
+  flex: 0 0 100%;
+}
+
+.v-note-wrapper .v-note-panel .v-note-navigation-wrapper {
+  display: flex;
+  flex-direction: column;
+}
+
+.v-note-wrapper .v-note-panel .v-note-navigation-wrapper .v-note-navigation-title {
+  flex: none;
+}
+
+.v-note-wrapper .v-note-panel .v-note-navigation-wrapper .v-note-navigation-content {
+  flex: 1;
+}
+.v-note-img-wrapper{
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+.v-note-img-wrapper img {
+    flex: 0 0 auto;
+}

BIN
static/css/theme-green/fonts/element-icons.ttf


BIN
static/css/theme-green/fonts/element-icons.woff


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
static/css/theme-green/index.css


Деякі файли не було показано, через те що забагато файлів було змінено