فهرست منبع

项目初始化

linwu 1 سال پیش
کامیت
c06da36698
100فایلهای تغییر یافته به همراه5890 افزوده شده و 0 حذف شده
  1. 14 0
      .editorconfig
  2. 5 0
      .env.development
  3. 5 0
      .env.production
  4. 8 0
      .env.staging
  5. 4 0
      .eslintignore
  6. 198 0
      .eslintrc.js
  7. 24 0
      .gitignore
  8. 5 0
      .travis.yml
  9. 21 0
      LICENSE
  10. 4 0
      README.md
  11. 14 0
      babel.config.js
  12. 35 0
      build/index.js
  13. 24 0
      jest.config.js
  14. 9 0
      jsconfig.json
  15. BIN
      node_modules.zip
  16. 125 0
      package.json
  17. 26 0
      plop-templates/component/index.hbs
  18. 55 0
      plop-templates/component/prompt.js
  19. 16 0
      plop-templates/store/index.hbs
  20. 62 0
      plop-templates/store/prompt.js
  21. 2 0
      plop-templates/utils.js
  22. 26 0
      plop-templates/view/index.hbs
  23. 55 0
      plop-templates/view/prompt.js
  24. 9 0
      plopfile.js
  25. 5 0
      postcss.config.js
  26. 15 0
      public/index.html
  27. 11 0
      src/App.vue
  28. BIN
      src/assets/401_images/401.gif
  29. BIN
      src/assets/404_images/404.png
  30. BIN
      src/assets/404_images/404_cloud.png
  31. BIN
      src/assets/custom-theme/fonts/element-icons.ttf
  32. BIN
      src/assets/custom-theme/fonts/element-icons.woff
  33. 0 0
      src/assets/custom-theme/index.css
  34. BIN
      src/assets/data.png
  35. BIN
      src/assets/diy/banner.png
  36. BIN
      src/assets/diy/coupon.png
  37. 99 0
      src/assets/diy/css/attr.scss
  38. 575 0
      src/assets/diy/css/diy.scss
  39. BIN
      src/assets/diy/duo.png
  40. BIN
      src/assets/diy/duo_style1.png
  41. BIN
      src/assets/diy/duo_style2.png
  42. BIN
      src/assets/diy/duo_style3.png
  43. BIN
      src/assets/diy/duo_style4.png
  44. 539 0
      src/assets/diy/font/demo.css
  45. 45 0
      src/assets/diy/font/iconfont.css
  46. BIN
      src/assets/diy/font/iconfont.eot
  47. 47 0
      src/assets/diy/font/iconfont.svg
  48. BIN
      src/assets/diy/font/iconfont.ttf
  49. BIN
      src/assets/diy/font/iconfont.woff
  50. BIN
      src/assets/diy/font/iconfont.woff2
  51. BIN
      src/assets/diy/goods.png
  52. BIN
      src/assets/diy/gou.png
  53. BIN
      src/assets/diy/imageSingle.png
  54. BIN
      src/assets/diy/img.jpg
  55. BIN
      src/assets/diy/img400x200.jpg
  56. BIN
      src/assets/diy/membergoods.png
  57. BIN
      src/assets/diy/miaosha.png
  58. BIN
      src/assets/diy/navBar.png
  59. BIN
      src/assets/diy/nodata.png
  60. BIN
      src/assets/diy/notice.png
  61. BIN
      src/assets/diy/notice_default.png
  62. BIN
      src/assets/diy/search.png
  63. BIN
      src/assets/diy/technical.png
  64. BIN
      src/assets/diy/tuan.png
  65. BIN
      src/assets/diy/tuwen.png
  66. BIN
      src/assets/diy/tuwen_style1.png
  67. BIN
      src/assets/diy/tuwen_style2.png
  68. BIN
      src/assets/diy/video.png
  69. BIN
      src/assets/diy/window.png
  70. BIN
      src/assets/diy/window_01.jpg
  71. BIN
      src/assets/diy/window_02.jpg
  72. BIN
      src/assets/diy/window_02_1.jpg
  73. BIN
      src/assets/diy/window_03.jpg
  74. BIN
      src/assets/diy/window_03_1.jpg
  75. BIN
      src/assets/diy/window_04.jpg
  76. BIN
      src/assets/diy/window_style1.png
  77. BIN
      src/assets/diy/window_style2.png
  78. BIN
      src/assets/diy/window_style3.png
  79. BIN
      src/assets/diy/xcx.png
  80. BIN
      src/assets/new_order.mp3
  81. BIN
      src/assets/no_admintouxiang.png
  82. BIN
      src/assets/no_touxiang.png
  83. BIN
      src/assets/qqEmoji.png
  84. 111 0
      src/components/BackToTop/index.vue
  85. 82 0
      src/components/Breadcrumb/index.vue
  86. 155 0
      src/components/Charts/Keyboard.vue
  87. 227 0
      src/components/Charts/LineMarker.vue
  88. 271 0
      src/components/Charts/MixChart.vue
  89. 56 0
      src/components/Charts/mixins/resize.js
  90. 166 0
      src/components/DndList/index.vue
  91. 65 0
      src/components/DragSelect/index.vue
  92. 297 0
      src/components/Dropzone/index.vue
  93. 78 0
      src/components/ErrorLog/index.vue
  94. 44 0
      src/components/Hamburger/index.vue
  95. 180 0
      src/components/HeaderSearch/index.vue
  96. 1779 0
      src/components/ImageCropper/index.vue
  97. 19 0
      src/components/ImageCropper/utils/data2blob.js
  98. 39 0
      src/components/ImageCropper/utils/effectRipple.js
  99. 232 0
      src/components/ImageCropper/utils/language.js
  100. 7 0
      src/components/ImageCropper/utils/mimes.js

+ 14 - 0
.editorconfig

@@ -0,0 +1,14 @@
+# https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 5 - 0
.env.development

@@ -0,0 +1,5 @@
+# just a flag
+ENV = 'development'
+
+# base api
+VUE_APP_BASE_API = 'http://bd.jbangjia.com/public/index.php?s='

+ 5 - 0
.env.production

@@ -0,0 +1,5 @@
+# just a flag
+ENV = 'production'
+
+# base api
+VUE_APP_BASE_API = 'public/index.php?s='

+ 8 - 0
.env.staging

@@ -0,0 +1,8 @@
+NODE_ENV = production
+
+# just a flag
+ENV = 'staging'
+
+# base api
+VUE_APP_BASE_API = '/stage-api'
+

+ 4 - 0
.eslintignore

@@ -0,0 +1,4 @@
+build/*.js
+src/assets
+public
+dist

+ 198 - 0
.eslintrc.js

@@ -0,0 +1,198 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  rules: {
+    "vue/max-attributes-per-line": [2, {
+      "singleline": 10,
+      "multiline": {
+        "max": 1,
+        "allowFirstLine": false
+      }
+    }],
+    "vue/singleline-html-element-content-newline": "off",
+    "vue/multiline-html-element-content-newline":"off",
+    "vue/name-property-casing": ["error", "PascalCase"],
+    "vue/no-v-html": "off",
+    'accessor-pairs': 2,
+    'arrow-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'block-spacing': [2, 'always'],
+    'brace-style': [2, '1tbs', {
+      'allowSingleLine': true
+    }],
+    'camelcase': [0, {
+      'properties': 'always'
+    }],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    'curly': [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 2,
+    'eqeqeq': ["off","error", "always", {"null": "ignore"}],
+    'generator-star-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'handle-callback-err': [2, '^(err|error)$'],
+    'indent': [2, 2, {
+      'SwitchCase': 1
+    }],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [2, {
+      'beforeColon': false,
+      'afterColon': true
+    }],
+    'keyword-spacing': [2, {
+      'before': true,
+      'after': true
+    }],
+    'new-cap': [2, {
+      'newIsCap': true,
+      'capIsNew': false
+    }],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 0,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [2, {
+      'allowLoop': false,
+      'allowSwitch': false
+    }],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [2, {
+      'max': 1
+    }],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [2, {
+      'defaultAssignment': false
+    }],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [2, {
+      'vars': 'all',
+      'args': 'none'
+    }],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [2, {
+      'initialized': 'never'
+    }],
+    'operator-linebreak': [2, 'after', {
+      'overrides': {
+        '?': 'before',
+        ':': 'before'
+      }
+    }],
+    'padded-blocks': [2, 'never'],
+    'quotes': [2, 'single', {
+      'avoidEscape': true,
+      'allowTemplateLiterals': true
+    }],
+    'semi': [2, 'never'],
+    'semi-spacing': [2, {
+      'before': false,
+      'after': true
+    }],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [2, {
+      'words': true,
+      'nonwords': false
+    }],
+    'spaced-comment': [2, 'always', {
+      'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
+    }],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    'yoda': [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [2, 'always', {
+      objectsInObjects: false
+    }],
+    'array-bracket-spacing': [2, 'never']
+  }
+}

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules/
+admin/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+**/*.log
+
+tests/**/coverage/
+tests/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.local
+
+package-lock.json
+yarn.lock
+" " 

+ 5 - 0
.travis.yml

@@ -0,0 +1,5 @@
+language: node_js
+node_js: 10
+script: npm run test
+notifications:
+  email: false

+ 21 - 0
LICENSE

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

+ 4 - 0
README.md

@@ -0,0 +1,4 @@
+新麦同城后台
+内部项目
+打包 npm run build:prod
+运行 npm run dev

+ 14 - 0
babel.config.js

@@ -0,0 +1,14 @@
+module.exports = {
+  presets: [
+    // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app
+    '@vue/cli-plugin-babel/preset'
+  ],
+  'env': {
+    'development': {
+      // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require().
+      // This plugin can significantly increase the speed of hot updates, when you have a large number of pages.
+      // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html
+      'plugins': ['dynamic-import-node']
+    }
+  }
+}

+ 35 - 0
build/index.js

@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = config.publicPath
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 24 - 0
jest.config.js

@@ -0,0 +1,24 @@
+module.exports = {
+  moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
+  transform: {
+    '^.+\\.vue$': 'vue-jest',
+    '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
+      'jest-transform-stub',
+    '^.+\\.jsx?$': 'babel-jest'
+  },
+  moduleNameMapper: {
+    '^@/(.*)$': '<rootDir>/src/$1'
+  },
+  snapshotSerializers: ['jest-serializer-vue'],
+  testMatch: [
+    '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
+  ],
+  collectCoverageFrom: ['src/utils/**/*.{js,vue}', '!src/utils/auth.js', '!src/utils/request.js', 'src/components/**/*.{js,vue}'],
+  coverageDirectory: '<rootDir>/tests/unit/coverage',
+  // 'collectCoverage': true,
+  'coverageReporters': [
+    'lcov',
+    'text-summary'
+  ],
+  testURL: 'http://localhost/'
+}

+ 9 - 0
jsconfig.json

@@ -0,0 +1,9 @@
+{ 
+  "compilerOptions": {
+    "baseUrl": "./",
+    "paths": {
+        "@/*": ["src/*"]
+    }
+  },
+  "exclude": ["node_modules", "dist"]
+}

BIN
node_modules.zip


+ 125 - 0
package.json

