소스 검색

初始化

sunlupeng 8 달 전
커밋
0a8b977686
100개의 변경된 파일7506개의 추가작업 그리고 0개의 파일을 삭제
  1. 12 0
      .babelrc
  2. 14 0
      .editorconfig
  3. 3 0
      .eslintignore
  4. 199 0
      .eslintrc.js
  5. 21 0
      .gitignore
  6. 10 0
      .postcssrc.js
  7. 5 0
      .travis.yml
  8. 48 0
      build/build.js
  9. 54 0
      build/check-versions.js
  10. BIN
      build/logo.png
  11. 102 0
      build/utils.js
  12. 22 0
      build/vue-loader.conf.js
  13. 101 0
      build/webpack.base.conf.js
  14. 94 0
      build/webpack.dev.conf.js
  15. 175 0
      build/webpack.prod.conf.js
  16. 14 0
      config/dev.env.js
  17. 84 0
      config/index.js
  18. 10 0
      config/prod.env.js
  19. BIN
      favicon.ico
  20. BIN
      favicon.png
  21. 15 0
      index.html
  22. 91 0
      package.json
  23. 29 0
      src/App.vue
  24. 37 0
      src/api/activityManage.js
  25. 41 0
      src/api/activitytag.js
  26. 57 0
      src/api/admin.js
  27. 12 0
      src/api/answerGame.js
  28. 10 0
      src/api/approvehistory.js
  29. 41 0
      src/api/banner.js
  30. 83 0
      src/api/certManage.js
  31. 101 0
      src/api/commendManage.js
  32. 41 0
      src/api/couponsManage.js
  33. 9 0
      src/api/dashboard.js
  34. 75 0
      src/api/dictManage.js
  35. 29 0
      src/api/exchangeManage.js
  36. 37 0
      src/api/festivalManage.js
  37. 73 0
      src/api/giftManage.js
  38. 57 0
      src/api/goodsManage.js
  39. 37 0
      src/api/login.js
  40. 83 0
      src/api/lotteryManage.js
  41. 70 0
      src/api/medalList.js
  42. 87 0
      src/api/news.js
  43. 49 0
      src/api/newsCenter.js
  44. 37 0
      src/api/noticeList.js
  45. 75 0
      src/api/organize.js
  46. 36 0
      src/api/organizeuser.js
  47. 125 0
      src/api/pointManage.js
  48. 60 0
      src/api/postManage.js
  49. 85 0
      src/api/public.js
  50. 41 0
      src/api/questionDetail.js
  51. 41 0
      src/api/questionSort.js
  52. 50 0
      src/api/questions.js
  53. 83 0
      src/api/raffleManage.js
  54. 30 0
      src/api/rankingList.js
  55. 73 0
      src/api/role.js
  56. 71 0
      src/api/storage.js
  57. 39 0
      src/api/trainManage.js
  58. 17 0
      src/api/trend.js
  59. 108 0
      src/api/user.js
  60. 25 0
      src/api/visitor.js
  61. 26 0
      src/api/welfare.js
  62. 37 0
      src/api/welfareManage.js
  63. BIN
      src/assets/401_images/401.gif
  64. BIN
      src/assets/404_images/404.png
  65. BIN
      src/assets/404_images/404_cloud.png
  66. 199 0
      src/assets/echarts-macarons.js
  67. 112 0
      src/components/BackToTop/index.vue
  68. 52 0
      src/components/Breadcrumb/index.vue
  69. 150 0
      src/components/Charts/keyboard.vue
  70. 225 0
      src/components/Charts/lineMarker.vue
  71. 268 0
      src/components/Charts/mixChart.vue
  72. 161 0
      src/components/Crontab/day.vue
  73. 114 0
      src/components/Crontab/hour.vue
  74. 430 0
      src/components/Crontab/index.vue
  75. 116 0
      src/components/Crontab/min.vue
  76. 114 0
      src/components/Crontab/month.vue
  77. 559 0
      src/components/Crontab/result.vue
  78. 117 0
      src/components/Crontab/second.vue
  79. 202 0
      src/components/Crontab/week.vue
  80. 131 0
      src/components/Crontab/year.vue
  81. 45 0
      src/components/Hamburger/index.vue
  82. 104 0
      src/components/RightToolbar/index.vue
  83. 65 0
      src/components/Screenfull/index.vue
  84. 57 0
      src/components/ScrollBar/index.vue
  85. 72 0
      src/components/ScrollPane/index.vue
  86. 181 0
      src/components/SkuDesc/index.vue
  87. 7 0
      src/components/SkuDesc/plugins.js
  88. 6 0
      src/components/SkuDesc/toolbar.js
  89. 76 0
      src/components/Sticky/index.vue
  90. 42 0
      src/components/SvgIcon/index.vue
  91. 181 0
      src/components/Tinymce/index.vue
  92. 7 0
      src/components/Tinymce/plugins.js
  93. 6 0
      src/components/Tinymce/toolbar.js
  94. 123 0
      src/components/Upload/singleImage.vue
  95. 118 0
      src/components/Upload/singleImage2.vue
  96. 145 0
      src/components/Upload/singleImage3.vue
  97. 91 0
      src/directive/sticky.js
  98. 13 0
      src/directive/waves/index.js
  99. 26 0
      src/directive/waves/waves.css
  100. 0 0
      src/directive/waves/waves.js

+ 12 - 0
.babelrc

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

+ 14 - 0
.editorconfig

@@ -0,0 +1,14 @@
+# http://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

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+build/*.js
+config/*.js
+src/assets

+ 199 - 0
.eslintrc.js

@@ -0,0 +1,199 @@
+module.exports = {
+  root: true,
+  parser: 'babel-eslint',
+  parserOptions: {
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true,
+  },
+  extends: 'eslint:recommended',
+  // required to lint *.vue files
+  plugins: [
+    'html'
+  ],
+  // check if imports actually resolve
+  'settings': {
+    'import/resolver': {
+      'webpack': {
+        'config': 'build/webpack.base.conf.js'
+      }
+    }
+  },
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  'rules': {
+    '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': [2, 'allow-null'],
+    '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']
+  }
+}
+

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+.DS_Store
+node_modules/
+dist/
+dist-admin.zip*
+dist-admin.7z*
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+test/unit/coverage
+test/e2e/reports
+selenium-debug.log
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+

+ 10 - 0
.postcssrc.js

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

+ 5 - 0
.travis.yml

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

+ 48 - 0
build/build.js

@@ -0,0 +1,48 @@
+'use strict'
+require('./check-versions')()
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
+const server = require('pushstate-server')
+
+var spinner = ora('building for '+ process.env.env_config+ ' environment...' )
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false,
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+    if(process.env.npm_config_preview){
+      server.start({
+          port: 9526,
+          directory: './dist',
+          file: '/index.html'
+      });
+      console.log('> Listening at ' +  'http://localhost:9526' + '\n')
+    }
+  })
+})

+ 54 - 0
build/check-versions.js

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

BIN
build/logo.png


+ 102 - 0
build/utils.js

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

+ 22 - 0
build/vue-loader.conf.js

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

+ 101 - 0
build/webpack.base.conf.js

@@ -0,0 +1,101 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const createLintingRule = () => ({
+  test: /\.(js|vue)$/,
+  loader: 'eslint-loader',
+  enforce: 'pre',
+  include: [resolve('src'), resolve('test')],
+  options: {
+    formatter: require('eslint-friendly-formatter'),
+    emitWarning: !config.dev.showEslintErrorsInOverlay
+  }
+})
+
+module.exports = {
+  context: path.resolve(__dirname, '../'),
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src'),
+    }
+  },
+  module: {
+    rules: [
+      ...(config.dev.useEslint ? [createLintingRule()] : []),
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader?cacheDirectory',
+        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
+      },
+      {
+        test: /\.svg$/,
+        loader: 'svg-sprite-loader',
+        include: [resolve('src/icons')],
+        options: {
+          symbolId: 'icon-[name]'
+        }
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        exclude: [resolve('src/icons')],
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
+}

+ 94 - 0
build/webpack.dev.conf.js

@@ -0,0 +1,94 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: true,
+    hot: true,
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true,
+      favicon: resolve('favicon.ico'),
+      title: 'zmall-admin',
+      path: config.dev.assetsPublicPath + config.dev.assetsSubDirectory
+    }),
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+            // Add FriendlyErrorsPlugin
+            devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+              compilationSuccessInfo: {
+                // messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+                // 修改启动后终端显示localhost和network访问地址
+                messages: [
+                  `App runing at: `,
+                  `Local: http://localhost:${port}`,
+                  `Network: http://${require('ip').address()}:${port}`,
+                ],
+              },
+              onErrors: config.dev.notifyOnErrors
+              ? utils.createNotifierCallback()
+              : undefined
+            }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 175 - 0
build/webpack.prod.conf.js

@@ -0,0 +1,175 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const env = require('../config/'+process.env.env_config+'.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: false,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: config.build.index,
+      template: 'index.html',
+      inject: true,
+      favicon: resolve('favicon.ico'),
+      title: 'vue-element-admin',
+      path: config.build.assetsPublicPath + config.build.assetsSubDirectory,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vender modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+    // split echarts into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      async: 'echarts',
+      minChunks(module) {
+        var context = module.context;
+        return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0);
+      }
+    }),
+    // split xlsx into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      async: 'xlsx',
+      minChunks(module) {
+        var context = module.context;
+        return context && (context.indexOf('xlsx') >= 0);
+      }
+    }),
+     // split codemirror into its own file
+     new webpack.optimize.CommonsChunkPlugin({
+      async: 'codemirror',
+      minChunks(module) {
+        var context = module.context;
+        return context && (context.indexOf('codemirror') >= 0);
+      }
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 14 - 0
config/dev.env.js

@@ -0,0 +1,14 @@
+module.exports = {
+	NODE_ENV: '"development"',
+	ENV_CONFIG: '"dev"',
+  // 本地数据库:登录用户名 admin123,密码 admin123
+  // BASE_API: '"http://192.168.100.87:9083/admin"',
+  // OS_API: '"http://192.168.100.87:9083/admin"',
+  // BASE_API: '"http://47.103.79.143:9085/admin"',
+  // OS_API: '"http://47.103.79.143:9085/admin"',
+  OS_API: '"https://xiaoyou.dgtis.com/admin"',
+  BASE_API: '"https://xiaoyou.dgtis.com/admin"',
+  
+  
+  
+}

+ 84 - 0
config/index.js

@@ -0,0 +1,84 @@
+'use strict'
+// Template version: 1.2.6
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+  dev: {
+
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {},
+
+    // Various Dev Server settings
+    // host: 'localhost', // can be overwritten by process.env.HOST
+    host: '192.168.100.104', // can be overwritten by process.env.HOST
+    port: 8081, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: true,
+    errorOverlay: true,
+    notifyOnErrors: false,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    // Use Eslint Loader?
+    // If true, your code will be linted during bundling and
+    // linting errors and warnings will be shown in the console.
+    useEslint: false,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: '#cheap-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    // CSS Sourcemaps off by default because relative paths are "buggy"
+    // with this option, according to the CSS-Loader README
+    // (https://github.com/webpack/css-loader#sourcemaps)
+    // In our experience, they generally work as expected,
+    // just be aware of this issue when enabling this option.
+    cssSourceMap: false,
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+
+    // you can set by youself according to actual condition
+    assetsPublicPath: './',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: false,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
+}

+ 10 - 0
config/prod.env.js

@@ -0,0 +1,10 @@
+module.exports = {
+	NODE_ENV: '"production"',
+	ENV_CONFIG: '"prod"',
+	// 阿里云环境
+  // BASE_API: '"http://47.103.79.143:9085/admin"',
+  // OS_API: '"http://47.103.79.143:9085/admin"'
+  //正式环境
+  BASE_API: '"https://xiaoyou.dgtis.com/admin"',
+  OS_API: '"https://xiaoyou.dgtis.com/admin"'
+}

BIN
favicon.ico


BIN
favicon.png


+ 15 - 0
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">
+		<title>积分商城后台管理</title>
+	</head>
+	<script src=<%= htmlWebpackPlugin.options.path %>/tinymce4.7.11/tinymce.min.js></script>
+	<body>
+		<div id="app"></div>
+		<!-- built files will be auto injected -->
+	</body>
+</html>

+ 91 - 0
package.json

@@ -0,0 +1,91 @@
+{
+  "name": "alumni-record-admin",
+  "version": "0.1.0",
+  "description": "litemall-admin basing on vue-element-admin 3.6.2",
+  "author": "linlinjava <linlinjava@163.com>",
+  "license": "MIT",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --open --inline --progress --config build/webpack.dev.conf.js",
+    "build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
+    "lint": "eslint --ext .js,.vue src",
+    "test": "npm run lint"
+  },
+  "dependencies": {
+    "@riophae/vue-treeselect": "0.4.0",
+    "axios": "0.17.1",
+    "clipboard": "1.7.1",
+    "echarts": "3.8.5",
+    "element-ui": "2.15.13",
+    "file-saver": "1.3.3",
+    "font-awesome": "4.7.0",
+    "js-cookie": "2.2.0",
+    "mockjs": "1.0.1-beta3",
+    "normalize.css": "7.0.0",
+    "nprogress": "0.2.0",
+    "screenfull": "3.3.2",
+    "vue": "2.6.12",
+    "vue-count-to": "1.0.13",
+    "vue-router": "3.0.1",
+    "vue-splitpane": "1.0.2",
+    "vuex": "3.0.1",
+    "xlsx": "^0.11.16"
+  },
+  "devDependencies": {
+    "autoprefixer": "7.2.3",
+    "babel-core": "6.26.0",
+    "babel-eslint": "8.0.3",
+    "babel-helper-vue-jsx-merge-props": "2.0.3",
+    "babel-loader": "7.1.2",
+    "babel-plugin-syntax-jsx": "6.18.0",
+    "babel-plugin-transform-runtime": "6.23.0",
+    "babel-plugin-transform-vue-jsx": "3.5.0",
+    "babel-preset-env": "1.6.1",
+    "babel-preset-stage-2": "6.24.1",
+    "chalk": "2.3.0",
+    "copy-webpack-plugin": "4.3.0",
+    "cross-env": "5.1.1",
+    "css-loader": "0.28.7",
+    "eslint": "4.13.1",
+    "eslint-friendly-formatter": "3.0.0",
+    "eslint-loader": "1.9.0",
+    "eslint-plugin-html": "4.0.1",
+    "extract-text-webpack-plugin": "3.0.2",
+    "file-loader": "1.1.5",
+    "friendly-errors-webpack-plugin": "1.6.1",
+    "html-webpack-plugin": "2.30.1",
+    "node-notifier": "5.1.2",
+    "node-sass": "7.0.1",
+    "optimize-css-assets-webpack-plugin": "3.2.0",
+    "ora": "1.3.0",
+    "portfinder": "1.0.13",
+    "postcss-import": "11.0.0",
+    "postcss-loader": "2.0.9",
+    "postcss-url": "7.3.0",
+    "pushstate-server": "3.0.1",
+    "rimraf": "2.6.2",
+    "sass-loader": "6.0.6",
+    "script-loader": "0.7.2",
+    "semver": "5.4.1",
+    "shelljs": "0.7.8",
+    "svg-sprite-loader": "3.5.2",
+    "uglifyjs-webpack-plugin": "1.1.3",
+    "url-loader": "0.6.2",
+    "vue-loader": "13.5.0",
+    "vue-style-loader": "3.0.3",
+    "vue-template-compiler": "2.6.12",
+    "webpack": "3.10.0",
+    "webpack-bundle-analyzer": "2.9.1",
+    "webpack-dev-server": "2.9.7",
+    "webpack-merge": "4.1.1"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

+ 29 - 0
src/App.vue

@@ -0,0 +1,29 @@
+<template>
+	<div id="app">
+		<router-view v-if="isRouterAlive"></router-view>
+	</div>
+</template>
+
+<script>
+  export default{
+    name: 'APP',
+    provide (){
+      return {
+        reload: this.reload
+      }
+    },
+    data () {
+      return {
+        isRouterAlive:true
+      }
+    },
+    methods: {
+      reload(){
+        this.isRouterAlive = false
+        this.$nextTick(function () {
+          this.isRouterAlive = true
+        })
+      }
+    }
+  }
+</script>

+ 37 - 0
src/api/activityManage.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-act/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-act/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function activityList(query) {
+  return request({
+    url: '/mall-act/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function activityState(query) {
+  return request({
+    url: '/mall-act/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+

+ 41 - 0
src/api/activitytag.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function listActivityTag(query) {
+  return request({
+    url: '/tag/list',
+    method: 'get',
+    param: query
+  })
+}
+
+export function createActivityTag(data) {
+  return request({
+    url: '/tag/create',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteActivityTag(data) {
+  return request({
+    url: '/tag/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function ActivityUser(query) {
+  return request({
+    url: '/ActivityUser/listActUser',
+    method: 'post',
+    params: query
+  })
+}
+
+export function ActivityUserAll(query) {
+  return request({
+    url: '/ActivityUser/listActUserAll',
+    method: 'post',
+    params: query
+  })
+}

+ 57 - 0
src/api/admin.js

@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+export function listAdmin(query) {
+  return request({
+    url: '/admin/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createAdmin(data) {
+  return request({
+    url: '/admin/create',
+    method: 'post',
+    data
+  })
+}
+
+export function getUser(query) {
+  return request({
+    url: '/admin/getUser',
+    method: 'get',
+    params: query
+  })
+}
+
+export function readminAdmin(data) {
+  return request({
+    url: '/admin/readmin',
+    method: 'get',
+    data
+  })
+}
+
+export function updateAdmin(data) {
+  return request({
+    url: '/admin/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteAdmin(data) {
+  return request({
+    url: '/admin/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function getWxUserId(query) {
+  return request({
+    url: '/admin/getWxUserId',
+    method: 'get',
+    params: query
+  })
+}

+ 12 - 0
src/api/answerGame.js

@@ -0,0 +1,12 @@
+import request from '@/utils/request'
+
+export function answerList(query) {
+  return request({
+    url: '/answer/list',
+    method: 'get',
+    params:query
+  })
+}
+
+
+

+ 10 - 0
src/api/approvehistory.js

@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+/*查询驳回记录列表*/
+export function listApproveHistory(query) {
+  return request({
+    url:'/approveHistory/list',
+    method:'get',
+    params:query
+  })
+}

+ 41 - 0
src/api/banner.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function listBanner(query) {
+  return request({
+    url: '/banner/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createBanner(data) {
+  return request({
+    url: '/banner/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readBanner(data) {
+  return request({
+    url: '/banner/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateBanner(data) {
+  return request({
+    url: '/banner/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteBanner(data) {
+  return request({
+    url: '/banner/delete',
+    method: 'post',
+    data
+  })
+}

+ 83 - 0
src/api/certManage.js

@@ -0,0 +1,83 @@
+import request from '@/utils/request'
+
+// 查询证书列表
+export function listDept(query) {
+  return request({
+    url: '/mall-certificat/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询证书列表(排除节点)
+export function listDeptExcludeChild(query) {
+  return request({
+    url: '/mall-certificat/list/excludeChild',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询证书详细
+export function getDept(query) {
+  return request({
+    url: '/mall-certificat/getInfo',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增证书
+export function addDept(data) {
+  return request({
+    url: '/mall-certificat/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改证书
+export function updateDept(data) {
+  return request({
+    url: '/mall-certificat/edit',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除证书
+export function delDept(query) {
+  return request({
+    url: '/mall-certificat/remove',
+    method: 'post',
+    params: query
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-integral-obtain/certificate/sys/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function complete(query) {
+  return request({
+    url: '/mall-integral-obtain/certificate/complete',
+    method: 'post',
+    params:query
+  })
+}
+
+export function types(query) {
+  return request({
+    url: '/mall-integral-obtain/certificate/types',
+    method: 'get',
+    params:query
+  })
+}
+
+
+
+

+ 101 - 0
src/api/commendManage.js

@@ -0,0 +1,101 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-integral-obtain/commend/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-integral-obtain/commend/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-integral-obtain/commend/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function state(query) {
+  return request({
+    url: '/mall-integral-obtain/commend/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+export function types(query) {
+  return request({
+    url: '/mall-integral-obtain/commend/types',
+    method: 'get',
+    params:query
+  })
+}
+
+
+export function dataList(query) {
+  return request({
+    url: '/mall-commend/commend/info',
+    method: 'get',
+    params:query
+  })
+}
+
+export function dataAdd(data) {
+  return request({
+    url: '/mall-commend/commend/staff/add',
+    method: 'post',
+    data
+  })
+}
+
+export function dataEdit(data) {
+  return request({
+    url: '/mall-commend/commend/staff/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function dataRemove(query) {
+  return request({
+    url: '/mall-commend/commend/staff/del',
+    method: 'post',
+    params:query
+  })
+}
+
+// 客户表彰审批列表
+export function customerPage(query) {
+  return request({
+    url: '/customer/commend/page',
+    method: 'get',
+    params:query
+  })
+}
+
+// 客户表彰审批
+export function complete(query) {
+  return request({
+    url: '/customer/commend/complete',
+    method: 'post',
+    params:query
+  })
+}
+
+// 客户表彰详情
+export function detail(query) {
+  return request({
+    url: '/customer/commend/info',
+    method: 'get',
+    params:query
+  })
+}

+ 41 - 0
src/api/couponsManage.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/coupon/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/coupon/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/coupon/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function deleteitem(query) {
+  return request({
+    url: '/coupon/modify',
+    method: 'post',
+    params:query
+  })
+}
+
+export function optionSelect(data) {
+  return request({
+    url: '/system/dict/type/optionselect',
+    method: 'get',
+    data
+  })
+}

+ 9 - 0
src/api/dashboard.js

@@ -0,0 +1,9 @@
+import request from '@/utils/request'
+
+export function info(query) {
+  return request({
+    url: '/dashboard',
+    method: 'get',
+    params: query
+  })
+}

+ 75 - 0
src/api/dictManage.js

@@ -0,0 +1,75 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/system/dict/type/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/system/dict/type/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/system/dict/type/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function deleteitem(query) {
+  return request({
+    url: '/system/dict/type/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function optionSelect(data) {
+  return request({
+    url: '/system/dict/type/optionselect',
+    method: 'get',
+    data
+  })
+}
+export function dataList(query) {
+  return request({
+    url: '/system/dict/data/list',
+    method: 'get',
+    params:query
+  })
+}
+export function dataAdd(data) {
+  return request({
+    url: '/system/dict/data/add',
+    method: 'post',
+    data
+  })
+}
+export function dataEdit(data) {
+  return request({
+    url: '/system/dict/data/edit',
+    method: 'post',
+    data
+  })
+}
+export function dataRemove(query) {
+  return request({
+    url: '/system/dict/data/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+
+
+

+ 29 - 0
src/api/exchangeManage.js

@@ -0,0 +1,29 @@
+import request from '@/utils/request'
+
+export function complete(query) {
+  return request({
+    url: '/mall-order/complete',
+    method: 'post',
+    params:query
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-order/page',
+    method: 'post',
+    params:query
+  })
+}
+
+export function detail(query) {
+  return request({
+    url: '/mall-order/info',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+

+ 37 - 0
src/api/festivalManage.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-integral-obtain/festival/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-integral-obtain/festival/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-integral-obtain/festival/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function state(query) {
+  return request({
+    url: '/mall-integral-obtain/festival/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+

+ 73 - 0
src/api/giftManage.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+export function giftExchangeRulesDetail(query) {
+  return request({
+    url: '/news/getdgtIndustryNews',
+    method: 'post',
+    data: query
+  })
+}
+
+export function updateGiftExchangeRules(query) {
+  return request({
+    url: '/mall-sku/modify/status',
+    method: 'post',
+    data: query
+  })
+}
+
+export function createItem(data) {
+  return request({
+    url: '/mall-sku/festival/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-sku/festival/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function giftList(query) {
+  return request({
+    url: '/mall-sku/festival/page',
+    method: 'post',
+    params:query
+  })
+}
+
+export function giftState(query) {
+  return request({
+    url: '/mall-sku/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+export function exchangeHistory(query) {
+  return request({
+    url: '/mall-sku/skuLog',
+    method: 'post',
+    params:query
+  })
+}
+
+export function relatedWelfare(query) {
+  return request({
+    url: '/mall-sku/festival/relation',
+    method: 'post',
+    params:query
+  })
+}
+
+export function welfareList(query) {
+  return request({
+    url: '/mall-welfare/welfare/statusList',
+    method: 'post',
+    params:query
+  })
+}

+ 57 - 0
src/api/goodsManage.js

@@ -0,0 +1,57 @@
+import request from '@/utils/request'
+
+export function goodsExchangeRulesDetail(query) {
+  return request({
+    url: '/news/mall/getNotice',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateGoodsExchangeRules(query) {
+  return request({
+    url: '/news/update',
+    method: 'post',
+    data: query
+  })
+}
+
+export function createItem(data) {
+  return request({
+    url: '/mall-sku/integral/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(query) {
+  return request({
+    url: '/mall-sku/integral/edit',
+    method: 'post',
+    data:query
+  })
+}
+
+export function goodsList(query) {
+  return request({
+    url: '/mall-sku/integral/page',
+    method: 'post',
+    params:query
+  })
+}
+
+export function goodsState(query) {
+  return request({
+    url: '/mall-sku/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+export function exchangeHistory(query) {
+  return request({
+    url: '/mall-sku/skuLog',
+    method: 'post',
+    params:query
+  })
+}

+ 37 - 0
src/api/login.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export function loginByUsername(username, password) {
+  const data = {
+    username,
+    password
+  }
+  return request({
+    url: '/login/login',
+    method: 'post',
+    data
+  })
+}
+
+export function logout() {
+  return request({
+    url: '/login/logout',
+    method: 'post'
+  })
+}
+
+export function getUserInfo(token) {
+  return request({
+    url: '/admin/info',
+    method: 'get',
+    params: { token }
+  })
+}
+
+export function getUserMenu(token) {
+  return request({
+    url: '/role/selectAdminMenu',
+    method: 'post',
+    params: { token }
+  })
+}
+

+ 83 - 0
src/api/lotteryManage.js

@@ -0,0 +1,83 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-draw-pool/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-draw-pool/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-draw-pool/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function deleteitem(query) {
+  return request({
+    url: '/mall-draw-pool/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function optionSelect(data) {
+  return request({
+    url: '/mall-draw-pool/optionselect',
+    method: 'get',
+    data
+  })
+}
+export function dataList(query) {
+  return request({
+    url: '/mall-draw-pool-data/list',
+    method: 'get',
+    params:query
+  })
+}
+export function dataAdd(data) {
+  return request({
+    url: '/mall-draw-pool-data/add',
+    method: 'post',
+    data
+  })
+}
+export function dataEdit(data) {
+  return request({
+    url: '/mall-draw-pool-data/edit',
+    method: 'post',
+    data
+  })
+}
+export function dataRemove(query) {
+  return request({
+    url: '/mall-draw-pool-data/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function logsList(query) {
+  return request({
+    url: '/MidAutumn/Activity/midAutumn/list',
+    method: 'get',
+    params:query
+  })
+}
+
+
+
+
+
+

+ 70 - 0
src/api/medalList.js

@@ -0,0 +1,70 @@
+import request from '@/utils/request'
+
+export function list(query) {
+  return request({
+    url: '/medal-type/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function createItem(data) {
+  return request({
+    url: '/medal-type/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/medal-type/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteitem(query) {
+  return request({
+    url: '/medal-type/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function dataList(query) {
+  return request({
+    url: '/medal-level/list',
+    method: 'get',
+    params:query
+  })
+}
+export function dataAdd(data) {
+  return request({
+    url: '/medal-level/add',
+    method: 'post',
+    data
+  })
+}
+export function dataEdit(data) {
+  return request({
+    url: '/medal-level/edit',
+    method: 'post',
+    data
+  })
+}
+export function dataRemove(query) {
+  return request({
+    url: '/medal-level/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function refresh(query) {
+  return request({
+    url: '/medal-type/refresh',
+    method: 'get',
+    params:query
+  })
+}

+ 87 - 0
src/api/news.js

@@ -0,0 +1,87 @@
+import request from '@/utils/request'
+
+export function listNews(query) {
+  return request({
+    url: '/news/list',
+    method: 'post',
+    data: query
+  })
+}
+
+export function createNews(data) {
+  return request({
+    url: '/news/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readNews(data) {
+  return request({
+    url: '/news/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateNews(data) {
+  return request({
+    url: '/news/update',
+    method: 'post',
+    data
+  })
+}
+
+export function updateNewsAuth(data) {
+  return request({
+    url: '/news/updateNewsAuth',
+    method: 'post',
+    data
+  })
+}
+
+
+export function deleteNews(data) {
+  return request({
+    url: '/news/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteAllNews(data) {
+  return request({
+    url: '/news/deleteQuantity',
+    method: 'post',
+    params: data
+  })
+}
+
+export function listActivityOrg(data) {
+  return request({
+    url: '/news/listActivityOrg',
+    method: 'get',
+    params: data
+  })
+}
+export function listCommon(data) {
+  return request({
+    url: '/newsComment/listCommon',
+    method: 'get',
+    params: data
+  })
+}
+export function deleteCommon(data) {
+  return request({
+    url: '/newsComment/deleteCommon',
+    method: 'post',
+    data
+  })
+}
+  export function deleteAllConent(data) {
+    return request({
+      url: '/newsComment/deleteQuantity',
+      method: 'post',
+      params: data
+    })
+}

+ 49 - 0
src/api/newsCenter.js

@@ -0,0 +1,49 @@
+import request from '@/utils/request'
+
+export function getdgtIndustryNews(query) {
+  return request({
+    url: '/news/getdgtIndustryNews',
+    method: 'post',
+    data: query
+  })
+}
+
+export function listNews(query) {
+  return request({
+    url: '/news/listForDGTSiteNews',
+    method: 'post',
+    data: query
+  })
+}
+
+export function createNews(data) {
+  return request({
+    url: '/news/createForDGTSiteNews',
+    method: 'post',
+    data
+  })
+}
+
+export function updateNews(data) {
+  return request({
+    url: '/news/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteNews(data) {
+  return request({
+    url: '/news/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteAllNews(data) {
+  return request({
+    url: '/news/deleteQuantity',
+    method: 'post',
+    params: data
+  })
+}

+ 37 - 0
src/api/noticeList.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-msg/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(query) {
+  return request({
+    url: '/mall-msg/edit',
+    method: 'post',
+    data:query
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-msg/sys/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function state(query) {
+  return request({
+    url: '/mall-msg/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+

+ 75 - 0
src/api/organize.js

@@ -0,0 +1,75 @@
+import request from '@/utils/request'
+
+export function selectOrg(query) {
+  return request({
+    url: '/organize/selectOrganize',
+    method: 'post',
+    params: query
+  })
+}
+
+
+export function createOrg(query) {
+  return request({
+    url: '/organize/createOrganize',
+    method: 'post',
+    params: query
+  })
+}
+
+export function updateOrg(query) {
+  return request({
+    url: '/organize/updateOrganize',
+    method: 'post',
+    params: query
+  })
+}
+
+export function updateOrgAuth(data) {
+  return request({
+    url: '/organize/updateOrgAuth',
+    method: 'post',
+    data
+  })
+}
+
+export function sendWXMsg(query) {
+  return request({
+    url: '/organize/sendWXMsg',
+    method: 'post',
+    params: query
+  })
+}
+
+export function deleteOrg(data) {
+  return request({
+    url: '/organize/deleteOrgById',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteOrgList(query) {
+  return request({
+    url: '/organize/deleteOrgQuantity',
+    method: 'post',
+    params: query
+  })
+}
+
+export function auditOrganizeInterest(query) {
+  return request({
+    url: '/organize/auditOrganizeInterest',
+    method: 'post',
+    params: query
+  })
+}
+
+
+export function selectUserByUserName(query) {
+  return request({
+    url: '/organize/selectUserByUserName',
+    method: 'post',
+    params: query
+  })
+}

+ 36 - 0
src/api/organizeuser.js

@@ -0,0 +1,36 @@
+import request from '@/utils/request'
+
+export function selectOrganizeUserByOrgId(query) {
+  return request({
+    url: '/organizeuser/selectOrganizeUserByOrgId',
+    method: 'post',
+    params: query
+  })
+}
+
+export function selectOrganizeUserByOrgIdNoDel(query) {
+  return request({
+    url: '/organizeuser/selectOrganizeUserByOrgIdNoDel',
+    method: 'post',
+    params: query
+  })
+}
+
+
+export function updateOrganizeUserById(query) {
+  return request({
+    url: '/organizeuser/updateOrganizeUserById',
+    method: 'post',
+    params: query
+  })
+}
+
+
+
+export function sendWXMsg(query) {
+  return request({
+    url: '/organizeuser/sendWXMsg',
+    method: 'post',
+    params: query
+  })
+}

+ 125 - 0
src/api/pointManage.js

@@ -0,0 +1,125 @@
+import request from '@/utils/request'
+
+//积分有效期列表
+export function getPointIndateList(query) {
+  return request({
+    url: '/mall-integral-rule-type/page',
+    method: 'get',
+    params:query
+  })
+}
+//积分有效期新增
+export function pointIndateCreateItem(data) {
+  return request({
+    url: '/mall-integral-rule-type/add',
+    method: 'post',
+    data
+  })
+}
+//积分有效期修改
+export function pointIndateUpdateItem(data) {
+  return request({
+    url: '/mall-integral-rule-type/edit',
+    method: 'post',
+    data
+  })
+}
+//积分有效期状态修改
+export function pointIndateState(query) {
+  return request({
+    url: '/mall-integral-rule-type/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+//积分有效期删除
+export function pointIndateDel(query) {
+  return request({
+    url: '/mall-integral-rule-type/modify/del',
+    method: 'post',
+    params:query
+  })
+}
+
+//积分加减
+export function addSub(data) {
+  return request({
+    url: '/mall-integral/addSub',
+    method: 'post',
+    data
+  })
+}
+
+export function pointRulesTypeList(data) {
+  return request({
+    url: '/mall-integral/getTypes',
+    method: 'get',
+    data
+  })
+}
+
+export function getPointRulesList(query) {
+  return request({
+    url: '/mall-integral/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function pointRulesCreateItem(data) {
+  return request({
+    url: '/mall-integral/add',
+    method: 'post',
+    data
+  })
+}
+
+export function pointRulesUpdateItem(data) {
+  return request({
+    url: '/mall-integral/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function pointRulesState(query) {
+  return request({
+    url: '/mall-integral/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+export function updatePointRulesItem(data) {
+  return request({
+    url: '/news/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function getPointList(query) {
+  return request({
+    url: '/mall-integral/user/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function getPointDetailList(query) {
+  return request({
+    url: '/mall-integral/details/page',
+    method: 'get',
+    params:query
+  })
+}
+
+export function getItemPointList(query) {
+  return request({
+    url: '/mall-integral/user/log',
+    method: 'get',
+    params:query
+  })
+}
+
+

+ 60 - 0
src/api/postManage.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+export function create(data) {
+  return request({
+    url: '/mall-post/add',
+    method: 'post',
+    data
+  })
+}
+
+export function update(query) {
+  return request({
+    url: '/mall-post/edit',
+    method: 'post',
+    data:query
+  })
+}
+
+export function getList(query) {
+  return request({
+    url: '/mall-post/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function changeState(query) {
+  return request({
+    url: '/mall-post/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+export function approvalList(query) {
+  return request({
+    url: 'mall-post/comment/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function getDetailInfo(query) {
+  return request({
+    url: '/mall-post/answer/info',
+    method: 'get',
+    params:query
+  })
+}
+
+export function complete(data) {
+  return request({
+    url: '/mall-post/complete/answer',
+    method: 'post',
+    data
+  })
+}
+
+
+

+ 85 - 0
src/api/public.js

@@ -0,0 +1,85 @@
+import request from '@/utils/request'
+// 添加规则
+export function rulesDetail(query) {
+  return request({
+    url: '/news/mall/getNotice',
+    method: 'get',
+    params: query
+  })
+}
+// 更新规则
+export function updateRule(query) {
+  return request({
+    url: '/news/update',
+    method: 'post',
+    data: query
+  })
+}
+// 获取全部公司
+export function companyTypeList(query) {
+    return request({
+      url: '/sys/user/getCompanys',
+      method: 'post',
+      params:query
+    })
+  }
+  // 获取全部部门
+  export function depTypeList(query) {
+    return request({
+      url: '/sys/user/getDepts',
+      method: 'post',
+      params:query
+    })
+  }
+  //获取全部用户
+  export function allUserList(query) {
+    return request({
+      url: '/sys/user/getAll',
+      method: 'post',
+      params:query
+    })
+  }
+
+  //字典下拉列表
+  export function dataTypeList(query) {
+    return request({
+      url: '/system/dict/data/type',
+      method: 'get',
+      params:query
+    })
+  }
+  // 获取全部商品
+  export function goodsList(query) {
+    return request({
+      url: '/mall-sku/integral/optionselect',
+      method: 'get',
+      params:query
+    })
+  }
+// 获取已开启兑换券下拉
+export function couponsList(query) {
+  return request({
+    url: '/coupon/actOptionselect',
+    method: 'get',
+    params:query
+  })
+}
+
+// 获取已开启抽奖奖池下拉
+export function prizePoolList(query) {
+  return request({
+    url: '/mall-pool-type/actOptionselect',
+    method: 'get',
+    params:query
+  })
+}
+
+// 获取已开启开奖奖池下拉
+export function drawPoolList(query) {
+  return request({
+    url: '/mall-draw-pool/actOptionselect',
+    method: 'get',
+    params:query
+  })
+}
+  

+ 41 - 0
src/api/questionDetail.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function listQuestionDetail(query) {
+  return request({
+    url: '/questionDetail/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createQuestionDetail(data) {
+  return request({
+    url: '/questionDetail/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readQuestionDetail(data) {
+  return request({
+    url: '/questionDetail/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateQuestionDetail(data) {
+  return request({
+    url: '/questionDetail/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteQuestionDetail(data) {
+  return request({
+    url: '/questionDetail/delete',
+    method: 'post',
+    data
+  })
+}

+ 41 - 0
src/api/questionSort.js

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function listQuestionSort(query) {
+  return request({
+    url: '/questionSort/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createQuestionSort(data) {
+  return request({
+    url: '/questionSort/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readQuestionSort(data) {
+  return request({
+    url: '/questionSort/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateQuestionSort(data) {
+  return request({
+    url: '/questionSort/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteQuestionSort(data) {
+  return request({
+    url: '/questionSort/delete',
+    method: 'post',
+    data
+  })
+}

+ 50 - 0
src/api/questions.js

@@ -0,0 +1,50 @@
+import request from '@/utils/request'
+
+export function listBanner(query) {
+  return request({
+    url: '/questions/list',
+    method: 'get',
+    params: query
+  })
+}
+
+
+export function createBanner(data) {
+  return request({
+    url: '/questions/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readBanner(data) {
+  return request({
+    url: '/questions/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateBanner(data) {
+  return request({
+    url: '/questions/update',
+    method: 'post',
+    data
+  })
+}
+
+export function deleteBanner(data) {
+  return request({
+    url: '/questions/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function createContribute(data) {
+  return request({
+    url: '/contribute/create',
+    method: 'post',
+    data
+  })
+}

+ 83 - 0
src/api/raffleManage.js

@@ -0,0 +1,83 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-pool-type/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(data) {
+  return request({
+    url: '/mall-pool-type/edit',
+    method: 'post',
+    data
+  })
+}
+
+export function list(query) {
+  return request({
+    url: '/mall-pool-type/list',
+    method: 'get',
+    params:query
+  })
+}
+
+export function deleteitem(query) {
+  return request({
+    url: '/mall-pool-type/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function optionSelect(data) {
+  return request({
+    url: '/mall-pool-type/optionselect',
+    method: 'get',
+    data
+  })
+}
+export function dataList(query) {
+  return request({
+    url: '/mall-pool-data/list',
+    method: 'get',
+    params:query
+  })
+}
+export function dataAdd(data) {
+  return request({
+    url: '/mall-pool-data/add',
+    method: 'post',
+    data
+  })
+}
+export function dataEdit(data) {
+  return request({
+    url: '/mall-pool-data/edit',
+    method: 'post',
+    data
+  })
+}
+export function dataRemove(query) {
+  return request({
+    url: '/mall-pool-data/remove',
+    method: 'post',
+    params:query
+  })
+}
+
+export function logsList(query) {
+  return request({
+    url: '/mall-prize/prize/log',
+    method: 'get',
+    params:query
+  })
+}
+
+
+
+
+
+

+ 30 - 0
src/api/rankingList.js

@@ -0,0 +1,30 @@
+import request from '@/utils/request'
+
+
+// 积分排行
+export function list(query) {
+  return request({
+    url: '/mall-integral/sys/top',
+    method: 'get',
+    params:query
+  })
+}
+
+// 答题排行
+export function answerList(query) {
+  return request({
+    url: '/answer/admin/top',
+    method: 'get',
+    params:query
+  })
+}
+
+// 游戏排行
+export function gameList(query) {
+  return request({
+    url: '/MoonFestival/admin/top',
+    method: 'get',
+    params:query
+  })
+}
+

+ 73 - 0
src/api/role.js

@@ -0,0 +1,73 @@
+import request from '@/utils/request'
+
+/* 权限添加 */
+export function updateRoleMenu(query) {
+  return request({
+    url: '/role/insertRoleMenu',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 查询权限 */
+export function getRoleMenu(query) {
+  return request({
+    url: '/role/selectMenuByRoleId',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 添加角色 */
+export function addRole(query) {
+  return request({
+    url: '/role/insertSelective',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 修改角色名称 */
+export function updateRoleName(query) {
+  return request({
+    url: '/role/updateByPrimaryKeySelective',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 角色列表 */
+export function roleList(query) {
+  return request({
+    url: '/role/selectByRoleName',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 查询用户拥有的角色 */
+export function selectAdminRole(query) {
+  return request({
+    url: '/role/selectAdminRole',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 添加用户与角色关联 */
+export function addAdminRole(query) {
+  return request({
+    url: '/role/insertAdminRole',
+    method: 'post',
+    params: query
+  })
+}
+
+/* 查询当前用户权限列表 */
+// export function addAdminRole(data) {
+//   return request({
+//     url: 'role/selectAdminMenu',
+//     method: 'post',
+//     data
+//   })
+// }

+ 71 - 0
src/api/storage.js

@@ -0,0 +1,71 @@
+import axios from 'axios'
+
+// create an axios instance
+const service = axios.create({
+  // baseURL: process.env.OS_API, // api的base_url
+  baseURL: process.env.BASE_API,
+  timeout: 10000 // request timeout
+})
+
+const myService = axios.create({
+  baseURL: process.env.OS_API, // api的base_url
+  // baseURL: process.env.BASE_API,
+  timeout: 10000 // request timeout
+})
+
+/*批量导入员工福利信息 excel*/
+export function insetListWelfare(data) {
+  return service({
+    url: '/welfare/insertList',
+    method: 'post',
+    data
+  })
+}
+
+export function listStorage(query) {
+  return service({
+    url: '/storage/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createStorage(data) {
+  return service({
+    url: '/storage/create',
+    method: 'post',
+    data
+  })
+}
+export function uploadPic(data) {
+  return myService({
+    url: '/storage/uploadPic',
+    method: 'post',
+    data
+  })
+}
+
+export function readStorage(data) {
+  return service({
+    url: '/storage/read',
+    method: 'get',
+    data
+  })
+}
+
+export function updateStorage(data) {
+  return service({
+    url: '/storage/update',
+    method: 'post',
+    params: data
+  })
+}
+
+export function deleteStorage(data) {
+  debugger;
+  return service({
+    url: '/storage/delete',
+    method: 'post',
+    params: data
+  })
+}

+ 39 - 0
src/api/trainManage.js

@@ -0,0 +1,39 @@
+import request from '@/utils/request'
+
+//获取材料备注详情
+export function fileRulesDetail(query) {
+  return request({
+    url: '/news/mall/getNotice',
+    method: 'get',
+    params: query
+  })
+}
+//修改材料备注
+export function updateFileRules(query) {
+  return request({
+    url: '/news/update',
+    method: 'post',
+    data: query
+  })
+}
+
+
+// 培训列表
+export function trainList(query) {
+  return request({
+    url: '/mall-train/admin/page',
+    method: 'get',
+    params:query
+  })
+}
+
+
+
+// 客户表彰审批
+export function complete(data) {
+  return request({
+    url: '/mall-train/admin/complete',
+    method: 'post',
+    data
+  })
+}

+ 17 - 0
src/api/trend.js

@@ -0,0 +1,17 @@
+import request from '@/utils/request'
+
+export function listTrend(query) {
+  return request({
+    url: '/news/listTidings',
+    method: 'post',
+    params: query
+  })
+}
+
+export function deleteTrend(data) {
+  return request({
+    url: '/news/delete',
+    method: 'post',
+    data
+  })
+}

+ 108 - 0
src/api/user.js

@@ -0,0 +1,108 @@
+import request from '@/utils/request'
+
+/*查询校友录列表*/
+export function fetchList(query) {
+  return request({
+    url: '/user/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function exportAllUser(query) {
+  return request({
+    url: '/user/exportAllUser',
+    method: 'get',
+    params: query
+  })
+}
+
+/*查询游客列表*/
+export function fetYKList(query) {
+  return request({
+    url:'/user/findYKList',
+    method:'get',
+    params:query
+  })
+
+}
+
+export function fetchCheckUserList(query) {
+  return request({
+    url: '/user/searchAwaitAuditUserList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function createUser(data) {
+  return request({
+    url: '/user/create',
+    method: 'post',
+    data
+  })
+}
+
+export function readUser(data) {
+  return request({
+    url: '/user/detail',
+    method: 'get',
+    params: data
+  })
+}
+
+export function updateUser(data) {
+  return request({
+    url: '/user/update',
+    method: 'post',
+    data
+  })
+}
+
+export function auditUser(data) {
+  return request({
+    url: '/user/auditUser',
+    method: 'post',
+    data
+  })
+}
+
+export function getUserRole(data) {
+  return request({
+    url: '/role/selectAdminRole',
+    method: 'post',
+    data
+  })
+}
+
+export function passUserAudit(data) {
+  return request({
+    url: '/user/passAudit',
+    method: 'post',
+    params: data
+  })
+}
+
+export function getConcernedList(data) {
+  return request({
+    url: '/user/findUserConcerned',
+    method: 'post',
+    data
+  })
+}
+
+export function getMessageUser(data) {
+  return request({
+    url: '/message/list',
+    method: 'post',
+    params: data
+  })
+}
+
+export function getOrganizeList(data) {
+  return request({
+    url: '/user/findMyOrganize',
+    method: 'post',
+    params: data
+  })
+}

+ 25 - 0
src/api/visitor.js

@@ -0,0 +1,25 @@
+import request from '@/utils/request'
+/*查询访客列表*/
+export function fetchList(query) {
+  return request({
+    url: '/alumniVisitor/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function deleteVisitor(data) {
+  return request({
+    url: '/alumniVisitor/delete',
+    method: 'post',
+    data
+  })
+}
+
+export function uploadVisitor(data) {
+  return request({
+    url: '/alumniVisitor/upload',
+    method: 'post',
+    data
+  })
+}

+ 26 - 0
src/api/welfare.js

@@ -0,0 +1,26 @@
+import request from '@/utils/request'
+
+export function listWelfare(query) {
+  return request({
+    url: '/welfare/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function updateWelfare(data) {
+  return request({
+    url: '/welfare/update',
+    method: 'post',
+    data
+  })
+}
+
+
+export function deleteWelfare(data) {
+  return request({
+    url: '/welfare/delete',
+    method: 'post',
+    data
+  })
+}

+ 37 - 0
src/api/welfareManage.js

@@ -0,0 +1,37 @@
+import request from '@/utils/request'
+
+export function createItem(data) {
+  return request({
+    url: '/mall-welfare/welfare/add',
+    method: 'post',
+    data
+  })
+}
+
+export function updateItem(query) {
+  return request({
+    url: '/mall-welfare/welfare/edit',
+    method: 'post',
+    data:query
+  })
+}
+
+export function welfareList(query) {
+  return request({
+    url: '/mall-welfare/welfare/list',
+    method: 'post',
+    params:query
+  })
+}
+
+export function welfareState(query) {
+  return request({
+    url: 'mall-welfare/modify/status',
+    method: 'post',
+    params:query
+  })
+}
+
+
+
+

BIN
src/assets/401_images/401.gif


BIN
src/assets/404_images/404.png


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


+ 199 - 0
src/assets/echarts-macarons.js

@@ -0,0 +1,199 @@
+/* eslint-disable */
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        // AMD. Register as an anonymous module.
+        define(['exports', 'echarts'], factory);
+    } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
+        // CommonJS
+        factory(exports, require('echarts'));
+    } else {
+        // Browser globals
+        factory({}, root.echarts);
+    }
+}(this, function (exports, echarts) {
+    var log = function (msg) {
+        if (typeof console !== 'undefined') {
+            console && console.error && console.error(msg);
+        }
+    };
+    if (!echarts) {
+        log('ECharts is not Loaded');
+        return;
+    }
+
+    var colorPalette = [
+        '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80',
+        '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa',
+        '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050',
+        '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089'
+    ];
+
+
+    var theme = {
+        color: colorPalette,
+
+        title: {
+            textStyle: {
+                fontWeight: 'normal',
+                color: '#008acd'
+            }
+        },
+
+        visualMap: {
+            itemWidth: 15,
+            color: ['#5ab1ef','#e0ffff']
+        },
+
+        toolbox: {
+            iconStyle: {
+                normal: {
+                    borderColor: colorPalette[0]
+                }
+            }
+        },
+
+        tooltip: {
+            backgroundColor: 'rgba(50,50,50,0.5)',
+            axisPointer : {
+                type : 'line',
+                lineStyle : {
+                    color: '#008acd'
+                },
+                crossStyle: {
+                    color: '#008acd'
+                },
+                shadowStyle : {
+                    color: 'rgba(200,200,200,0.2)'
+                }
+            }
+        },
+
+        dataZoom: {
+            dataBackgroundColor: '#efefff',
+            fillerColor: 'rgba(182,162,222,0.2)',
+            handleColor: '#008acd'
+        },
+
+        grid: {
+            borderColor: '#eee'
+        },
+
+        categoryAxis: {
+            axisLine: {
+                lineStyle: {
+                    color: '#008acd'
+                }
+            },
+            splitLine: {
+                lineStyle: {
+                    color: ['#eee']
+                }
+            }
+        },
+
+        valueAxis: {
+            axisLine: {
+                lineStyle: {
+                    color: '#008acd'
+                }
+            },
+            splitArea : {
+                show : true,
+                areaStyle : {
+                    color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)']
+                }
+            },
+            splitLine: {
+                lineStyle: {
+                    color: ['#eee']
+                }
+            }
+        },
+
+        timeline : {
+            lineStyle : {
+                color : '#008acd'
+            },
+            controlStyle : {
+                normal : { color : '#008acd'},
+                emphasis : { color : '#008acd'}
+            },
+            symbol : 'emptyCircle',
+            symbolSize : 3
+        },
+
+        line: {
+            smooth : true,
+            symbol: 'emptyCircle',
+            symbolSize: 3
+        },
+
+        candlestick: {
+            itemStyle: {
+                normal: {
+                    color: '#d87a80',
+                    color0: '#2ec7c9',
+                    lineStyle: {
+                        color: '#d87a80',
+                        color0: '#2ec7c9'
+                    }
+                }
+            }
+        },
+
+        scatter: {
+            symbol: 'circle',
+            symbolSize: 4
+        },
+
+        map: {
+            label: {
+                normal: {
+                    textStyle: {
+                        color: '#d87a80'
+                    }
+                }
+            },
+            itemStyle: {
+                normal: {
+                    borderColor: '#eee',
+                    areaColor: '#ddd'
+                },
+                emphasis: {
+                    areaColor: '#fe994e'
+                }
+            }
+        },
+
+        graph: {
+            color: colorPalette
+        },
+
+        gauge : {
+            axisLine: {
+                lineStyle: {
+                    color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']],
+                    width: 10
+                }
+            },
+            axisTick: {
+                splitNumber: 10,
+                length :15,
+                lineStyle: {
+                    color: 'auto'
+                }
+            },
+            splitLine: {
+                length :22,
+                lineStyle: {
+                    color: 'auto'
+                }
+            },
+            pointer : {
+                width : 5
+            }
+        }
+    };
+
+    echarts.registerTheme('macarons', theme);
+}));

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

@@ -0,0 +1,112 @@
+<template>
+  <transition :name="transitionName">
+    <div class="back-to-ceiling" @click="backToTop" v-show="visible" :style="customStyle">
+      <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;">
+        <title>回到顶部</title>
+        <g>
+          <path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd"></path>
+        </g>
+      </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
+    }
+  },
+  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() {
+      const start = window.pageYOffset
+      let i = 0
+      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)
+        } 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>

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

@@ -0,0 +1,52 @@
+<template>
+  <el-breadcrumb class="app-breadcrumb" separator="/">
+    <transition-group name="breadcrumb">
+      <el-breadcrumb-item v-for="(item,index)  in levelList" :key="item.path" v-if='item.meta.title'>
+        <span v-if='item.redirect==="noredirect"||index==levelList.length-1' class="no-redirect">{{item.meta.title}}</span>
+        <router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script>
+
+export default {
+  created() {
+    this.getBreadcrumb()
+  },
+  data() {
+    return {
+      levelList: null
+    }
+  },
+  watch: {
+    $route() {
+      this.getBreadcrumb()
+    }
+  },
+  methods: {
+    getBreadcrumb() {
+      let matched = this.$route.matched.filter(item => item.name)
+      const first = matched[0]
+      if (first && first.name !== 'dashboard') {
+        matched = [{ path: '/dashboard', meta: { title: '主页' }}].concat(matched)
+      }
+      this.levelList = matched
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  .app-breadcrumb.el-breadcrumb {
+    display: inline-block;
+    font-size: 14px;
+    line-height: 50px;
+    margin-left: 10px;
+    .no-redirect {
+      color: #97a8be;
+      cursor: text;
+    }
+  }
+</style>

+ 150 - 0
src/components/Charts/keyboard.vue

@@ -0,0 +1,150 @@
+<template>
+  <div :class="className" :id="id" :style="{height:height,width:width}"></div>
+</template>
+
+<script>
+import echarts from 'echarts'
+
+export default {
+  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',
+          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>

+ 225 - 0
src/components/Charts/lineMarker.vue

@@ -0,0 +1,225 @@
+<template>
+  <div :class="className" :id="id" :style="{height:height,width:width}"></div>
+</template>
+
+<script>
+import echarts from 'echarts'
+
+export default {
+  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: '3%',
+          right: '4%',
+          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>

+ 268 - 0
src/components/Charts/mixChart.vue

@@ -0,0 +1,268 @@
+<template>
+  <div :class="className" :id="id" :style="{height:height,width:width}"></div>
+</template>
+
+<script>
+import echarts from 'echarts'
+
+export default {
+  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()
+    this.chart = null
+  },
+  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: {
+          borderWidth: 0,
+          top: 110,
+          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>

+ 161 - 0
src/components/Crontab/day.vue

@@ -0,0 +1,161 @@
+<template>
+	<el-form size="small">
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				日,允许的通配符[, - * ? / L W]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				不指定
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				周期从
+				<el-input-number v-model='cycle01' :min="1" :max="30" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 2" :max="31" /> 日
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				从
+				<el-input-number v-model='average01' :min="1" :max="30" /> 号开始,每
+				<el-input-number v-model='average02' :min="1" :max="31 - average01 || 1" /> 日执行一次
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="5">
+				每月
+				<el-input-number v-model='workday' :min="1" :max="31" /> 号最近的那个工作日
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="6">
+				本月最后一天
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="7">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="item in 31" :key="item" :value="item">{{item}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 1,
+			workday: 1,
+			cycle01: 1,
+			cycle02: 2,
+			average01: 1,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-day',
+	props: ['check', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			('day rachange');
+			if (this.radioValue !== 2 && this.cron.week !== '?') {
+				this.$emit('update', 'week', '?', 'day')
+			}
+
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'day', '*');
+					break;
+				case 2:
+					this.$emit('update', 'day', '?');
+					break;
+				case 3:
+					this.$emit('update', 'day', this.cycleTotal);
+					break;
+				case 4:
+					this.$emit('update', 'day', this.averageTotal);
+					break;
+				case 5:
+					this.$emit('update', 'day', this.workday + 'W');
+					break;
+				case 6:
+					this.$emit('update', 'day', 'L');
+					break;
+				case 7:
+					this.$emit('update', 'day', this.checkboxString);
+					break;
+			}
+			('day rachange end');
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'day', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'day', this.averageTotal);
+			}
+		},
+		// 最近工作日值变化时
+		workdayChange() {
+			if (this.radioValue == '5') {
+				this.$emit('update', 'day', this.workdayCheck + 'W');
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '7') {
+				this.$emit('update', 'day', this.checkboxString);
+			}
+		}
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'workdayCheck': 'workdayChange',
+		'checkboxString': 'checkboxChange',
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, 1, 30)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 31, 31)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, 1, 30)
+			const average02 = this.checkNum(this.average02, 1, 31 - average01 || 0)
+			return average01 + '/' + average02;
+		},
+		// 计算工作日格式
+		workdayCheck: function () {
+			const workday = this.checkNum(this.workday, 1, 31)
+			return workday;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 114 - 0
src/components/Crontab/hour.vue

@@ -0,0 +1,114 @@
+<template>
+	<el-form size="small">
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				小时,允许的通配符[, - * /]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				周期从
+				<el-input-number v-model='cycle01' :min="0" :max="22" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="23" /> 小时
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				从
+				<el-input-number v-model='average01' :min="0" :max="22" /> 小时开始,每
+				<el-input-number v-model='average02' :min="1" :max="23 - average01 || 0" /> 小时执行一次
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="item in 24" :key="item" :value="item-1">{{item-1}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 1,
+			cycle01: 0,
+			cycle02: 1,
+			average01: 0,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-hour',
+	props: ['check', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			switch (this.radioValue) {
+				case 1:
+        	this.$emit('update', 'hour', '*')
+        	break;
+				case 2:
+					this.$emit('update', 'hour', this.cycleTotal);
+					break;
+				case 3:
+					this.$emit('update', 'hour', this.averageTotal);
+					break;
+				case 4:
+					this.$emit('update', 'hour', this.checkboxString);
+					break;
+			}
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '2') {
+				this.$emit('update', 'hour', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'hour', this.averageTotal);
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'hour', this.checkboxString);
+			}
+		}
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'checkboxString': 'checkboxChange'
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, 0, 22)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 23)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, 0, 22)
+			const average02 = this.checkNum(this.average02, 1, 23 - average01 || 0)
+			return average01 + '/' + average02;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 430 - 0
src/components/Crontab/index.vue

@@ -0,0 +1,430 @@
+<template>
+  <div>
+    <el-tabs type="border-card">
+      <el-tab-pane label="秒" v-if="shouldHide('second')">
+        <CrontabSecond
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronsecond"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="分钟" v-if="shouldHide('min')">
+        <CrontabMin
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronmin"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="小时" v-if="shouldHide('hour')">
+        <CrontabHour
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronhour"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="日" v-if="shouldHide('day')">
+        <CrontabDay
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronday"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="月" v-if="shouldHide('month')">
+        <CrontabMonth
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronmonth"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="周" v-if="shouldHide('week')">
+        <CrontabWeek
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronweek"
+        />
+      </el-tab-pane>
+
+      <el-tab-pane label="年" v-if="shouldHide('year')">
+        <CrontabYear
+          @update="updateCrontabValue"
+          :check="checkNumber"
+          :cron="crontabValueObj"
+          ref="cronyear"
+        />
+      </el-tab-pane>
+    </el-tabs>
+
+    <div class="popup-main">
+      <div class="popup-result">
+        <p class="title">时间表达式</p>
+        <table>
+          <thead>
+            <th v-for="item of tabTitles" width="40" :key="item">{{item}}</th>
+            <th>Cron 表达式</th>
+          </thead>
+          <tbody>
+            <td>
+              <span>{{crontabValueObj.second}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.min}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.hour}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.day}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.month}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.week}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueObj.year}}</span>
+            </td>
+            <td>
+              <span>{{crontabValueString}}</span>
+            </td>
+          </tbody>
+        </table>
+      </div>
+      <!-- <CrontabResult :ex="crontabValueString"></CrontabResult> -->
+
+      <div class="pop_btn">
+        <el-button size="small" type="primary" @click="submitFill">确定</el-button>
+        <el-button size="small" type="warning" @click="clearCron">重置</el-button>
+        <el-button size="small" @click="hidePopup">取消</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import CrontabSecond from "./second.vue";
+import CrontabMin from "./min.vue";
+import CrontabHour from "./hour.vue";
+import CrontabDay from "./day.vue";
+import CrontabMonth from "./month.vue";
+import CrontabWeek from "./week.vue";
+import CrontabYear from "./year.vue";
+import CrontabResult from "./result.vue";
+
+export default {
+  data() {
+    return {
+      tabTitles: ["秒", "分钟", "小时", "日", "月", "周", "年"],
+      tabActive: 0,
+      myindex: 0,
+      crontabValueObj: {
+        second: "*",
+        min: "*",
+        hour: "*",
+        day: "*",
+        month: "*",
+        week: "?",
+        year: "",
+      },
+    };
+  },
+  name: "vcrontab",
+  props: ["expression", "hideComponent"],
+  methods: {
+    shouldHide(key) {
+      if (this.hideComponent && this.hideComponent.includes(key)) return false;
+      return true;
+    },
+    resolveExp() {
+      // 反解析 表达式
+      if (this.expression) {
+        let arr = this.expression.split(" ");
+        if (arr.length >= 6) {
+          //6 位以上是合法表达式
+          let obj = {
+            second: arr[0],
+            min: arr[1],
+            hour: arr[2],
+            day: arr[3],
+            month: arr[4],
+            week: arr[5],
+            year: arr[6] ? arr[6] : "",
+          };
+          this.crontabValueObj = {
+            ...obj,
+          };
+          for (let i in obj) {
+            if (obj[i]) this.changeRadio(i, obj[i]);
+          }
+        }
+      } else {
+        // 没有传入的表达式 则还原
+        this.clearCron();
+      }
+    },
+    // tab切换值
+    tabCheck(index) {
+      this.tabActive = index;
+    },
+    // 由子组件触发,更改表达式组成的字段值
+    updateCrontabValue(name, value, from) {
+      "updateCrontabValue", name, value, from;
+      this.crontabValueObj[name] = value;
+      if (from && from !== name) {
+        console.log(`来自组件 ${from} 改变了 ${name} ${value}`);
+        this.changeRadio(name, value);
+      }
+    },
+    // 赋值到组件
+    changeRadio(name, value) {
+      let arr = ["second", "min", "hour", "month"],
+        refName = "cron" + name,
+        insValue;
+
+      if (!this.$refs[refName]) return;
+
+      if (arr.includes(name)) {
+        if (value === "*") {
+          insValue = 1;
+        } else if (value.indexOf("-") > -1) {
+          let indexArr = value.split("-");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].cycle01 = 0)
+            : (this.$refs[refName].cycle01 = indexArr[0]);
+          this.$refs[refName].cycle02 = indexArr[1];
+          insValue = 2;
+        } else if (value.indexOf("/") > -1) {
+          let indexArr = value.split("/");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].average01 = 0)
+            : (this.$refs[refName].average01 = indexArr[0]);
+          this.$refs[refName].average02 = indexArr[1];
+          insValue = 3;
+        } else {
+          insValue = 4;
+          this.$refs[refName].checkboxList = value.split(",");
+        }
+      } else if (name == "day") {
+        if (value === "*") {
+          insValue = 1;
+        } else if (value == "?") {
+          insValue = 2;
+        } else if (value.indexOf("-") > -1) {
+          let indexArr = value.split("-");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].cycle01 = 0)
+            : (this.$refs[refName].cycle01 = indexArr[0]);
+          this.$refs[refName].cycle02 = indexArr[1];
+          insValue = 3;
+        } else if (value.indexOf("/") > -1) {
+          let indexArr = value.split("/");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].average01 = 0)
+            : (this.$refs[refName].average01 = indexArr[0]);
+          this.$refs[refName].average02 = indexArr[1];
+          insValue = 4;
+        } else if (value.indexOf("W") > -1) {
+          let indexArr = value.split("W");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].workday = 0)
+            : (this.$refs[refName].workday = indexArr[0]);
+          insValue = 5;
+        } else if (value === "L") {
+          insValue = 6;
+        } else {
+          this.$refs[refName].checkboxList = value.split(",");
+          insValue = 7;
+        }
+      } else if (name == "week") {
+        if (value === "*") {
+          insValue = 1;
+        } else if (value == "?") {
+          insValue = 2;
+        } else if (value.indexOf("-") > -1) {
+          let indexArr = value.split("-");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].cycle01 = 0)
+            : (this.$refs[refName].cycle01 = indexArr[0]);
+          this.$refs[refName].cycle02 = indexArr[1];
+          insValue = 3;
+        } else if (value.indexOf("#") > -1) {
+          let indexArr = value.split("#");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].average01 = 1)
+            : (this.$refs[refName].average01 = indexArr[0]);
+          this.$refs[refName].average02 = indexArr[1];
+          insValue = 4;
+        } else if (value.indexOf("L") > -1) {
+          let indexArr = value.split("L");
+          isNaN(indexArr[0])
+            ? (this.$refs[refName].weekday = 1)
+            : (this.$refs[refName].weekday = indexArr[0]);
+          insValue = 5;
+        } else {
+          this.$refs[refName].checkboxList = value.split(",");
+          insValue = 6;
+        }
+      } else if (name == "year") {
+        if (value == "") {
+          insValue = 1;
+        } else if (value == "*") {
+          insValue = 2;
+        } else if (value.indexOf("-") > -1) {
+          insValue = 3;
+        } else if (value.indexOf("/") > -1) {
+          insValue = 4;
+        } else {
+          this.$refs[refName].checkboxList = value.split(",");
+          insValue = 5;
+        }
+      }
+      this.$refs[refName].radioValue = insValue;
+    },
+    // 表单选项的子组件校验数字格式(通过-props传递)
+    checkNumber(value, minLimit, maxLimit) {
+      // 检查必须为整数
+      value = Math.floor(value);
+      if (value < minLimit) {
+        value = minLimit;
+      } else if (value > maxLimit) {
+        value = maxLimit;
+      }
+      return value;
+    },
+    // 隐藏弹窗
+    hidePopup() {
+      this.$emit("hide");
+    },
+    // 填充表达式
+    submitFill() {
+      this.$emit("fill", this.crontabValueString);
+      this.hidePopup();
+    },
+    clearCron() {
+      // 还原选择项
+      ("准备还原");
+      this.crontabValueObj = {
+        second: "*",
+        min: "*",
+        hour: "*",
+        day: "*",
+        month: "*",
+        week: "?",
+        year: "",
+      };
+      for (let j in this.crontabValueObj) {
+        this.changeRadio(j, this.crontabValueObj[j]);
+      }
+    },
+  },
+  computed: {
+    crontabValueString: function() {
+      let obj = this.crontabValueObj;
+      let str =
+        obj.second +
+        " " +
+        obj.min +
+        " " +
+        obj.hour +
+        " " +
+        obj.day +
+        " " +
+        obj.month +
+        " " +
+        obj.week +
+        (obj.year == "" ? "" : " " + obj.year);
+      return str;
+    },
+  },
+  components: {
+    CrontabSecond,
+    CrontabMin,
+    CrontabHour,
+    CrontabDay,
+    CrontabMonth,
+    CrontabWeek,
+    CrontabYear,
+    CrontabResult,
+  },
+  watch: {
+    expression: "resolveExp",
+    hideComponent(value) {
+      // 隐藏部分组件
+    },
+  },
+  mounted: function() {
+    this.resolveExp();
+  },
+};
+</script>
+<style scoped>
+.pop_btn {
+  text-align: center;
+  margin-top: 20px;
+}
+.popup-main {
+  position: relative;
+  margin: 10px auto;
+  background: #fff;
+  border-radius: 5px;
+  font-size: 12px;
+  overflow: hidden;
+}
+.popup-title {
+  overflow: hidden;
+  line-height: 34px;
+  padding-top: 6px;
+  background: #f2f2f2;
+}
+.popup-result {
+  box-sizing: border-box;
+  line-height: 24px;
+  margin: 25px auto;
+  padding: 15px 10px 10px;
+  border: 1px solid #ccc;
+  position: relative;
+}
+.popup-result .title {
+  position: absolute;
+  top: -28px;
+  left: 50%;
+  width: 140px;
+  font-size: 14px;
+  margin-left: -70px;
+  text-align: center;
+  line-height: 30px;
+  background: #fff;
+}
+.popup-result table {
+  text-align: center;
+  width: 100%;
+  margin: 0 auto;
+}
+.popup-result table span {
+  display: block;
+  width: 100%;
+  font-family: arial;
+  line-height: 30px;
+  height: 30px;
+  white-space: nowrap;
+  overflow: hidden;
+  border: 1px solid #e8e8e8;
+}
+.popup-result-scroll {
+  font-size: 12px;
+  line-height: 24px;
+  height: 10em;
+  overflow-y: auto;
+}
+</style>

+ 116 - 0
src/components/Crontab/min.vue

@@ -0,0 +1,116 @@
+<template>
+	<el-form size="small">
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				分钟,允许的通配符[, - * /]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				周期从
+				<el-input-number v-model='cycle01' :min="0" :max="58" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="59" /> 分钟
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				从
+				<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始,每
+				<el-input-number v-model='average02' :min="1" :max="59 - average01 || 0" /> 分钟执行一次
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 1,
+			cycle01: 1,
+			cycle02: 2,
+			average01: 0,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-min',
+	props: ['check', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'min', '*', 'min');
+					break;
+				case 2:
+					this.$emit('update', 'min', this.cycleTotal, 'min');
+					break;
+				case 3:
+					this.$emit('update', 'min', this.averageTotal, 'min');
+					break;
+				case 4:
+					this.$emit('update', 'min', this.checkboxString, 'min');
+					break;
+			}
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '2') {
+				this.$emit('update', 'min', this.cycleTotal, 'min');
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'min', this.averageTotal, 'min');
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'min', this.checkboxString, 'min');
+			}
+		},
+
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'checkboxString': 'checkboxChange',
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, 0, 58)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, 0, 58)
+			const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
+			return average01 + '/' + average02;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 114 - 0
src/components/Crontab/month.vue

@@ -0,0 +1,114 @@
+<template>
+	<el-form size='small'>
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				月,允许的通配符[, - * /]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				周期从
+				<el-input-number v-model='cycle01' :min="1" :max="11" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 2" :max="12" /> 月
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				从
+				<el-input-number v-model='average01' :min="1" :max="11" /> 月开始,每
+				<el-input-number v-model='average02' :min="1" :max="12 - average01 || 0" /> 月月执行一次
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="item in 12" :key="item" :value="item">{{item}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 1,
+			cycle01: 1,
+			cycle02: 2,
+			average01: 1,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.check
+		}
+	},
+	name: 'crontab-month',
+	props: ['check', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'month', '*');
+					break;
+				case 2:
+					this.$emit('update', 'month', this.cycleTotal);
+					break;
+				case 3:
+					this.$emit('update', 'month', this.averageTotal);
+					break;
+				case 4:
+					this.$emit('update', 'month', this.checkboxString);
+					break;
+			}
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '2') {
+				this.$emit('update', 'month', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'month', this.averageTotal);
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'month', this.checkboxString);
+			}
+		}
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'checkboxString': 'checkboxChange'
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, 1, 11)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 2, 12)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, 1, 11)
+			const average02 = this.checkNum(this.average02, 1, 12 - average01 || 0)
+			return average01 + '/' + average02;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 559 - 0
src/components/Crontab/result.vue

@@ -0,0 +1,559 @@
+<template>
+	<div class="popup-result">
+		<p class="title">最近5次运行时间</p>
+		<ul class="popup-result-scroll">
+			<template v-if='isShow'>
+				<li v-for='item in resultList' :key="item">{{item}}</li>
+			</template>
+			<li v-else>计算结果中...</li>
+		</ul>
+	</div>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			dayRule: '',
+			dayRuleSup: '',
+			dateArr: [],
+			resultList: [],
+			isShow: false
+		}
+	},
+	name: 'crontab-result',
+	methods: {
+		// 表达式值变化时,开始去计算结果
+		expressionChange() {
+
+			// 计算开始-隐藏结果
+			this.isShow = false;
+			// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
+			let ruleArr = this.$options.propsData.ex.split(' ');
+			// 用于记录进入循环的次数
+			let nums = 0;
+			// 用于暂时存符号时间规则结果的数组
+			let resultArr = [];
+			// 获取当前时间精确至[年、月、日、时、分、秒]
+			let nTime = new Date();
+			let nYear = nTime.getFullYear();
+			let nMonth = nTime.getMonth() + 1;
+			let nDay = nTime.getDate();
+			let nHour = nTime.getHours();
+			let nMin = nTime.getMinutes();
+			let nSecond = nTime.getSeconds();
+			// 根据规则获取到近100年可能年数组、月数组等等
+			this.getSecondArr(ruleArr[0]);
+			this.getMinArr(ruleArr[1]);
+			this.getHourArr(ruleArr[2]);
+			this.getDayArr(ruleArr[3]);
+			this.getMonthArr(ruleArr[4]);
+			this.getWeekArr(ruleArr[5]);
+			this.getYearArr(ruleArr[6], nYear);
+			// 将获取到的数组赋值-方便使用
+			let sDate = this.dateArr[0];
+			let mDate = this.dateArr[1];
+			let hDate = this.dateArr[2];
+			let DDate = this.dateArr[3];
+			let MDate = this.dateArr[4];
+			let YDate = this.dateArr[5];
+			// 获取当前时间在数组中的索引
+			let sIdx = this.getIndex(sDate, nSecond);
+			let mIdx = this.getIndex(mDate, nMin);
+			let hIdx = this.getIndex(hDate, nHour);
+			let DIdx = this.getIndex(DDate, nDay);
+			let MIdx = this.getIndex(MDate, nMonth);
+			let YIdx = this.getIndex(YDate, nYear);
+			// 重置月日时分秒的函数(后面用的比较多)
+			const resetSecond = function () {
+				sIdx = 0;
+				nSecond = sDate[sIdx]
+			}
+			const resetMin = function () {
+				mIdx = 0;
+				nMin = mDate[mIdx]
+				resetSecond();
+			}
+			const resetHour = function () {
+				hIdx = 0;
+				nHour = hDate[hIdx]
+				resetMin();
+			}
+			const resetDay = function () {
+				DIdx = 0;
+				nDay = DDate[DIdx]
+				resetHour();
+			}
+			const resetMonth = function () {
+				MIdx = 0;
+				nMonth = MDate[MIdx]
+				resetDay();
+			}
+			// 如果当前年份不为数组中当前值
+			if (nYear !== YDate[YIdx]) {
+				resetMonth();
+			}
+			// 如果当前月份不为数组中当前值
+			if (nMonth !== MDate[MIdx]) {
+				resetDay();
+			}
+			// 如果当前“日”不为数组中当前值
+			if (nDay !== DDate[DIdx]) {
+				resetHour();
+			}
+			// 如果当前“时”不为数组中当前值
+			if (nHour !== hDate[hIdx]) {
+				resetMin();
+			}
+			// 如果当前“分”不为数组中当前值
+			if (nMin !== mDate[mIdx]) {
+				resetSecond();
+			}
+
+			// 循环年份数组
+			goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
+				let YY = YDate[Yi];
+				// 如果到达最大值时
+				if (nMonth > MDate[MDate.length - 1]) {
+					resetMonth();
+					continue;
+				}
+				// 循环月份数组
+				goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
+					// 赋值、方便后面运算
+					let MM = MDate[Mi];
+					MM = MM < 10 ? '0' + MM : MM;
+					// 如果到达最大值时
+					if (nDay > DDate[DDate.length - 1]) {
+						resetDay();
+						if (Mi == MDate.length - 1) {
+							resetMonth();
+							continue goYear;
+						}
+						continue;
+					}
+					// 循环日期数组
+					goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
+						// 赋值、方便后面运算
+						let DD = DDate[Di];
+						let thisDD = DD < 10 ? '0' + DD : DD;
+
+						// 如果到达最大值时
+						if (nHour > hDate[hDate.length - 1]) {
+							resetHour();
+							if (Di == DDate.length - 1) {
+								resetDay();
+								if (Mi == MDate.length - 1) {
+									resetMonth();
+									continue goYear;
+								}
+								continue goMonth;
+							}
+							continue;
+						}
+
+						// 判断日期的合法性,不合法的话也是跳出当前循环
+						if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && this.dayRule !== 'workDay' && this.dayRule !== 'lastWeek' && this.dayRule !== 'lastDay') {
+							resetDay();
+							continue goMonth;
+						}
+						// 如果日期规则中有值时
+						if (this.dayRule == 'lastDay') {
+							// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
+
+							if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+								while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+									DD--;
+
+									thisDD = DD < 10 ? '0' + DD : DD;
+								}
+							}
+						} else if (this.dayRule == 'workDay') {
+							// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
+							if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+								while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+									DD--;
+									thisDD = DD < 10 ? '0' + DD : DD;
+								}
+							}
+							// 获取达到条件的日期是星期X
+							let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
+							// 当星期日时
+							if (thisWeek == 1) {
+								// 先找下一个日,并判断是否为月底
+								DD++;
+								thisDD = DD < 10 ? '0' + DD : DD;
+								// 判断下一日已经不是合法日期
+								if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+									DD -= 3;
+								}
+							} else if (thisWeek == 7) {
+								// 当星期6时只需判断不是1号就可进行操作
+								if (this.dayRuleSup !== 1) {
+									DD--;
+								} else {
+									DD += 2;
+								}
+							}
+						} else if (this.dayRule == 'weekDay') {
+							// 如果指定了是星期几
+							// 获取当前日期是属于星期几
+							let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
+							// 校验当前星期是否在星期池(dayRuleSup)中
+							if (this.dayRuleSup.indexOf(thisWeek) < 0) {
+								// 如果到达最大值时
+								if (Di == DDate.length - 1) {
+									resetDay();
+									if (Mi == MDate.length - 1) {
+										resetMonth();
+										continue goYear;
+									}
+									continue goMonth;
+								}
+								continue;
+							}
+						} else if (this.dayRule == 'assWeek') {
+							// 如果指定了是第几周的星期几
+							// 获取每月1号是属于星期几
+							let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
+							if (this.dayRuleSup[1] >= thisWeek) {
+								DD = (this.dayRuleSup[0] - 1) * 7 + this.dayRuleSup[1] - thisWeek + 1;
+							} else {
+								DD = this.dayRuleSup[0] * 7 + this.dayRuleSup[1] - thisWeek + 1;
+							}
+						} else if (this.dayRule == 'lastWeek') {
+							// 如果指定了每月最后一个星期几
+							// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
+							if (this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+								while (DD > 0 && this.checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
+									DD--;
+									thisDD = DD < 10 ? '0' + DD : DD;
+								}
+							}
+							// 获取月末最后一天是星期几
+							let thisWeek = this.formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
+							// 找到要求中最近的那个星期几
+							if (this.dayRuleSup < thisWeek) {
+								DD -= thisWeek - this.dayRuleSup;
+							} else if (this.dayRuleSup > thisWeek) {
+								DD -= 7 - (this.dayRuleSup - thisWeek)
+							}
+						}
+						// 判断时间值是否小于10置换成“05”这种格式
+						DD = DD < 10 ? '0' + DD : DD;
+
+						// 循环“时”数组
+						goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
+							let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
+
+							// 如果到达最大值时
+							if (nMin > mDate[mDate.length - 1]) {
+								resetMin();
+								if (hi == hDate.length - 1) {
+									resetHour();
+									if (Di == DDate.length - 1) {
+										resetDay();
+										if (Mi == MDate.length - 1) {
+											resetMonth();
+											continue goYear;
+										}
+										continue goMonth;
+									}
+									continue goDay;
+								}
+								continue;
+							}
+							// 循环"分"数组
+							goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
+								let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
+
+								// 如果到达最大值时
+								if (nSecond > sDate[sDate.length - 1]) {
+									resetSecond();
+									if (mi == mDate.length - 1) {
+										resetMin();
+										if (hi == hDate.length - 1) {
+											resetHour();
+											if (Di == DDate.length - 1) {
+												resetDay();
+												if (Mi == MDate.length - 1) {
+													resetMonth();
+													continue goYear;
+												}
+												continue goMonth;
+											}
+											continue goDay;
+										}
+										continue goHour;
+									}
+									continue;
+								}
+								// 循环"秒"数组
+								goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
+									let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
+									// 添加当前时间(时间合法性在日期循环时已经判断)
+									if (MM !== '00' && DD !== '00') {
+										resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
+										nums++;
+									}
+									// 如果条数满了就退出循环
+									if (nums == 5) break goYear;
+									// 如果到达最大值时
+									if (si == sDate.length - 1) {
+										resetSecond();
+										if (mi == mDate.length - 1) {
+											resetMin();
+											if (hi == hDate.length - 1) {
+												resetHour();
+												if (Di == DDate.length - 1) {
+													resetDay();
+													if (Mi == MDate.length - 1) {
+														resetMonth();
+														continue goYear;
+													}
+													continue goMonth;
+												}
+												continue goDay;
+											}
+											continue goHour;
+										}
+										continue goMin;
+									}
+								} //goSecond
+							} //goMin
+						}//goHour
+					}//goDay
+				}//goMonth
+			}
+			// 判断100年内的结果条数
+			if (resultArr.length == 0) {
+				this.resultList = ['没有达到条件的结果!'];
+			} else {
+				this.resultList = resultArr;
+				if (resultArr.length !== 5) {
+					this.resultList.push('最近100年内只有上面' + resultArr.length + '条结果!')
+				}
+			}
+			// 计算完成-显示结果
+			this.isShow = true;
+
+
+		},
+		// 用于计算某位数字在数组中的索引
+		getIndex(arr, value) {
+			if (value <= arr[0] || value > arr[arr.length - 1]) {
+				return 0;
+			} else {
+				for (let i = 0; i < arr.length - 1; i++) {
+					if (value > arr[i] && value <= arr[i + 1]) {
+						return i + 1;
+					}
+				}
+			}
+		},
+		// 获取"年"数组
+		getYearArr(rule, year) {
+			this.dateArr[5] = this.getOrderArr(year, year + 100);
+			if (rule !== undefined) {
+				if (rule.indexOf('-') >= 0) {
+					this.dateArr[5] = this.getCycleArr(rule, year + 100, false)
+				} else if (rule.indexOf('/') >= 0) {
+					this.dateArr[5] = this.getAverageArr(rule, year + 100)
+				} else if (rule !== '*') {
+					this.dateArr[5] = this.getAssignArr(rule)
+				}
+			}
+		},
+		// 获取"月"数组
+		getMonthArr(rule) {
+			this.dateArr[4] = this.getOrderArr(1, 12);
+			if (rule.indexOf('-') >= 0) {
+				this.dateArr[4] = this.getCycleArr(rule, 12, false)
+			} else if (rule.indexOf('/') >= 0) {
+				this.dateArr[4] = this.getAverageArr(rule, 12)
+			} else if (rule !== '*') {
+				this.dateArr[4] = this.getAssignArr(rule)
+			}
+		},
+		// 获取"日"数组-主要为日期规则
+		getWeekArr(rule) {
+			// 只有当日期规则的两个值均为“”时则表达日期是有选项的
+			if (this.dayRule == '' && this.dayRuleSup == '') {
+				if (rule.indexOf('-') >= 0) {
+					this.dayRule = 'weekDay';
+					this.dayRuleSup = this.getCycleArr(rule, 7, false)
+				} else if (rule.indexOf('#') >= 0) {
+					this.dayRule = 'assWeek';
+					let matchRule = rule.match(/[0-9]{1}/g);
+					this.dayRuleSup = [Number(matchRule[1]), Number(matchRule[0])];
+					this.dateArr[3] = [1];
+					if (this.dayRuleSup[1] == 7) {
+						this.dayRuleSup[1] = 0;
+					}
+				} else if (rule.indexOf('L') >= 0) {
+					this.dayRule = 'lastWeek';
+					this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
+					this.dateArr[3] = [31];
+					if (this.dayRuleSup == 7) {
+						this.dayRuleSup = 0;
+					}
+				} else if (rule !== '*' && rule !== '?') {
+					this.dayRule = 'weekDay';
+					this.dayRuleSup = this.getAssignArr(rule)
+				}
+			}
+		},
+		// 获取"日"数组-少量为日期规则
+		getDayArr(rule) {
+			this.dateArr[3] = this.getOrderArr(1, 31);
+			this.dayRule = '';
+			this.dayRuleSup = '';
+			if (rule.indexOf('-') >= 0) {
+				this.dateArr[3] = this.getCycleArr(rule, 31, false)
+				this.dayRuleSup = 'null';
+			} else if (rule.indexOf('/') >= 0) {
+				this.dateArr[3] = this.getAverageArr(rule, 31)
+				this.dayRuleSup = 'null';
+			} else if (rule.indexOf('W') >= 0) {
+				this.dayRule = 'workDay';
+				this.dayRuleSup = Number(rule.match(/[0-9]{1,2}/g)[0]);
+				this.dateArr[3] = [this.dayRuleSup];
+			} else if (rule.indexOf('L') >= 0) {
+				this.dayRule = 'lastDay';
+				this.dayRuleSup = 'null';
+				this.dateArr[3] = [31];
+			} else if (rule !== '*' && rule !== '?') {
+				this.dateArr[3] = this.getAssignArr(rule)
+				this.dayRuleSup = 'null';
+			} else if (rule == '*') {
+				this.dayRuleSup = 'null';
+			}
+		},
+		// 获取"时"数组
+		getHourArr(rule) {
+			this.dateArr[2] = this.getOrderArr(0, 23);
+			if (rule.indexOf('-') >= 0) {
+				this.dateArr[2] = this.getCycleArr(rule, 24, true)
+			} else if (rule.indexOf('/') >= 0) {
+				this.dateArr[2] = this.getAverageArr(rule, 23)
+			} else if (rule !== '*') {
+				this.dateArr[2] = this.getAssignArr(rule)
+			}
+		},
+		// 获取"分"数组
+		getMinArr(rule) {
+			this.dateArr[1] = this.getOrderArr(0, 59);
+			if (rule.indexOf('-') >= 0) {
+				this.dateArr[1] = this.getCycleArr(rule, 60, true)
+			} else if (rule.indexOf('/') >= 0) {
+				this.dateArr[1] = this.getAverageArr(rule, 59)
+			} else if (rule !== '*') {
+				this.dateArr[1] = this.getAssignArr(rule)
+			}
+		},
+		// 获取"秒"数组
+		getSecondArr(rule) {
+			this.dateArr[0] = this.getOrderArr(0, 59);
+			if (rule.indexOf('-') >= 0) {
+				this.dateArr[0] = this.getCycleArr(rule, 60, true)
+			} else if (rule.indexOf('/') >= 0) {
+				this.dateArr[0] = this.getAverageArr(rule, 59)
+			} else if (rule !== '*') {
+				this.dateArr[0] = this.getAssignArr(rule)
+			}
+		},
+		// 根据传进来的min-max返回一个顺序的数组
+		getOrderArr(min, max) {
+			let arr = [];
+			for (let i = min; i <= max; i++) {
+				arr.push(i);
+			}
+			return arr;
+		},
+		// 根据规则中指定的零散值返回一个数组
+		getAssignArr(rule) {
+			let arr = [];
+			let assiginArr = rule.split(',');
+			for (let i = 0; i < assiginArr.length; i++) {
+				arr[i] = Number(assiginArr[i])
+			}
+			arr.sort(this.compare)
+			return arr;
+		},
+		// 根据一定算术规则计算返回一个数组
+		getAverageArr(rule, limit) {
+			let arr = [];
+			let agArr = rule.split('/');
+			let min = Number(agArr[0]);
+			let step = Number(agArr[1]);
+			while (min <= limit) {
+				arr.push(min);
+				min += step;
+			}
+			return arr;
+		},
+		// 根据规则返回一个具有周期性的数组
+		getCycleArr(rule, limit, status) {
+			// status--表示是否从0开始(则从1开始)
+			let arr = [];
+			let cycleArr = rule.split('-');
+			let min = Number(cycleArr[0]);
+			let max = Number(cycleArr[1]);
+			if (min > max) {
+				max += limit;
+			}
+			for (let i = min; i <= max; i++) {
+				let add = 0;
+				if (status == false && i % limit == 0) {
+					add = limit;
+				}
+				arr.push(Math.round(i % limit + add))
+			}
+			arr.sort(this.compare)
+			return arr;
+		},
+		// 比较数字大小(用于Array.sort)
+		compare(value1, value2) {
+			if (value2 - value1 > 0) {
+				return -1;
+			} else {
+				return 1;
+			}
+		},
+		// 格式化日期格式如:2017-9-19 18:04:33
+		formatDate(value, type) {
+			// 计算日期相关值
+			let time = typeof value == 'number' ? new Date(value) : value;
+			let Y = time.getFullYear();
+			let M = time.getMonth() + 1;
+			let D = time.getDate();
+			let h = time.getHours();
+			let m = time.getMinutes();
+			let s = time.getSeconds();
+			let week = time.getDay();
+			// 如果传递了type的话
+			if (type == undefined) {
+				return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
+			} else if (type == 'week') {
+				// 在quartz中 1为星期日
+				return week + 1;
+			}
+		},
+		// 检查日期是否存在
+		checkDate(value) {
+			let time = new Date(value);
+			let format = this.formatDate(time)
+			return value === format;
+		}
+	},
+	watch: {
+		'ex': 'expressionChange'
+	},
+	props: ['ex'],
+	mounted: function () {
+		// 初始化 获取一次结果
+		this.expressionChange();
+	}
+}
+
+</script>

+ 117 - 0
src/components/Crontab/second.vue

@@ -0,0 +1,117 @@
+<template>
+	<el-form size="small">
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				秒,允许的通配符[, - * /]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				周期从
+				<el-input-number v-model='cycle01' :min="0" :max="58" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : 1" :max="59" /> 秒
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				从
+				<el-input-number v-model='average01' :min="0" :max="58" /> 秒开始,每
+				<el-input-number v-model='average02' :min="1" :max="59 - average01 || 0" /> 秒执行一次
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="item in 60" :key="item" :value="item-1">{{item-1}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 1,
+			cycle01: 1,
+			cycle02: 2,
+			average01: 0,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-second',
+	props: ['check', 'radioParent'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'second', '*', 'second');
+					break;
+				case 2:
+					this.$emit('update', 'second', this.cycleTotal);
+					break;
+				case 3:
+					this.$emit('update', 'second', this.averageTotal);
+					break;
+				case 4:
+					this.$emit('update', 'second', this.checkboxString);
+					break;
+			}
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '2') {
+				this.$emit('update', 'second', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'second', this.averageTotal);
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'second', this.checkboxString);
+			}
+		}
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'checkboxString': 'checkboxChange',
+		radioParent() {
+			this.radioValue = this.radioParent
+		}
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, 0, 58)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : 1, 59)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, 0, 58)
+			const average02 = this.checkNum(this.average02, 1, 59 - average01 || 0)
+			return average01 + '/' + average02;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 202 - 0
src/components/Crontab/week.vue

@@ -0,0 +1,202 @@
+<template>
+	<el-form size='small'>
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="1">
+				周,允许的通配符[, - * ? / L #]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="2">
+				不指定
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="3">
+				周期从星期
+				<el-select clearable v-model="cycle01">
+					<el-option
+						v-for="(item,index) of weekList"
+						:key="index"
+						:label="item.value"
+						:value="item.key"
+						:disabled="item.key === 1"
+					>{{item.value}}</el-option>
+				</el-select>
+				-
+				<el-select clearable v-model="cycle02">
+					<el-option
+						v-for="(item,index) of weekList"
+						:key="index"
+						:label="item.value"
+						:value="item.key"
+						:disabled="item.key < cycle01 && item.key !== 1"
+					>{{item.value}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="4">
+				第
+				<el-input-number v-model='average01' :min="1" :max="4" /> 周的星期
+				<el-select clearable v-model="average02">
+					<el-option v-for="(item,index) of weekList" :key="index" :label="item.value" :value="item.key">{{item.value}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="5">
+				本月最后一个星期
+				<el-select clearable v-model="weekday">
+					<el-option v-for="(item,index) of weekList" :key="index" :label="item.value" :value="item.key">{{item.value}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio v-model='radioValue' :label="6">
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple style="width:100%">
+					<el-option v-for="(item,index) of weekList" :key="index" :label="item.value" :value="String(item.key)">{{item.value}}</el-option>
+				</el-select>
+			</el-radio>
+		</el-form-item>
+
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			radioValue: 2,
+			weekday: 2,
+			cycle01: 2,
+			cycle02: 3,
+			average01: 1,
+			average02: 2,
+			checkboxList: [],
+			weekList: [
+				{
+					key: 2,
+					value: '星期一'
+				},
+				{
+					key: 3,
+					value: '星期二'
+				},
+				{
+					key: 4,
+					value: '星期三'
+				},
+				{
+					key: 5,
+					value: '星期四'
+				},
+				{
+					key: 6,
+					value: '星期五'
+				},
+				{
+					key: 7,
+					value: '星期六'
+				},
+				{
+					key: 1,
+					value: '星期日'
+				}
+			],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-week',
+	props: ['check', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			if (this.radioValue !== 2 && this.cron.day !== '?') {
+				this.$emit('update', 'day', '?', 'week');
+			}
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'week', '*');
+					break;
+				case 2:
+					this.$emit('update', 'week', '?');
+					break;
+				case 3:
+					this.$emit('update', 'week', this.cycleTotal);
+					break;
+				case 4:
+					this.$emit('update', 'week', this.averageTotal);
+					break;
+				case 5:
+					this.$emit('update', 'week', this.weekdayCheck + 'L');
+					break;
+				case 6:
+					this.$emit('update', 'week', this.checkboxString);
+					break;
+			}
+		},
+
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'week', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'week', this.averageTotal);
+			}
+		},
+		// 最近工作日值变化时
+		weekdayChange() {
+			if (this.radioValue == '5') {
+				this.$emit('update', 'week', this.weekday + 'L');
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '6') {
+				this.$emit('update', 'week', this.checkboxString);
+			}
+		},
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'weekdayCheck': 'weekdayChange',
+		'checkboxString': 'checkboxChange',
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			this.cycle01 = this.checkNum(this.cycle01, 1, 7)
+			this.cycle02 = this.checkNum(this.cycle02, 1, 7)
+			return this.cycle01 + '-' + this.cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			this.average01 = this.checkNum(this.average01, 1, 4)
+			this.average02 = this.checkNum(this.average02, 1, 7)
+			return this.average02 + '#' + this.average01;
+		},
+		// 最近的工作日(格式)
+		weekdayCheck: function () {
+			this.weekday = this.checkNum(this.weekday, 1, 7)
+			return this.weekday;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str == '' ? '*' : str;
+		}
+	}
+}
+</script>

+ 131 - 0
src/components/Crontab/year.vue

@@ -0,0 +1,131 @@
+<template>
+	<el-form size="small">
+		<el-form-item>
+			<el-radio :label="1" v-model='radioValue'>
+				不填,允许的通配符[, - * /]
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio :label="2" v-model='radioValue'>
+				每年
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio :label="3" v-model='radioValue'>
+				周期从
+				<el-input-number v-model='cycle01' :min='fullYear' :max="2098" /> -
+				<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : fullYear + 1" :max="2099" />
+			</el-radio>
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio :label="4" v-model='radioValue'>
+				从
+				<el-input-number v-model='average01' :min='fullYear' :max="2098"/> 年开始,每
+				<el-input-number v-model='average02' :min="1" :max="2099 - average01 || fullYear" /> 年执行一次
+			</el-radio>
+
+		</el-form-item>
+
+		<el-form-item>
+			<el-radio :label="5" v-model='radioValue'>
+				指定
+				<el-select clearable v-model="checkboxList" placeholder="可多选" multiple>
+					<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
+				</el-select>
+			</el-radio>
+		</el-form-item>
+	</el-form>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			fullYear: 0,
+			radioValue: 1,
+			cycle01: 0,
+			cycle02: 0,
+			average01: 0,
+			average02: 1,
+			checkboxList: [],
+			checkNum: this.$options.propsData.check
+		}
+	},
+	name: 'crontab-year',
+	props: ['check', 'month', 'cron'],
+	methods: {
+		// 单选按钮值变化时
+		radioChange() {
+			switch (this.radioValue) {
+				case 1:
+					this.$emit('update', 'year', '');
+					break;
+				case 2:
+					this.$emit('update', 'year', '*');
+					break;
+				case 3:
+					this.$emit('update', 'year', this.cycleTotal);
+					break;
+				case 4:
+					this.$emit('update', 'year', this.averageTotal);
+					break;
+				case 5:
+					this.$emit('update', 'year', this.checkboxString);
+					break;
+			}
+		},
+		// 周期两个值变化时
+		cycleChange() {
+			if (this.radioValue == '3') {
+				this.$emit('update', 'year', this.cycleTotal);
+			}
+		},
+		// 平均两个值变化时
+		averageChange() {
+			if (this.radioValue == '4') {
+				this.$emit('update', 'year', this.averageTotal);
+			}
+		},
+		// checkbox值变化时
+		checkboxChange() {
+			if (this.radioValue == '5') {
+				this.$emit('update', 'year', this.checkboxString);
+			}
+		}
+	},
+	watch: {
+		'radioValue': 'radioChange',
+		'cycleTotal': 'cycleChange',
+		'averageTotal': 'averageChange',
+		'checkboxString': 'checkboxChange'
+	},
+	computed: {
+		// 计算两个周期值
+		cycleTotal: function () {
+			const cycle01 = this.checkNum(this.cycle01, this.fullYear, 2098)
+			const cycle02 = this.checkNum(this.cycle02, cycle01 ? cycle01 + 1 : this.fullYear + 1, 2099)
+			return cycle01 + '-' + cycle02;
+		},
+		// 计算平均用到的值
+		averageTotal: function () {
+			const average01 = this.checkNum(this.average01, this.fullYear, 2098)
+			const average02 = this.checkNum(this.average02, 1, 2099 - average01 || this.fullYear)
+			return average01 + '/' + average02;
+		},
+		// 计算勾选的checkbox值合集
+		checkboxString: function () {
+			let str = this.checkboxList.join();
+			return str;
+		}
+	},
+	mounted: function () {
+		// 仅获取当前年份
+		this.fullYear = Number(new Date().getFullYear());
+		this.cycle01 = this.fullYear
+		this.average01 = this.fullYear
+	}
+}
+</script>

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

@@ -0,0 +1,45 @@
+<template>
+  <div>
+    <svg t="1492500959545" @click="toggleClick" class="wscn-icon hamburger" :class="{'is-active':isActive}" style="" viewBox="0 0 1024 1024"
+      version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1691" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64">
+      <path d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
+        p-id="1692"></path>
+      <path d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
+        p-id="1693"></path>
+      <path d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
+        p-id="1694"></path>
+    </svg>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'hamburger',
+  props: {
+    isActive: {
+      type: Boolean,
+      default: false
+    },
+    toggleClick: {
+      type: Function,
+      default: null
+    }
+  }
+}
+</script>
+
+<style scoped>
+.hamburger {
+	display: inline-block;
+	cursor: pointer;
+	width: 20px;
+	height: 20px;
+	transform: rotate(90deg);
+	transition: .38s;
+	transform-origin: 50% 50%;
+}
+
+.hamburger.is-active {
+	transform: rotate(0deg);
+}
+</style>

+ 104 - 0
src/components/RightToolbar/index.vue

@@ -0,0 +1,104 @@
+<template>
+  <div class="top-right-btn" :style="style">
+    <el-row>
+      <el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
+        <el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
+      </el-tooltip>
+      <el-tooltip class="item" effect="dark" content="刷新" placement="top">
+        <el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
+      </el-tooltip>
+      <el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
+        <el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" />
+      </el-tooltip>
+    </el-row>
+    <el-dialog :title="title" :visible.sync="open" append-to-body>
+      <el-transfer
+        :titles="['显示', '隐藏']"
+        v-model="value"
+        :data="columns"
+        @change="dataChange"
+      ></el-transfer>
+    </el-dialog>
+  </div>
+</template>
+<script>
+export default {
+  name: "RightToolbar",
+  data() {
+    return {
+      // 显隐数据
+      value: [],
+      // 弹出层标题
+      title: "显示/隐藏",
+      // 是否显示弹出层
+      open: false,
+    };
+  },
+  props: {
+    showSearch: {
+      type: Boolean,
+      default: true,
+    },
+    columns: {
+      type: Array,
+    },
+    search: {
+      type: Boolean,
+      default: true,
+    },
+    gutter: {
+      type: Number,
+      default: 10,
+    },
+  },
+  computed: {
+    style() {
+      const ret = {};
+      if (this.gutter) {
+        ret.marginRight = `${this.gutter / 2}px`;
+      }
+      return ret;
+    }
+  },
+  created() {
+    // 显隐列初始默认隐藏列
+    for (let item in this.columns) {
+      if (this.columns[item].visible === false) {
+        this.value.push(parseInt(item));
+      }
+    }
+  },
+  methods: {
+    // 搜索
+    toggleSearch() {
+      this.$emit("update:showSearch", !this.showSearch);
+    },
+    // 刷新
+    refresh() {
+      this.$emit("queryTable");
+    },
+    // 右侧列表元素变化
+    dataChange(data) {
+      for (let item in this.columns) {
+        const key = this.columns[item].key;
+        this.columns[item].visible = !data.includes(key);
+      }
+    },
+    // 打开显隐列dialog
+    showColumn() {
+      this.open = true;
+    },
+  },
+};
+</script>
+<style lang="scss" scoped>
+::v-deep .el-transfer__button {
+  border-radius: 50%;
+  padding: 12px;
+  display: block;
+  margin-left: 0px;
+}
+::v-deep .el-transfer__button:first-child {
+  margin-bottom: 10px;
+}
+</style>

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

@@ -0,0 +1,65 @@
+<template>
+  <div>
+    <svg t="1508738709248" @click='click' class="screenfull-svg" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
+      p-id="2069" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32">
+      <path d="M333.493443 428.647617 428.322206 333.832158 262.572184 168.045297 366.707916 64.444754 64.09683 64.444754 63.853283 366.570793 167.283957 262.460644Z"
+        p-id="2070"></path>
+      <path d="M854.845439 760.133334 688.61037 593.95864 593.805144 688.764889 759.554142 854.56096 655.44604 958.161503 958.055079 958.161503 958.274066 656.035464Z"
+        p-id="2071"></path>
+      <path d="M688.535669 428.550403 854.31025 262.801405 957.935352 366.921787 957.935352 64.34754 655.809313 64.081481 759.919463 167.535691 593.70793 333.731874Z"
+        p-id="2072"></path>
+      <path d="M333.590658 594.033341 167.8171 759.804852 64.218604 655.67219 64.218604 958.270996 366.342596 958.502263 262.234493 855.071589 428.421466 688.86108Z"
+        p-id="2073"></path>
+    </svg>
+  </div>
+</template>
+
+<script>
+import screenfull from 'screenfull'
+
+export default {
+  name: 'screenfull',
+  props: {
+    width: {
+      type: Number,
+      default: 22
+    },
+    height: {
+      type: Number,
+      default: 22
+    },
+    fill: {
+      type: String,
+      default: '#48576a'
+    }
+  },
+  data() {
+    return {
+      isFullscreen: false
+    }
+  },
+  methods: {
+    click() {
+      if (!screenfull.enabled) {
+        this.$message({
+          message: 'you browser can not work',
+          type: 'warning'
+        })
+        return false
+      }
+      screenfull.toggle()
+    }
+  }
+}
+</script>
+
+<style scoped>
+.screenfull-svg {
+  display: inline-block;
+  cursor: pointer;
+  fill: #5a5e66;;
+  width: 20px;
+  height: 20px;
+  vertical-align: 10px;
+}
+</style>

+ 57 - 0
src/components/ScrollBar/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll" >
+    <div class="scroll-wrapper" ref="scrollWrapper" :style="{top: top + 'px'}">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+const delta = 15
+
+export default {
+  name: 'scrollBar',
+  data() {
+    return {
+      top: 0
+    }
+  },
+  methods: {
+    handleScroll(e) {
+      const eventDelta = e.wheelDelta || -e.deltaY * 3
+      const $container = this.$refs.scrollContainer
+      const $containerHeight = $container.offsetHeight
+      const $wrapper = this.$refs.scrollWrapper
+      const $wrapperHeight = $wrapper.offsetHeight
+      if (eventDelta > 0) {
+        this.top = Math.min(0, this.top + eventDelta)
+      } else {
+        if ($containerHeight - delta < $wrapperHeight) {
+          if (this.top < -($wrapperHeight - $containerHeight + delta)) {
+            this.top = this.top
+          } else {
+            this.top = Math.max(this.top + eventDelta, $containerHeight - $wrapperHeight - delta)
+          }
+        } else {
+          this.top = 0
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+@import '../../styles/variables.scss';
+
+.scroll-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background-color: $menuBg;
+  .scroll-wrapper {
+    position: absolute;
+     width: 100%!important;
+  }
+}
+</style>

+ 72 - 0
src/components/ScrollPane/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <div class="scroll-container" ref="scrollContainer" @wheel.prevent="handleScroll">
+    <div class="scroll-wrapper" ref="scrollWrapper" :style="{left: left + 'px'}">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+const padding = 15 // tag's padding
+
+export default {
+  name: 'scrollPane',
+  data() {
+    return {
+      left: 0
+    }
+  },
+  methods: {
+    handleScroll(e) {
+      const eventDelta = e.wheelDelta || -e.deltaY * 3
+      const $container = this.$refs.scrollContainer
+      const $containerWidth = $container.offsetWidth
+      const $wrapper = this.$refs.scrollWrapper
+      const $wrapperWidth = $wrapper.offsetWidth
+
+      if (eventDelta > 0) {
+        this.left = Math.min(0, this.left + eventDelta)
+      } else {
+        if ($containerWidth - padding < $wrapperWidth) {
+          if (this.left < -($wrapperWidth - $containerWidth + padding)) {
+            this.left = this.left
+          } else {
+            this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding)
+          }
+        } else {
+          this.left = 0
+        }
+      }
+    },
+    moveToTarget($target) {
+      const $container = this.$refs.scrollContainer
+      const $containerWidth = $container.offsetWidth
+      const $targetLeft = $target.offsetLeft
+      const $targetWidth = $target.offsetWidth
+
+      if ($targetLeft < -this.left) {
+        // tag in the left
+        this.left = -$targetLeft + padding
+      } else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
+        // tag in the current view
+        // eslint-disable-line
+      } else {
+        // tag in the right
+        this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
+      }
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+.scroll-container {
+  white-space: nowrap;
+  position: relative;
+  overflow: hidden;
+  width: 100%;
+  .scroll-wrapper {
+    position: absolute;
+  }
+}
+</style>

+ 181 - 0
src/components/SkuDesc/index.vue

@@ -0,0 +1,181 @@
+<template>
+  <div class="tinymce-container editor-container">
+    <textarea class="tinymce-textarea" :id="tinymceId"></textarea>
+  </div>
+</template>
+
+<script>
+import plugins from './plugins'
+import toolbar from './toolbar'
+import { uploadPic } from '@/api/storage'
+
+export default {
+  name: 'tinymce',
+  props: {
+    id: {
+      type: String
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    toolbar: {
+      type: Array,
+      required: false,
+      default() {
+        return []
+      }
+    },
+    menubar: {
+      default: 'file edit insert view format table'
+    },
+    height: {
+      type: Number,
+      required: false,
+      default: 360
+    }
+  },
+  data() {
+    return {
+      hasChange: false,
+      hasInit: false,
+      tinymceId: this.id || 'vue-tinymce-' + +new Date()
+    }
+  },
+  watch: {
+    value(val) {
+      if (!this.hasChange && this.hasInit) {
+        this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val))
+      }
+    }
+  },
+  mounted() {
+    this.initTinymce()
+  },
+  activated() {
+    this.initTinymce()
+  },
+  deactivated() {
+    this.destroyTinymce()
+  },
+  methods: {
+    initTinymce() {
+      const _this = this
+      window.tinymce.init({
+        selector: `#${this.tinymceId}`,
+        height: this.height,
+        language: 'zh_CN',
+        body_class: 'panel-body ',
+        object_resizing: false,
+        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+        menubar: this.menubar,
+        plugins: plugins,
+        end_container_on_empty_block: true,
+        powerpaste_word_import: 'clean',
+        code_dialog_height: 450,
+        code_dialog_width: 1000,
+        advlist_bullet_styles: 'square',
+        advlist_number_styles: 'default',
+        default_link_target: '_blank',
+        relative_urls : false,
+		remove_script_host : false,
+		document_base_url : 'http://xiaoyou.dgtis.com/',
+        link_title: false,
+        init_instance_callback: editor => {
+          if (_this.value) {
+            editor.setContent(_this.value)
+          }
+          _this.hasInit = true
+          editor.on('NodeChange Change KeyUp', () => {
+            this.hasChange = true
+            this.$emit('input', editor.getContent({ format: 'raw' }))
+          })
+        },
+        images_upload_handler: function(blobInfo, success, failure) {
+          const formData = new FormData()
+          formData.append('file', blobInfo.blob())
+          uploadPic(formData).then(res => {
+          var resurl=res.data.data.url;
+          console.log(resurl);
+            success(resurl)
+          }).catch(() => {
+            failure('上传失败,请重新上传')
+          })
+        }
+        // 整合七牛上传
+        // images_dataimg_filter(img) {
+        //   setTimeout(() => {
+        //     const $image = $(img);
+        //     $image.removeAttr('width');
+        //     $image.removeAttr('height');
+        //     if ($image[0].height && $image[0].width) {
+        //       $image.attr('data-wscntype', 'image');
+        //       $image.attr('data-wscnh', $image[0].height);
+        //       $image.attr('data-wscnw', $image[0].width);
+        //       $image.addClass('wscnph');
+        //     }
+        //   }, 0);
+        //   return img
+        // },
+        // images_upload_handler(blobInfo, success, failure, progress) {
+        //   progress(0);
+        //   const token = _this.$store.getters.token;
+        //   getToken(token).then(response => {
+        //     const url = response.data.qiniu_url;
+        //     const formData = new FormData();
+        //     formData.append('token', response.data.qiniu_token);
+        //     formData.append('key', response.data.qiniu_key);
+        //     formData.append('file', blobInfo.blob(), url);
+        //     upload(formData).then(() => {
+        //       success(url);
+        //       progress(100);
+        //     })
+        //   }).catch(err => {
+        //     failure('出现未知问题,刷新页面,或者联系程序员')
+        //     console.log(err);
+        //   });
+        // },
+      })
+    },
+    destroyTinymce() {
+      if (window.tinymce.get(this.tinymceId)) {
+        window.tinymce.get(this.tinymceId).destroy()
+      }
+    },
+    setContent(value) {
+      window.tinymce.get(this.tinymceId).setContent(value)
+    },
+    getContent() {
+      window.tinymce.get(this.tinymceId).getContent()
+    },
+    imageSuccessCBK(arr) {
+      const _this = this
+      arr.forEach(v => {
+        window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
+      })
+    }
+  },
+  destroyed() {
+    this.destroyTinymce()
+  }
+}
+</script>
+
+<style scoped>
+.tinymce-container {
+  position: relative
+}
+.tinymce-textarea {
+  visibility: hidden;
+  z-index: -1;
+}
+.editor-custom-btn-container {
+  position: absolute;
+  right: 4px;
+  top: 4px;
+  /*z-index: 2005;*/
+}
+.editor-upload-btn {
+  display: inline-block;
+}
+</style>

+ 7 - 0
src/components/SkuDesc/plugins.js

@@ -0,0 +1,7 @@
+// Any plugins you want to use has to be imported
+// Detail plugins list see https://www.tinymce.com/docs/plugins/
+// Custom builds see https://www.tinymce.com/download/custom-builds/
+
+const plugins = ['advlist anchor autolink autoresize autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
+
+export default plugins

+ 6 - 0
src/components/SkuDesc/toolbar.js

@@ -0,0 +1,6 @@
+// Here is a list of the toolbar
+// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
+
+const toolbar = ['fontsizeselect bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen insertdatetime media table emoticons forecolor backcolor']
+
+export default toolbar

+ 76 - 0
src/components/Sticky/index.vue

@@ -0,0 +1,76 @@
+<template>
+  <div :style="{height:height+'px',zIndex:zIndex}">
+    <div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}">
+      <slot>
+        <div>sticky</div>
+      </slot>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'Sticky',
+  props: {
+    stickyTop: {
+      type: Number,
+      default: 0
+    },
+    zIndex: {
+      type: Number,
+      default: 1
+    },
+    className: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      active: false,
+      position: '',
+      currentTop: '',
+      width: undefined,
+      height: undefined,
+      child: null,
+      stickyHeight: 0
+    }
+  },
+  mounted() {
+    this.height = this.$el.getBoundingClientRect().height
+    window.addEventListener('scroll', this.handleScroll)
+  },
+  activated() {
+    this.handleScroll()
+  },
+  destroyed() {
+    window.removeEventListener('scroll', this.handleScroll)
+  },
+  methods: {
+    sticky() {
+      if (this.active) {
+        return
+      }
+      this.position = 'fixed'
+      this.active = true
+      this.width = this.width + 'px'
+    },
+    reset() {
+      if (!this.active) {
+        return
+      }
+      this.position = ''
+      this.width = 'auto'
+      this.active = false
+    },
+    handleScroll() {
+      this.width = this.$el.getBoundingClientRect().width
+      const offsetTop = this.$el.getBoundingClientRect().top
+      if (offsetTop <= this.stickyTop) {
+        this.sticky()
+        return
+      }
+      this.reset()
+    }
+  }
+}
+</script>

+ 42 - 0
src/components/SvgIcon/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <svg :class="svgClass" aria-hidden="true">
+    <use :xlink:href="iconName"></use>
+  </svg>
+</template>
+
+<script>
+export default {
+  name: 'svg-icon',
+  props: {
+    iconClass: {
+      type: String,
+      required: true
+    },
+    className: {
+      type: String
+    }
+  },
+  computed: {
+    iconName() {
+      return `#icon-${this.iconClass}`
+    },
+    svgClass() {
+      if (this.className) {
+        return 'svg-icon ' + this.className
+      } else {
+        return 'svg-icon'
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+</style>

+ 181 - 0
src/components/Tinymce/index.vue

@@ -0,0 +1,181 @@
+<template>
+  <div class="tinymce-container editor-container">
+    <textarea class="tinymce-textarea" :id="tinymceId"></textarea>
+  </div>
+</template>
+
+<script>
+import plugins from './plugins'
+import toolbar from './toolbar'
+import { uploadPic } from '@/api/storage'
+
+export default {
+  name: 'tinymce',
+  props: {
+    id: {
+      type: String
+    },
+    value: {
+      type: String,
+      default: ''
+    },
+    toolbar: {
+      type: Array,
+      required: false,
+      default() {
+        return []
+      }
+    },
+    menubar: {
+      default: 'file edit insert view format table'
+    },
+    height: {
+      type: Number,
+      required: false,
+      default: 360
+    }
+  },
+  data() {
+    return {
+      hasChange: false,
+      hasInit: false,
+      tinymceId: this.id || 'vue-tinymce-' + +new Date()
+    }
+  },
+  watch: {
+    value(val) {
+      if (!this.hasChange && this.hasInit) {
+        this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val))
+      }
+    }
+  },
+  mounted() {
+    this.initTinymce()
+  },
+  activated() {
+    this.initTinymce()
+  },
+  deactivated() {
+    this.destroyTinymce()
+  },
+  methods: {
+    initTinymce() {
+      const _this = this
+      window.tinymce.init({
+        selector: `#${this.tinymceId}`,
+        height: this.height,
+        language: 'zh_CN',
+        body_class: 'panel-body ',
+        object_resizing: false,
+        toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+        menubar: this.menubar,
+        plugins: plugins,
+        end_container_on_empty_block: true,
+        powerpaste_word_import: 'clean',
+        code_dialog_height: 450,
+        code_dialog_width: 1000,
+        advlist_bullet_styles: 'square',
+        advlist_number_styles: 'default',
+        default_link_target: '_blank',
+        relative_urls : false,
+		remove_script_host : false,
+		document_base_url : 'http://xiaoyou.dgtis.com/',
+        link_title: false,
+        init_instance_callback: editor => {
+          if (_this.value) {
+            editor.setContent(_this.value)
+          }
+          _this.hasInit = true
+          editor.on('NodeChange Change KeyUp', () => {
+            this.hasChange = true
+            this.$emit('input', editor.getContent({ format: 'raw' }))
+          })
+        },
+        images_upload_handler: function(blobInfo, success, failure) {
+          const formData = new FormData()
+          formData.append('file', blobInfo.blob())
+          uploadPic(formData).then(res => {
+          var resurl=res.data.data.url;
+          console.log(resurl);
+            success(resurl)
+          }).catch(() => {
+            failure('上传失败,请重新上传')
+          })
+        }
+        // 整合七牛上传
+        // images_dataimg_filter(img) {
+        //   setTimeout(() => {
+        //     const $image = $(img);
+        //     $image.removeAttr('width');
+        //     $image.removeAttr('height');
+        //     if ($image[0].height && $image[0].width) {
+        //       $image.attr('data-wscntype', 'image');
+        //       $image.attr('data-wscnh', $image[0].height);
+        //       $image.attr('data-wscnw', $image[0].width);
+        //       $image.addClass('wscnph');
+        //     }
+        //   }, 0);
+        //   return img
+        // },
+        // images_upload_handler(blobInfo, success, failure, progress) {
+        //   progress(0);
+        //   const token = _this.$store.getters.token;
+        //   getToken(token).then(response => {
+        //     const url = response.data.qiniu_url;
+        //     const formData = new FormData();
+        //     formData.append('token', response.data.qiniu_token);
+        //     formData.append('key', response.data.qiniu_key);
+        //     formData.append('file', blobInfo.blob(), url);
+        //     upload(formData).then(() => {
+        //       success(url);
+        //       progress(100);
+        //     })
+        //   }).catch(err => {
+        //     failure('出现未知问题,刷新页面,或者联系程序员')
+        //     console.log(err);
+        //   });
+        // },
+      })
+    },
+    destroyTinymce() {
+      if (window.tinymce.get(this.tinymceId)) {
+        window.tinymce.get(this.tinymceId).destroy()
+      }
+    },
+    setContent(value) {
+      window.tinymce.get(this.tinymceId).setContent(value)
+    },
+    getContent() {
+      window.tinymce.get(this.tinymceId).getContent()
+    },
+    imageSuccessCBK(arr) {
+      const _this = this
+      arr.forEach(v => {
+        window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
+      })
+    }
+  },
+  destroyed() {
+    this.destroyTinymce()
+  }
+}
+</script>
+
+<style scoped>
+.tinymce-container {
+  position: relative
+}
+.tinymce-textarea {
+  visibility: hidden;
+  z-index: -1;
+}
+.editor-custom-btn-container {
+  position: absolute;
+  right: 4px;
+  top: 4px;
+  /*z-index: 2005;*/
+}
+.editor-upload-btn {
+  display: inline-block;
+}
+</style>

+ 7 - 0
src/components/Tinymce/plugins.js

@@ -0,0 +1,7 @@
+// Any plugins you want to use has to be imported
+// Detail plugins list see https://www.tinymce.com/docs/plugins/
+// Custom builds see https://www.tinymce.com/download/custom-builds/
+
+const plugins = ['advlist anchor autolink autoresize autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime legacyoutput link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
+
+export default plugins

+ 6 - 0
src/components/Tinymce/toolbar.js

@@ -0,0 +1,6 @@
+// Here is a list of the toolbar
+// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
+
+const toolbar = ['fontsizeselect bold italic underline strikethrough alignleft aligncenter alignright outdent indent  blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak fullscreen insertdatetime media table emoticons forecolor backcolor']
+
+export default toolbar

+ 123 - 0
src/components/Upload/singleImage.vue

@@ -0,0 +1,123 @@
+<template>
+    <div class="upload-container">
+        <el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
+            :on-success="handleImageScucess">
+            <i class="el-icon-upload"></i>
+            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+        </el-upload>
+        <div class="image-preview">
+            <div class="image-preview-wrapper" v-show="imageUrl.length>1">
+                <img :src="imageUrl+'?imageView2/1/w/200/h/200'">
+                <div class="image-preview-action">
+                    <i @click="rmImage" class="el-icon-delete"></i>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+// 预览效果见付费文章
+import { getToken } from '@/api/qiniu'
+
+export default {
+  name: 'singleImageUpload',
+  props: {
+    value: String
+  },
+  computed: {
+    imageUrl() {
+      return this.value
+    }
+  },
+  data() {
+    return {
+      tempUrl: '',
+      dataObj: { token: '', key: '' }
+    }
+  },
+  methods: {
+    rmImage() {
+      this.emitInput('')
+    },
+    emitInput(val) {
+      this.$emit('input', val)
+    },
+    handleImageScucess() {
+      this.emitInput(this.tempUrl)
+    },
+    beforeUpload() {
+      const _self = this
+      return new Promise((resolve, reject) => {
+        getToken().then(response => {
+          const key = response.data.qiniu_key
+          const token = response.data.qiniu_token
+          _self._data.dataObj.token = token
+          _self._data.dataObj.key = key
+          this.tempUrl = response.data.qiniu_url
+          resolve(true)
+        }).catch(err => {
+          console.log(err)
+          reject(false)
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+    @import "src/styles/mixin.scss";
+    .upload-container {
+        width: 100%;
+        position: relative;
+        @include clearfix;
+        .image-uploader {
+            width: 60%;
+            float: left;
+        }
+        .image-preview {
+            width: 200px;
+            height: 200px;
+            position: relative;
+            border: 1px dashed #d9d9d9;
+            float: left;
+            margin-left: 50px;
+            .image-preview-wrapper {
+                position: relative;
+                width: 100%;
+                height: 100%;
+                img {
+                    width: 100%;
+                    height: 100%;
+                }
+            }
+            .image-preview-action {
+                position: absolute;
+                width: 100%;
+                height: 100%;
+                left: 0;
+                top: 0;
+                cursor: default;
+                text-align: center;
+                color: #fff;
+                opacity: 0;
+                font-size: 20px;
+                background-color: rgba(0, 0, 0, .5);
+                transition: opacity .3s;
+                cursor: pointer;
+                text-align: center;
+                line-height: 200px;
+                .el-icon-delete {
+                    font-size: 36px;
+                }
+            }
+            &:hover {
+                .image-preview-action {
+                    opacity: 1;
+                }
+            }
+        }
+    }
+
+</style>

+ 118 - 0
src/components/Upload/singleImage2.vue

@@ -0,0 +1,118 @@
+<template>
+	<div class="singleImageUpload2 upload-container">
+		<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
+		  :on-success="handleImageScucess">
+			<i class="el-icon-upload"></i>
+			<div class="el-upload__text">Drag或<em>点击上传</em></div>
+		</el-upload>
+		<div v-show="imageUrl.length>0" class="image-preview">
+			<div class="image-preview-wrapper" v-show="imageUrl.length>1">
+				<img :src="imageUrl">
+				<div class="image-preview-action">
+					<i @click="rmImage" class="el-icon-delete"></i>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import { getToken } from '@/api/qiniu'
+
+export default {
+  name: 'singleImageUpload2',
+  props: {
+    value: String
+  },
+  computed: {
+    imageUrl() {
+      return this.value
+    }
+  },
+  data() {
+    return {
+      tempUrl: '',
+      dataObj: { token: '', key: '' }
+    }
+  },
+  methods: {
+    rmImage() {
+      this.emitInput('')
+    },
+    emitInput(val) {
+      this.$emit('input', val)
+    },
+    handleImageScucess() {
+      this.emitInput(this.tempUrl)
+    },
+    beforeUpload() {
+      const _self = this
+      return new Promise((resolve, reject) => {
+        getToken().then(response => {
+          const key = response.data.qiniu_key
+          const token = response.data.qiniu_token
+          _self._data.dataObj.token = token
+          _self._data.dataObj.key = key
+          this.tempUrl = response.data.qiniu_url
+          resolve(true)
+        }).catch(() => {
+          reject(false)
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+.upload-container {
+	width: 100%;
+	height: 100%;
+	position: relative;
+	.image-uploader {
+		height: 100%;
+	}
+	.image-preview {
+		width: 100%;
+		height: 100%;
+		position: absolute;
+		left: 0px;
+		top: 0px;
+		border: 1px dashed #d9d9d9;
+		.image-preview-wrapper {
+			position: relative;
+			width: 100%;
+			height: 100%;
+			img {
+				width: 100%;
+				height: 100%;
+			}
+		}
+		.image-preview-action {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			left: 0;
+			top: 0;
+			cursor: default;
+			text-align: center;
+			color: #fff;
+			opacity: 0;
+			font-size: 20px;
+			background-color: rgba(0, 0, 0, .5);
+			transition: opacity .3s;
+			cursor: pointer;
+			text-align: center;
+			line-height: 200px;
+			.el-icon-delete {
+				font-size: 36px;
+			}
+		}
+		&:hover {
+			.image-preview-action {
+				opacity: 1;
+			}
+		}
+	}
+}
+</style>

+ 145 - 0
src/components/Upload/singleImage3.vue

@@ -0,0 +1,145 @@
+<template>
+	<div class="upload-container">
+		<el-upload class="image-uploader" :data="dataObj" drag :multiple="false" :show-file-list="false" action="https://httpbin.org/post"
+		  :on-success="handleImageScucess">
+			<i class="el-icon-upload"></i>
+			<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
+		</el-upload>
+		<div class="image-preview image-app-preview">
+			<div class="image-preview-wrapper" v-show="imageUrl.length>1">
+				<img :src="imageUrl">
+				<div class="image-preview-action">
+					<i @click="rmImage" class="el-icon-delete"></i>
+				</div>
+			</div>
+		</div>
+		<div class="image-preview">
+			<div class="image-preview-wrapper" v-show="imageUrl.length>1">
+				<img :src="imageUrl">
+				<div class="image-preview-action">
+					<i @click="rmImage" class="el-icon-delete"></i>
+				</div>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import { getToken } from '@/api/qiniu'
+
+export default {
+  name: 'singleImageUpload3',
+  props: {
+    value: String
+  },
+  computed: {
+    imageUrl() {
+      return this.value
+    }
+  },
+  data() {
+    return {
+      tempUrl: '',
+      dataObj: { token: '', key: '' }
+    }
+  },
+  methods: {
+    rmImage() {
+      this.emitInput('')
+    },
+    emitInput(val) {
+      this.$emit('input', val)
+    },
+    handleImageScucess(file) {
+      this.emitInput(file.files.file)
+    },
+    beforeUpload() {
+      const _self = this
+      return new Promise((resolve, reject) => {
+        getToken().then(response => {
+          const key = response.data.qiniu_key
+          const token = response.data.qiniu_token
+          _self._data.dataObj.token = token
+          _self._data.dataObj.key = key
+          this.tempUrl = response.data.qiniu_url
+          resolve(true)
+        }).catch(err => {
+          console.log(err)
+          reject(false)
+        })
+      })
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+@import "src/styles/mixin.scss";
+.upload-container {
+	width: 100%;
+	position: relative;
+	@include clearfix;
+	.image-uploader {
+		width: 35%;
+		float: left;
+	}
+	.image-preview {
+		width: 200px;
+		height: 200px;
+		position: relative;
+		border: 1px dashed #d9d9d9;
+		float: left;
+		margin-left: 50px;
+		.image-preview-wrapper {
+			position: relative;
+			width: 100%;
+			height: 100%;
+			img {
+				width: 100%;
+				height: 100%;
+			}
+		}
+		.image-preview-action {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			left: 0;
+			top: 0;
+			cursor: default;
+			text-align: center;
+			color: #fff;
+			opacity: 0;
+			font-size: 20px;
+			background-color: rgba(0, 0, 0, .5);
+			transition: opacity .3s;
+			cursor: pointer;
+			text-align: center;
+			line-height: 200px;
+			.el-icon-delete {
+				font-size: 36px;
+			}
+		}
+		&:hover {
+			.image-preview-action {
+				opacity: 1;
+			}
+		}
+	}
+	.image-app-preview {
+		width: 320px;
+		height: 180px;
+		position: relative;
+		border: 1px dashed #d9d9d9;
+		float: left;
+		margin-left: 50px;
+		.app-fake-conver {
+			height: 44px;
+			position: absolute;
+			width: 100%; // background: rgba(0, 0, 0, .1);
+			text-align: center;
+			line-height: 64px;
+			color: #fff;
+		}
+	}
+}
+</style>

+ 91 - 0
src/directive/sticky.js

@@ -0,0 +1,91 @@
+const vueSticky = {}
+let listenAction
+vueSticky.install = Vue => {
+  Vue.directive('sticky', {
+    inserted(el, binding) {
+      const params = binding.value || {}
+      const stickyTop = params.stickyTop || 0
+      const zIndex = params.zIndex || 1000
+      const elStyle = el.style
+
+      elStyle.position = '-webkit-sticky'
+      elStyle.position = 'sticky'
+      // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
+      // if (~elStyle.position.indexOf('sticky')) {
+      //     elStyle.top = `${stickyTop}px`;
+      //     elStyle.zIndex = zIndex;
+      //     return
+      // }
+      const elHeight = el.getBoundingClientRect().height
+      const elWidth = el.getBoundingClientRect().width
+      elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
+
+      const parentElm = el.parentNode || document.documentElement
+      const placeholder = document.createElement('div')
+      placeholder.style.display = 'none'
+      placeholder.style.width = `${elWidth}px`
+      placeholder.style.height = `${elHeight}px`
+      parentElm.insertBefore(placeholder, el)
+
+      let active = false
+
+      const getScroll = (target, top) => {
+        const prop = top ? 'pageYOffset' : 'pageXOffset'
+        const method = top ? 'scrollTop' : 'scrollLeft'
+        let ret = target[prop]
+        if (typeof ret !== 'number') {
+          ret = window.document.documentElement[method]
+        }
+        return ret
+      }
+
+      const sticky = () => {
+        if (active) {
+          return
+        }
+        if (!elStyle.height) {
+          elStyle.height = `${el.offsetHeight}px`
+        }
+
+        elStyle.position = 'fixed'
+        elStyle.width = `${elWidth}px`
+        placeholder.style.display = 'inline-block'
+        active = true
+      }
+
+      const reset = () => {
+        if (!active) {
+          return
+        }
+
+        elStyle.position = ''
+        placeholder.style.display = 'none'
+        active = false
+      }
+
+      const check = () => {
+        const scrollTop = getScroll(window, true)
+        const offsetTop = el.getBoundingClientRect().top
+        if (offsetTop < stickyTop) {
+          sticky()
+        } else {
+          if (scrollTop < elHeight + stickyTop) {
+            reset()
+          }
+        }
+      }
+      listenAction = () => {
+        check()
+      }
+
+      window.addEventListener('scroll', listenAction)
+    },
+
+    unbind() {
+      window.removeEventListener('scroll', listenAction)
+    }
+  })
+}
+
+export default vueSticky
+

+ 13 - 0
src/directive/waves/index.js

@@ -0,0 +1,13 @@
+import waves from './waves'
+
+const install = function(Vue) {
+  Vue.directive('waves', waves)
+}
+
+if (window.Vue) {
+  window.waves = waves
+  Vue.use(install); // eslint-disable-line
+}
+
+waves.install = install
+export default waves

+ 26 - 0
src/directive/waves/waves.css

@@ -0,0 +1,26 @@
+.waves-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;
+}
+
+.waves-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;
+}

+ 0 - 0
src/directive/waves/waves.js


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.