@@ -0,0 +1,125 @@
+{
+  "name": "vue-element-admin",
+  "version": "4.4.0",
+  "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
+  "author": "Pan <panfree23@gmail.com>",
+  "scripts": {
+    "dev": "vue-cli-service serve",
+    "lint": "eslint --ext .js,.vue src",
+    "build:prod": "vue-cli-service build",
+    "build:stage": "vue-cli-service build --mode staging",
+    "preview": "node build/index.js --preview",
+    "new": "plop",
+    "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
+    "test:unit": "jest --clearCache && vue-cli-service test:unit",
+    "test:ci": "npm run lint && npm run test:unit"
+  },
+  "dependencies": {
+    "@riophae/vue-treeselect": "^0.4.0",
+    "axios": "0.18.1",
+    "clipboard": "2.0.4",
+    "codemirror": "5.45.0",
+    "core-js": "3.6.5",
+    "driver.js": "0.9.5",
+    "dropzone": "5.5.1",
+    "echarts": "4.2.1",
+    "element-ui": "2.13.2",
+    "file-saver": "2.0.1",
+    "fuse.js": "3.4.4",
+    "js-cookie": "2.2.0",
+    "jsonlint": "1.6.3",
+    "jszip": "3.2.1",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "path-to-regexp": "2.4.0",
+    "screenfull": "4.2.0",
+    "script-loader": "0.7.2",
+    "sortablejs": "1.8.4",
+    "tui-editor": "1.3.3",
+    "vue": "2.6.10",
+    "vue-count-to": "1.0.13",
+    "vue-router": "3.0.2",
+    "vue-splitpane": "1.0.4",
+    "vuedraggable": "2.20.0",
+    "vuex": "3.1.0",
+    "wangeditor": "^4.7.10",
+    "xlsx": "0.14.1",
+    "jquery": "^3.5.1",
+    "swiper": "^6.3.3",
+    "vcolorpicker": "^1.0.3",
+    "vue-awesome-swiper": "^4.1.1",
+    "node-sass": "^4.12.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "4.4.4",
+    "@vue/cli-plugin-eslint": "4.4.4",
+    "@vue/cli-plugin-unit-jest": "4.4.4",
+    "@vue/cli-service": "4.4.4",
+    "@vue/test-utils": "1.0.0-beta.29",
+    "autoprefixer": "9.5.1",
+    "babel-eslint": "10.1.0",
+    "babel-jest": "23.6.0",
+    "babel-plugin-dynamic-import-node": "2.3.3",
+    "chalk": "2.4.2",
+    "chokidar": "2.1.5",
+    "connect": "3.6.6",
+    "eslint": "6.7.2",
+    "eslint-plugin-vue": "6.2.2",
+    "html-webpack-plugin": "3.2.0",
+    "husky": "1.3.1",
+    "lint-staged": "8.1.5",
+    "mockjs": "1.0.1-beta3",
+    "plop": "2.3.0",
+    "runjs": "4.3.2",
+    "sass": "1.26.2",
+    "sass-loader": "8.0.2",
+    "less": "^2.7.3",
+    "less-loader": "^4.0.6",
+    "script-ext-html-webpack-plugin": "2.1.3",
+    "serve-static": "1.13.2",
+    "svg-sprite-loader": "4.1.3",
+    "svgo": "1.2.0",
+    "vue-template-compiler": "2.6.10",
+    "socket.io": "^2.1.0",
+    "socket.io-client": "^2.1.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ],
+  "bugs": {
+    "url": "https://github.com/PanJiaChen/vue-element-admin/issues"
+  },
+  "engines": {
+    "node": ">=8.9",
+    "npm": ">= 3.0.0"
+  },
+  "keywords": [
+    "vue",
+    "admin",
+    "dashboard",
+    "element-ui",
+    "boilerplate",
+    "admin-template",
+    "management-system"
+  ],
+  "license": "MIT",
+  "lint-staged": {
+    "src/**/*.{js,vue}": [
+      "eslint --fix",
+      "git add"
+    ]
+  },
+  "husky": {
+    "hooks": {
+      "pre-commit": "lint-staged"
+    }
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
+  }
+}

+ 26 - 0
plop-templates/component/index.hbs

@@ -0,0 +1,26 @@
+{{#if template}}
+<template>
+  <div />
+</template>
+{{/if}}
+
+{{#if script}}
+<script>
+export default {
+  name: '{{ properCase name }}',
+  props: {},
+  data() {
+    return {}
+  },
+  created() {},
+  mounted() {},
+  methods: {}
+}
+</script>
+{{/if}}
+
+{{#if style}}
+<style lang="scss" scoped>
+
+</style>
+{{/if}}

+ 55 - 0
plop-templates/component/prompt.js

@@ -0,0 +1,55 @@
+const { notEmpty } = require('../utils.js')
+
+module.exports = {
+  description: 'generate vue component',
+  prompts: [{
+    type: 'input',
+    name: 'name',
+    message: 'component name please',
+    validate: notEmpty('name')
+  },
+  {
+    type: 'checkbox',
+    name: 'blocks',
+    message: 'Blocks:',
+    choices: [{
+      name: '<template>',
+      value: 'template',
+      checked: true
+    },
+    {
+      name: '<script>',
+      value: 'script',
+      checked: true
+    },
+    {
+      name: 'style',
+      value: 'style',
+      checked: true
+    }
+    ],
+    validate(value) {
+      if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
+        return 'Components require at least a <script> or <template> tag.'
+      }
+      return true
+    }
+  }
+  ],
+  actions: data => {
+    const name = '{{properCase name}}'
+    const actions = [{
+      type: 'add',
+      path: `src/components/${name}/index.vue`,
+      templateFile: 'plop-templates/component/index.hbs',
+      data: {
+        name: name,
+        template: data.blocks.includes('template'),
+        script: data.blocks.includes('script'),
+        style: data.blocks.includes('style')
+      }
+    }]
+
+    return actions
+  }
+}

+ 16 - 0
plop-templates/store/index.hbs

@@ -0,0 +1,16 @@
+{{#if state}}
+const state = {}
+{{/if}}
+
+{{#if mutations}}
+const mutations = {}
+{{/if}}
+
+{{#if actions}}
+const actions = {}
+{{/if}}
+
+export default {
+  namespaced: true,
+  {{options}}
+}

+ 62 - 0
plop-templates/store/prompt.js

@@ -0,0 +1,62 @@
+const { notEmpty } = require('../utils.js')
+
+module.exports = {
+  description: 'generate store',
+  prompts: [{
+    type: 'input',
+    name: 'name',
+    message: 'store name please',
+    validate: notEmpty('name')
+  },
+  {
+    type: 'checkbox',
+    name: 'blocks',
+    message: 'Blocks:',
+    choices: [{
+      name: 'state',
+      value: 'state',
+      checked: true
+    },
+    {
+      name: 'mutations',
+      value: 'mutations',
+      checked: true
+    },
+    {
+      name: 'actions',
+      value: 'actions',
+      checked: true
+    }
+    ],
+    validate(value) {
+      if (!value.includes('state') || !value.includes('mutations')) {
+        return 'store require at least state and mutations'
+      }
+      return true
+    }
+  }
+  ],
+  actions(data) {
+    const name = '{{name}}'
+    const { blocks } = data
+    const options = ['state', 'mutations']
+    const joinFlag = `,
+  `
+    if (blocks.length === 3) {
+      options.push('actions')
+    }
+
+    const actions = [{
+      type: 'add',
+      path: `src/store/modules/${name}.js`,
+      templateFile: 'plop-templates/store/index.hbs',
+      data: {
+        options: options.join(joinFlag),
+        state: blocks.includes('state'),
+        mutations: blocks.includes('mutations'),
+        actions: blocks.includes('actions')
+      }
+    }]
+    return actions
+  }
+}

+ 2 - 0
plop-templates/utils.js

@@ -0,0 +1,2 @@
+exports.notEmpty = name => v =>
+  !v || v.trim() === '' ? `${name} is required` : true

+ 26 - 0
plop-templates/view/index.hbs

@@ -0,0 +1,26 @@
+{{#if template}}
+<template>
+  <div />
+</template>
+{{/if}}
+
+{{#if script}}
+<script>
+export default {
+  name: '{{ properCase name }}',
+  props: {},
+  data() {
+    return {}
+  },
+  created() {},
+  mounted() {},
+  methods: {}
+}
+</script>
+{{/if}}
+
+{{#if style}}
+<style lang="scss" scoped>
+
+</style>
+{{/if}}

+ 55 - 0
plop-templates/view/prompt.js

@@ -0,0 +1,55 @@
+const { notEmpty } = require('../utils.js')
+
+module.exports = {
+  description: 'generate a view',
+  prompts: [{
+    type: 'input',
+    name: 'name',
+    message: 'view name please',
+    validate: notEmpty('name')
+  },
+  {
+    type: 'checkbox',
+    name: 'blocks',
+    message: 'Blocks:',
+    choices: [{
+      name: '<template>',
+      value: 'template',
+      checked: true
+    },
+    {
+      name: '<script>',
+      value: 'script',
+      checked: true
+    },
+    {
+      name: 'style',
+      value: 'style',
+      checked: true
+    }
+    ],
+    validate(value) {
+      if (value.indexOf('script') === -1 && value.indexOf('template') === -1) {
+        return 'View require at least a <script> or <template> tag.'
+      }
+      return true
+    }
+  }
+  ],
+  actions: data => {
+    const name = '{{name}}'
+    const actions = [{
+      type: 'add',
+      path: `src/views/${name}/index.vue`,
+      templateFile: 'plop-templates/view/index.hbs',
+      data: {
+        name: name,
+        template: data.blocks.includes('template'),
+        script: data.blocks.includes('script'),
+        style: data.blocks.includes('style')
+      }
+    }]
+
+    return actions
+  }
+}

+ 9 - 0
plopfile.js

@@ -0,0 +1,9 @@
+const viewGenerator = require('./plop-templates/view/prompt')
+const componentGenerator = require('./plop-templates/component/prompt')
+const storeGenerator = require('./plop-templates/store/prompt.js')
+
+module.exports = function(plop) {
+  plop.setGenerator('view', viewGenerator)
+  plop.setGenerator('component', componentGenerator)
+  plop.setGenerator('store', storeGenerator)
+}

+ 5 - 0
postcss.config.js

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

+ 15 - 0
public/index.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+    <meta name="renderer" content="webkit">
+    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= webpackConfig.name %></title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 11 - 0
src/App.vue

@@ -0,0 +1,11 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'App'
+}
+</script>

BIN
src/assets/401_images/401.gif


BIN
src/assets/404_images/404.png


BIN
src/assets/404_images/404_cloud.png


BIN
src/assets/custom-theme/fonts/element-icons.ttf


BIN
src/assets/custom-theme/fonts/element-icons.woff


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
src/assets/custom-theme/index.css


BIN
src/assets/data.png


BIN
src/assets/diy/banner.png


BIN
src/assets/diy/coupon.png


+ 99 - 0
src/assets/diy/css/attr.scss

@@ -0,0 +1,99 @@
+$color:#1890ff;
+.appdiy-module-attr::-webkit-scrollbar{ width:0;}
+.appdiy-module-attr{ width:360px;border-top: 1px solid #eee; border-left: 1px solid #eee; position: fixed; right:0; top:48px; height:100%; overflow:hidden auto; background:#fff;
+	 	.module-attr-tab{line-height: 23px; display: flex; border-bottom:1px solid #f1f1f1;
+			  li{ flex:1; padding:12px 0; cursor: pointer; border-right:1px solid #f1f1f1; text-align: center; font-size:14px; color:#333; position: relative;
+					  &.on{ color:$color}
+							&.on:after{ width:56px; height:2px; background:$color; content:''; position: absolute; bottom:0; left:0; right:0; margin:auto;}
+					}
+			}
+			.attr-con-item{ padding:12px; font-weight: bold; border-bottom:1px solid #eee; font-size:14px;}
+			.att-con-box{ font-size:13px; color:#555; padding-top: 18px; padding-bottom: 80px;
+      .label {width: 80px;text-align: right;}
+			/***导航***/
+			.attr-nav-item{
+				 .ht{ height:46px; padding:0 12px; overflow: hidden; display: flex;  border-bottom:1px solid #eee; justify-content: space-between; align-items: center;
+					  .t{ font-size:14px; font-weight: bold; flex:1; cursor: pointer; height:46px; line-height:46px; &.on{ span{ transform: rotate(-180deg); } } }
+							.opt{
+										.el-button.is-circle{padding:5px; transform: scale(0.8); font-size:12px; margin-left:0;}
+										.el-button--primary{background-color:$color; border-color:$color;}
+							 }
+					}
+					.hm{ padding:12px; padding-top:15px; padding-bottom:0; border-bottom:1px solid #eee;}
+					.goods{ display: flex; background:#f1f1f1; margin-bottom:12px; margin-top:-5px;
+						  img{ width:80px; height:80px; margin-right:12px;}
+								.text{ height:80px; overflow: hidden;
+								  .p1{color:#333;}
+								  p{ padding-top:5px; white-space: nowrap; text-overflow: ellipsis; color:#999;}
+								}
+					}
+          .tuan{ display: flex; background:#f1f1f1; margin-bottom:12px; margin-top:-5px;
+          	  img{ width:80px; height:80px; margin-right:12px;}
+          			.text{ height:80px; overflow: hidden;
+          			  .p1{color:#333;}
+          			  p{ padding-top:5px; white-space: nowrap; text-overflow: ellipsis; color:#999;}
+          			}
+          }
+          .miaosha{ display: flex; background:#f1f1f1; margin-bottom:12px; margin-top:-5px;
+          	  img{ width:80px; height:80px; margin-right:12px;}
+          			.text{ height:80px; overflow: hidden;
+          			  .p1{color:#333;}
+          			  p{ padding-top:5px; white-space: nowrap; text-overflow: ellipsis; color:#999;}
+          			}
+          }
+          .membergoods{ display: flex; background:#f1f1f1; margin-bottom:12px; margin-top:-5px;
+          	  img{ width:80px; height:80px; margin-right:12px;}
+          			.text{ height:80px; overflow: hidden;
+          			  .p1{color:#333;}
+          			  p{ padding-top:5px; white-space: nowrap; text-overflow: ellipsis; color:#999;}
+          			}
+          }
+          .technical{ display: flex; background:#f1f1f1; margin-bottom:12px; margin-top:-5px;
+          	  img{ width:80px; height:80px; margin-right:12px;}
+          			.text{ height:80px; overflow: hidden;
+          			  .p1{color:#333;}
+          			  p{ padding-top:5px; white-space: nowrap; text-overflow: ellipsis; color:#999;}
+          			}
+          }
+			}
+			.attr-nav-add{ height:40px; display: flex; cursor:pointer; &:hover{ color:$color;} border-bottom:1px solid #eee; justify-content:center; align-items: center; span{ margin:2px 5px 0 0; font-size:15px;} }
+			.att-con-pd{ padding:20px;}
+			.item{ margin-bottom:20px; display: flex; &.c{ align-items: center;}
+			  .tit.mg{margin-top:-4px;}
+					.input{ flex:1; &.flex{ display: flex; align-items: center;}
+							.intxt{ padding:6px 10px; color:#555; border:1px solid #eee; border-radius:2px; width:160px; margin-right:10px;}
+							.colorBtn{ width:16px; height:16px; border-radius:50%; border:1px solid #ddd; cursor: pointer; }
+							.module-upload{ border:1px dashed #eee; overflow: hidden; margin-right:12px; cursor:pointer; position:relative; display: flex; align-items: center; justify-content: center; width:60px; height:60px; background:#f7f8fa; border-radius:2px;
+							  &.c{ width:150px; height:100px; }
+									.el-icon-camera-solid{ font-size:25px; color:#bfbfbf; opacity:0.8;}
+									.el-icon-close{ width:15px; height:14px; font-size:12px; line-height:14px; text-align:center; background-color: rgba(0, 0, 0, 0.7); border-radius: 0 0 0 12px; position:absolute; right:0; top:0; color:#fff; z-index:1;}
+									img{ display:block; width:100%; height:100%;}
+							}
+							.tips{color:#bfbfbf;}
+							.el-textarea__inner{ font-family: Avenir, Helvetica, Arial, sans-serif;}
+							.input-line{ &:last-child{margin-bottom:0;}  margin-bottom:15px; display: flex; align-items: center; .sp1{ display: flex; cursor: pointer; padding-left: 5px;padding-right: 10px; align-items: center;}
+              .sp2{ display: flex; cursor: pointer; padding-left: 5px;padding-right: 10px; align-items: center;}
+									.tit{ margin-top:-4px;}
+
+									.el-input__inner{ height:29px; line-height:29px; }
+									.el-input__icon{ line-height:29px;}
+									.input.f{ display: flex; align-items: center;
+									  .font{ margin-left:12px; width:16px;border-radius:50%; height:16px; cursor:pointer; &:hover,&.on{ color:$color;} }
+									}
+									.el-button{ padding:0 5px; height:29px; line-height:29px; margin-top:5px; margin-right:5px;}
+							}
+					}
+					.attr-style{ display: flex; flex-wrap: wrap;}
+					.style{ width:100px; margin-right:12px; margin-bottom:12px; cursor: pointer;
+					  img{ width:100%; display: block; margin-bottom:5px;}
+							dd{ font-size:12px; text-align: center;
+								 .el-radio__input.is-checked .el-radio__inner{ background:$color; border-color: $color;}
+								 .el-radio__input.is-checked+.el-radio__label{ color:$color;}
+							}
+					}
+			}
+	 }
+}
+.m-colorPicker .box{
+    left: -60px;
+  }

+ 575 - 0
src/assets/diy/css/diy.scss

@@ -0,0 +1,575 @@
+$color:#29c8a1;
+.dis-flex {
+                 display: -webkit-box;
+                 display: -webkit-flex;
+                 display: -ms-flexbox;
+                 display: flex;
+                 }
+                 .flex-x-around {
+                   justify-content: space-around;
+                   }
+                   .flex-dir-row {
+                     flex-direction: row; }
+                   .flex-dir-column {
+                     -webkit-box-orient: vertical;
+                     -webkit-flex-direction: column;
+                     -ms-flex-direction: column;
+                     flex-direction: column; }
+                     .flex-x-center {
+                       -webkit-box-pack: center;
+                       -webkit-justify-content: center;
+                       -ms-flex-pack: center;
+                       justify-content: center; }
+                       .flex-y-center {
+                         -webkit-box-align: center;
+                         -webkit-align-items: center;
+                         -ms-flex-align: center;
+                         align-items: center; }
+.appdiy-module-container{
+	width:375px; height:667px; border: 1px solid #eee; display: flex; flex-direction: column; top:70px; left:711px; position: fixed; background:#fff; box-shadow:#dcdcdc 0 3px 16px;
+	/***页面标题***/
+	.appdiy-title{ height:50px; position: relative; background:#fff; text-align:center;
+			img{position: absolute; z-index:1; right:12px; top:50%; transform: translateY(-50%); width:72px; height:30px; background:rgba(0,0,0,0.2); border-radius:30px; }
+			.tit{ font-size:15px; line-height:50px; margin:0; padding:0;position: relative; z-index:1; }
+			.el-icon-arrow-left{ position: absolute; left:0; top:0; z-index:1; width:40px; height:100%; color:#888; cursor: pointer; font-size:20px; display: flex; justify-content: center; align-items: center;}
+	}
+	/**模块大框架***/
+	.appdiy-module{ flex:1; background:#ffffff; position: relative; height:100%; overflow-y:auto;z-index:99;}
+	.appdiy-module::-webkit-scrollbar{ width:0;}
+	/**无模块***/
+	.appdiy-nomodule{ width:100%; height:100%; display: flex; justify-content: center; align-items: center;
+			h2{ font-size:15px;}
+	}
+	/***模块项***/
+	.appdiy-module-item{ position: relative;
+			.xline{  z-index:2; position: absolute; display: none;
+			  &:nth-child(1){ left:0; border-left:1px dashed $color; top:0; height:100%;}
+				&:nth-child(2){ left:0; border-top:1px dashed $color; top:0; width:100%;}
+				&:nth-child(3){ right:0; border-right:1px dashed $color; top:0; height:100%;}
+				&:nth-child(4){ left:0; border-bottom:1px dashed $color; bottom:0; width:100%;}
+			}
+		  &.on { .xline{ display: block;} }
+	}
+		/**空白提示***/
+		.placeholder{ height:80px; margin:10px 0; display: flex; justify-content: center; background:#eee; color:#eee; align-items: center; border:1px dashed #eee;
+				&.on{ color:$color; border-color:$color; background:#e4fff1; }
+		}
+		/***右侧操作按钮***/
+		.appdiy-module-option{ position: absolute; top:0; right:-25px; width:25px; background:rgba(0,0,0,0.6); z-index:20;
+				li{ cursor: pointer;	span{ height:25px; transform: scale(1); display: block; font-size:12px; display: flex; justify-content: center; align-items:center; color:#fff;}
+					&:hover{background:$color;}
+				}
+		}
+		/***模块标题***/
+		.module-item-title{ background:#fff; position: relative; z-index: 1; font-size:14px; padding:14px 12px; border-bottom:1px solid #eee; display: flex; justify-content: space-between; align-items: center;
+		  .mo{ color:#666;}
+		}
+		/***标题模块2***/
+		.module-item-twtit{ font-size:14px; position: relative; z-index: 1; padding:0 12px; padding-top:10px;}
+		/***导航模块***/
+		.module-common{ margin-top:10px; position: relative; .mg0{ margin-top:0;} }
+		.module-background{ width: 100%; background:#fff; position: absolute; left:0; top:0; right:0; bottom:0;background-size:100%;}
+		.module-item-nav{
+			 .item-nav-con{ position: relative; z-index:1; padding:0 5px; padding-bottom:10px; display: flex; flex-wrap: wrap;
+				  &.col5 { .item{ width:20%; } }
+				  .item{ padding-top:12px; width:25%; text-align: center;
+						  .item-nav-img{ width:55px; height:55px; margin:0 auto;}
+								.item-nav-text{ font-size:12px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color:#333; padding-top:5px; }
+						}
+				}
+		}
+
+    /***幻灯片组***/
+    .module-item-slide{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+    	  .item-slide-con{ position: relative; overflow: hidden;}
+    			.diy-item-slide{ position: relative; z-index: 1;  padding:10px;
+    			   .item{
+    							.pic{ img { display:block; width:100%; height:100%; object-fit: cover;}}
+    					}
+    			}
+    	}
+
+		/***多图级***/
+		.module-item-duo{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+			  .item-duo-con{ position: relative; overflow: hidden;}
+					.item-duo-display__list{ position: relative; display:flex; flex-wrap: wrap; z-index: 1; padding-right:10px; padding-top:10px; overflow: hidden;
+					   &.col1{ .item{ width:100%; } }
+						 &.col2{ .item{ width:50%; } }
+						 &.col3{ .item{ width:33.333333%; } }
+						 &.col4{ .item{ width:25%; } }
+             &.col5{ .item{ width:20%; } }
+					   .item{ width:33.3333333%; padding-left:10px; padding-bottom:10px;
+										.c{ overflow:hidden;
+                    img{ object-fit: cover; display:block; width:100%;}
+                    .txt{ white-space: nowrap; padding:5px 10px; text-align:center; overflow: hidden; text-overflow: ellipsis;} }
+								}
+					}
+          .item-duo-display__slide{
+            width: 100000px;
+          	position: relative; z-index: 1; padding-right:10px; padding-top:10px; overflow: hidden;
+            &.col1{ .item{ width:363px } }
+            &.col2{ .item{ width:181px; } }
+            &.col3{ .item{ width:121px; } }
+            &.col4{ .item{ width:90px; } }
+            &.col5{ .item{ width:72px; } }
+            .item{float: left; width:91px; padding-left:10px; padding-bottom:10px;
+            										.c{ overflow:hidden;
+                   img{ object-fit: cover; display:block; width:100%;}
+                   .txt{ white-space: nowrap; padding:5px 10px; text-align:center; overflow: hidden; text-overflow: ellipsis;} }
+            								}
+          }
+			}
+      /***图片橱窗***/
+      .module-item-window{ margin-top:0px; position: relative; .mg0{ margin-top:0;}
+      	  .item-window-con{ position: relative; overflow: hidden;}
+      			.item-window-style1{ position: relative; display:flex; flex-wrap: wrap; z-index: 1; overflow: hidden;
+                 .display {
+                      height: 0;
+                      width: 100%;
+                      margin: 0;
+                      padding-bottom: 50%;
+                      position: relative; }
+                  .display img {
+                      width: 100%;
+                      height: 100%; }
+                  .display .display-left {
+                      width: 50%;
+                      height: 100%;
+                      position: absolute;
+                      top: 0;
+                      left: 0; }
+                  .display .display-right {
+                      width: 50%;
+                      height: 100%;
+                      position: absolute;
+                      top: 0;
+                      left: 50%; }
+                  .display .display-right1 {
+                      width: 100%;
+                      height: 50%;
+                      position: absolute;
+                      overflow: hidden;
+                      top: 0;
+                      left: 0; }
+                  .display .display-right1-style3 {
+                      width: 100%;
+                      height: 100%;
+                      position: absolute;
+                      overflow: hidden;
+                      top: 0;
+                      left: 0; }
+                  .display .display-right2 {
+                      width: 100%;
+                      height: 50%;
+                      position: absolute;
+                      overflow: hidden;
+                      top: 50%;
+                      left: 0; }
+                  .display .display-right2 .left {
+                      width: 50%;
+                      height: 100%;
+                      position: absolute;
+                      top: 0;
+                      left: 0; }
+                  .display .display-right2 .right {
+                      width: 50%;
+                      height: 100%;
+                      position: absolute;
+                      top: 0;
+                      left: 50%; }
+      			}
+      	}
+        /***搜索框***/
+        .module-item-search{position: relative; width: 100%; .mg0{ margin-top:0;}
+        	  .item-search-con{ width: 100%;position: relative; overflow: hidden;}
+        			.item-search{ width: 100%;position: relative; display:flex; flex-wrap: wrap; z-index: 1;overflow: hidden;
+                  .diy-search {
+                    width: 100%;
+                    padding-top: 10px;
+                      padding-left: 10px;
+                      padding-right: 10px;
+                      }
+                 .diy-search .hotkeybox {
+                   padding-top: 5px;
+                 }
+                  .diy-search .inner {
+                      height: 30px;
+                      background: #fff;
+                      overflow: hidden; }
+                  .diy-search .inner.radius {
+                      border-radius: 5px; }
+                  .diy-search .inner.round {
+                      border-radius: 30px; }
+                  .diy-search .inner .search-input {
+                      height: 30px;
+                      width: 100%;
+                      line-height: 30px;
+                      color: #999999;
+                      padding: 0 10px; }
+        			}
+        	}
+      /***优惠券***/
+      .module-item-coupon{position: relative; width: 100%;
+      	  .item-coupon-con{ width: 100%;position: relative; overflow: hidden;}
+      			.item-coupon{ width: 100%;position: relative; z-index: 1;overflow: hidden;
+               .diy-coupon {padding: 10px 0;}
+           .diy-coupon .coupon-item {
+                 display: block;
+                 font-size: 1.3rem;
+    width: 180px;
+    height: 60px;
+    position: relative;
+    color: #fff;
+    overflow: hidden;
+    box-sizing: border-box; }
+.diy-coupon .coupon-item i.before {
+    content: "";
+    position: absolute;
+    z-index: 1;
+    width: 1.6rem;
+    height: 1.6rem;
+    top: 50%;
+    left: -.8rem;
+    -webkit-transform: translateY(-50%);
+    transform: translateY(-50%);
+    -webkit-border-radius: 80%;
+    border-radius: 80%;
+    background-color: #fff; }
+.diy-coupon .coupon-item .left-content {
+    position: relative;
+    float: left;
+    width: 70%;
+    height: 100%;
+    background-color: #E5004F;
+    font-size: 12px; }
+.diy-coupon .coupon-item .left-content .content-top .price {
+    font-size: 2rem; }
+.diy-coupon .coupon-item .left-content.color__blue {
+    background: linear-gradient(-125deg, #57bdbf, #2f9de2); }
+.diy-coupon .coupon-item .left-content.color__red {
+    background: linear-gradient(-128deg, #ff6d6d, #ff3636); }
+.diy-coupon .coupon-item .left-content.color__violet {
+    background: linear-gradient(-113deg, #ef86ff, #b66ff5); }
+.diy-coupon .coupon-item .left-content.color__yellow {
+    background: linear-gradient(-141deg, #f7d059, #fdb054); }
+.diy-coupon .coupon-item .right-receive {
+    float: right;
+    width: 30%;
+    height: 100%;
+    background-color: #4e4e4e;
+    text-align: center; }
+.diy-coupon .coupon-item .right-receive span {
+    font-size: 1rem; }
+      			}
+      	}
+
+      /***单图模块***/
+      .module-item-imagesingle{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+      	  .item-imagesingle-con{ position: relative; overflow: hidden;}
+      			.item-imagesingle{ position: relative; z-index: 1; overflow: hidden;
+      						.item{ position: relative; overflow: hidden;
+      								img{ object-fit: cover; display:block; width:100%;}
+      						}
+      			}
+      	}
+        /***视频模块***/
+        .module-item-video{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+        	  .item-video-con{ position: relative; overflow: hidden;}
+        			.item-video{ position: relative; z-index: 1;  padding:10px 0;  overflow: hidden;
+        						.diy-video video {
+        						    display: block;
+        						    width: 100%;
+        						    height: 100%; }
+        			}
+        	}
+      /***公告模块***/
+      .module-item-notice{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+      	  .item-notice-con{ position: relative; overflow: hidden;}
+      			.item-notice{ position: relative; z-index: 1;  overflow: hidden;
+      						 .diy-notice {
+      						    padding: 4px 10px;
+      						    line-height: 26px; }
+      						 .diy-notice .notice__icon img {
+      						    width: 16px;
+      						    height: 16px; }
+      						 .diy-notice .notice__text {
+      						    padding-left: 5px; }
+      			}
+      	}
+			/***图文模块***/
+			.module-item-tuwen{ margin-top:10px; position: relative; .mg0{ margin-top:0;}
+				  .item-tuwen-con{ position: relative; overflow: hidden;}
+						.item-tuwen-style1{ position: relative; z-index: 1;  padding:10px 0;  overflow: hidden;
+									.tit{ white-space: nowrap; font-size:14px; font-weight: bold; padding:10px; padding-top:0; text-align:left; overflow: hidden; text-overflow: ellipsis;}
+									.item{ margin:0 10px; position: relative; overflow: hidden;
+											img{ object-fit: cover; display:block; height:150px; width:100%;}
+										.note{ line-height:18px; color:#fff; background:rgba(0,0,0,0.5); position: absolute; bottom:0; padding:6px 10px; width:100%;}
+									}
+						}
+						.item-tuwen-style2{ position: relative; display:flex; flex-wrap: wrap; z-index: 1; padding-bottom:10px;  overflow: hidden;
+									.item{ padding:0 10px; padding-top:10px;
+											img{ object-fit: cover; display:block; height:150px; width:100%;}
+											.tit{ white-space: nowrap; font-size:14px; font-weight: bold; padding-top:10px; text-align:left; overflow: hidden; text-overflow: ellipsis;}
+											.note{ line-height:20px; color:#999; padding-top:10px;}
+									}
+						}
+				}
+				/***商品模块***/
+		  .module-item-goods{margin-top:10px; position: relative; .mg0{ margin-top:0;}
+				.item-goods-con{ position: relative; overflow: hidden;}
+					.noadd{ height:200px; display: flex; align-items: center; justify-content: center; width:100%;}
+          .item-goods-display__list{
+					.item-goods{ position: relative; z-index: 1; display: flex; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+								&.style2{ .item{ width: calc(100%/2 - 5px);
+											.yh { font-size:10px;}
+											.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+									}
+								}
+                &.style3{ .item{ width: calc(100%/3 - 5px);
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+								.item{ width:100%; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+										.pic{ overflow: hidden; position: relative; height:150px;
+													img{ object-fit: cover; display:block; height:100%; width:100%;}
+													span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+										}
+										.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+										.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+											span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+												label{flex:1; overflow: hidden; text-align: right;}
+										}
+										.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+											span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+												label{ padding:2px 5px; background:#F73E44; color:#fff;}
+										}
+										.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+								}
+					}
+          }
+          .item-goods-display__slide{width: 100000px;
+          	position: relative; z-index: 1; padding-right:10px; padding-top:10px; overflow: hidden;
+          .item-goods{ z-index: 1; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+          			&.style2{ .item{ width:181px;
+          						.yh { font-size:10px;}
+          						.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+          				}
+          			}
+                &.style3{ .item{ width:121px;
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+          			.item{ float: left; width:353px; padding-right:10px; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+          					.pic{ overflow: hidden; position: relative; height:150px;
+          								img{ object-fit: cover; display:block; height:100%; width:100%;}
+          								span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+          					}
+          					.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+          					.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+          						span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+          							label{flex:1; overflow: hidden; text-align: right;}
+          					}
+          					.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+          						span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+          							label{ padding:2px 5px; background:#F73E44; color:#fff;}
+          					}
+          					.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+          			}
+          }
+          }
+          .item-goods-display__li{
+          .item-goods{ z-index: 1; padding:10px; padding-top:0;  overflow: hidden;
+          			.item{ width:353px; padding-right:10px; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+          					.pic{ float: left; width: 100px; height:100px;
+          								img{ object-fit: cover; display:block; height:100%; width:100%;}
+          								span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+          					}
+                    .c{ float: left; padding-left: 10px;padding-right: 10px; width: 180px; line-height: 20px;
+          					.tit { padding-top:5px; font-size:14px; }
+          					.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+          						span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+          							label{flex:1; overflow: hidden; text-align: right;}
+          					}
+          					.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+          						span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+          							label{ padding:2px 5px; background:#F73E44; color:#fff;}
+          					}
+          					.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+          			}
+                .settechnical{ float: left; width: 60px; text-align: center; padding:5px 5px; margin-top: 30px; font-size: 12px; background:#F73E44; color:#fff;}
+                }
+          }
+          }
+		}
+
+    /***拼团模块***/
+      .module-item-tuan{margin-top:10px; position: relative; .mg0{ margin-top:0;}
+    		.item-tuan-con{ position: relative; overflow: hidden;}
+    			.noadd{ height:200px; display: flex; align-items: center; justify-content: center; width:100%;}
+    			.item-tuan{ position: relative; z-index: 1; display: flex; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+    						&.style2{ .item{ width: calc(100%/2 - 5px);
+    									.yh { font-size:10px;}
+    									.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+    							}
+    						}
+                &.style3{ .item{ width: calc(100%/3 - 5px);
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+    						.item{ width:100%; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+    								.pic{ overflow: hidden; position: relative; height:150px;
+    											img{ object-fit: cover; display:block; height:100%; width:100%;}
+    											span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+    								}
+    								.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+    								.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+    									span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+    										label{flex:1; overflow: hidden; text-align: right;}
+    								}
+    								.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+    									span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+    										label{ padding:2px 5px; background:#F73E44; color:#fff;}
+    								}
+    								.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+    						}
+    			}
+    }
+
+    /***秒杀模块***/
+      .module-item-miaosha{margin-top:10px; position: relative; .mg0{ margin-top:0;}
+    		.item-miaosha-con{ position: relative; overflow: hidden;}
+    			.noadd{ height:200px; display: flex; align-items: center; justify-content: center; width:100%;}
+    			.item-miaosha{ position: relative; z-index: 1; display: flex; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+    						&.style2{ .item{ width: calc(100%/2 - 5px);
+    									.yh { font-size:10px;}
+    									.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+    							}
+    						}
+                &.style3{ .item{ width: calc(100%/3 - 5px);
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+    						.item{ width:100%; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+    								.pic{ overflow: hidden; position: relative; height:150px;
+    											img{ object-fit: cover; display:block; height:100%; width:100%;}
+    											span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+    								}
+    								.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+    								.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+    									span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+    										label{flex:1; overflow: hidden; text-align: right;}
+    								}
+    								.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+    									span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+    										label{ padding:2px 5px; background:#F73E44; color:#fff;}
+    								}
+    								.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+    						}
+    			}
+    }
+
+    /***会员礼包模块***/
+      .module-item-membergoods{margin-top:10px; position: relative; .mg0{ margin-top:0;}
+    		.item-membergoods-con{ position: relative; overflow: hidden;}
+    			.noadd{ height:200px; display: flex; align-items: center; justify-content: center; width:100%;}
+    			.item-membergoods{ position: relative; z-index: 1; display: flex; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+    						&.style2{ .item{ width: calc(100%/2 - 5px);
+    									.yh { font-size:10px;}
+    									.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+    							}
+    						}
+                &.style3{ .item{ width: calc(100%/3 - 5px);
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+    						.item{ width:100%; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+    								.pic{ overflow: hidden; position: relative; height:150px;
+    											img{ object-fit: cover; display:block; height:100%; width:100%;}
+    											span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+    								}
+    								.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+    								.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+    									span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+    										label{flex:1; overflow: hidden; text-align: right;}
+    								}
+    								.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+    									span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+    										label{ padding:2px 5px; background:#F73E44; color:#fff;}
+    								}
+    								.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+    						}
+    			}
+    }
+
+    /***师傅模块***/
+      .module-item-technical{margin-top:10px; position: relative; .mg0{ margin-top:0;}
+    		.item-technical-con{ position: relative; overflow: hidden;}
+    			.noadd{ height:200px; display: flex; align-items: center; justify-content: center; width:100%;}
+    			.item-technical{ position: relative; z-index: 1; display: flex; flex-wrap: wrap; justify-content: space-between; padding:10px; padding-top:0;  overflow: hidden;
+    						&.style2{ .item{ width: calc(100%/2 - 5px);
+    									.yh { font-size:10px;}
+    									.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+    							}
+    						}
+                &.style3{ .item{ width: calc(100%/3 - 5px);
+                .pic{ overflow: hidden; position: relative; height:100px;
+                			img{ object-fit: cover; display:block; height:100%; width:100%;}
+                			span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+                }
+                			.yh { font-size:10px;}
+                			.price{ span { b { font-size:12px;} i{ font-size:10px;} } }
+                	}
+                }
+    						.item{ width:100%; margin-top:12px; border-radius:3px; position: relative; overflow: hidden;
+    								.pic{ overflow: hidden; position: relative; height:150px;
+    											img{ object-fit: cover; display:block; height:100%; width:100%;}
+    											span{ background:red; color:#fff; padding:2px 5px; border-bottom-right-radius:5px; position: absolute; left:0; top:0;}
+    								}
+    								.tit{ white-space: nowrap; padding-top:5px; font-size:14px; overflow: hidden; text-overflow: ellipsis;}
+    								.yh{ display: flex; overflow: hidden; justify-content: space-between; align-items:center; color:#999; margin-top:5px;
+    									span{ padding:2px 5px; background:#F73E44; color:#fff; font-size:12px;}
+    										label{flex:1; overflow: hidden; text-align: right;}
+    								}
+    								.price{ padding-top:5px; color:#666; display: flex; overflow: hidden; justify-content: space-between; align-items:center;
+    									span{ flex:1; overflow: hidden; b{ font-size:14px; color:#F73E44; } i{ color:#bfbfbf; font-style: normal; padding-left:5px; text-decoration: line-through;} }
+    										label{ padding:2px 5px; background:#F73E44; color:#fff;}
+    								}
+    								.ju{ padding-top:5px; color:#999; font-size:11px; white-space: nowrap; overflow: hidden; text-overflow:ellipsis; }
+    						}
+    			}
+    }
+     /***师傅模块end***/
+
+}
+
+
+/**预览***/
+.appdiy-preview{position: fixed; left:0; right:0; margin:auto; top:50%; width:375px; margin-top:-333px; z-index:1000;
+		.el-icon-close{ position: absolute; width:25px; height:25px; right:-30px; top:0; cursor: pointer; color:#fff; border:1px solid #fff; border-radius:50%; display: flex; align-items: center; justify-content: center; font-size:16px; cursor: pointer;}
+		.appdiy-module-container{ position: static; top:0; left:0; box-shadow:none; border-radius:0;}
+}

BIN
src/assets/diy/duo.png


BIN
src/assets/diy/duo_style1.png


BIN
src/assets/diy/duo_style2.png


BIN
src/assets/diy/duo_style3.png


BIN
src/assets/diy/duo_style4.png


+ 539 - 0
src/assets/diy/font/demo.css

@@ -0,0 +1,539 @@
+/* Logo 字体 */
+@font-face {
+  font-family: "iconfont logo";
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
+  src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
+    url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
+}
+
+.logo {
+  font-family: "iconfont logo";
+  font-size: 160px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+/* tabs */
+.nav-tabs {
+  position: relative;
+}
+
+.nav-tabs .nav-more {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  height: 42px;
+  line-height: 42px;
+  color: #666;
+}
+
+#tabs {
+  border-bottom: 1px solid #eee;
+}
+
+#tabs li {
+  cursor: pointer;
+  width: 100px;
+  height: 40px;
+  line-height: 40px;
+  text-align: center;
+  font-size: 16px;
+  border-bottom: 2px solid transparent;
+  position: relative;
+  z-index: 1;
+  margin-bottom: -1px;
+  color: #666;
+}
+
+
+#tabs .active {
+  border-bottom-color: #f00;
+  color: #222;
+}
+
+.tab-container .content {
+  display: none;
+}
+
+/* 页面布局 */
+.main {
+  padding: 30px 100px;
+  width: 960px;
+  margin: 0 auto;
+}
+
+.main .logo {
+  color: #333;
+  text-align: left;
+  margin-bottom: 30px;
+  line-height: 1;
+  height: 110px;
+  margin-top: -50px;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.main .logo a {
+  font-size: 160px;
+  color: #333;
+}
+
+.helps {
+  margin-top: 40px;
+}
+
+.helps pre {
+  padding: 20px;
+  margin: 10px 0;
+  border: solid 1px #e7e1cd;
+  background-color: #fffdef;
+  overflow: auto;
+}
+
+.icon_lists {
+  width: 100% !important;
+  overflow: hidden;
+  *zoom: 1;
+}
+
+.icon_lists li {
+  width: 100px;
+  margin-bottom: 10px;
+  margin-right: 20px;
+  text-align: center;
+  list-style: none !important;
+  cursor: default;
+}
+
+.icon_lists li .code-name {
+  line-height: 1.2;
+}
+
+.icon_lists .icon {
+  display: block;
+  height: 100px;
+  line-height: 100px;
+  font-size: 42px;
+  margin: 10px auto;
+  color: #333;
+  -webkit-transition: font-size 0.25s linear, width 0.25s linear;
+  -moz-transition: font-size 0.25s linear, width 0.25s linear;
+  transition: font-size 0.25s linear, width 0.25s linear;
+}
+
+.icon_lists .icon:hover {
+  font-size: 100px;
+}
+
+.icon_lists .svg-icon {
+  /* 通过设置 font-size 来改变图标大小 */
+  width: 1em;
+  /* 图标和文字相邻时,垂直对齐 */
+  vertical-align: -0.15em;
+  /* 通过设置 color 来改变 SVG 的颜色/fill */
+  fill: currentColor;
+  /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
+      normalize.css 中也包含这行 */
+  overflow: hidden;
+}
+
+.icon_lists li .name,
+.icon_lists li .code-name {
+  color: #666;
+}
+
+/* markdown 样式 */
+.markdown {
+  color: #666;
+  font-size: 14px;
+  line-height: 1.8;
+}
+
+.highlight {
+  line-height: 1.5;
+}
+
+.markdown img {
+  vertical-align: middle;
+  max-width: 100%;
+}
+
+.markdown h1 {
+  color: #404040;
+  font-weight: 500;
+  line-height: 40px;
+  margin-bottom: 24px;
+}
+
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  color: #404040;
+  margin: 1.6em 0 0.6em 0;
+  font-weight: 500;
+  clear: both;
+}
+
+.markdown h1 {
+  font-size: 28px;
+}
+
+.markdown h2 {
+  font-size: 22px;
+}
+
+.markdown h3 {
+  font-size: 16px;
+}
+
+.markdown h4 {
+  font-size: 14px;
+}
+
+.markdown h5 {
+  font-size: 12px;
+}
+
+.markdown h6 {
+  font-size: 12px;
+}
+
+.markdown hr {
+  height: 1px;
+  border: 0;
+  background: #e9e9e9;
+  margin: 16px 0;
+  clear: both;
+}
+
+.markdown p {
+  margin: 1em 0;
+}
+
+.markdown>p,
+.markdown>blockquote,
+.markdown>.highlight,
+.markdown>ol,
+.markdown>ul {
+  width: 80%;
+}
+
+.markdown ul>li {
+  list-style: circle;
+}
+
+.markdown>ul li,
+.markdown blockquote ul>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown>ul li p,
+.markdown>ol li p {
+  margin: 0.6em 0;
+}
+
+.markdown ol>li {
+  list-style: decimal;
+}
+
+.markdown>ol li,
+.markdown blockquote ol>li {
+  margin-left: 20px;
+  padding-left: 4px;
+}
+
+.markdown code {
+  margin: 0 3px;
+  padding: 0 5px;
+  background: #eee;
+  border-radius: 3px;
+}
+
+.markdown strong,
+.markdown b {
+  font-weight: 600;
+}
+
+.markdown>table {
+  border-collapse: collapse;
+  border-spacing: 0px;
+  empty-cells: show;
+  border: 1px solid #e9e9e9;
+  width: 95%;
+  margin-bottom: 24px;
+}
+
+.markdown>table th {
+  white-space: nowrap;
+  color: #333;
+  font-weight: 600;
+}
+
+.markdown>table th,
+.markdown>table td {
+  border: 1px solid #e9e9e9;
+  padding: 8px 16px;
+  text-align: left;
+}
+
+.markdown>table th {
+  background: #F7F7F7;
+}
+
+.markdown blockquote {
+  font-size: 90%;
+  color: #999;
+  border-left: 4px solid #e9e9e9;
+  padding-left: 0.8em;
+  margin: 1em 0;
+}
+
+.markdown blockquote p {
+  margin: 0;
+}
+
+.markdown .anchor {
+  opacity: 0;
+  transition: opacity 0.3s ease;
+  margin-left: 8px;
+}
+
+.markdown .waiting {
+  color: #ccc;
+}
+
+.markdown h1:hover .anchor,
+.markdown h2:hover .anchor,
+.markdown h3:hover .anchor,
+.markdown h4:hover .anchor,
+.markdown h5:hover .anchor,
+.markdown h6:hover .anchor {
+  opacity: 1;
+  display: inline-block;
+}
+
+.markdown>br,
+.markdown>p>br {
+  clear: both;
+}
+
+
+.hljs {
+  display: block;
+  background: white;
+  padding: 0.5em;
+  color: #333333;
+  overflow-x: auto;
+}
+
+.hljs-comment,
+.hljs-meta {
+  color: #969896;
+}
+
+.hljs-string,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-strong,
+.hljs-emphasis,
+.hljs-quote {
+  color: #df5000;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-type {
+  color: #a71d5d;
+}
+
+.hljs-literal,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-attribute {
+  color: #0086b3;
+}
+
+.hljs-section,
+.hljs-name {
+  color: #63a35c;
+}
+
+.hljs-tag {
+  color: #333333;
+}
+
+.hljs-title,
+.hljs-attr,
+.hljs-selector-id,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo {
+  color: #795da3;
+}
+
+.hljs-addition {
+  color: #55a532;
+  background-color: #eaffea;
+}
+
+.hljs-deletion {
+  color: #bd2c00;
+  background-color: #ffecec;
+}
+
+.hljs-link {
+  text-decoration: underline;
+}
+
+/* 代码高亮 */
+/* PrismJS 1.15.0
+https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
+/**
+ * prism.js default theme for JavaScript, CSS and HTML
+ * Based on dabblet (http://dabblet.com)
+ * @author Lea Verou
+ */
+code[class*="language-"],
+pre[class*="language-"] {
+  color: black;
+  background: none;
+  text-shadow: 0 1px white;
+  font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
+  text-align: left;
+  white-space: pre;
+  word-spacing: normal;
+  word-break: normal;
+  word-wrap: normal;
+  line-height: 1.5;
+
+  -moz-tab-size: 4;
+  -o-tab-size: 4;
+  tab-size: 4;
+
+  -webkit-hyphens: none;
+  -moz-hyphens: none;
+  -ms-hyphens: none;
+  hyphens: none;
+}
+
+pre[class*="language-"]::-moz-selection,
+pre[class*="language-"] ::-moz-selection,
+code[class*="language-"]::-moz-selection,
+code[class*="language-"] ::-moz-selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+pre[class*="language-"]::selection,
+pre[class*="language-"] ::selection,
+code[class*="language-"]::selection,
+code[class*="language-"] ::selection {
+  text-shadow: none;
+  background: #b3d4fc;
+}
+
+@media print {
+
+  code[class*="language-"],
+  pre[class*="language-"] {
+    text-shadow: none;
+  }
+}
+
+/* Code blocks */
+pre[class*="language-"] {
+  padding: 1em;
+  margin: .5em 0;
+  overflow: auto;
+}
+
+:not(pre)>code[class*="language-"],
+pre[class*="language-"] {
+  background: #f5f2f0;
+}
+
+/* Inline code */
+:not(pre)>code[class*="language-"] {
+  padding: .1em;
+  border-radius: .3em;
+  white-space: normal;
+}
+
+.token.comment,
+.token.prolog,
+.token.doctype,
+.token.cdata {
+  color: slategray;
+}
+
+.token.punctuation {
+  color: #999;
+}
+
+.namespace {
+  opacity: .7;
+}
+
+.token.property,
+.token.tag,
+.token.boolean,
+.token.number,
+.token.constant,
+.token.symbol,
+.token.deleted {
+  color: #905;
+}
+
+.token.selector,
+.token.attr-name,
+.token.string,
+.token.char,
+.token.builtin,
+.token.inserted {
+  color: #690;
+}
+
+.token.operator,
+.token.entity,
+.token.url,
+.language-css .token.string,
+.style .token.string {
+  color: #9a6e3a;
+  background: hsla(0, 0%, 100%, .5);
+}
+
+.token.atrule,
+.token.attr-value,
+.token.keyword {
+  color: #07a;
+}
+
+.token.function,
+.token.class-name {
+  color: #DD4A68;
+}
+
+.token.regex,
+.token.important,
+.token.variable {
+  color: #e90;
+}
+
+.token.important,
+.token.bold {
+  font-weight: bold;
+}
+
+.token.italic {
+  font-style: italic;
+}
+
+.token.entity {
+  cursor: help;
+}

+ 45 - 0
src/assets/diy/font/iconfont.css

@@ -0,0 +1,45 @@
+@font-face {font-family: "iconfont";
+  src: url('iconfont.eot?t=1603333133607'); /* IE9 */
+  src: url('iconfont.eot?t=1603333133607#iefix') format('embedded-opentype'), /* IE6-IE8 */
+  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAVEAAsAAAAACpgAAAT3AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDdAqHOIYBATYCJAMgCxIABCAFhG0HchsFCREVpD2RfXVgHnrE/kgrnt1zwrJnMFmypO88Bz381eb9u/ukqBbXAzkpjNJIbsQI8k2sfp5OfXc/UmWlgFNAQ2olo5R2czpIx0Pv7d92b6qT+SCQZBZIwEnEkSeUhQJuprugUYbFOU9kRNTlSE4ePfquS2mcpvsfAAfHTJcJlmezgUwxO3A0nn0APVhEYYXgP0gByrO/gng2E/4JuhgCZXPmsMEPiwMaR9tKQEdRQTbQYlauhCnJEHIOTNANZKn04pcAeLmfrw9sowGGr6Bd6pAfkAvez9kvpEhICCENtUN7f26AWxMsyABO/7Jc7xWQn80gZaWc5ARAU/+NCAX7Oe+56nOn5w3PP7yQEkRqHFq1GkEWCOI6/+F5loEaaNoqsOdcRME2+QgoeNI8nSrMCDonmHHQNcCMh+4D+LD4SrswAJLd0QSMgWBboFFynS04saQvTBKLhFOyVG14NFoAnd6EMeHIgMqOmFAJv7YaDLyZjbO82IgVz4RyZ2KMyjhyE85Azps0MTBmGAMqJ0e0oXUjk9u/XrhOMDWVgMQbpVUAfyXx09OJqJV/dwX81q2ShYprDEUiTKSE7ZYYMfuNJoUTiqv0J5TT5imsNpichEn5PY6MPn2RkkjMXGu0Vi2HzOyXgPBWEEicjRgZwLymsVZGEs6esGAqIWHDEiPxbhNJ/KSCyUIls+lEJbG+ar/CpFB+Ql7+1q24zdmiWMf8Q2sU80WKqml1XK5qIloqalUSi5UKjFvJpuZ6FuS551NaKAUeufleeVRHqfDr2FfrkzYTNnusXp4c/zb+a4kfFexGcsRLjUD0MATZJwmEwH43LkXMFiCBduKupBzJLTda6TH2dYzb0+nB9reyumanYcex4upp6BkUdKJt21DHACdZyh3A83t7CywVzorwWgNcO009zgDq2LYNiDXRV9KFWT0UhOi4N77NRfcWlbsga/nst/qF7tGXZy/PaqTSaHIyDToS528jN1FWzGLpASf+SvMaMiO9+ZlP8XBqWm64YKCpon31h5SgKH/P0hGBP7MhPC7Qq2MnXGouqyguunFjXTvnvUrv6TOnT+YnU25sTjp4+MjhywnZJ9XiC9XVKHnnS0pvSkpKZv7v2r13V++7Qd7P3/BwT9Ljg+J/p8+cP7Mg3uf4aG6+uqri/bz+vp46mMnkT4xfnJl58ODy6CgfdL8sPqu6HBOlzn9xYpwPXgD4X0d8pAcA5sF/Udf5cYL4S8x2ZNQO8F+GpCiL6LAnc6VL5p2RTK77t8QkAcBnL5uNkXZ20nPxCZ+nmYSVIP4qVYVmSylPMMU1FaEV9eXdgAyfNqCsz8BHGWx72Ke+V7seJOmFYEQzsJIF0mEz8CpyCJI1lK0EHl7RUQ2Ikg6w1ElAmraCUfcGq+ky0mEfgNf3DoJmCMqOmNwJK1zH7O5MKBhLKP5gZdcqw8E7r/GBOtSCogqX9UHyNgFplCQLd2yR+tjFjzpjVqCoa+BGLod13UFPnUPJkWHu8zhWeQ8Uya6Z7JwJBWMJij+wsmuVPZ93qY9/oA61oJKJiuAHyduVg1QkqYG8a9taE+eyuh91hlnRmynqGnCDG2s924E+fz2HkiOmRbrPxdQ0VVcbja9vrvQyoEy7gxSj2Wp3uj29Nou+aBuR7n9WmCBsRpsu90C209PaG9FKEw6uWbvsMHdhVywPLJMJAA==') format('woff2'),
+  url('iconfont.woff?t=1603333133607') format('woff'),
+  url('iconfont.ttf?t=1603333133607') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
+  url('iconfont.svg?t=1603333133607#iconfont') format('svg'); /* iOS 4.1- */
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-erweima1:before {
+  content: "\e61a";
+}
+
+.icon-xiahuaxian1:before {
+  content: "\e7e0";
+}
+
+.icon-xieti:before {
+  content: "\e6ef";
+}
+
+.icon-B:before {
+  content: "\e637";
+}
+
+.icon-shanchu:before {
+  content: "\e677";
+}
+
+.icon-shangjiantou:before {
+  content: "\e60d";
+}
+
+.icon-xiajiantou:before {
+  content: "\e610";
+}
+

BIN
src/assets/diy/font/iconfont.eot


+ 47 - 0
src/assets/diy/font/iconfont.svg

@@ -0,0 +1,47 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<!--
+2013-9-30: Created.
+-->
+<svg>
+<metadata>
+Created by iconfont
+</metadata>
+<defs>
+
+<font id="iconfont" horiz-adv-x="1024" >
+  <font-face
+    font-family="iconfont"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1024"
+    ascent="896"
+    descent="-128"
+  />
+    <missing-glyph />
+    
+    <glyph glyph-name="erweima1" unicode="&#58906;" d="M877.714286-128 877.714286 115.809524 1024 115.809524 1024-128 877.714286-128ZM975.433143 67.730286 926.232381 67.730286 926.232381-79.872 975.433143-79.872 975.433143 67.730286ZM536.380952-128 536.380952 359.619048 730.989714 359.619048 730.989714 213.089524 975.676952 213.089524 975.676952 359.619048 1024 359.619048 1024 165.10781 682.812952 165.10781 682.812952 310.125714 584.94781 310.125714 584.94781-127.268571M536.380952 896 1024 896 1024 408.380952 536.380952 408.380952 536.380952 896ZM584.46019 456.46019 975.920762 456.46019 975.920762 847.969524 584.46019 847.969524 584.46019 456.46019ZM682.666667 749.714286 877.714286 749.714286 877.714286 554.666667 682.666667 554.666667 682.666667 749.714286ZM730.989714 602.989714 829.391238 602.989714 829.391238 701.391238 730.989714 701.391238 730.989714 602.989714ZM0 359.619048 487.619048 359.619048 487.619048-128 0-128 0 359.619048ZM48.030476-79.920762 439.53981-79.920762 439.53981 311.588571 48.030476 311.588571 48.030476-79.920762ZM146.285714 213.333333 341.333333 213.333333 341.333333 18.285714 146.285714 18.285714 146.285714 213.333333ZM194.608762 66.608762 293.010286 66.608762 293.010286 165.010286 194.608762 165.010286 194.608762 66.608762ZM0 896 487.619048 896 487.619048 408.380952 0 408.380952 0 896ZM48.030476 456.46019 439.53981 456.46019 439.53981 847.969524 48.030476 847.969524 48.030476 456.46019ZM146.285714 749.714286 341.333333 749.714286 341.333333 554.666667 146.285714 554.666667 146.285714 749.714286ZM194.608762 602.989714 293.010286 602.989714 293.010286 701.391238 194.608762 701.391238 194.608762 602.989714ZM828.952381-128 682.666667-128 682.666667 115.809524 828.952381 115.809524 828.952381-128ZM780.385524 67.730286 731.184762 67.730286 731.184762-79.872 780.385524-79.872 780.385524 67.730286Z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="xiahuaxian1" unicode="&#59360;" d="M802.909091-46.545455h-558.545455a34.909091 34.909091 0 0 0 0 69.818182h558.545455a34.909091 34.909091 0 0 0 0-69.818182z m-279.272727 104.727273C350.394182 58.181818 209.454545 199.121455 209.454545 372.363636v395.636364a34.909091 34.909091 0 0 0 69.818182 0v-395.636364C279.272727 237.614545 388.887273 128 523.636364 128S768 237.614545 768 372.363636v395.636364a34.909091 34.909091 0 0 0 69.818182 0v-395.636364C837.818182 199.121455 696.878545 58.181818 523.636364 58.181818z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="xieti" unicode="&#59119;" d="M512 678.89573a33.048659 33.048659 0 0 1-7.118173 33.048659 92.027805 92.027805 0 0 1-47.285005 15.253228 17.286991 17.286991 0 0 0-15.761668 16.270109 26.438928 26.438928 0 0 0 0 7.118173v9.660377q33.5571-3.559086 66.097319-5.084409t60.504468 0h56.945383q33.5571 0 63.555114 5.084409a94.061569 94.061569 0 0 0 0-16.77855 14.744786 14.744786 0 0 0-15.761668-16.77855 107.789474 107.789474 0 0 1-50.844092-15.761669 53.894737 53.894737 0 0 1-18.812314-33.5571L498.780536 104.357498a31.014896 31.014896 0 0 1 7.626614-32.540219 112.365442 112.365442 0 0 1 50.844091-15.761668 16.270109 16.270109 0 0 0 14.744787-16.77855 96.095333 96.095333 0 0 0 0-17.795432q-33.5571 3.559086-68.131083 5.084409H384.889772q-33.048659 0-63.046674-5.084409a96.095333 96.095333 0 0 0 0 17.795432 14.236346 14.236346 0 0 0 13.219464 15.761668 131.686197 131.686197 0 0 1 50.844091 16.270109 50.844091 50.844091 0 0 1 20.337637 33.04866z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="B" unicode="&#58935;" d="M270.528 1.536V766.464h224.704c67.136 0 120.768-16.064 160.896-48.128 40.256-32.064 60.224-74.624 60.224-127.616 0-42.176-11.904-79.36-35.776-111.616-23.872-32.256-56.768-55.168-98.496-68.672v-1.984c52.032-5.888 93.504-25.6 124.608-59.008s46.72-75.84 46.72-127.104c0-65.856-23.616-119.104-70.848-159.744-47.232-40.704-107.776-60.992-181.504-60.992H270.528zM369.28 680v-239.488h89.856c47.744 0 85.184 11.648 112.32 34.816 27.2 23.232 40.768 55.424 40.768 96.576 0 72.064-48.064 108.16-144.192 108.16H369.28z m0-325.376v-266.624h118.528c51.648 0 91.584 11.968 119.68 35.776 28.096 23.872 42.24 57.088 42.24 99.52 0 87.552-59.392 131.392-178.24 131.392H369.28z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="shanchu" unicode="&#58999;" d="M460.8 379.345c-146.618 148.945-288.582 288.582-428.218 428.218-34.909 34.909-41.891 55.855-18.618 76.8 20.945 18.618 39.564 13.964 74.473-20.945 132.655-132.655 262.982-265.309 395.636-395.636 9.309-9.309 16.291-20.945 30.255-39.564 18.618 25.6 32.582 41.891 46.545 58.182 125.673 125.673 251.345 253.673 379.345 379.345 9.309 9.309 16.291 20.945 25.6 23.273 16.291 4.655 39.564 6.982 46.545-2.327 9.309-11.636 6.982-32.582 4.655-48.873 0-9.309-13.964-16.291-20.945-23.273-144.291-144.291-283.927-286.255-428.218-430.545 20.945-23.273 41.891-44.218 62.836-65.164 121.018-121.018 242.036-242.036 365.382-363.055l9.309-9.309c18.618-18.618 30.255-39.564 6.982-60.509-23.273-23.273-44.218-11.636-65.164 6.982l-400.291 400.291c-11.636 11.636-18.618 23.273-30.255 37.236-16.291-16.291-27.927-25.6-37.236-34.909-132.655-132.655-265.309-265.309-400.291-397.964-6.982-6.982-13.964-16.291-20.945-18.618-16.291-2.327-37.236-2.327-46.545 4.655-9.309 9.309-6.982 32.582-4.655 46.545 2.327 9.309 13.964 18.618 23.273 25.6l395.636 395.636c11.636 11.636 23.273 18.618 34.909 27.927z"  horiz-adv-x="1025" />
+
+    
+    <glyph glyph-name="shangjiantou" unicode="&#58893;" d="M481.856-96V741.568L76.608 337.856l-44.48 44.608L513.664 864l478.144-478.144-44.544-46.08-401.792 401.792V-96h-63.616z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="xiajiantou" unicode="&#58896;" d="M545.472 864v-837.504L947.2 428.288l44.544-46.144-478.08-478.144L32.128 385.6l44.48 44.544 405.248-403.712V864h63.616z"  horiz-adv-x="1024" />
+
+    
+
+
+  </font>
+</defs></svg>

BIN
src/assets/diy/font/iconfont.ttf


BIN
src/assets/diy/font/iconfont.woff


BIN
src/assets/diy/font/iconfont.woff2


BIN
src/assets/diy/goods.png


BIN
src/assets/diy/gou.png


BIN
src/assets/diy/imageSingle.png


BIN
src/assets/diy/img.jpg


BIN
src/assets/diy/img400x200.jpg


BIN
src/assets/diy/membergoods.png


BIN
src/assets/diy/miaosha.png


BIN
src/assets/diy/navBar.png


BIN
src/assets/diy/nodata.png


BIN
src/assets/diy/notice.png


BIN
src/assets/diy/notice_default.png


BIN
src/assets/diy/search.png


BIN
src/assets/diy/technical.png


BIN
src/assets/diy/tuan.png


BIN
src/assets/diy/tuwen.png


BIN
src/assets/diy/tuwen_style1.png


BIN
src/assets/diy/tuwen_style2.png


BIN
src/assets/diy/video.png


BIN
src/assets/diy/window.png


BIN
src/assets/diy/window_01.jpg


BIN
src/assets/diy/window_02.jpg


BIN
src/assets/diy/window_02_1.jpg


BIN
src/assets/diy/window_03.jpg


BIN
src/assets/diy/window_03_1.jpg


BIN
src/assets/diy/window_04.jpg


BIN
src/assets/diy/window_style1.png


BIN
src/assets/diy/window_style2.png


BIN
src/assets/diy/window_style3.png


BIN
src/assets/diy/xcx.png


BIN
src/assets/new_order.mp3


BIN
src/assets/no_admintouxiang.png


BIN
src/assets/no_touxiang.png


BIN
src/assets/qqEmoji.png


+ 111 - 0
src/components/BackToTop/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <transition :name="transitionName">
+    <div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
+      <svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height:16px;width:16px"><path d="M12.036 15.59a1 1 0 0 1-.997.995H5.032a.996.996 0 0 1-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29a1.003 1.003 0 0 1 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" /></svg>
+    </div>
+  </transition>
+</template>
+
+<script>
+export default {
+  name: 'BackToTop',
+  props: {
+    visibilityHeight: {
+      type: Number,
+      default: 400
+    },
+    backPosition: {
+      type: Number,
+      default: 0
+    },
+    customStyle: {
+      type: Object,
+      default: function() {
+        return {
+          right: '50px',
+          bottom: '50px',
+          width: '40px',
+          height: '40px',
+          'border-radius': '4px',
+          'line-height': '45px',
+          background: '#e7eaf1'
+        }
+      }
+    },
+    transitionName: {
+      type: String,
+      default: 'fade'
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      interval: null,
+      isMoving: false
+    }
+  },
+  mounted() {
+    window.addEventListener('scroll', this.handleScroll)
+  },
+  beforeDestroy() {
+    window.removeEventListener('scroll', this.handleScroll)
+    if (this.interval) {
+      clearInterval(this.interval)
+    }
+  },
+  methods: {
+    handleScroll() {
+      this.visible = window.pageYOffset > this.visibilityHeight
+    },
+    backToTop() {
+      if (this.isMoving) return
+      const start = window.pageYOffset
+      let i = 0
+      this.isMoving = true
+      this.interval = setInterval(() => {
+        const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
+        if (next <= this.backPosition) {
+          window.scrollTo(0, this.backPosition)
+          clearInterval(this.interval)
+          this.isMoving = false
+        } else {
+          window.scrollTo(0, next)
+        }
+        i++
+      }, 16.7)
+    },
+    easeInOutQuad(t, b, c, d) {
+      if ((t /= d / 2) < 1) return c / 2 * t * t + b
+      return -c / 2 * (--t * (t - 2) - 1) + b
+    }
+  }
+}
+</script>
+
+<style scoped>
+.back-to-ceiling {
+  position: fixed;
+  display: inline-block;
+  text-align: center;
+  cursor: pointer;
+}
+
+.back-to-ceiling:hover {
+  background: #d5dbe7;
+}
+
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity .5s;
+}
+
+.fade-enter,
+.fade-leave-to {
+  opacity: 0
+}
+
+.back-to-ceiling .Icon {
+  fill: #9aaabf;
+  background: none;
+}
+</style>

+ 82 - 0
src/components/Breadcrumb/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
+        <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
+        <a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+import pathToRegexp from 'path-to-regexp'
+
+export default {
+  data() {
+    return {
+      levelList: null
+    }
+  },
+  watch: {
+    $route(route) {
+      // if you go to the redirect page, do not update the breadcrumbs
+      if (route.path.startsWith('/redirect/')) {
+        return
+      }
+      this.getBreadcrumb()
+    }
+  },
+  created() {
+    this.getBreadcrumb()
+  },
+  methods: {
+    getBreadcrumb() {
+      // only show routes with meta.title
+      let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
+      const first = matched[0]
+
+      if (!this.isDashboard(first)) {
+        matched = [{ path: '/dashboard', meta: { title: '首页' }}].concat(matched)
+      }
+
+      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
+    },
+    isDashboard(route) {
+      const name = route && route.name
+      if (!name) {
+        return false
+      }
+      return name.trim().toLocaleLowerCase() === '首页'.toLocaleLowerCase()
+    },
+    pathCompile(path) {
+      // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
+      const { params } = this.$route
+      var toPath = pathToRegexp.compile(path)
+      return toPath(params)
+    },
+    handleLink(item) {
+      const { redirect, path } = item
+      if (redirect) {
+        this.$router.push(redirect)
+        return
+      }
+      this.$router.push(this.pathCompile(path))
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  font-size: 14px;
+  line-height: 50px;
+  margin-left: 8px;
+
+  .no-redirect {
+    color: #97a8be;
+    cursor: text;
+  }
+}
+</style>

+ 155 - 0
src/components/Charts/Keyboard.vue

@@ -0,0 +1,155 @@
+<template>
+  <div :id="id" :class="className" :style="{height:height,width:width}" />
+</template>
+
+<script>
+import echarts from 'echarts'
+import resize from './mixins/resize'
+
+export default {
+  mixins: [resize],
+  props: {
+    className: {
+      type: String,
+      default: 'chart'
+    },
+    id: {
+      type: String,
+      default: 'chart'
+    },
+    width: {
+      type: String,
+      default: '200px'
+    },
+    height: {
+      type: String,
+      default: '200px'
+    }
+  },
+  data() {
+    return {
+      chart: null
+    }
+  },
+  mounted() {
+    this.initChart()
+  },
+  beforeDestroy() {
+    if (!this.chart) {
+      return
+    }
+    this.chart.dispose()
+    this.chart = null
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(document.getElementById(this.id))
+
+      const xAxisData = []
+      const data = []
+      const data2 = []
+      for (let i = 0; i < 50; i++) {
+        xAxisData.push(i)
+        data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)
+        data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)
+      }
+      this.chart.setOption({
+        backgroundColor: '#08263a',
+        grid: {
+          left: '5%',
+          right: '5%'
+        },
+        xAxis: [{
+          show: false,
+          data: xAxisData
+        }, {
+          show: false,
+          data: xAxisData
+        }],
+        visualMap: {
+          show: false,
+          min: 0,
+          max: 50,
+          dimension: 0,
+          inRange: {
+            color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
+          }
+        },
+        yAxis: {
+          axisLine: {
+            show: false
+          },
+          axisLabel: {
+            textStyle: {
+              color: '#4a657a'
+            }
+          },
+          splitLine: {
+            show: true,
+            lineStyle: {
+              color: '#08263f'
+            }
+          },
+          axisTick: {
+            show: false
+          }
+        },
+        series: [{
+          name: 'back',
+          type: 'bar',
+          data: data2,
+          z: 1,
+          itemStyle: {
+            normal: {
+              opacity: 0.4,
+              barBorderRadius: 5,
+              shadowBlur: 3,
+              shadowColor: '#111'
+            }
+          }
+        }, {
+          name: 'Simulate Shadow',
+          type: 'line',
+          data,
+          z: 2,
+          showSymbol: false,
+          animationDelay: 0,
+          animationEasing: 'linear',
+          animationDuration: 1200,
+          lineStyle: {
+            normal: {
+              color: 'transparent'
+            }
+          },
+          areaStyle: {
+            normal: {
+              color: '#08263a',
+              shadowBlur: 50,
+              shadowColor: '#000'
+            }
+          }
+        }, {
+          name: 'front',
+          type: 'bar',
+          data,
+          xAxisIndex: 1,
+          z: 3,
+          itemStyle: {
+            normal: {
+              barBorderRadius: 5
+            }
+          }
+        }],
+        animationEasing: 'elasticOut',
+        animationEasingUpdate: 'elasticOut',
+        animationDelay(idx) {
+          return idx * 20
+        },
+        animationDelayUpdate(idx) {
+          return idx * 20
+        }
+      })
+    }
+  }
+}
+</script>

+ 227 - 0
src/components/Charts/LineMarker.vue

@@ -0,0 +1,227 @@
+<template>
+  <div :id="id" :class="className" :style="{height:height,width:width}" />
+</template>
+
+<script>
+import echarts from 'echarts'
+import resize from './mixins/resize'
+
+export default {
+  mixins: [resize],
+  props: {
+    className: {
+      type: String,
+      default: 'chart'
+    },
+    id: {
+      type: String,
+      default: 'chart'
+    },
+    width: {
+      type: String,
+      default: '200px'
+    },
+    height: {
+      type: String,
+      default: '200px'
+    }
+  },
+  data() {
+    return {
+      chart: null
+    }
+  },
+  mounted() {
+    this.initChart()
+  },
+  beforeDestroy() {
+    if (!this.chart) {
+      return
+    }
+    this.chart.dispose()
+    this.chart = null
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(document.getElementById(this.id))
+
+      this.chart.setOption({
+        backgroundColor: '#394056',
+        title: {
+          top: 20,
+          text: 'Requests',
+          textStyle: {
+            fontWeight: 'normal',
+            fontSize: 16,
+            color: '#F1F1F3'
+          },
+          left: '1%'
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            lineStyle: {
+              color: '#57617B'
+            }
+          }
+        },
+        legend: {
+          top: 20,
+          icon: 'rect',
+          itemWidth: 14,
+          itemHeight: 5,
+          itemGap: 13,
+          data: ['CMCC', 'CTCC', 'CUCC'],
+          right: '4%',
+          textStyle: {
+            fontSize: 12,
+            color: '#F1F1F3'
+          }
+        },
+        grid: {
+          top: 100,
+          left: '2%',
+          right: '2%',
+          bottom: '2%',
+          containLabel: true
+        },
+        xAxis: [{
+          type: 'category',
+          boundaryGap: false,
+          axisLine: {
+            lineStyle: {
+              color: '#57617B'
+            }
+          },
+          data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
+        }],
+        yAxis: [{
+          type: 'value',
+          name: '(%)',
+          axisTick: {
+            show: false
+          },
+          axisLine: {
+            lineStyle: {
+              color: '#57617B'
+            }
+          },
+          axisLabel: {
+            margin: 10,
+            textStyle: {
+              fontSize: 14
+            }
+          },
+          splitLine: {
+            lineStyle: {
+              color: '#57617B'
+            }
+          }
+        }],
+        series: [{
+          name: 'CMCC',
+          type: 'line',
+          smooth: true,
+          symbol: 'circle',
+          symbolSize: 5,
+          showSymbol: false,
+          lineStyle: {
+            normal: {
+              width: 1
+            }
+          },
+          areaStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
+                offset: 0,
+                color: 'rgba(137, 189, 27, 0.3)'
+              }, {
+                offset: 0.8,
+                color: 'rgba(137, 189, 27, 0)'
+              }], false),
+              shadowColor: 'rgba(0, 0, 0, 0.1)',
+              shadowBlur: 10
+            }
+          },
+          itemStyle: {
+            normal: {
+              color: 'rgb(137,189,27)',
+              borderColor: 'rgba(137,189,2,0.27)',
+              borderWidth: 12
+
+            }
+          },
+          data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
+        }, {
+          name: 'CTCC',
+          type: 'line',
+          smooth: true,
+          symbol: 'circle',
+          symbolSize: 5,
+          showSymbol: false,
+          lineStyle: {
+            normal: {
+              width: 1
+            }
+          },
+          areaStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
+                offset: 0,
+                color: 'rgba(0, 136, 212, 0.3)'
+              }, {
+                offset: 0.8,
+                color: 'rgba(0, 136, 212, 0)'
+              }], false),
+              shadowColor: 'rgba(0, 0, 0, 0.1)',
+              shadowBlur: 10
+            }
+          },
+          itemStyle: {
+            normal: {
+              color: 'rgb(0,136,212)',
+              borderColor: 'rgba(0,136,212,0.2)',
+              borderWidth: 12
+
+            }
+          },
+          data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
+        }, {
+          name: 'CUCC',
+          type: 'line',
+          smooth: true,
+          symbol: 'circle',
+          symbolSize: 5,
+          showSymbol: false,
+          lineStyle: {
+            normal: {
+              width: 1
+            }
+          },
+          areaStyle: {
+            normal: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
+                offset: 0,
+                color: 'rgba(219, 50, 51, 0.3)'
+              }, {
+                offset: 0.8,
+                color: 'rgba(219, 50, 51, 0)'
+              }], false),
+              shadowColor: 'rgba(0, 0, 0, 0.1)',
+              shadowBlur: 10
+            }
+          },
+          itemStyle: {
+            normal: {
+              color: 'rgb(219,50,51)',
+              borderColor: 'rgba(219,50,51,0.2)',
+              borderWidth: 12
+            }
+          },
+          data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
+        }]
+      })
+    }
+  }
+}
+</script>

+ 271 - 0
src/components/Charts/MixChart.vue

@@ -0,0 +1,271 @@
+<template>
+  <div :id="id" :class="className" :style="{height:height,width:width}" />
+</template>
+
+<script>
+import echarts from 'echarts'
+import resize from './mixins/resize'
+
+export default {
+  mixins: [resize],
+  props: {
+    className: {
+      type: String,
+      default: 'chart'
+    },
+    id: {
+      type: String,
+      default: 'chart'
+    },
+    width: {
+      type: String,
+      default: '200px'
+    },
+    height: {
+      type: String,
+      default: '200px'
+    }
+  },
+  data() {
+    return {
+      chart: null
+    }
+  },
+  mounted() {
+    this.initChart()
+  },
+  beforeDestroy() {
+    if (!this.chart) {
+      return
+    }
+    this.chart.dispose()
+    this.chart = null
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(document.getElementById(this.id))
+      const xData = (function() {
+        const data = []
+        for (let i = 1; i < 13; i++) {
+          data.push(i + 'month')
+        }
+        return data
+      }())
+      this.chart.setOption({
+        backgroundColor: '#344b58',
+        title: {
+          text: 'statistics',
+          x: '20',
+          top: '20',
+          textStyle: {
+            color: '#fff',
+            fontSize: '22'
+          },
+          subtextStyle: {
+            color: '#90979c',
+            fontSize: '16'
+          }
+        },
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            textStyle: {
+              color: '#fff'
+            }
+          }
+        },
+        grid: {
+          left: '5%',
+          right: '5%',
+          borderWidth: 0,
+          top: 150,
+          bottom: 95,
+          textStyle: {
+            color: '#fff'
+          }
+        },
+        legend: {
+          x: '5%',
+          top: '10%',
+          textStyle: {
+            color: '#90979c'
+          },
+          data: ['female', 'male', 'average']
+        },
+        calculable: true,
+        xAxis: [{
+          type: 'category',
+          axisLine: {
+            lineStyle: {
+              color: '#90979c'
+            }
+          },
+          splitLine: {
+            show: false
+          },
+          axisTick: {
+            show: false
+          },
+          splitArea: {
+            show: false
+          },
+          axisLabel: {
+            interval: 0
+
+          },
+          data: xData
+        }],
+        yAxis: [{
+          type: 'value',
+          splitLine: {
+            show: false
+          },
+          axisLine: {
+            lineStyle: {
+              color: '#90979c'
+            }
+          },
+          axisTick: {
+            show: false
+          },
+          axisLabel: {
+            interval: 0
+          },
+          splitArea: {
+            show: false
+          }
+        }],
+        dataZoom: [{
+          show: true,
+          height: 30,
+          xAxisIndex: [
+            0
+          ],
+          bottom: 30,
+          start: 10,
+          end: 80,
+          handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
+          handleSize: '110%',
+          handleStyle: {
+            color: '#d3dee5'
+
+          },
+          textStyle: {
+            color: '#fff' },
+          borderColor: '#90979c'
+
+        }, {
+          type: 'inside',
+          show: true,
+          height: 15,
+          start: 1,
+          end: 35
+        }],
+        series: [{
+          name: 'female',
+          type: 'bar',
+          stack: 'total',
+          barMaxWidth: 35,
+          barGap: '10%',
+          itemStyle: {
+            normal: {
+              color: 'rgba(255,144,128,1)',
+              label: {
+                show: true,
+                textStyle: {
+                  color: '#fff'
+                },
+                position: 'insideTop',
+                formatter(p) {
+                  return p.value > 0 ? p.value : ''
+                }
+              }
+            }
+          },
+          data: [
+            709,
+            1917,
+            2455,
+            2610,
+            1719,
+            1433,
+            1544,
+            3285,
+            5208,
+            3372,
+            2484,
+            4078
+          ]
+        },
+
+        {
+          name: 'male',
+          type: 'bar',
+          stack: 'total',
+          itemStyle: {
+            normal: {
+              color: 'rgba(0,191,183,1)',
+              barBorderRadius: 0,
+              label: {
+                show: true,
+                position: 'top',
+                formatter(p) {
+                  return p.value > 0 ? p.value : ''
+                }
+              }
+            }
+          },
+          data: [
+            327,
+            1776,
+            507,
+            1200,
+            800,
+            482,
+            204,
+            1390,
+            1001,
+            951,
+            381,
+            220
+          ]
+        }, {
+          name: 'average',
+          type: 'line',
+          stack: 'total',
+          symbolSize: 10,
+          symbol: 'circle',
+          itemStyle: {
+            normal: {
+              color: 'rgba(252,230,48,1)',
+              barBorderRadius: 0,
+              label: {
+                show: true,
+                position: 'top',
+                formatter(p) {
+                  return p.value > 0 ? p.value : ''
+                }
+              }
+            }
+          },
+          data: [
+            1036,
+            3693,
+            2962,
+            3810,
+            2519,
+            1915,
+            1748,
+            4675,
+            6209,
+            4323,
+            2865,
+            4298
+          ]
+        }
+        ]
+      })
+    }
+  }
+}
+</script>

+ 56 - 0
src/components/Charts/mixins/resize.js

@@ -0,0 +1,56 @@
+import { debounce } from '@/utils'
+
+export default {
+  data() {
+    return {
+      $_sidebarElm: null,
+      $_resizeHandler: null
+    }
+  },
+  mounted() {
+    this.initListener()
+  },
+  activated() {
+    if (!this.$_resizeHandler) {
+      // avoid duplication init
+      this.initListener()
+    }
+
+    // when keep-alive chart activated, auto resize
+    this.resize()
+  },
+  beforeDestroy() {
+    this.destroyListener()
+  },
+  deactivated() {
+    this.destroyListener()
+  },
+  methods: {
+    // use $_ for mixins properties
+    // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
+    $_sidebarResizeHandler(e) {
+      if (e.propertyName === 'width') {
+        this.$_resizeHandler()
+      }
+    },
+    initListener() {
+      this.$_resizeHandler = debounce(() => {
+        this.resize()
+      }, 100)
+      window.addEventListener('resize', this.$_resizeHandler)
+
+      this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
+      this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    destroyListener() {
+      window.removeEventListener('resize', this.$_resizeHandler)
+      this.$_resizeHandler = null
+
+      this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
+    },
+    resize() {
+      const { chart } = this
+      chart && chart.resize()
+    }
+  }
+}

+ 166 - 0
src/components/DndList/index.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="dndList">
+    <div :style="{width:width1}" class="dndList-list">
+      <h3>{{ list1Title }}</h3>
+      <draggable :set-data="setData" :list="list1" group="article" class="dragArea">
+        <div v-for="element in list1" :key="element.id" class="list-complete-item">
+          <div class="list-complete-item-handle">
+            {{ element.id }}[{{ element.author }}] {{ element.title }}
+          </div>
+          <div style="position:absolute;right:0px;">
+            <span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
+              <i style="color:#ff4949" class="el-icon-delete" />
+            </span>
+          </div>
+        </div>
+      </draggable>
+    </div>
+    <div :style="{width:width2}" class="dndList-list">
+      <h3>{{ list2Title }}</h3>
+      <draggable :list="list2" group="article" class="dragArea">
+        <div v-for="element in list2" :key="element.id" class="list-complete-item">
+          <div class="list-complete-item-handle2" @click="pushEle(element)">
+            {{ element.id }} [{{ element.author }}] {{ element.title }}
+          </div>
+        </div>
+      </draggable>
+    </div>
+  </div>
+</template>
+
+<script>
+import draggable from 'vuedraggable'
+
+export default {
+  name: 'DndList',
+  components: { draggable },
+  props: {
+    list1: {
+      type: Array,
+      default() {
+        return []
+      }
+    },
+    list2: {
+      type: Array,
+      default() {
+        return []
+      }
+    },
+    list1Title: {
+      type: String,
+      default: 'list1'
+    },
+    list2Title: {
+      type: String,
+      default: 'list2'
+    },
+    width1: {
+      type: String,
+      default: '48%'
+    },
+    width2: {
+      type: String,
+      default: '48%'
+    }
+  },
+  methods: {
+    isNotInList1(v) {
+      return this.list1.every(k => v.id !== k.id)
+    },
+    isNotInList2(v) {
+      return this.list2.every(k => v.id !== k.id)
+    },
+    deleteEle(ele) {
+      for (const item of this.list1) {
+        if (item.id === ele.id) {
+          const index = this.list1.indexOf(item)
+          this.list1.splice(index, 1)
+          break
+        }
+      }
+      if (this.isNotInList2(ele)) {
+        this.list2.unshift(ele)
+      }
+    },
+    pushEle(ele) {
+      for (const item of this.list2) {
+        if (item.id === ele.id) {
+          const index = this.list2.indexOf(item)
+          this.list2.splice(index, 1)
+          break
+        }
+      }
+      if (this.isNotInList1(ele)) {
+        this.list1.push(ele)
+      }
+    },
+    setData(dataTransfer) {
+      // to avoid Firefox bug
+      // Detail see : https://github.com/RubaXa/Sortable/issues/1012
+      dataTransfer.setData('Text', '')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.dndList {
+  background: #fff;
+  padding-bottom: 40px;
+  &:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+  .dndList-list {
+    float: left;
+    padding-bottom: 30px;
+    &:first-of-type {
+      margin-right: 2%;
+    }
+    .dragArea {
+      margin-top: 15px;
+      min-height: 50px;
+      padding-bottom: 30px;
+    }
+  }
+}
+
+.list-complete-item {
+  cursor: pointer;
+  position: relative;
+  font-size: 14px;
+  padding: 5px 12px;
+  margin-top: 4px;
+  border: 1px solid #bfcbd9;
+  transition: all 1s;
+}
+
+.list-complete-item-handle {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  margin-right: 50px;
+}
+
+.list-complete-item-handle2 {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  margin-right: 20px;
+}
+
+.list-complete-item.sortable-chosen {
+  background: #4AB7BD;
+}
+
+.list-complete-item.sortable-ghost {
+  background: #30B08F;
+}
+
+.list-complete-enter,
+.list-complete-leave-active {
+  opacity: 0;
+}
+</style>

+ 65 - 0
src/components/DragSelect/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
+    <slot />
+  </el-select>
+</template>
+
+<script>
+import Sortable from 'sortablejs'
+
+export default {
+  name: 'DragSelect',
+  props: {
+    value: {
+      type: Array,
+      required: true
+    }
+  },
+  computed: {
+    selectVal: {
+      get() {
+        return [...this.value]
+      },
+      set(val) {
+        this.$emit('input', [...val])
+      }
+    }
+  },
+  mounted() {
+    this.setSort()
+  },
+  methods: {
+    setSort() {
+      const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
+      this.sortable = Sortable.create(el, {
+        ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
+        setData: function(dataTransfer) {
+          dataTransfer.setData('Text', '')
+          // to avoid Firefox bug
+          // Detail see : https://github.com/RubaXa/Sortable/issues/1012
+        },
+        onEnd: evt => {
+          const targetRow = this.value.splice(evt.oldIndex, 1)[0]
+          this.value.splice(evt.newIndex, 0, targetRow)
+        }
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.drag-select {
+  ::v-deep {
+    .sortable-ghost {
+      opacity: .8;
+      color: #fff !important;
+      background: #42b983 !important;
+    }
+
+    .el-tag {
+      cursor: pointer;
+    }
+  }
+}
+</style>

+ 297 - 0
src/components/Dropzone/index.vue

@@ -0,0 +1,297 @@
+<template>
+  <div :id="id" :ref="id" :action="url" class="dropzone">
+    <input type="file" name="file">
+  </div>
+</template>
+
+<script>
+import Dropzone from 'dropzone'
+import 'dropzone/dist/dropzone.css'
+// import { getToken } from 'api/qiniu';
+
+Dropzone.autoDiscover = false
+
+export default {
+  props: {
+    id: {
+      type: String,
+      required: true
+    },
+    url: {
+      type: String,
+      required: true
+    },
+    clickable: {
+      type: Boolean,
+      default: true
+    },
+    defaultMsg: {
+      type: String,
+      default: '上传图片'
+    },
+    acceptedFiles: {
+      type: String,
+      default: ''
+    },
+    thumbnailHeight: {
+      type: Number,
+      default: 200
+    },
+    thumbnailWidth: {
+      type: Number,
+      default: 200
+    },
+    showRemoveLink: {
+      type: Boolean,
+      default: true
+    },
+    maxFilesize: {
+      type: Number,
+      default: 2
+    },
+    maxFiles: {
+      type: Number,
+      default: 3
+    },
+    autoProcessQueue: {
+      type: Boolean,
+      default: true
+    },
+    useCustomDropzoneOptions: {
+      type: Boolean,
+      default: false
+    },
+    defaultImg: {
+      default: '',
+      type: [String, Array]
+    },
+    couldPaste: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {
+      dropzone: '',
+      initOnce: true
+    }
+  },
+  watch: {
+    defaultImg(val) {
+      if (val.length === 0) {
+        this.initOnce = false
+        return
+      }
+      if (!this.initOnce) return
+      this.initImages(val)
+      this.initOnce = false
+    }
+  },
+  mounted() {
+    const element = document.getElementById(this.id)
+    const vm = this
+    this.dropzone = new Dropzone(element, {
+      clickable: this.clickable,
+      thumbnailWidth: this.thumbnailWidth,
+      thumbnailHeight: this.thumbnailHeight,
+      maxFiles: this.maxFiles,
+      maxFilesize: this.maxFilesize,
+      dictRemoveFile: 'Remove',
+      addRemoveLinks: this.showRemoveLink,
+      acceptedFiles: this.acceptedFiles,
+      autoProcessQueue: this.autoProcessQueue,
+      dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
+      dictMaxFilesExceeded: '只能一个图',
+      previewTemplate: '<div class="dz-preview dz-file-preview">  <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div>  <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div>  <div class="dz-error-message"><span data-dz-errormessage></span></div>  <div class="dz-success-mark"> <i class="material-icons">done</i> </div>  <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
+      init() {
+        const val = vm.defaultImg
+        if (!val) return
+        if (Array.isArray(val)) {
+          if (val.length === 0) return
+          val.map((v, i) => {
+            const mockFile = { name: 'name' + i, size: 12345, url: v }
+            this.options.addedfile.call(this, mockFile)
+            this.options.thumbnail.call(this, mockFile, v)
+            mockFile.previewElement.classList.add('dz-success')
+            mockFile.previewElement.classList.add('dz-complete')
+            vm.initOnce = false
+            return true
+          })
+        } else {
+          const mockFile = { name: 'name', size: 12345, url: val }
+          this.options.addedfile.call(this, mockFile)
+          this.options.thumbnail.call(this, mockFile, val)
+          mockFile.previewElement.classList.add('dz-success')
+          mockFile.previewElement.classList.add('dz-complete')
+          vm.initOnce = false
+        }
+      },
+      accept: (file, done) => {
+        /* 七牛*/
+        // const token = this.$store.getters.token;
+        // getToken(token).then(response => {
+        //   file.token = response.data.qiniu_token;
+        //   file.key = response.data.qiniu_key;
+        //   file.url = response.data.qiniu_url;
+        //   done();
+        // })
+        done()
+      },
+      sending: (file, xhr, formData) => {
+        // formData.append('token', file.token);
+        // formData.append('key', file.key);
+        vm.initOnce = false
+      }
+    })
+
+    if (this.couldPaste) {
+      document.addEventListener('paste', this.pasteImg)
+    }
+
+    this.dropzone.on('success', file => {
+      vm.$emit('dropzone-success', file, vm.dropzone.element)
+    })
+    this.dropzone.on('addedfile', file => {
+      vm.$emit('dropzone-fileAdded', file)
+    })
+    this.dropzone.on('removedfile', file => {
+      vm.$emit('dropzone-removedFile', file)
+    })
+    this.dropzone.on('error', (file, error, xhr) => {
+      vm.$emit('dropzone-error', file, error, xhr)
+    })
+    this.dropzone.on('successmultiple', (file, error, xhr) => {
+      vm.$emit('dropzone-successmultiple', file, error, xhr)
+    })
+  },
+  destroyed() {
+    document.removeEventListener('paste', this.pasteImg)
+    this.dropzone.destroy()
+  },
+  methods: {
+    removeAllFiles() {
+      this.dropzone.removeAllFiles(true)
+    },
+    processQueue() {
+      this.dropzone.processQueue()
+    },
+    pasteImg(event) {
+      const items = (event.clipboardData || event.originalEvent.clipboardData).items
+      if (items[0].kind === 'file') {
+        this.dropzone.addFile(items[0].getAsFile())
+      }
+    },
+    initImages(val) {
+      if (!val) return
+      if (Array.isArray(val)) {
+        val.map((v, i) => {
+          const mockFile = { name: 'name' + i, size: 12345, url: v }
+          this.dropzone.options.addedfile.call(this.dropzone, mockFile)
+          this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v)
+          mockFile.previewElement.classList.add('dz-success')
+          mockFile.previewElement.classList.add('dz-complete')
+          return true
+        })
+      } else {
+        const mockFile = { name: 'name', size: 12345, url: val }
+        this.dropzone.options.addedfile.call(this.dropzone, mockFile)
+        this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val)
+        mockFile.previewElement.classList.add('dz-success')
+        mockFile.previewElement.classList.add('dz-complete')
+      }
+    }
+
+  }
+}
+</script>
+
+<style scoped>
+    .dropzone {
+        border: 2px solid #E5E5E5;
+        font-family: 'Roboto', sans-serif;
+        color: #777;
+        transition: background-color .2s linear;
+        padding: 5px;
+    }
+
+    .dropzone:hover {
+        background-color: #F6F6F6;
+    }
+
+    i {
+        color: #CCC;
+    }
+
+    .dropzone .dz-image img {
+        width: 100%;
+        height: 100%;
+    }
+
+    .dropzone input[name='file'] {
+        display: none;
+    }
+
+    .dropzone .dz-preview .dz-image {
+        border-radius: 0px;
+    }
+
+    .dropzone .dz-preview:hover .dz-image img {
+        transform: none;
+        filter: none;
+        width: 100%;
+        height: 100%;
+    }
+
+    .dropzone .dz-preview .dz-details {
+        bottom: 0px;
+        top: 0px;
+        color: white;
+        background-color: rgba(33, 150, 243, 0.8);
+        transition: opacity .2s linear;
+        text-align: left;
+    }
+
+    .dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
+        background-color: transparent;
+    }
+
+    .dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
+        border: none;
+    }
+
+    .dropzone .dz-preview .dz-details .dz-filename:hover span {
+        background-color: transparent;
+        border: none;
+    }
+
+    .dropzone .dz-preview .dz-remove {
+        position: absolute;
+        z-index: 30;
+        color: white;
+        margin-left: 15px;
+        padding: 10px;
+        top: inherit;
+        bottom: 15px;
+        border: 2px white solid;
+        text-decoration: none;
+        text-transform: uppercase;
+        font-size: 0.8rem;
+        font-weight: 800;
+        letter-spacing: 1.1px;
+        opacity: 0;
+    }
+
+    .dropzone .dz-preview:hover .dz-remove {
+        opacity: 1;
+    }
+
+    .dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
+        margin-left: -40px;
+        margin-top: -50px;
+    }
+
+    .dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i {
+        color: white;
+        font-size: 5rem;
+    }
+</style>

+ 78 - 0
src/components/ErrorLog/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <div v-if="errorLogs.length>0">
+    <el-badge :is-dot="true" style="line-height: 25px;margin-top: -5px;" @click.native="dialogTableVisible=true">
+      <el-button style="padding: 8px 10px;" size="small" type="danger">
+        <svg-icon icon-class="bug" />
+      </el-button>
+    </el-badge>
+
+    <el-dialog :visible.sync="dialogTableVisible" width="80%" append-to-body>
+      <div slot="title">
+        <span style="padding-right: 10px;">Error Log</span>
+        <el-button size="mini" type="primary" icon="el-icon-delete" @click="clearAll">Clear All</el-button>
+      </div>
+      <el-table :data="errorLogs" border>
+        <el-table-column label="Message">
+          <template slot-scope="{row}">
+            <div>
+              <span class="message-title">Msg:</span>
+              <el-tag type="danger">
+                {{ row.err.message }}
+              </el-tag>
+            </div>
+            <br>
+            <div>
+              <span class="message-title" style="padding-right: 10px;">Info: </span>
+              <el-tag type="warning">
+                {{ row.vm.$vnode.tag }} error in {{ row.info }}
+              </el-tag>
+            </div>
+            <br>
+            <div>
+              <span class="message-title" style="padding-right: 16px;">Url: </span>
+              <el-tag type="success">
+                {{ row.url }}
+              </el-tag>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="Stack">
+          <template slot-scope="scope">
+            {{ scope.row.err.stack }}
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ErrorLog',
+  data() {
+    return {
+      dialogTableVisible: false
+    }
+  },
+  computed: {
+    errorLogs() {
+      return this.$store.getters.errorLogs
+    }
+  },
+  methods: {
+    clearAll() {
+      this.dialogTableVisible = false
+      this.$store.dispatch('errorLog/clearErrorLog')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.message-title {
+  font-size: 16px;
+  color: #333;
+  font-weight: bold;
+  padding-right: 8px;
+}
+</style>

+ 44 - 0
src/components/Hamburger/index.vue

@@ -0,0 +1,44 @@
+<template>
+  <div style="padding: 0 15px;" @click="toggleClick">
+    <svg
+      :class="{'is-active':isActive}"
+      class="hamburger"
+      viewBox="0 0 1024 1024"
+      xmlns="http://www.w3.org/2000/svg"
+      width="64"
+      height="64"
+    >
+      <path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    toggleClick() {
+      this.$emit('toggleClick')
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+  display: inline-block;
+  vertical-align: middle;
+  width: 20px;
+  height: 20px;
+}
+
+.hamburger.is-active {
+  transform: rotate(180deg);
+}
+</style>

+ 180 - 0
src/components/HeaderSearch/index.vue

@@ -0,0 +1,180 @@
+<template>
+  <div :class="{'show':show}" class="header-search">
+    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
+    <el-select
+      ref="headerSearchSelect"
+      v-model="search"
+      :remote-method="querySearch"
+      filterable
+      default-first-option
+      remote
+      placeholder="Search"
+      class="header-search-select"
+      @change="change"
+    >
+      <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
+    </el-select>
+  </div>
+</template>
+
+<script>
+// fuse is a lightweight fuzzy-search module
+// make search results more in line with expectations
+import Fuse from 'fuse.js'
+import path from 'path'
+
+export default {
+  name: 'HeaderSearch',
+  data() {
+    return {
+      search: '',
+      options: [],
+      searchPool: [],
+      show: false,
+      fuse: undefined
+    }
+  },
+  computed: {
+    routes() {
+      return this.$store.getters.permission_routes
+    }
+  },
+  watch: {
+    routes() {
+      this.searchPool = this.generateRoutes(this.routes)
+    },
+    searchPool(list) {
+      this.initFuse(list)
+    },
+    show(value) {
+      if (value) {
+        document.body.addEventListener('click', this.close)
+      } else {
+        document.body.removeEventListener('click', this.close)
+      }
+    }
+  },
+  mounted() {
+    this.searchPool = this.generateRoutes(this.routes)
+  },
+  methods: {
+    click() {
+      this.show = !this.show
+      if (this.show) {
+        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
+      }
+    },
+    close() {
+      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
+      this.options = []
+      this.show = false
+    },
+    change(val) {
+      this.$router.push(val.path)
+      this.search = ''
+      this.options = []
+      this.$nextTick(() => {
+        this.show = false
+      })
+    },
+    initFuse(list) {
+      this.fuse = new Fuse(list, {
+        shouldSort: true,
+        threshold: 0.4,
+        location: 0,
+        distance: 100,
+        maxPatternLength: 32,
+        minMatchCharLength: 1,
+        keys: [{
+          name: 'title',
+          weight: 0.7
+        }, {
+          name: 'path',
+          weight: 0.3
+        }]
+      })
+    },
+    // Filter out the routes that can be displayed in the sidebar
+    // And generate the internationalized title
+    generateRoutes(routes, basePath = '/', prefixTitle = []) {
+      let res = []
+
+      for (const router of routes) {
+        // skip hidden router
+        if (router.hidden) { continue }
+
+        const data = {
+          path: path.resolve(basePath, router.path),
+          title: [...prefixTitle]
+        }
+
+        if (router.meta && router.meta.title) {
+          data.title = [...data.title, router.meta.title]
+
+          if (router.redirect !== 'noRedirect') {
+            // only push the routes with title
+            // special case: need to exclude parent router without redirect
+            res.push(data)
+          }
+        }
+
+        // recursive child routes
+        if (router.children) {
+          const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
+          if (tempRoutes.length >= 1) {
+            res = [...res, ...tempRoutes]
+          }
+        }
+      }
+      return res
+    },
+    querySearch(query) {
+      if (query !== '') {
+        this.options = this.fuse.search(query)
+      } else {
+        this.options = []
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.header-search {
+  font-size: 0 !important;
+
+  .search-icon {
+    cursor: pointer;
+    font-size: 18px;
+    vertical-align: middle;
+  }
+
+  .header-search-select {
+    font-size: 18px;
+    transition: width 0.2s;
+    width: 0;
+    overflow: hidden;
+    background: transparent;
+    border-radius: 0;
+    display: inline-block;
+    vertical-align: middle;
+
+    ::v-deep .el-input__inner {
+      border-radius: 0;
+      border: 0;
+      padding-left: 0;
+      padding-right: 0;
+      box-shadow: none !important;
+      border-bottom: 1px solid #d9d9d9;
+      vertical-align: middle;
+    }
+  }
+
+  &.show {
+    .header-search-select {
+      width: 210px;
+      margin-left: 10px;
+    }
+  }
+}
+</style>

+ 1779 - 0
src/components/ImageCropper/index.vue

@@ -0,0 +1,1779 @@
+<template>
+  <div v-show="value" class="vue-image-crop-upload">
+    <div class="vicp-wrap">
+      <div class="vicp-close" @click="off">
+        <i class="vicp-icon4" />
+      </div>
+
+      <div v-show="step == 1" class="vicp-step1">
+        <div
+          class="vicp-drop-area"
+          @dragleave="preventDefault"
+          @dragover="preventDefault"
+          @dragenter="preventDefault"
+          @click="handleClick"
+          @drop="handleChange"
+        >
+          <i v-show="loading != 1" class="vicp-icon1">
+            <i class="vicp-icon1-arrow" />
+            <i class="vicp-icon1-body" />
+            <i class="vicp-icon1-bottom" />
+          </i>
+          <span v-show="loading !== 1" class="vicp-hint">{{ lang.hint }}</span>
+          <span v-show="!isSupported" class="vicp-no-supported-hint">{{ lang.noSupported }}</span>
+          <input v-show="false" v-if="step == 1" ref="fileinput" type="file" @change="handleChange">
+        </div>
+        <div v-show="hasError" class="vicp-error">
+          <i class="vicp-icon2" />
+          {{ errorMsg }}
+        </div>
+        <div class="vicp-operate">
+          <a @click="off" @mousedown="ripple">{{ lang.btn.off }}</a>
+        </div>
+      </div>
+
+      <div v-if="step == 2" class="vicp-step2">
+        <div class="vicp-crop">
+          <div v-show="true" class="vicp-crop-left">
+            <div class="vicp-img-container">
+              <img
+                ref="img"
+                :src="sourceImgUrl"
+                :style="sourceImgStyle"
+                class="vicp-img"
+                draggable="false"
+                @drag="preventDefault"
+                @dragstart="preventDefault"
+                @dragend="preventDefault"
+                @dragleave="preventDefault"
+                @dragover="preventDefault"
+                @dragenter="preventDefault"
+                @drop="preventDefault"
+                @touchstart="imgStartMove"
+                @touchmove="imgMove"
+                @touchend="createImg"
+                @touchcancel="createImg"
+                @mousedown="imgStartMove"
+                @mousemove="imgMove"
+                @mouseup="createImg"
+                @mouseout="createImg"
+              >
+              <div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-1" />
+              <div :style="sourceImgShadeStyle" class="vicp-img-shade vicp-img-shade-2" />
+            </div>
+
+            <div class="vicp-range">
+              <input
+                :value="scale.range"
+                type="range"
+                step="1"
+                min="0"
+                max="100"
+                @input="zoomChange"
+              >
+              <i
+                class="vicp-icon5"
+                @mousedown="startZoomSub"
+                @mouseout="endZoomSub"
+                @mouseup="endZoomSub"
+              />
+              <i
+                class="vicp-icon6"
+                @mousedown="startZoomAdd"
+                @mouseout="endZoomAdd"
+                @mouseup="endZoomAdd"
+              />
+            </div>
+
+            <div v-if="!noRotate" class="vicp-rotate">
+              <i @mousedown="startRotateLeft" @mouseout="endRotate" @mouseup="endRotate">↺</i>
+              <i @mousedown="startRotateRight" @mouseout="endRotate" @mouseup="endRotate">↻</i>
+            </div>
+          </div>
+          <div v-show="true" class="vicp-crop-right">
+            <div class="vicp-preview">
+              <div v-if="!noSquare" class="vicp-preview-item">
+                <img :src="createImgUrl" :style="previewStyle">
+                <span>{{ lang.preview }}</span>
+              </div>
+              <div v-if="!noCircle" class="vicp-preview-item vicp-preview-item-circle">
+                <img :src="createImgUrl" :style="previewStyle">
+                <span>{{ lang.preview }}</span>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="vicp-operate">
+          <a @click="setStep(1)" @mousedown="ripple">{{ lang.btn.back }}</a>
+          <a class="vicp-operate-btn" @click="prepareUpload" @mousedown="ripple">{{ lang.btn.save }}</a>
+        </div>
+      </div>
+
+      <div v-if="step == 3" class="vicp-step3">
+        <div class="vicp-upload">
+          <span v-show="loading === 1" class="vicp-loading">{{ lang.loading }}</span>
+          <div class="vicp-progress-wrap">
+            <span v-show="loading === 1" :style="progressStyle" class="vicp-progress" />
+          </div>
+          <div v-show="hasError" class="vicp-error">
+            <i class="vicp-icon2" />
+            {{ errorMsg }}
+          </div>
+          <div v-show="loading === 2" class="vicp-success">
+            <i class="vicp-icon3" />
+            {{ lang.success }}
+          </div>
+        </div>
+        <div class="vicp-operate">
+          <a @click="setStep(2)" @mousedown="ripple">{{ lang.btn.back }}</a>
+          <a @click="off" @mousedown="ripple">{{ lang.btn.close }}</a>
+        </div>
+      </div>
+      <canvas v-show="false" ref="canvas" :width="width" :height="height" />
+    </div>
+  </div>
+</template>
+
+<script>
+'use strict'
+import request from '@/utils/request'
+import language from './utils/language.js'
+import mimes from './utils/mimes.js'
+import data2blob from './utils/data2blob.js'
+import effectRipple from './utils/effectRipple.js'
+export default {
+  props: {
+    // 域,上传文件name,触发事件会带上(如果一个页面多个图片上传控件,可以做区分
+    field: {
+      type: String,
+      default: 'avatar'
+    },
+    // 原名key,类似于id,触发事件会带上(如果一个页面多个图片上传控件,可以做区分
+    ki: {
+      type: Number,
+      default: 0
+    },
+    // 显示该控件与否
+    value: {
+      type: Boolean,
+      default: true
+    },
+    // 上传地址
+    url: {
+      type: String,
+      default: ''
+    },
+    // 其他要上传文件附带的数据,对象格式
+    params: {
+      type: Object,
+      default: null
+    },
+    // Add custom headers
+    headers: {
+      type: Object,
+      default: null
+    },
+    // 剪裁图片的宽
+    width: {
+      type: Number,
+      default: 200
+    },
+    // 剪裁图片的高
+    height: {
+      type: Number,
+      default: 200
+    },
+    // 不显示旋转功能
+    noRotate: {
+      type: Boolean,
+      default: true
+    },
+    // 不预览圆形图片
+    noCircle: {
+      type: Boolean,
+      default: false
+    },
+    // 不预览方形图片
+    noSquare: {
+      type: Boolean,
+      default: false
+    },
+    // 单文件大小限制
+    maxSize: {
+      type: Number,
+      default: 10240
+    },
+    // 语言类型
+    langType: {
+      type: String,
+      default: 'zh'
+    },
+    // 语言包
+    langExt: {
+      type: Object,
+      default: null
+    },
+    // 图片上传格式
+    imgFormat: {
+      type: String,
+      default: 'png'
+    },
+    // 是否支持跨域
+    withCredentials: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    const { imgFormat, langType, langExt, width, height } = this
+    let isSupported = true
+    const allowImgFormat = ['jpg', 'png']
+    const tempImgFormat =
+      allowImgFormat.indexOf(imgFormat) === -1 ? 'jpg' : imgFormat
+    const lang = language[langType] ? language[langType] : language['en']
+    const mime = mimes[tempImgFormat]
+    // 规范图片格式
+    this.imgFormat = tempImgFormat
+    if (langExt) {
+      Object.assign(lang, langExt)
+    }
+    if (typeof FormData !== 'function') {
+      isSupported = false
+    }
+    return {
+      // 图片的mime
+      mime,
+      // 语言包
+      lang,
+      // 浏览器是否支持该控件
+      isSupported,
+      // 浏览器是否支持触屏事件
+      // eslint-disable-next-line no-prototype-builtins
+      isSupportTouch: document.hasOwnProperty('ontouchstart'),
+      // 步骤
+      step: 1, // 1选择文件 2剪裁 3上传
+      // 上传状态及进度
+      loading: 0, // 0未开始 1正在 2成功 3错误
+      progress: 0,
+      // 是否有错误及错误信息
+      hasError: false,
+      errorMsg: '',
+      // 需求图宽高比
+      ratio: width / height,
+      // 原图地址、生成图片地址
+      sourceImg: null,
+      sourceImgUrl: '',
+      createImgUrl: '',
+      // 原图片拖动事件初始值
+      sourceImgMouseDown: {
+        on: false,
+        mX: 0, // 鼠标按下的坐标
+        mY: 0,
+        x: 0, // scale原图坐标
+        y: 0
+      },
+      // 生成图片预览的容器大小
+      previewContainer: {
+        width: 100,
+        height: 100
+      },
+      // 原图容器宽高
+      sourceImgContainer: {
+        // sic
+        width: 240,
+        height: 184 // 如果生成图比例与此一致会出现bug,先改成特殊的格式吧,哈哈哈
+      },
+      // 原图展示属性
+      scale: {
+        zoomAddOn: false, // 按钮缩放事件开启
+        zoomSubOn: false, // 按钮缩放事件开启
+        range: 1, // 最大100
+        rotateLeft: false, // 按钮向左旋转事件开启
+        rotateRight: false, // 按钮向右旋转事件开启
+        degree: 0, // 旋转度数
+        x: 0,
+        y: 0,
+        width: 0,
+        height: 0,
+        maxWidth: 0,
+        maxHeight: 0,
+        minWidth: 0, // 最宽
+        minHeight: 0,
+        naturalWidth: 0, // 原宽
+        naturalHeight: 0
+      }
+    }
+  },
+  computed: {
+    // 进度条样式
+    progressStyle() {
+      const { progress } = this
+      return {
+        width: progress + '%'
+      }
+    },
+    // 原图样式
+    sourceImgStyle() {
+      const { scale, sourceImgMasking } = this
+      const top = scale.y + sourceImgMasking.y + 'px'
+      const left = scale.x + sourceImgMasking.x + 'px'
+      return {
+        top,
+        left,
+        width: scale.width + 'px',
+        height: scale.height + 'px',
+        transform: 'rotate(' + scale.degree + 'deg)', // 旋转时 左侧原始图旋转样式
+        '-ms-transform': 'rotate(' + scale.degree + 'deg)', // 兼容IE9
+        '-moz-transform': 'rotate(' + scale.degree + 'deg)', // 兼容FireFox
+        '-webkit-transform': 'rotate(' + scale.degree + 'deg)', // 兼容Safari 和 chrome
+        '-o-transform': 'rotate(' + scale.degree + 'deg)' // 兼容 Opera
+      }
+    },
+    // 原图蒙版属性
+    sourceImgMasking() {
+      const { width, height, ratio, sourceImgContainer } = this
+      const sic = sourceImgContainer
+      const sicRatio = sic.width / sic.height // 原图容器宽高比
+      let x = 0
+      let y = 0
+      let w = sic.width
+      let h = sic.height
+      let scale = 1
+      if (ratio < sicRatio) {
+        scale = sic.height / height
+        w = sic.height * ratio
+        x = (sic.width - w) / 2
+      }
+      if (ratio > sicRatio) {
+        scale = sic.width / width
+        h = sic.width / ratio
+        y = (sic.height - h) / 2
+      }
+      return {
+        scale, // 蒙版相对需求宽高的缩放
+        x,
+        y,
+        width: w,
+        height: h
+      }
+    },
+    // 原图遮罩样式
+    sourceImgShadeStyle() {
+      const { sourceImgMasking, sourceImgContainer } = this
+      const sic = sourceImgContainer
+      const sim = sourceImgMasking
+      const w =
+        sim.width === sic.width ? sim.width : (sic.width - sim.width) / 2
+      const h =
+        sim.height === sic.height ? sim.height : (sic.height - sim.height) / 2
+      return {
+        width: w + 'px',
+        height: h + 'px'
+      }
+    },
+    previewStyle() {
+      const { ratio, previewContainer } = this
+      const pc = previewContainer
+      let w = pc.width
+      let h = pc.height
+      const pcRatio = w / h
+      if (ratio < pcRatio) {
+        w = pc.height * ratio
+      }
+      if (ratio > pcRatio) {
+        h = pc.width / ratio
+      }
+      return {
+        width: w + 'px',
+        height: h + 'px'
+      }
+    }
+  },
+  watch: {
+    value(newValue) {
+      if (newValue && this.loading !== 1) {
+        this.reset()
+      }
+    }
+  },
+  created() {
+    // 绑定按键esc隐藏此插件事件
+    document.addEventListener('keyup', this.closeHandler)
+  },
+  destroyed() {
+    document.removeEventListener('keyup', this.closeHandler)
+  },
+  methods: {
+    // 点击波纹效果
+    ripple(e) {
+      effectRipple(e)
+    },
+    // 关闭控件
+    off() {
+      setTimeout(() => {
+        this.$emit('input', false)
+        this.$emit('close')
+        if (this.step === 3 && this.loading === 2) {
+          this.setStep(1)
+        }
+      }, 200)
+    },
+    // 设置步骤
+    setStep(no) {
+      // 延时是为了显示动画效果呢,哈哈哈
+      setTimeout(() => {
+        this.step = no
+      }, 200)
+    },
+    /* 图片选择区域函数绑定
+     ---------------------------------------------------------------*/
+    preventDefault(e) {
+      e.preventDefault()
+      return false
+    },
+    handleClick(e) {
+      if (this.loading !== 1) {
+        if (e.target !== this.$refs.fileinput) {
+          e.preventDefault()
+          if (document.activeElement !== this.$refs) {
+            this.$refs.fileinput.click()
+          }
+        }
+      }
+    },
+    handleChange(e) {
+      e.preventDefault()
+      if (this.loading !== 1) {
+        const files = e.target.files || e.dataTransfer.files
+        this.reset()
+        if (this.checkFile(files[0])) {
+          this.setSourceImg(files[0])
+        }
+      }
+    },
+    /* ---------------------------------------------------------------*/
+    // 检测选择的文件是否合适
+    checkFile(file) {
+      const { lang, maxSize } = this
+      // 仅限图片
+      if (file.type.indexOf('image') === -1) {
+        this.hasError = true
+        this.errorMsg = lang.error.onlyImg
+        return false
+      }
+      // 超出大小
+      if (file.size / 1024 > maxSize) {
+        this.hasError = true
+        this.errorMsg = lang.error.outOfSize + maxSize + 'kb'
+        return false
+      }
+      return true
+    },
+    // 重置控件
+    reset() {
+      this.loading = 0
+      this.hasError = false
+      this.errorMsg = ''
+      this.progress = 0
+    },
+    // 设置图片源
+    setSourceImg(file) {
+      const fr = new FileReader()
+      fr.onload = e => {
+        this.sourceImgUrl = fr.result
+        this.startCrop()
+      }
+      fr.readAsDataURL(file)
+    },
+    // 剪裁前准备工作
+    startCrop() {
+      const {
+        width,
+        height,
+        ratio,
+        scale,
+        sourceImgUrl,
+        sourceImgMasking,
+        lang
+      } = this
+      const sim = sourceImgMasking
+      const img = new Image()
+      img.src = sourceImgUrl
+      img.onload = () => {
+        const nWidth = img.naturalWidth
+        const nHeight = img.naturalHeight
+        const nRatio = nWidth / nHeight
+        let w = sim.width
+        let h = sim.height
+        let x = 0
+        let y = 0
+        // 图片像素不达标
+        if (nWidth < width || nHeight < height) {
+          this.hasError = true
+          this.errorMsg = lang.error.lowestPx + width + '*' + height
+          return false
+        }
+        if (ratio > nRatio) {
+          h = w / nRatio
+          y = (sim.height - h) / 2
+        }
+        if (ratio < nRatio) {
+          w = h * nRatio
+          x = (sim.width - w) / 2
+        }
+        scale.range = 0
+        scale.x = x
+        scale.y = y
+        scale.width = w
+        scale.height = h
+        scale.degree = 0
+        scale.minWidth = w
+        scale.minHeight = h
+        scale.maxWidth = nWidth * sim.scale
+        scale.maxHeight = nHeight * sim.scale
+        scale.naturalWidth = nWidth
+        scale.naturalHeight = nHeight
+        this.sourceImg = img
+        this.createImg()
+        this.setStep(2)
+      }
+    },
+    // 鼠标按下图片准备移动
+    imgStartMove(e) {
+      e.preventDefault()
+      // 支持触摸事件,则鼠标事件无效
+      if (this.isSupportTouch && !e.targetTouches) {
+        return false
+      }
+      const et = e.targetTouches ? e.targetTouches[0] : e
+      const { sourceImgMouseDown, scale } = this
+      const simd = sourceImgMouseDown
+      simd.mX = et.screenX
+      simd.mY = et.screenY
+      simd.x = scale.x
+      simd.y = scale.y
+      simd.on = true
+    },
+    // 鼠标按下状态下移动,图片移动
+    imgMove(e) {
+      e.preventDefault()
+      // 支持触摸事件,则鼠标事件无效
+      if (this.isSupportTouch && !e.targetTouches) {
+        return false
+      }
+      const et = e.targetTouches ? e.targetTouches[0] : e
+      const {
+        sourceImgMouseDown: { on, mX, mY, x, y },
+        scale,
+        sourceImgMasking
+      } = this
+      const sim = sourceImgMasking
+      const nX = et.screenX
+      const nY = et.screenY
+      const dX = nX - mX
+      const dY = nY - mY
+      let rX = x + dX
+      let rY = y + dY
+      if (!on) return
+      if (rX > 0) {
+        rX = 0
+      }
+      if (rY > 0) {
+        rY = 0
+      }
+      if (rX < sim.width - scale.width) {
+        rX = sim.width - scale.width
+      }
+      if (rY < sim.height - scale.height) {
+        rY = sim.height - scale.height
+      }
+      scale.x = rX
+      scale.y = rY
+    },
+    // 按钮按下开始向右旋转
+    startRotateRight(e) {
+      const { scale } = this
+      scale.rotateRight = true
+      const rotate = () => {
+        if (scale.rotateRight) {
+          const degree = ++scale.degree
+          this.createImg(degree)
+          setTimeout(function() {
+            rotate()
+          }, 60)
+        }
+      }
+      rotate()
+    },
+    // 按钮按下开始向左旋转
+    startRotateLeft(e) {
+      const { scale } = this
+      scale.rotateLeft = true
+      const rotate = () => {
+        if (scale.rotateLeft) {
+          const degree = --scale.degree
+          this.createImg(degree)
+          setTimeout(function() {
+            rotate()
+          }, 60)
+        }
+      }
+      rotate()
+    },
+    // 停止旋转
+    endRotate() {
+      const { scale } = this
+      scale.rotateLeft = false
+      scale.rotateRight = false
+    },
+    // 按钮按下开始放大
+    startZoomAdd(e) {
+      const { scale } = this
+      scale.zoomAddOn = true
+      const zoom = () => {
+        if (scale.zoomAddOn) {
+          const range = scale.range >= 100 ? 100 : ++scale.range
+          this.zoomImg(range)
+          setTimeout(function() {
+            zoom()
+          }, 60)
+        }
+      }
+      zoom()
+    },
+    // 按钮松开或移开取消放大
+    endZoomAdd(e) {
+      this.scale.zoomAddOn = false
+    },
+    // 按钮按下开始缩小
+    startZoomSub(e) {
+      const { scale } = this
+      scale.zoomSubOn = true
+      const zoom = () => {
+        if (scale.zoomSubOn) {
+          const range = scale.range <= 0 ? 0 : --scale.range
+          this.zoomImg(range)
+          setTimeout(function() {
+            zoom()
+          }, 60)
+        }
+      }
+      zoom()
+    },
+    // 按钮松开或移开取消缩小
+    endZoomSub(e) {
+      const { scale } = this
+      scale.zoomSubOn = false
+    },
+    zoomChange(e) {
+      this.zoomImg(e.target.value)
+    },
+    // 缩放原图
+    zoomImg(newRange) {
+      const { sourceImgMasking, scale } = this
+      const {
+        maxWidth,
+        maxHeight,
+        minWidth,
+        minHeight,
+        width,
+        height,
+        x,
+        y
+      } = scale
+      const sim = sourceImgMasking
+      // 蒙版宽高
+      const sWidth = sim.width
+      const sHeight = sim.height
+      // 新宽高
+      const nWidth = minWidth + ((maxWidth - minWidth) * newRange) / 100
+      const nHeight = minHeight + ((maxHeight - minHeight) * newRange) / 100
+      // 新坐标(根据蒙版中心点缩放)
+      let nX = sWidth / 2 - (nWidth / width) * (sWidth / 2 - x)
+      let nY = sHeight / 2 - (nHeight / height) * (sHeight / 2 - y)
+      // 判断新坐标是否超过蒙版限制
+      if (nX > 0) {
+        nX = 0
+      }
+      if (nY > 0) {
+        nY = 0
+      }
+      if (nX < sWidth - nWidth) {
+        nX = sWidth - nWidth
+      }
+      if (nY < sHeight - nHeight) {
+        nY = sHeight - nHeight
+      }
+      // 赋值处理
+      scale.x = nX
+      scale.y = nY
+      scale.width = nWidth
+      scale.height = nHeight
+      scale.range = newRange
+      setTimeout(() => {
+        if (scale.range === newRange) {
+          this.createImg()
+        }
+      }, 300)
+    },
+    // 生成需求图片
+    createImg(e) {
+      const {
+        mime,
+        sourceImg,
+        scale: { x, y, width, height, degree },
+        sourceImgMasking: { scale }
+      } = this
+      const canvas = this.$refs.canvas
+      const ctx = canvas.getContext('2d')
+      if (e) {
+        // 取消鼠标按下移动状态
+        this.sourceImgMouseDown.on = false
+      }
+      canvas.width = this.width
+      canvas.height = this.height
+      ctx.clearRect(0, 0, this.width, this.height)
+      // 将透明区域设置为白色底边
+      ctx.fillStyle = '#fff'
+      ctx.fillRect(0, 0, this.width, this.height)
+      ctx.translate(this.width * 0.5, this.height * 0.5)
+      ctx.rotate((Math.PI * degree) / 180)
+      ctx.translate(-this.width * 0.5, -this.height * 0.5)
+      ctx.drawImage(
+        sourceImg,
+        x / scale,
+        y / scale,
+        width / scale,
+        height / scale
+      )
+      this.createImgUrl = canvas.toDataURL(mime)
+    },
+    prepareUpload() {
+      const { url, createImgUrl, field, ki } = this
+      this.$emit('crop-success', createImgUrl, field, ki)
+      if (typeof url === 'string' && url) {
+        this.upload()
+      } else {
+        this.off()
+      }
+    },
+    // 上传图片
+    upload() {
+      const {
+        lang,
+        imgFormat,
+        mime,
+        url,
+        params,
+        field,
+        ki,
+        createImgUrl
+      } = this
+      const fmData = new FormData()
+      fmData.append(
+        field,
+        data2blob(createImgUrl, mime),
+        field + '.' + imgFormat
+      )
+      // 添加其他参数
+      if (typeof params === 'object' && params) {
+        Object.keys(params).forEach(k => {
+          fmData.append(k, params[k])
+        })
+      }
+      // 监听进度回调
+      // const uploadProgress = (event) => {
+      //   if (event.lengthComputable) {
+      //     this.progress = 100 * Math.round(event.loaded) / event.total
+      //   }
+      // }
+      // 上传文件
+      this.reset()
+      this.loading = 1
+      this.setStep(3)
+      request({
+        url,
+        method: 'post',
+        data: fmData
+      })
+        .then(resData => {
+          this.loading = 2
+          this.$emit('crop-upload-success', resData.data)
+        })
+        .catch(err => {
+          if (this.value) {
+            this.loading = 3
+            this.hasError = true
+            this.errorMsg = lang.fail
+            this.$emit('crop-upload-fail', err, field, ki)
+          }
+        })
+    },
+    closeHandler(e) {
+      if (this.value && (e.key === 'Escape' || e.keyCode === 27)) {
+        this.off()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+@charset "UTF-8";
+@-webkit-keyframes vicp_progress {
+  0% {
+    background-position-y: 0;
+  }
+  100% {
+    background-position-y: 40px;
+  }
+}
+@keyframes vicp_progress {
+  0% {
+    background-position-y: 0;
+  }
+  100% {
+    background-position-y: 40px;
+  }
+}
+@-webkit-keyframes vicp {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(0) translatey(-60px);
+    transform: scale(0) translatey(-60px);
+  }
+  100% {
+    opacity: 1;
+    -webkit-transform: scale(1) translatey(0);
+    transform: scale(1) translatey(0);
+  }
+}
+@keyframes vicp {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(0) translatey(-60px);
+    transform: scale(0) translatey(-60px);
+  }
+  100% {
+    opacity: 1;
+    -webkit-transform: scale(1) translatey(0);
+    transform: scale(1) translatey(0);
+  }
+}
+.vue-image-crop-upload {
+  position: fixed;
+  display: block;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  z-index: 10000;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.65);
+  -webkit-tap-highlight-color: transparent;
+  -moz-tap-highlight-color: transparent;
+}
+.vue-image-crop-upload .vicp-wrap {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  position: fixed;
+  display: block;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  z-index: 10000;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  width: 600px;
+  height: 330px;
+  padding: 25px;
+  background-color: #fff;
+  border-radius: 2px;
+  -webkit-animation: vicp 0.12s ease-in;
+  animation: vicp 0.12s ease-in;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-close {
+  position: absolute;
+  right: -30px;
+  top: -30px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 {
+  position: relative;
+  display: block;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+  -webkit-transition: -webkit-transform 0.18s;
+  transition: -webkit-transform 0.18s;
+  transition: transform 0.18s;
+  transition: transform 0.18s, -webkit-transform 0.18s;
+  -webkit-transform: rotate(0);
+  -ms-transform: rotate(0);
+  transform: rotate(0);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after,
+.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  content: "";
+  position: absolute;
+  top: 12px;
+  left: 4px;
+  width: 20px;
+  height: 3px;
+  -webkit-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  transform: rotate(45deg);
+  background-color: #fff;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after {
+  -webkit-transform: rotate(-45deg);
+  -ms-transform: rotate(-45deg);
+  transform: rotate(-45deg);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover {
+  -webkit-transform: rotate(90deg);
+  -ms-transform: rotate(90deg);
+  transform: rotate(90deg);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area {
+  position: relative;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  padding: 35px;
+  height: 170px;
+  background-color: rgba(0, 0, 0, 0.03);
+  text-align: center;
+  border: 1px dashed rgba(0, 0, 0, 0.08);
+  overflow: hidden;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 {
+  display: block;
+  margin: 0 auto 6px;
+  width: 42px;
+  height: 42px;
+  overflow: hidden;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step1
+  .vicp-drop-area
+  .vicp-icon1
+  .vicp-icon1-arrow {
+  display: block;
+  margin: 0 auto;
+  width: 0;
+  height: 0;
+  border-bottom: 14.7px solid rgba(0, 0, 0, 0.3);
+  border-left: 14.7px solid transparent;
+  border-right: 14.7px solid transparent;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step1
+  .vicp-drop-area
+  .vicp-icon1
+  .vicp-icon1-body {
+  display: block;
+  width: 12.6px;
+  height: 14.7px;
+  margin: 0 auto;
+  background-color: rgba(0, 0, 0, 0.3);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step1
+  .vicp-drop-area
+  .vicp-icon1
+  .vicp-icon1-bottom {
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  display: block;
+  height: 12.6px;
+  border: 6px solid rgba(0, 0, 0, 0.3);
+  border-top: none;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint {
+  display: block;
+  padding: 15px;
+  font-size: 14px;
+  color: #666;
+  line-height: 30px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step1
+  .vicp-drop-area
+  .vicp-no-supported-hint {
+  display: block;
+  position: absolute;
+  top: 0;
+  left: 0;
+  padding: 30px;
+  width: 100%;
+  height: 60px;
+  line-height: 30px;
+  background-color: #eee;
+  text-align: center;
+  color: #666;
+  font-size: 14px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover {
+  cursor: pointer;
+  border-color: rgba(0, 0, 0, 0.1);
+  background-color: rgba(0, 0, 0, 0.05);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop {
+  overflow: hidden;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left {
+  float: left;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-img-container {
+  position: relative;
+  display: block;
+  width: 240px;
+  height: 180px;
+  background-color: #e5e5e0;
+  overflow: hidden;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-img-container
+  .vicp-img {
+  position: absolute;
+  display: block;
+  cursor: move;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-img-container
+  .vicp-img-shade {
+  -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  position: absolute;
+  background-color: rgba(241, 242, 243, 0.8);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-img-container
+  .vicp-img-shade.vicp-img-shade-1 {
+  top: 0;
+  left: 0;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-img-container
+  .vicp-img-shade.vicp-img-shade-2 {
+  bottom: 0;
+  right: 0;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-rotate {
+  position: relative;
+  width: 240px;
+  height: 18px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-rotate
+  i {
+  display: block;
+  width: 18px;
+  height: 18px;
+  border-radius: 100%;
+  line-height: 18px;
+  text-align: center;
+  font-size: 12px;
+  font-weight: bold;
+  background-color: rgba(0, 0, 0, 0.08);
+  color: #fff;
+  overflow: hidden;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-rotate
+  i:hover {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  cursor: pointer;
+  background-color: rgba(0, 0, 0, 0.14);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-rotate
+  i:first-child {
+  float: left;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-rotate
+  i:last-child {
+  float: right;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range {
+  position: relative;
+  margin: 30px 0 10px 0;
+  width: 240px;
+  height: 18px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon5,
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon6 {
+  position: absolute;
+  top: 0;
+  width: 18px;
+  height: 18px;
+  border-radius: 100%;
+  background-color: rgba(0, 0, 0, 0.08);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon5:hover,
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon6:hover {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  cursor: pointer;
+  background-color: rgba(0, 0, 0, 0.14);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon5 {
+  left: 0;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon5::before {
+  position: absolute;
+  content: "";
+  display: block;
+  left: 3px;
+  top: 8px;
+  width: 12px;
+  height: 2px;
+  background-color: #fff;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon6 {
+  right: 0;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon6::before {
+  position: absolute;
+  content: "";
+  display: block;
+  left: 3px;
+  top: 8px;
+  width: 12px;
+  height: 2px;
+  background-color: #fff;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  .vicp-icon6::after {
+  position: absolute;
+  content: "";
+  display: block;
+  top: 3px;
+  left: 8px;
+  width: 2px;
+  height: 12px;
+  background-color: #fff;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"] {
+  display: block;
+  padding-top: 5px;
+  margin: 0 auto;
+  width: 180px;
+  height: 8px;
+  vertical-align: top;
+  background: transparent;
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  cursor: pointer;
+  /* 滑块
+               ---------------------------------------------------------------*/
+  /* 轨道
+               ---------------------------------------------------------------*/
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:focus {
+  outline: none;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-webkit-slider-thumb {
+  -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  -webkit-appearance: none;
+  appearance: none;
+  margin-top: -3px;
+  width: 12px;
+  height: 12px;
+  background-color: #61c091;
+  border-radius: 100%;
+  border: none;
+  -webkit-transition: 0.2s;
+  transition: 0.2s;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-moz-range-thumb {
+  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  -moz-appearance: none;
+  appearance: none;
+  width: 12px;
+  height: 12px;
+  background-color: #61c091;
+  border-radius: 100%;
+  border: none;
+  -webkit-transition: 0.2s;
+  transition: 0.2s;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-ms-thumb {
+  box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18);
+  appearance: none;
+  width: 12px;
+  height: 12px;
+  background-color: #61c091;
+  border: none;
+  border-radius: 100%;
+  -webkit-transition: 0.2s;
+  transition: 0.2s;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:active::-moz-range-thumb {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  width: 14px;
+  height: 14px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:active::-ms-thumb {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  width: 14px;
+  height: 14px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:active::-webkit-slider-thumb {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23);
+  margin-top: -4px;
+  width: 14px;
+  height: 14px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-webkit-slider-runnable-track {
+  -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  width: 100%;
+  height: 6px;
+  cursor: pointer;
+  border-radius: 2px;
+  border: none;
+  background-color: rgba(68, 170, 119, 0.3);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-moz-range-track {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  width: 100%;
+  height: 6px;
+  cursor: pointer;
+  border-radius: 2px;
+  border: none;
+  background-color: rgba(68, 170, 119, 0.3);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-ms-track {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
+  width: 100%;
+  cursor: pointer;
+  background: transparent;
+  border-color: transparent;
+  color: transparent;
+  height: 6px;
+  border-radius: 2px;
+  border: none;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-ms-fill-lower {
+  background-color: rgba(68, 170, 119, 0.3);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]::-ms-fill-upper {
+  background-color: rgba(68, 170, 119, 0.15);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:focus::-webkit-slider-runnable-track {
+  background-color: rgba(68, 170, 119, 0.5);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:focus::-moz-range-track {
+  background-color: rgba(68, 170, 119, 0.5);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:focus::-ms-fill-lower {
+  background-color: rgba(68, 170, 119, 0.45);
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-left
+  .vicp-range
+  input[type="range"]:focus::-ms-fill-upper {
+  background-color: rgba(68, 170, 119, 0.25);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right {
+  float: right;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview {
+  height: 150px;
+  overflow: hidden;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview
+  .vicp-preview-item {
+  position: relative;
+  padding: 5px;
+  width: 100px;
+  height: 100px;
+  float: left;
+  margin-right: 16px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview
+  .vicp-preview-item
+  span {
+  position: absolute;
+  bottom: -30px;
+  width: 100%;
+  font-size: 14px;
+  color: #bbb;
+  display: block;
+  text-align: center;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview
+  .vicp-preview-item
+  img {
+  position: absolute;
+  display: block;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  margin: auto;
+  padding: 3px;
+  background-color: #fff;
+  border: 1px solid rgba(0, 0, 0, 0.15);
+  overflow: hidden;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview
+  .vicp-preview-item.vicp-preview-item-circle {
+  margin-right: 0;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step2
+  .vicp-crop
+  .vicp-crop-right
+  .vicp-preview
+  .vicp-preview-item.vicp-preview-item-circle
+  img {
+  border-radius: 100%;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload {
+  position: relative;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  padding: 35px;
+  height: 170px;
+  background-color: rgba(0, 0, 0, 0.03);
+  text-align: center;
+  border: 1px dashed #ddd;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading {
+  display: block;
+  padding: 15px;
+  font-size: 16px;
+  color: #999;
+  line-height: 30px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap {
+  margin-top: 12px;
+  background-color: rgba(0, 0, 0, 0.08);
+  border-radius: 3px;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step3
+  .vicp-upload
+  .vicp-progress-wrap
+  .vicp-progress {
+  position: relative;
+  display: block;
+  height: 5px;
+  border-radius: 3px;
+  background-color: #4a7;
+  -webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
+  box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3);
+  -webkit-transition: width 0.15s linear;
+  transition: width 0.15s linear;
+  background-image: -webkit-linear-gradient(
+    135deg,
+    rgba(255, 255, 255, 0.2) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.2) 50%,
+    rgba(255, 255, 255, 0.2) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-image: linear-gradient(
+    -45deg,
+    rgba(255, 255, 255, 0.2) 25%,
+    transparent 25%,
+    transparent 50%,
+    rgba(255, 255, 255, 0.2) 50%,
+    rgba(255, 255, 255, 0.2) 75%,
+    transparent 75%,
+    transparent
+  );
+  background-size: 40px 40px;
+  -webkit-animation: vicp_progress 0.5s linear infinite;
+  animation: vicp_progress 0.5s linear infinite;
+}
+.vue-image-crop-upload
+  .vicp-wrap
+  .vicp-step3
+  .vicp-upload
+  .vicp-progress-wrap
+  .vicp-progress::after {
+  content: "";
+  position: absolute;
+  display: block;
+  top: -3px;
+  right: -3px;
+  width: 9px;
+  height: 9px;
+  border: 1px solid rgba(245, 246, 247, 0.7);
+  -webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
+  box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7);
+  border-radius: 100%;
+  background-color: #4a7;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error,
+.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success {
+  height: 100px;
+  line-height: 100px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-operate {
+  position: absolute;
+  right: 20px;
+  bottom: 20px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-operate a {
+  position: relative;
+  float: left;
+  display: block;
+  margin-left: 10px;
+  width: 100px;
+  height: 36px;
+  line-height: 36px;
+  text-align: center;
+  cursor: pointer;
+  font-size: 14px;
+  color: #4a7;
+  border-radius: 2px;
+  overflow: hidden;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-operate a:hover {
+  background-color: rgba(0, 0, 0, 0.03);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-error,
+.vue-image-crop-upload .vicp-wrap .vicp-success {
+  display: block;
+  font-size: 14px;
+  line-height: 24px;
+  height: 24px;
+  color: #d10;
+  text-align: center;
+  vertical-align: top;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-success {
+  color: #4a7;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-icon3 {
+  position: relative;
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  top: 4px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-icon3::after {
+  position: absolute;
+  top: 3px;
+  left: 6px;
+  width: 6px;
+  height: 10px;
+  border-width: 0 2px 2px 0;
+  border-color: #4a7;
+  border-style: solid;
+  -webkit-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  transform: rotate(45deg);
+  content: "";
+}
+.vue-image-crop-upload .vicp-wrap .vicp-icon2 {
+  position: relative;
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+  top: 4px;
+}
+.vue-image-crop-upload .vicp-wrap .vicp-icon2::after,
+.vue-image-crop-upload .vicp-wrap .vicp-icon2::before {
+  content: "";
+  position: absolute;
+  top: 9px;
+  left: 4px;
+  width: 13px;
+  height: 2px;
+  background-color: #d10;
+  -webkit-transform: rotate(45deg);
+  -ms-transform: rotate(45deg);
+  transform: rotate(45deg);
+}
+.vue-image-crop-upload .vicp-wrap .vicp-icon2::after {
+  -webkit-transform: rotate(-45deg);
+  -ms-transform: rotate(-45deg);
+  transform: rotate(-45deg);
+}
+.e-ripple {
+  position: absolute;
+  border-radius: 100%;
+  background-color: rgba(0, 0, 0, 0.15);
+  background-clip: padding-box;
+  pointer-events: none;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -webkit-transform: scale(0);
+  -ms-transform: scale(0);
+  transform: scale(0);
+  opacity: 1;
+}
+.e-ripple.z-active {
+  opacity: 0;
+  -webkit-transform: scale(2);
+  -ms-transform: scale(2);
+  transform: scale(2);
+  -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
+  transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
+  transition: opacity 1.2s ease-out, transform 0.6s ease-out;
+  transition: opacity 1.2s ease-out, transform 0.6s ease-out,
+    -webkit-transform 0.6s ease-out;
+}
+</style>

+ 19 - 0
src/components/ImageCropper/utils/data2blob.js

@@ -0,0 +1,19 @@
+/**
+ * database64文件格式转换为2进制
+ *
+ * @param  {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
+ * @param  {[String]} mime [description]
+ * @return {[blob]}      [description]
+ */
+export default function(data, mime) {
+  data = data.split(',')[1]
+  data = window.atob(data)
+  var ia = new Uint8Array(data.length)
+  for (var i = 0; i < data.length; i++) {
+    ia[i] = data.charCodeAt(i)
+  }
+  // canvas.toDataURL 返回的默认格式就是 image/png
+  return new Blob([ia], {
+    type: mime
+  })
+}

+ 39 - 0
src/components/ImageCropper/utils/effectRipple.js

@@ -0,0 +1,39 @@
+/**
+ * 点击波纹效果
+ *
+ * @param  {[event]} e        [description]
+ * @param  {[Object]} arg_opts [description]
+ * @return {[bollean]}          [description]
+ */
+export default function(e, arg_opts) {
+  var opts = Object.assign({
+    ele: e.target, // 波纹作用元素
+    type: 'hit', // hit点击位置扩散center中心点扩展
+    bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
+  }, arg_opts)
+  var target = opts.ele
+  if (target) {
+    var rect = target.getBoundingClientRect()
+    var ripple = target.querySelector('.e-ripple')
+    if (!ripple) {
+      ripple = document.createElement('span')
+      ripple.className = 'e-ripple'
+      ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
+      target.appendChild(ripple)
+    } else {
+      ripple.className = 'e-ripple'
+    }
+    switch (opts.type) {
+      case 'center':
+        ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
+        ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
+        break
+      default:
+        ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
+        ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
+    }
+    ripple.style.backgroundColor = opts.bgc
+    ripple.className = 'e-ripple z-active'
+    return false
+  }
+}

+ 232 - 0
src/components/ImageCropper/utils/language.js

@@ -0,0 +1,232 @@
+export default {
+  zh: {
+    hint: '点击,或拖动图片至此处',
+    loading: '正在上传……',
+    noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!',
+    success: '上传成功',
+    fail: '图片上传失败',
+    preview: '头像预览',
+    btn: {
+      off: '取消',
+      close: '关闭',
+      back: '上一步',
+      save: '保存'
+    },
+    error: {
+      onlyImg: '仅限图片格式',
+      outOfSize: '单文件大小不能超过 ',
+      lowestPx: '图片最低像素为(宽*高):'
+    }
+  },
+  'zh-tw': {
+    hint: '點擊,或拖動圖片至此處',
+    loading: '正在上傳……',
+    noSupported: '瀏覽器不支持該功能,請使用IE10以上或其他現代瀏覽器!',
+    success: '上傳成功',
+    fail: '圖片上傳失敗',
+    preview: '頭像預覽',
+    btn: {
+      off: '取消',
+      close: '關閉',
+      back: '上一步',
+      save: '保存'
+    },
+    error: {
+      onlyImg: '僅限圖片格式',
+      outOfSize: '單文件大小不能超過 ',
+      lowestPx: '圖片最低像素為(寬*高):'
+    }
+  },
+  en: {
+    hint: 'Click or drag the file here to upload',
+    loading: 'Uploading…',
+    noSupported: 'Browser is not supported, please use IE10+ or other browsers',
+    success: 'Upload success',
+    fail: 'Upload failed',
+    preview: 'Preview',
+    btn: {
+      off: 'Cancel',
+      close: 'Close',
+      back: 'Back',
+      save: 'Save'
+    },
+    error: {
+      onlyImg: 'Image only',
+      outOfSize: 'Image exceeds size limit: ',
+      lowestPx: 'Image\'s size is too low. Expected at least: '
+    }
+  },
+  ro: {
+    hint: 'Atinge sau trage fișierul aici',
+    loading: 'Se încarcă',
+    noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.',
+    success: 'S-a încărcat cu succes',
+    fail: 'A apărut o problemă la încărcare',
+    preview: 'Previzualizează',
+
+    btn: {
+      off: 'Anulează',
+      close: 'Închide',
+      back: 'Înapoi',
+      save: 'Salvează'
+    },
+
+    error: {
+      onlyImg: 'Doar imagini',
+      outOfSize: 'Imaginea depășește limita de: ',
+      loewstPx: 'Imaginea este prea mică; Minim: '
+    }
+  },
+  ru: {
+    hint: 'Нажмите, или перетащите файл в это окно',
+    loading: 'Загружаю……',
+    noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры',
+    success: 'Загрузка выполнена успешно',
+    fail: 'Ошибка загрузки',
+    preview: 'Предпросмотр',
+    btn: {
+      off: 'Отменить',
+      close: 'Закрыть',
+      back: 'Назад',
+      save: 'Сохранить'
+    },
+    error: {
+      onlyImg: 'Только изображения',
+      outOfSize: 'Изображение превышает предельный размер: ',
+      lowestPx: 'Минимальный размер изображения: '
+    }
+  },
+  'pt-br': {
+    hint: 'Clique ou arraste o arquivo aqui para carregar',
+    loading: 'Carregando…',
+    noSupported: 'Browser não suportado, use o IE10+ ou outro browser',
+    success: 'Sucesso ao carregar imagem',
+    fail: 'Falha ao carregar imagem',
+    preview: 'Pré-visualizar',
+    btn: {
+      off: 'Cancelar',
+      close: 'Fechar',
+      back: 'Voltar',
+      save: 'Salvar'
+    },
+    error: {
+      onlyImg: 'Apenas imagens',
+      outOfSize: 'A imagem excede o limite de tamanho: ',
+      lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: '
+    }
+  },
+  fr: {
+    hint: 'Cliquez ou glissez le fichier ici.',
+    loading: 'Téléchargement…',
+    noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.',
+    success: 'Téléchargement réussit',
+    fail: 'Téléchargement echoué',
+    preview: 'Aperçu',
+    btn: {
+      off: 'Annuler',
+      close: 'Fermer',
+      back: 'Retour',
+      save: 'Enregistrer'
+    },
+    error: {
+      onlyImg: 'Image uniquement',
+      outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ',
+      lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: '
+    }
+  },
+  nl: {
+    hint: 'Klik hier of sleep een afbeelding in dit vlak',
+    loading: 'Uploaden…',
+    noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.',
+    success: 'Upload succesvol',
+    fail: 'Upload mislukt',
+    preview: 'Voorbeeld',
+    btn: {
+      off: 'Annuleren',
+      close: 'Sluiten',
+      back: 'Terug',
+      save: 'Opslaan'
+    },
+    error: {
+      onlyImg: 'Alleen afbeeldingen',
+      outOfSize: 'De afbeelding is groter dan: ',
+      lowestPx: 'De afbeelding is te klein! Minimale afmetingen: '
+    }
+  },
+  tr: {
+    hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
+    loading: 'Yükleniyor…',
+    noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın',
+    success: 'Yükleme başarılı',
+    fail: 'Yüklemede hata oluştu',
+    preview: 'Önizle',
+    btn: {
+      off: 'İptal',
+      close: 'Kapat',
+      back: 'Geri',
+      save: 'Kaydet'
+    },
+    error: {
+      onlyImg: 'Sadece resim',
+      outOfSize: 'Resim yükleme limitini aşıyor: ',
+      lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: '
+    }
+  },
+  'es-MX': {
+    hint: 'Selecciona o arrastra una imagen',
+    loading: 'Subiendo...',
+    noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes',
+    success: 'Subido exitosamente',
+    fail: 'Sucedió un error',
+    preview: 'Vista previa',
+    btn: {
+      off: 'Cancelar',
+      close: 'Cerrar',
+      back: 'Atras',
+      save: 'Guardar'
+    },
+    error: {
+      onlyImg: 'Unicamente imagenes',
+      outOfSize: 'La imagen excede el tamaño maximo:',
+      lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:'
+    }
+  },
+  de: {
+    hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
+    loading: 'Hochladen…',
+    noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser',
+    success: 'Upload erfolgreich',
+    fail: 'Upload fehlgeschlagen',
+    preview: 'Vorschau',
+    btn: {
+      off: 'Abbrechen',
+      close: 'Schließen',
+      back: 'Zurück',
+      save: 'Speichern'
+    },
+    error: {
+      onlyImg: 'Nur Bilder',
+      outOfSize: 'Das Bild ist zu groß: ',
+      lowestPx: 'Das Bild ist zu klein. Mindestens: '
+    }
+  },
+  ja: {
+    hint: 'クリック・ドラッグしてファイルをアップロード',
+    loading: 'アップロード中...',
+    noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。',
+    success: 'アップロード成功',
+    fail: 'アップロード失敗',
+    preview: 'プレビュー',
+    btn: {
+      off: 'キャンセル',
+      close: '閉じる',
+      back: '戻る',
+      save: '保存'
+    },
+    error: {
+      onlyImg: '画像のみ',
+      outOfSize: '画像サイズが上限を超えています。上限: ',
+      lowestPx: '画像が小さすぎます。最小サイズ: '
+    }
+  }
+}

+ 7 - 0
src/components/ImageCropper/utils/mimes.js

@@ -0,0 +1,7 @@
+export default {
+  'jpg': 'image/jpeg',
+  'png': 'image/png',
+  'gif': 'image/gif',
+  'svg': 'image/svg+xml',
+  'psd': 'image/photoshop'
+}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است