瀏覽代碼

init from phab

xielq 5 年之前
父節點
當前提交
7d8b078c8a
共有 100 個文件被更改,包括 5225 次插入0 次删除
  1. 16 0
      Dockerfile
  2. 36 0
      build/build.js
  3. 45 0
      build/check-versions.js
  4. 9 0
      build/dev-client.js
  5. 75 0
      build/dev-server.js
  6. 61 0
      build/utils.js
  7. 91 0
      build/webpack.base.conf.js
  8. 38 0
      build/webpack.dev.conf.js
  9. 101 0
      build/webpack.prod.conf.js
  10. 14 0
      build/zip.js
  11. 6 0
      config/dev.env.js
  12. 45 0
      config/index.js
  13. 3 0
      config/prod.env.js
  14. 31 0
      index.html
  15. 66 0
      package.json
  16. 16 0
      post.sh
  17. 65 0
      src/App.vue
  18. 18 0
      src/assets/common.css
  19. 二進制
      src/assets/logo-uas.png
  20. 54 0
      src/components/Common/Bread/Bread.vue
  21. 45 0
      src/components/Common/DialogInfo/DialogInfo.js
  22. 41 0
      src/components/Common/DialogInfo/DialogInfo.vue
  23. 2 0
      src/components/Common/DialogInfo/index.js
  24. 544 0
      src/components/Common/FormData/FormData.js
  25. 212 0
      src/components/Common/FormData/FormData.vue
  26. 2 0
      src/components/Common/FormData/index.js
  27. 179 0
      src/components/Common/HeadNav/HeadNav.js
  28. 194 0
      src/components/Common/HeadNav/HeadNav.vue
  29. 54 0
      src/components/Common/LeftMenu/LeftMenu.js
  30. 43 0
      src/components/Common/LeftMenu/LeftMenu.less
  31. 75 0
      src/components/Common/LeftMenu/LeftMenu.vue
  32. 255 0
      src/components/Common/ListData/ListData.js
  33. 173 0
      src/components/Common/ListData/ListData.vue
  34. 2 0
      src/components/Common/ListData/index.js
  35. 8 0
      src/components/Common/index.js
  36. 86 0
      src/components/Init/Init.js
  37. 25 0
      src/components/Init/Init.less
  38. 46 0
      src/components/Init/Init.vue
  39. 2 0
      src/components/Init/index.js
  40. 132 0
      src/components/Login/Login.js
  41. 36 0
      src/components/Login/Login.less
  42. 51 0
      src/components/Login/Login.vue
  43. 2 0
      src/components/Login/index.js
  44. 27 0
      src/components/Modules/Database/Master/List.js
  45. 32 0
      src/components/Modules/Database/Master/List.vue
  46. 2 0
      src/components/Modules/Database/Master/index.js
  47. 63 0
      src/components/Modules/Database/Setting/DBA/List.js
  48. 75 0
      src/components/Modules/Database/Setting/DBA/List.vue
  49. 2 0
      src/components/Modules/Database/Setting/DBA/index.js
  50. 3 0
      src/components/Modules/Database/Setting/index.js
  51. 4 0
      src/components/Modules/Database/index.js
  52. 73 0
      src/components/Modules/Deployment/Artifact/Edit/Edit.js
  53. 65 0
      src/components/Modules/Deployment/Artifact/Edit/Edit.vue
  54. 2 0
      src/components/Modules/Deployment/Artifact/Edit/index.js
  55. 26 0
      src/components/Modules/Deployment/Artifact/Item/Item.js
  56. 32 0
      src/components/Modules/Deployment/Artifact/Item/Item.vue
  57. 2 0
      src/components/Modules/Deployment/Artifact/Item/index.js
  58. 40 0
      src/components/Modules/Deployment/Artifact/List/List.js
  59. 48 0
      src/components/Modules/Deployment/Artifact/List/List.vue
  60. 2 0
      src/components/Modules/Deployment/Artifact/List/index.js
  61. 5 0
      src/components/Modules/Deployment/Artifact/index.js
  62. 79 0
      src/components/Modules/Deployment/Container/Edit/Edit.js
  63. 99 0
      src/components/Modules/Deployment/Container/Edit/Edit.vue
  64. 2 0
      src/components/Modules/Deployment/Container/Edit/index.js
  65. 80 0
      src/components/Modules/Deployment/Container/List/List.js
  66. 51 0
      src/components/Modules/Deployment/Container/List/List.vue
  67. 2 0
      src/components/Modules/Deployment/Container/List/index.js
  68. 4 0
      src/components/Modules/Deployment/Container/index.js
  69. 150 0
      src/components/Modules/Deployment/Project/Edit/Edit.js
  70. 87 0
      src/components/Modules/Deployment/Project/Edit/Edit.vue
  71. 2 0
      src/components/Modules/Deployment/Project/Edit/index.js
  72. 48 0
      src/components/Modules/Deployment/Project/List/List.js
  73. 49 0
      src/components/Modules/Deployment/Project/List/List.vue
  74. 2 0
      src/components/Modules/Deployment/Project/List/index.js
  75. 26 0
      src/components/Modules/Deployment/Project/Log/Log.js
  76. 25 0
      src/components/Modules/Deployment/Project/Log/Log.vue
  77. 2 0
      src/components/Modules/Deployment/Project/Log/index.js
  78. 5 0
      src/components/Modules/Deployment/Project/index.js
  79. 5 0
      src/components/Modules/Deployment/index.js
  80. 0 0
      src/components/Modules/Monitor/Alarm/Edit/index.js
  81. 0 0
      src/components/Modules/Monitor/Alarm/List/index.js
  82. 4 0
      src/components/Modules/Monitor/Alarm/index.js
  83. 0 0
      src/components/Modules/Monitor/Backup/Edit/index.js
  84. 0 0
      src/components/Modules/Monitor/Backup/List/index.js
  85. 4 0
      src/components/Modules/Monitor/Backup/index.js
  86. 4 0
      src/components/Modules/Monitor/index.js
  87. 242 0
      src/components/Modules/Schedule/List/List.js
  88. 119 0
      src/components/Modules/Schedule/List/List.vue
  89. 2 0
      src/components/Modules/Schedule/List/index.js
  90. 44 0
      src/components/Modules/Schedule/Setting/List.js
  91. 67 0
      src/components/Modules/Schedule/Setting/List.vue
  92. 2 0
      src/components/Modules/Schedule/Setting/index.js
  93. 173 0
      src/components/Modules/Schedule/Stat/Stat.js
  94. 220 0
      src/components/Modules/Schedule/Stat/Stat.vue
  95. 2 0
      src/components/Modules/Schedule/Stat/index.js
  96. 5 0
      src/components/Modules/Schedule/index.js
  97. 45 0
      src/components/Modules/Setting/Api/List/List.js
  98. 67 0
      src/components/Modules/Setting/Api/List/List.vue
  99. 2 0
      src/components/Modules/Setting/Api/List/index.js
  100. 3 0
      src/components/Modules/Setting/Api/index.js

+ 16 - 0
Dockerfile

@@ -0,0 +1,16 @@
+FROM nginx
+
+RUN mkdir -p /app
+WORKDIR /app
+
+RUN cnpm install
+
+RUN npm run build
+
+COPY dist/* /app
+
+COPY runtime/docker/* /app
+
+RUN chmod +x /app/run.sh
+
+CMD [ "/app/run.sh" ]

+ 36 - 0
build/build.js

@@ -0,0 +1,36 @@
+// https://github.com/shelljs/shelljs
+require('./check-versions')()
+require('shelljs/global')
+env.NODE_ENV = 'production'
+
+var path = require('path')
+var config = require('../config')
+var ora = require('ora')
+var webpack = require('webpack')
+var webpackConfig = require('./webpack.prod.conf')
+
+console.log(
+  '  Tip:\n' +
+  '  Built files are meant to be served over an HTTP server.\n' +
+  '  Opening index.html over file:// won\'t work.\n'
+)
+
+var spinner = ora('building for production...')
+spinner.start()
+
+var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
+rm('-rf', assetsPath)
+mkdir('-p', assetsPath)
+cp('-R', 'static/*', assetsPath)
+
+webpack(webpackConfig, function (err, stats) {
+  spinner.stop()
+  if (err) throw err
+  process.stdout.write(stats.toString({
+    colors: true,
+    modules: false,
+    children: false,
+    chunks: false,
+    chunkModules: false
+  }) + '\n')
+})

+ 45 - 0
build/check-versions.js

@@ -0,0 +1,45 @@
+var semver = require('semver')
+var chalk = require('chalk')
+var packageConfig = require('../package.json')
+var exec = function (cmd) {
+  return require('child_process')
+    .execSync(cmd).toString().trim()
+}
+
+var versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  },
+  {
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  }
+]
+
+module.exports = function () {
+  var warnings = []
+  for (var i = 0; i < versionRequirements.length; i++) {
+    var mod = versionRequirements[i]
+    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 (var i = 0; i < warnings.length; i++) {
+      var warning = warnings[i]
+      console.log('  ' + warning)
+    }
+    console.log()
+    process.exit(1)
+  }
+}

+ 9 - 0
build/dev-client.js

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

+ 75 - 0
build/dev-server.js

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

+ 61 - 0
build/utils.js

@@ -0,0 +1,61 @@
+var path = require('path')
+var config = require('../config')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+
+exports.assetsPath = function (_path) {
+  var assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loaders) {
+    var sourceLoader = loaders.map(function (loader) {
+      var extraParamChar
+      if (/\?/.test(loader)) {
+        loader = loader.replace(/\?/, '-loader?')
+        extraParamChar = '&'
+      } else {
+        loader = loader + '-loader'
+        extraParamChar = '?'
+      }
+      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
+    }).join('!')
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
+    } else {
+      return ['vue-style-loader', sourceLoader].join('!')
+    }
+  }
+
+  // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(['css']),
+    postcss: generateLoaders(['css']),
+    less: generateLoaders(['css', 'less']),
+    sass: generateLoaders(['css', 'sass?indentedSyntax']),
+    scss: generateLoaders(['css', 'sass']),
+    stylus: generateLoaders(['css', 'stylus']),
+    styl: generateLoaders(['css', 'stylus'])
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  var output = []
+  var loaders = exports.cssLoaders(options)
+  for (var extension in loaders) {
+    var loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      loader: loader
+    })
+  }
+  return output
+}

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

@@ -0,0 +1,91 @@
+var path = require('path')
+var config = require('../config')
+var utils = require('./utils')
+var projectRoot = path.resolve(__dirname, '../')
+
+var env = process.env.NODE_ENV
+    // check env & config/index.js to decide whether to enable CSS source maps for the
+    // various preprocessor loaders added to vue-loader at the end of this file
+var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
+var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
+var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
+
+module.exports = {
+    entry: {
+        app: './src/main.js'
+    },
+    output: {
+        path: config.build.assetsRoot,
+        publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
+        filename: '[name].js'
+    },
+    resolve: {
+        extensions: ['', '.js', '.vue', '.json'],
+        fallback: [path.join(__dirname, '../node_modules')],
+        alias: {
+            'vue$': 'vue/dist/vue.common.js',
+            'src': path.resolve(__dirname, '../src'),
+            'assets': path.resolve(__dirname, '../src/assets'),
+
+            'components': path.resolve(__dirname, '../src/components'),
+
+            'common': path.resolve(__dirname, '../src/components/Common'),
+
+            'modules': path.resolve(__dirname, '../src/components/Modules'),
+
+            'demo': path.resolve(__dirname, '../src/components/Modules/Demo'),
+            'function': path.resolve(__dirname, '../src/components/Modules/Function'),
+
+            'config': path.resolve(__dirname, '../src/config'),
+            'store': path.resolve(__dirname, '../src/store'),
+            'libs': path.resolve(__dirname, '../src/libs'),
+            'util': path.resolve(__dirname, '../src/util'),
+            'register': path.resolve(__dirname, '../src/register'),
+            'plugins': path.resolve(__dirname, '../src/plugins'),
+            'mixin': path.resolve(__dirname, '../src/mixin'),
+        }
+    },
+    resolveLoader: {
+        fallback: [path.join(__dirname, '../node_modules')]
+    },
+    module: {
+        loaders: [{
+            test: /\.vue$/,
+            loader: 'vue'
+        }, {
+            test: /\.js$/,
+            loader: 'babel',
+            include: [
+                path.join(projectRoot, 'src')
+            ],
+            exclude: /node_modules/
+        }, {
+            test: /\.json$/,
+            loader: 'json'
+        }, {
+            test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+            loader: 'url',
+            query: {
+                limit: 10000,
+                name: utils.assetsPath('img/[name].[hash:7].[ext]')
+            }
+        }, {
+            test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+            loader: 'url',
+            query: {
+                limit: 10000,
+                name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+            }
+        }]
+    },
+    vue: {
+        loaders: utils.cssLoaders({
+            sourceMap: useCssSourceMap
+        }),
+        postcss: [
+            require('autoprefixer')({
+                browsers: ['last 2 versions']
+            })
+        ]
+    }
+}

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

@@ -0,0 +1,38 @@
+var config = require('../config')
+var webpack = require('webpack')
+var merge = require('webpack-merge')
+var utils = require('./utils')
+var baseWebpackConfig = require('./webpack.base.conf')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var FriendlyErrors = require('friendly-errors-webpack-plugin')
+
+// add hot-reload related code to entry chunks
+Object.keys(baseWebpackConfig.entry).forEach(function(name) {
+    baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
+})
+
+module.exports = merge(baseWebpackConfig, {
+    module: {
+        loaders: utils.styleLoaders({
+            sourceMap: config.dev.cssSourceMap
+        })
+    },
+    // eval-source-map is faster for development
+    devtool: '#eval-source-map',
+    plugins: [
+        new webpack.DefinePlugin({
+            'process.env': config.dev.env
+        }),
+        // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
+        new webpack.optimize.OccurrenceOrderPlugin(),
+        new webpack.HotModuleReplacementPlugin(),
+        new webpack.NoErrorsPlugin(),
+        // https://github.com/ampedandwired/html-webpack-plugin
+        new HtmlWebpackPlugin({
+            filename: 'index.html',
+            template: 'index.html',
+            inject: true
+        }),
+        new FriendlyErrors()
+    ]
+})

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

@@ -0,0 +1,101 @@
+var path = require('path')
+var config = require('../config')
+var utils = require('./utils')
+var webpack = require('webpack')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var env = config.build.env
+
+var webpackConfig = merge(baseWebpackConfig, {
+    module: {
+        loaders: utils.styleLoaders({
+            sourceMap: config.build.productionSourceMap,
+            extract: true
+        })
+    },
+    devtool: config.build.productionSourceMap ? '#source-map' : false,
+    output: {
+        path: config.build.assetsRoot,
+        filename: utils.assetsPath('js/[name].[chunkhash].js'),
+        chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+    },
+    vue: {
+        loaders: utils.cssLoaders({
+            sourceMap: config.build.productionSourceMap,
+            extract: true
+        })
+    },
+    plugins: [
+        // http://vuejs.github.io/vue-loader/en/workflow/production.html
+        new webpack.DefinePlugin({
+            'process.env': env
+        }),
+        new webpack.optimize.UglifyJsPlugin({
+            compress: {
+                warnings: false
+            }
+        }),
+        new webpack.optimize.OccurrenceOrderPlugin(),
+        // extract css into its own file
+        new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
+        // 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,
+            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'
+        }),
+        // split vendor js into its own file
+        new webpack.optimize.CommonsChunkPlugin({
+            name: 'vendor',
+            minChunks: function(module, count) {
+                // 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',
+            chunks: ['vendor']
+        })
+    ]
+})
+
+if (config.build.productionGzip) {
+    var 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
+        })
+    )
+}
+
+module.exports = webpackConfig

+ 14 - 0
build/zip.js

@@ -0,0 +1,14 @@
+var fs = require('fs');
+var path = require('path');
+var archiver = require('archiver');
+
+var output = fs.createWriteStream(path.join(__dirname, '../dist.zip'));
+var archive = archiver('zip');
+
+archive.on('error', function(err){
+  throw err;
+});
+
+archive.pipe(output);
+archive.directory(path.join(__dirname, '../dist/'), '');
+archive.finalize();

+ 6 - 0
config/dev.env.js

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

+ 45 - 0
config/index.js

@@ -0,0 +1,45 @@
+// see http://vuejs-templates.github.io/webpack for documentation.
+var path = require('path');
+
+module.exports = {
+    build: {
+        env: require('./prod.env'),
+        index: path.resolve(__dirname, '../dist/index.html'),
+        assetsRoot: path.resolve(__dirname, '../dist'),
+        assetsSubDirectory: 'static',
+        assetsPublicPath: './',
+        productionSourceMap: true,
+        // 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']
+    },
+    dev: {
+        env: require('./dev.env'),
+        port: 8090,
+        assetsSubDirectory: 'static',
+        assetsPublicPath: '/',
+        proxyTable: {
+          '/uas_manage_client': {
+            target: 'http://localhost:9090',
+            changeOrigin: true
+          },
+          '/uas_schedular': {
+            target: 'http://localhost:9001',
+            changeOrigin: true
+          },
+          '/uas_database': {
+            target: 'http://localhost:9002',
+            changeOrigin: true
+          }
+        },
+        // CSS Sourcemaps off by default because relative paths are "buggy"
+        // with this option, according to the CSS-Loader README
+        // (https://github.com/webpack/css-loader#sourcemaps)
+        // In our experience, they generally work as expected,
+        // just be aware of this issue when enabling this option.
+        cssSourceMap: false
+    }
+}

+ 3 - 0
config/prod.env.js

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

+ 31 - 0
index.html

@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no">
+        <link rel="stylesheet" href="static/font-awesome/css/font-awesome.min.css">
+        <link rel="stylesheet" href="static/animate/css/animate.min.css">
+        <title>UAS系统客户端管理</title>
+        <style>
+          *{
+            -webkit-box-sizing: border-box;
+            -moz-box-sizing: border-box;
+            box-sizing: border-box;
+            margin:0px;
+            padding:0px;
+          }
+          .animated{
+            -webkit-animation-duration: .3s;
+            animation-duration: .3s;
+          }
+          .animated.hinge{
+            -webkit-animation-duration: .6s;
+            animation-duration: .6s
+          }
+        </style>
+    </head>
+    <body>
+        <div id="app"></div>
+        <script src='static/libs/jquery/jquery.min.js'></script>
+    </body>
+</html>

+ 66 - 0
package.json

@@ -0,0 +1,66 @@
+{
+  "name": "ops-uas-client-web",
+  "version": "1.0.0",
+  "description": "ops-uas-client-web",
+  "private": true,
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "build": "node build/build.js",
+    "zip": "node build/zip.js"
+  },
+  "dependencies": {
+    "archiver": "^2.1.0",
+    "axios": "^0.15.3",
+    "css-loader": "^0.25.0",
+    "element-ui": "^1.3.0-beta.3",
+    "font-awesome": "^4.7.0",
+    "g2": "^2.3.9",
+    "less": "^2.7.2",
+    "less-loader": "^2.2.3",
+    "nprogress": "^0.2.0",
+    "qs": "^6.4.0",
+    "vue": "^2.3.2",
+    "vue-axios": "^1.2.2",
+    "vue-loader": "^10.3.0",
+    "vue-router": "^2.1.1",
+    "vue-template-compiler": "^2.3.2",
+    "vuex": "^2.1.1"
+  },
+  "devDependencies": {
+    "autoprefixer": "^6.4.0",
+    "babel-core": "^6.0.0",
+    "babel-loader": "^6.0.0",
+    "babel-plugin-transform-runtime": "^6.0.0",
+    "babel-preset-es2015": "^6.0.0",
+    "babel-preset-stage-2": "^6.0.0",
+    "babel-register": "^6.0.0",
+    "chalk": "^1.1.3",
+    "connect-history-api-fallback": "^1.1.0",
+    "css-loader": "^0.25.0",
+    "eventsource-polyfill": "^0.9.6",
+    "express": "^4.13.3",
+    "extract-text-webpack-plugin": "^1.0.1",
+    "file-loader": "^0.9.0",
+    "friendly-errors-webpack-plugin": "^1.1.2",
+    "function-bind": "^1.0.2",
+    "html-webpack-plugin": "^2.8.1",
+    "http-proxy-middleware": "^0.17.2",
+    "json-loader": "^0.5.4",
+    "semver": "^5.3.0",
+    "opn": "^4.0.2",
+    "ora": "^0.3.0",
+    "shelljs": "^0.7.4",
+    "url-loader": "^0.5.7",
+    "vue-loader": "^10.0.0",
+    "vue-style-loader": "^1.0.0",
+    "vue-template-compiler": "^2.1.0",
+    "webpack": "^1.13.2",
+    "webpack-dev-middleware": "^1.8.3",
+    "webpack-hot-middleware": "^2.12.2",
+    "webpack-merge": "^0.14.1"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  }
+}

+ 16 - 0
post.sh

@@ -0,0 +1,16 @@
+#!/bin/sh
+
+groupId=com.uas.erp
+artifactId=uas-manage-client-web
+packaging=zip
+version=1.0.0
+repo_uri='http://10.10.100.23:23004/v1/artifact'
+
+basepath=$(cd `dirname $0`; pwd)
+buildfile=$basepath/dist.zip
+
+cnpm install
+npm run build
+npm run zip
+curl -H "Expect:" -F "groupId=$groupId" -F "artifactId=$artifactId" -F "packaging=$packaging"  -F "version=$version" -F "file=@$buildfile" $repo_uri
+rm -rf $buildfile

+ 65 - 0
src/App.vue

@@ -0,0 +1,65 @@
+<template>
+    <div id="app">
+        <transition name="bounce">
+            <router-view></router-view>
+        </transition>
+    </div>
+</template>
+
+<script>
+    export default {
+        name: 'app',
+        components: {},
+        methods:{
+
+        },
+        mounted(){
+            // console.log(this.$$ajax);
+        },
+        watch:{
+            $route(to,from){
+                // console.log(to);
+                if (!to.matched.length) {
+                    this.$router.push('/404');
+                }
+            }
+        }
+    }
+</script>
+<style scoped lang='less'>
+    @import "assets/common.css";
+
+    .bounce-enter-active {
+        animation: bounce-in .5s;
+        -webkit-animation:bounce-in .5s;
+    }
+
+    .bounce-leave-active {
+        animation: bounce-out .2s;
+        -webkit-animation: bounce-out .2s;
+    }
+
+    @keyframes bounce-in {
+        0% {
+            transform: scale(0);
+        }
+        50% {
+            transform: scale(1.05);
+        }
+        100% {
+            transform: scale(1);
+        }
+    }
+
+    @keyframes bounce-out {
+        0% {
+            transform: scale(1);
+        }
+        50% {
+            transform: scale(0.95);
+        }
+        100% {
+            transform: scale(0);
+        }
+    }
+</style>

+ 18 - 0
src/assets/common.css

@@ -0,0 +1,18 @@
+.text-primary {
+  color: #20a0ff !important;
+}
+.text-warning {
+  color: #f7ba2a !important;
+}
+.text-success {
+  color: #13ce66 !important;
+}
+.text-error {
+  color: #ff4949 !important;
+}
+.bg-error {
+  background-color: #ff4949 !important;
+}
+.bg-warning {
+  background-color: #f7ba2a !important;
+}

二進制
src/assets/logo-uas.png


+ 54 - 0
src/components/Common/Bread/Bread.vue

@@ -0,0 +1,54 @@
+<template>
+    <div class='bread'>
+        <strong>
+            {{strong}}
+        </strong>
+        <el-breadcrumb separator="/" class='el-bread'>
+            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+            <el-breadcrumb-item v-for='(item,index) in $route.matched'>{{item.name}}</el-breadcrumb-item>
+        </el-breadcrumb>
+    </div>
+</template>
+
+<script>
+    export default {
+        name: 'bread',
+        data () {
+            return {
+                strong:''
+            }
+        },
+        methods:{
+            getPageText(name){
+                return name=name.replace('编辑',this.$route.query.id ? '修改' : '添加');
+            }
+        },
+        mounted(){
+
+        },
+        created(){
+            if (this.$route.matched.length) {
+                var name=this.$route.matched[this.$route.matched.length-1].name;
+                this.strong=this.getPageText(name);
+            }
+        },
+        watch:{
+            $route(to,from){
+                this.strong=this.getPageText(to.name);
+            }
+        }
+    }
+</script>
+
+<style scoped lang='less'>
+    .bread{
+        height: 40px;
+        line-height: 26px;
+        .el-bread{
+            display: inline-block;
+            float: right;
+            text-align: right;
+            line-height: 26px;
+        }
+    }
+</style>

+ 45 - 0
src/components/Common/DialogInfo/DialogInfo.js

@@ -0,0 +1,45 @@
+module.exports = {
+    name: 'dialog-info',
+    data() {
+        return {
+            dialog: this.Dialog || {}
+        }
+    },
+    methods: {
+        onClose() {
+            this.dialog.show = false;
+        }
+    },
+
+    computed: {
+        show() {
+            return this.dialog.show;
+        }
+    },
+
+    mounted() {
+        // console.log(this.Show);
+    },
+
+    /**
+     * 接收参数
+     * @type {Object}
+     */
+    props: {
+        Dialog: {
+            type: Object,
+            validator: (v) => {
+                return true;
+            }
+        }
+    },
+
+
+    /**
+     * 监控参数
+     * @type {Object}
+     */
+    watch: {
+
+    }
+}

+ 41 - 0
src/components/Common/DialogInfo/DialogInfo.vue

@@ -0,0 +1,41 @@
+<template>
+    <div class="">
+        <el-dialog size="tiny" 
+            :title="dialog.title" 
+            v-model="show"
+            @close='onClose'>
+            <el-form style="margin:20px;width:60%;min-width:100%" 
+                label-width="100px">
+                <el-form-item class='edit-form'
+                    v-for='field in dialog.fields'
+                    :label='field.label'>
+                    {{dialog.data[field.key]}}
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="onClose">关 闭</el-button>
+            </span>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+    import DialogInfoJs from './DialogInfo.js';
+    module.exports=DialogInfoJs;
+</script>
+<style scoped lang='less'>
+    .demo-form-inline{
+        display: inline-block;
+        float: right;
+    }
+    .btm-action{
+        margin-top: 20px;
+        text-align: center;
+    }
+    .actions-top{
+        height: 46px;
+    }
+    .pagination{
+        display: inline-block;
+    }
+</style>

+ 2 - 0
src/components/Common/DialogInfo/index.js

@@ -0,0 +1,2 @@
+import DialogInfo from './DialogInfo.vue';
+module.exports = DialogInfo;

+ 544 - 0
src/components/Common/FormData/FormData.js

@@ -0,0 +1,544 @@
+//noinspection JSAnnotator
+module.exports = {
+	name   : 'list',
+	data() {
+		return {
+			checkall_temp: '_checkall_temp',
+
+			fields     : this.FieldList || [],
+			editor     : this.Editor || {},
+			submit_data: this.DefaultValue || {},
+			rules      : this.Rules || {},
+
+			/**
+			 * 富文本编辑器信息
+			 * @type {Object}
+			 */
+			wangEditor: {
+				//存富文本实例的对象,支持多个
+				//key为富文本对象ID,value为实例
+				editor: {},
+				//默认为单个编辑器,如果为多个,此值为true,因为多个编辑器时,地图菜单不可用
+				many  : false,
+				has   : false,
+				//默认显示菜单,支持自定义
+				bar   : [
+					'source', '|',
+					'bold',
+					'underline',
+					'italic',
+					'strikethrough',
+					'eraser',
+					'forecolor',
+					'bgcolor', '|',
+					'quote',
+					'fontfamily',
+					'fontsize',
+					'head',
+					'unorderlist',
+					'orderlist',
+					'alignleft',
+					'aligncenter',
+					'alignright', '|',
+					'link',
+					'unlink',
+					'table',
+					'emotion', '|',
+					'img',
+					'video',
+					// 'location',
+					'insertcode', '|',
+					'undo',
+					'redo',
+					'fullscreen'
+				],
+				temp  : {},
+			},
+		}
+	},
+	methods: {
+		/**
+		 * 初始化编辑器
+		 * @param  {string} id     编辑器ID属性
+		 * @param  {object} config 初始化配置
+		 * @return {object}        所有编辑器所在对象,属性已ID为key
+		 */
+		initEditor(id, config) {
+			if (id) {
+				this.wangEditor.editor[id] = new wangEditor(id);
+				this.wangEditor.temp[id]   = {
+					html: '',
+					text: ''
+				};
+			}
+			this.configEditor(id, config).eventEditor(id).createEditor(id);
+		},
+
+
+		/**
+		 * 配置编辑器参数
+		 * @param  {string} id     编辑器ID
+		 * @param  {object} config 编辑器配置信息
+		 */
+		configEditor(id, config) {
+			if (id && config) {
+				this.wangEditor.editor[id].config={};
+				this.wangEditor.editor[id].config.uploadImgFns={};
+				this.wangEditor.editor[id].config.uploadImgFileName = config.name || this.editor.name || 'sls-admin';
+				this.wangEditor.editor[id].config.uploadImgUrl      = config.url || this.editor.url || '';
+				this.wangEditor.editor[id].config.uploadParams      = config.params || this.editor.params || {};
+
+				/**
+				 * 显示的菜单,分四种情况
+				 * 1-只传显示的菜单,直接赋值
+				 * 2-只传隐藏的菜单,过滤不需要显示的
+				 * 3-显示隐藏都传了,显示优先级高于隐藏优先级
+				 * 4-啥都不传,取默认全部显示
+				 * @type {object}
+				 */
+				if (Array.isArray(config.show_bar) && config.show_bar.length) {
+					var bar = config.show_bar;
+				} else if (Array.isArray(config.hide_bar) && config.hide_bar.length) {
+					var bar = this.wangEditor.bar.filter((item) => {
+						return config.hide_bar.indexOf(item) === -1;
+					});
+				} else if (Array.isArray(this.editor.show_bar) && this.editor.show_bar.length) {
+					var bar = this.editor.show_bar;
+				} else if (Array.isArray(this.editor.hide_bar) && this.editor.hide_bar.length) {
+					var bar = this.wangEditor.bar.filter((item) => {
+						return this.editor.hide_bar.indexOf(item) === -1;
+					});
+				} else {
+					var bar = this.wangEditor.bar;
+				}
+
+				if (this.wangEditor.many === true && bar.indexOf('location') !== -1) {
+					var bar = bar.splice(bar.indexOf('location'), 1);
+				}
+
+				this.wangEditor.editor[id].config.menus = bar;
+				console.log(this.wangEditor.editor[id].config.menus);
+			}
+
+			return this;
+		},
+
+
+		/**
+		 * 编辑器常用事件
+		 * @param  {string} id 编辑器ID
+		 */
+		eventEditor(id) {
+			var self                                              = this;
+			this.wangEditor.editor[id].config.uploadImgFns.onload = function (data) {
+				if (data.status === 200) {
+					var originalName = this.uploadImgOriginalName || '';
+
+					this.command(null, 'insertHtml', '<img src="' + data.data.fileinfo.getSaveName + '" alt="' + originalName + '" style="max-width:100%;"/>');
+				} else {
+					if (data.status === 404) {
+						self.$message.error('上传错误信息:token无效!');
+					} else {
+						self.$message.error('上传错误信息:' + data.msg);
+					}
+				}
+
+			};
+
+			this.wangEditor.editor[id].config.uploadImgFns.onerror = (xhr) => {
+				this.$message.error('上传错误信息:网络错误!');
+			};
+
+			this.wangEditor.editor[id].onchange = function () {
+				var text = this.$txt.text().replace(/(^\s*)|(\s*$)/g, ""),
+					html = this.$txt.html();
+
+				self.wangEditor.temp[id].html = html;
+				self.wangEditor.temp[id].text = text;
+
+				self.$emit('onEditorChange', {
+					id,
+					data: {
+						html,
+						text
+					}
+				});
+			};
+
+			return this;
+		},
+
+
+		/**
+		 * 创建编辑器
+		 * @param  {string} id 编辑器ID
+		 */
+		createEditor(id) {
+			console.log(this.wangEditor.editor[id]);
+			this.wangEditor.editor[id].create();
+			console.log(id);
+		},
+
+
+		/**
+		 * 处理全选全部选
+		 * @param  {object} field 字段对象
+		 */
+		initCheckall(field) {
+			var temp            = {};
+			temp.text           = field.checkall.text;
+			temp.value          = field.checkall.value;
+			temp.indeterminate  = field.checkall.indeterminate;
+			temp.checkbox_list  = field.value.list;
+			temp.checkbox_value = field.value.default;
+			this.$set(this.submit_data, field.key + this.checkall_temp, temp);
+		},
+
+
+		/**
+		 * 日期时间空间增加改变回掉
+		 * @param  {array} fields 字段数组
+		 * @param  {number} i      当前对象索引
+		 */
+		onDateTimeChange(fields, i, type) {
+			//如果没有传改变事件回调,需要补回调
+			if (!fields[i].change || typeof fields[i].change !== 'function') {
+				this.$set(fields[i], 'change', v => {
+					switch (type) {
+						case 'range':
+							var temp                        = v.split(' - ');
+							this.submit_data[fields[i].key] = temp;
+							break;
+						default:
+							this.submit_data[fields[i].key] = v;
+					}
+				});
+			}
+		},
+
+
+		/**
+		 * 日期时间设置一些参数
+		 * @param  {array} fields 字段数组
+		 * @param  {number} i      [当前对象索引]
+		 */
+		onDateOptions(fields, i, type) {
+			var field = fields[i];
+
+			if (!field.options) {
+				var options      = {},
+
+					//默认快捷方式
+					shortcuts    = [{
+						text: '今天',
+						onClick(picker) {
+							picker.$emit('pick', new Date());
+						}
+					}, {
+						text: '昨天',
+						onClick(picker) {
+							const date = new Date();
+							date.setTime(date.getTime() - 3600 * 1000 * 24);
+							picker.$emit('pick', date);
+						}
+					}, {
+						text: '一周前',
+						onClick(picker) {
+							const date = new Date();
+							date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+							picker.$emit('pick', date);
+						}
+					}],
+					//默认禁止今天以前的日期
+					disabledDate = function (time) {
+						return time.getTime() < Date.now() - 8.64e7;
+					};
+
+
+				if (field.shortcuts === true) {
+					options.shortcuts = shortcuts;
+				}
+				if (field.disabledDate === true) {
+					options.disabledDate = disabledDate;
+				}
+
+				if (field.disabledDate && field.disabledDate.constructor === Function) {
+					options.disabledDate = field.disabledDate;
+				}
+				if (field.shortcuts && field.shortcuts.constructor === Array) {
+					options.shortcuts = field.shortcuts;
+				}
+
+
+				this.$set(fields[i], 'options', options);
+			}
+		},
+
+
+		/**
+		 * 设置时间参数
+		 * @param fields	字段数组
+		 * @param i			当前索引
+		 * @param type		类型
+		 */
+		onTimeOptions(fields, i, type) {
+			var field = fields[i];
+
+			if (!field.options) {
+				var options = {};
+
+
+				this.$set(fields[i], 'options', options);
+			}
+		},
+
+
+		/**
+		 * 处理日期
+		 * @param  {array} fields 字段数组
+		 * @param  {number} i      当前日期对象的索引
+		 */
+		initDate(fields, i, type) {
+			//设置回调
+			this.onDateTimeChange(fields, i, type);
+
+			//设置日期参数
+			this.onDateOptions(fields, i, type);
+		},
+
+
+		/**
+		 * 处理时间
+		 * @param  {array} fields 字段数组
+		 * @param  {number} i      当前日期对象的索引
+		 */
+		initTime(fields, i, type) {
+			//设置回调
+			this.onDateTimeChange(fields, i, type);
+
+			//设置日期参数
+			this.onTimeOptions(fields, i, type);
+		},
+
+
+		initYear(fields, i) {
+			//设置回调
+			this.onDateTimeChange(fields, i);
+		},
+
+		initMonth(fields, i) {
+			//设置回调
+			this.onDateTimeChange(fields, i);
+		},
+
+		initWeek(fields, i) {
+			//设置回调
+			this.onDateTimeChange(fields, i);
+
+			if (!fields[i].format) {
+				this.$set(fields[i], 'format', 'yyyy 第 WW 周');
+			}
+		},
+
+
+		/**
+		 * 从字段列表中提取出来表单字段
+		 * @return {object} [表单需要的字段]
+		 */
+		deepObj() {
+			if (this.fields) {
+				var fields = this.fields,
+					k      = 0,
+					update = this.submit_data.id ? true : false;
+				for (var i = 0; i < fields.length; i++) {
+					var field = fields[i];
+
+					if (field.value && field.value.constructor === Object) {
+						if (field.checkall && typeof field.checkall === 'object') {
+							this.initCheckall(field);
+						} else {
+							this.$set(this.submit_data, field.key, field.value.default);
+						}
+					} else {
+						this.$set(this.submit_data, field.key, field.value);
+					}
+
+
+					if (field.type) {
+						switch (field.type) {
+							case 'editor':
+								k++;
+								this.initEditor(field.id, field.config || {});
+								if (k == 2) {
+									this.wangEditor.many = true;
+								}
+								if (k == 1) {
+									this.wangEditor.has = true;
+								}
+								break;
+
+							case 'date':
+								this.initDate(fields, i);
+								break;
+
+							case 'daterange':
+								this.initDate(fields, i, 'range');
+								break;
+
+							case 'year':
+								this.initYear(fields, i);
+								break;
+
+							case 'month':
+								this.initMonth(fields, i);
+								break;
+
+							case 'week':
+								this.initWeek(fields, i);
+								break;
+
+							case 'time':
+								this.initTime(fields, i);
+								break;
+
+							case 'datetime':
+								this.initDate(fields, i);
+								break;
+
+							case 'datetimerange':
+								this.initDate(fields, i, 'range');
+								break;
+
+						}
+					}
+				}
+
+				// console.log(this.submit_data);
+			}
+		},
+
+
+		/**
+		 * 表单提交事件
+		 */
+		onSubmit(ref) {
+			var data = {
+				data: this.submit_data,
+			};
+			if (this.wangEditor.has === true) {
+				data.editor_temp_data = this.wangEditor.temp;
+			}
+
+			if (this.rules) {
+				this.$refs[ref].validate((valid) => {
+					if (valid) {
+						this.$emit('onSubmit', data);
+					}
+				});
+			} else {
+				this.$emit('onSubmit', data);
+			}
+		},
+
+
+		/**
+		 * checkbox改变时触发
+		 * @param  {string} key 当前元素的key
+		 */
+		onCheckboxChange(key) {
+			var checkall_temp = this.submit_data[key + this.checkall_temp];
+
+			if (checkall_temp.checkbox_value.length > 0 && checkall_temp.checkbox_value.length < checkall_temp.checkbox_list.length) {
+				checkall_temp.indeterminate = true;
+			} else {
+				checkall_temp.indeterminate = false;
+			}
+
+			checkall_temp.value = checkall_temp.checkbox_value.length === checkall_temp.checkbox_list.length;
+		},
+
+
+		/**
+		 * checkallbox改变时触发
+		 * @param  {string} key 当前元素的key
+		 */
+		onCheckallChange(key) {
+			var checkall_temp           = this.submit_data[key + this.checkall_temp];
+			checkall_temp.indeterminate = false;
+
+			var value = [];
+			if (checkall_temp.value == true) {
+				for (var i = 0; i < checkall_temp.checkbox_list.length; i++) {
+					value.push(checkall_temp.checkbox_list[i].value);
+				}
+			}
+
+			checkall_temp.checkbox_value = value;
+		},
+
+
+		onCascaderItemChange(value) {
+			// console.log(value);
+		},
+
+
+		onChangeDateTime(v) {
+			console.log(v + '    formdata');
+		}
+	},
+
+
+	created() {
+		this.deepObj();
+	},
+
+
+	/**
+	 * ready
+	 */
+	mounted() {
+		// this.deepObj();
+	},
+
+
+	/**
+	 * 接收参数
+	 * @type {Object}
+	 */
+	props: {
+		FieldList   : {
+			type    : Array,
+			required: true
+		},
+		Editor      : {
+			type: Object
+		},
+		Rules       : {
+			type: Object
+		},
+		DefaultValue: {
+			type: Object
+		}
+	},
+
+
+	/**
+	 * 监控参数
+	 * @type {Object}
+	 */
+	watch: {
+		FieldList: {
+			deep: true,
+			handler(v){
+				if (v) {
+					this.fields = v;
+				}
+			}
+		},
+		DefaultValue(v) {
+			if (v) {
+				this.submit_data = v;
+			}
+		}
+	}
+}

+ 212 - 0
src/components/Common/FormData/FormData.vue

@@ -0,0 +1,212 @@
+<template>
+    <div class="form">
+        <el-form style=""
+                 label-width="100px"
+                 ref='form-data'
+                 :rules='rules'
+                 :model='submit_data'>
+            <el-form-item
+                    class='edit-form'
+                    v-for='(field,index) in fields'
+                    :label="field.label"
+                    :prop='field.key'
+                    :style="field.item_style">
+
+                <!-- 单选CheckBox -->
+                <el-checkbox
+                        v-if='field.type==="checkbox" && field.multiple!==true'
+                        v-model="submit_data[field.key]">{{field.label}}
+                </el-checkbox>
+
+                <el-cascader
+                        v-if='field.type==="cascader"'
+                        :options="field.value.list"
+                        :props="field.props || {}"
+                        @active-item-change='onCascaderItemChange'></el-cascader>
+
+
+                <!-- 复选CheckBox -->
+                <!-- 是否全选全不选 -->
+                <el-checkbox
+                        v-if='field.checkall && typeof field.checkall==="object" && submit_data[field.key+checkall_temp]'
+                        :indeterminate="submit_data[field.key+checkall_temp].indeterminate"
+                        v-model="submit_data[field.key+checkall_temp].value"
+                        @change='onCheckallChange(field.key)'>{{submit_data[field.key+checkall_temp].text}}
+                </el-checkbox>
+                <!-- CheckBox选项列表 -->
+                <el-checkbox-group
+                        v-if='(field.type==="checkbox" && field.multiple===true && !field.checkall) || (field.type==="checkbox" && field.multiple===true && field.checkall && submit_data[field.key+checkall_temp])'
+                        v-model="submit_data[field.key+checkall_temp].checkbox_value"
+                        @change='onCheckboxChange(field.key)'>
+                    <el-checkbox
+                            v-for='item in submit_data[field.key+checkall_temp].checkbox_list'
+                            :label="item.value">{{item.text}}
+                    </el-checkbox>
+                </el-checkbox-group>
+
+                <!-- wangeditor -->
+                <div
+                        v-if='field.type==="editor"'
+                        :id="field.id"
+                        :style="field.style"
+                        v-html='submit_data[field.key]'></div>
+
+                <!--
+                    input,textarea 
+                 -->
+                <el-input
+                        v-if='!field.type || field.type==="input" || field.type==="textarea"'
+                        :type='!field.type ? "input" : field.type'
+                        v-model="submit_data[field.key]"
+                        :placeholder='field.desc'></el-input>
+
+                <!-- 
+                    radia,单选
+                 -->
+                <el-radio-group
+                        v-if='field.type==="radio"'
+                        v-model="submit_data[field.key]">
+                    <el-radio
+                            v-for='item in field.value.list'
+                            :label="item.value">{{item.text || item.value}}
+                    </el-radio>
+                </el-radio-group>
+
+                <!-- select,下拉框 -->
+                <el-select
+                        v-if='field.type==="select"'
+                        v-model="submit_data[field.key]"
+                        :multiple='field.multiple ? true : false'
+                        :placeholder="field.desc">
+                    <el-option
+                            v-for='item in field.value.list'
+                            :value="item.value"
+                            :label="item.text"></el-option>
+                </el-select>
+
+                <!-- 
+                    switch,开关
+                 -->
+                <el-switch
+                        v-if='field.type==="switch"'
+                        :on-text="field.value.on"
+                        :off-text="field.value.off"
+                        :disabled='field.disabled'
+                        v-model="submit_data[field.key]"></el-switch>
+
+                <!-- date,日期类型 -->
+                <el-date-picker
+                        v-if='field.type==="date"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'
+                        :picker-options="field.options">
+                </el-date-picker>
+
+                <!-- date,日期类型-选择范围 -->
+                <!-- 
+                    @change='field.change'
+                 -->
+                <el-date-picker
+                        v-if='field.type==="daterange"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'>
+                </el-date-picker>
+
+                <!-- date,日期类型-年 -->
+                <el-date-picker
+                        v-if='field.type==="year"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'>
+                </el-date-picker>
+
+                <!-- date,日期类型-月 -->
+                <el-date-picker
+                        v-if='field.type==="month"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'>
+                </el-date-picker>
+
+                <!-- date,日期类型-周 -->
+                <el-date-picker
+                        v-if='field.type==="week"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :format="field.format"
+                        :placeholder="field.placeholder"
+                        @change='field.change'>
+                </el-date-picker>
+
+
+                <!-- time,时间类型 -->
+                <el-time-select
+                        v-if='field.type==="time"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'
+                        :picker-options="field.options">
+                </el-time-select>
+
+
+                <!-- 日期时间组合 -->
+                <el-date-picker
+                        v-if='field.type==="datetime"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        @change='field.change'
+                        :placeholder="field.placeholder"
+                        :picker-options="field.options">
+                </el-date-picker>
+
+
+                <!-- datetime,日期时间类型-选择范围 -->
+                <!--
+                    @change='field.change'
+                 -->
+                <el-date-picker
+                        v-if='field.type==="datetimerange"'
+                        v-model="submit_data[field.key]"
+                        :type="field.type"
+                        :placeholder="field.placeholder"
+                        @change='field.change'>
+                </el-date-picker>
+            </el-form-item>
+
+            <el-form-item>
+                <el-button type="primary" @click='onSubmit("form-data")'>提交</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+
+<script>
+	import FormDataJs from './FormData.js';
+	module.exports = FormDataJs;
+</script>
+<style scoped lang='less'>
+    .demo-form-inline {
+        display: inline-block;
+        float: right;
+    }
+
+    .btm-action {
+        margin-top: 20px;
+        text-align: center;
+    }
+
+    .actions-top {
+        height: 46px;
+    }
+
+    .pagination {
+        display: inline-block;
+    }
+</style>

+ 2 - 0
src/components/Common/FormData/index.js

@@ -0,0 +1,2 @@
+import FormData from './FormData.vue';
+module.exports = FormData;

+ 179 - 0
src/components/Common/HeadNav/HeadNav.js

@@ -0,0 +1,179 @@
+import {
+    user as UserApi,
+    system as SystemApi
+} from '../../../config/request.js';
+
+module.exports = {
+    name: 'head-nav',
+    data() {
+        return {
+            dialog: {
+                show_access: false,
+                show_set: false,
+                show_pass: false,
+                title: '修改密码',
+                user_info: this.$store.state.user.userinfo,
+
+                set_info: {
+                    login_style: '',
+                    disabled_update_pass: [],
+                    select_users: []
+                },
+
+                user_info_rules: {
+                    old_password: [{
+                        required: true,
+                        message: '旧密码不能为空!',
+                        trigger: 'blur'
+                    }],
+                    password: [{
+                        required: true,
+                        message: '新密码不能为空!',
+                        trigger: 'blur'
+                    }, {
+                        trigger: 'blur',
+                        validator: (rule, value, callback) => {
+                            if (value === '') {
+                                callback(new Error('请再次输入密码'));
+                            } else {
+                                if ('' !== this.dialog.user_info.password) {
+                                    this.$refs.user_info.validateField('password_confirm');
+                                }
+                                callback();
+                            }
+                        }
+                    }],
+                    password_confirm: [{
+                        required: true,
+                        message: '确认密码不能为空!',
+                        trigger: 'blur'
+                    }, {
+                        trigger: 'blur',
+                        validator: (rule, value, callback) => {
+                            if (value === '') {
+                                callback(new Error('请再次输入密码'));
+                            } else if (value !== this.dialog.user_info.password) {
+                                callback(new Error('两次输入密码不一致!'));
+                            } else {
+                                callback();
+                            }
+                        }
+                    }],
+                }
+            }
+        }
+    },
+    mounted() {
+        // this.setDialogInfo('access');
+
+        // this.onGetSetting();
+    },
+    methods: {
+        /**
+         * 退出登录
+         */
+        logout() {
+            this.$confirm('你确定退出登录么?', '确认退出', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(() => {
+                this.$store.dispatch('remove_userinfo').then(() => {
+                    this.$router.push('/login');
+                });
+            });
+        },
+
+        /**
+         * 弹出框-修改密码或者系统设置   
+         * @param {string} cmditem 弹框类型
+         */
+        setDialogInfo(cmditem) {
+            if (!cmditem) {
+                console.log('test');
+                this.$message('菜单选项缺少command属性');
+                return;
+            }
+            switch (cmditem) {
+                case 'info':
+                    this.$router.push({
+                        path: '/demo/user/edit',
+                        query: {
+                            id: this.$store.state.user.userinfo.id
+                        }
+                    });
+                    break;
+                case 'pass':
+                    this.dialog.show_pass = true;
+                    this.dialog.title = '修改密码';
+                    break;
+                case 'set':
+                    this.onGetSetting();
+                    this.dialog.show_set = true;
+                    this.dialog.title = '系统设置';
+                    break;
+            }
+        },
+
+        /**
+         * 修改密码
+         * @param  {object} userinfo 当前修改密码的表单信息
+         */
+        updUserPass(userinfo) {
+            this.$refs[userinfo].validate((valid) => {
+                if (valid) {
+                    UserApi.updPass.call(this, {
+                        old_password: this.dialog[userinfo].old_password,
+                        password: this.dialog[userinfo].password,
+                        password_confirm: this.dialog[userinfo].password_confirm
+                    }, (data) => {
+                        this.dialog.show_pass = false;
+                        // this.$nextTick(() => {
+                        this.$message.success('修改成功!');
+                        // });
+                    });
+                }
+            });
+        },
+
+        /**
+         * 获取系统设置信息
+         */
+        onGetSetting() {
+            //获取系统设置信息
+            if (this.$store.state.user.userinfo.pid == 0) {
+                SystemApi.getSetting.call(this, (data) => {
+                    // console.log(data);
+                    if (data.setting_info.disabled_update_pass) {
+                        data.setting_info.disabled_update_pass = data.setting_info.disabled_update_pass.split(',');
+                    } else {
+                        data.setting_info.disabled_update_pass = [];
+                    }
+                    data.setting_info.login_style = data.setting_info.login_style + '';
+
+                    this.dialog.set_info = data.setting_info;
+                });
+            } else {
+                this.$message.error('只有管理员才能操作!');
+            }
+        },
+
+        /**
+         * 修改系统设置信息
+         */
+        onUpdateSetting() {
+            // console.log(this.dialog.set_info.login_style);
+            // console.log(this.dialog.set_info.disabled_update_pass);
+            // console.log(this.dialog.set_info.id);
+
+            SystemApi.updateSetting.call(this, {
+                id: this.dialog.set_info.id,
+                login_style: this.dialog.set_info.login_style,
+                disabled_update_pass: this.dialog.set_info.disabled_update_pass && this.dialog.set_info.disabled_update_pass.length ? this.dialog.set_info.disabled_update_pass.join(',') : ''
+            }, (data) => {
+                // console.log(data);
+                this.dialog.show_set = false;
+            });
+        }
+    }
+}

+ 194 - 0
src/components/Common/HeadNav/HeadNav.vue

@@ -0,0 +1,194 @@
+<template>
+    <div>
+
+        <header class="head-nav">
+            <el-row>
+                <el-col :span="4" class='logo-container'>
+                    <img src="../../../assets/logo-uas.png" class='logo' alt="">
+                </el-col>
+                <el-col :span="16">
+                    <el-menu theme="dark" :default-active="$store.state.router.headerCurRouter" class="el-menu-demo"
+                             mode="horizontal" unique-opened router>
+                        <!-- v-if='!item.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[item.path]) || $store.state.user.userinfo.access_status!==1)' -->
+                        <el-menu-item
+                                :index="item.path"
+                                v-for='(item,index) in $router.options.routes'
+                                v-if='!item.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[item.path]) || $store.state.user.userinfo.access_status!==1)'>
+                            {{item.name}}<!-- {{item.path}} -->
+                        </el-menu-item>
+                    </el-menu>
+                </el-col>
+                <el-col :span="4" class="userinfo">
+                    <!-- <span class='username'><i class='fa fa-user'></i>{{this.$store.state.user.userinfo.username}}</span> -->
+                    <span class='username'>
+                        <el-dropdown
+                                trigger="click"
+                                @command='setDialogInfo'>
+                            <span class="el-dropdown-link">
+                                {{this.$store.state.user.userinfo.username}}<i
+                                    class="el-icon-caret-bottom el-icon--right"></i>
+                            </span>
+                            <el-dropdown-menu slot="dropdown">
+                                <el-dropdown-item command='info'>修改信息</el-dropdown-item>
+                                <el-dropdown-item
+                                        command='pass'
+                                        v-if='$store.state.user.userinfo.is_update_pass'>修改密码</el-dropdown-item>
+                                <el-dropdown-item
+                                        command='set'
+                                        v-if='$store.state.user.userinfo.pid==0'>系统设置</el-dropdown-item>
+                            </el-dropdown-menu>
+                        </el-dropdown>
+                    </span>
+                    <i class="fa fa-sign-out logout" @click='logout'></i>
+                </el-col>
+            </el-row>
+        </header>
+
+
+        <el-dialog size="small" :title="dialog.title"
+                   v-model="dialog.show_pass">
+            <el-form style="margin:20px;width:80%;"
+                     label-width="100px"
+                     :model="dialog.user_info"
+                     :rules="dialog.user_info_rules"
+                     ref='user_info'>
+                <el-form-item class='edit-form'
+                              label="邮箱"
+                              prop='email'>
+                    <el-input v-model="dialog.user_info.email" disabled placeholder='常用邮箱'></el-input>
+                </el-form-item>
+                <el-form-item class='edit-form'
+                              label="用户名称"
+                              prop='username'>
+                    <el-input v-model="dialog.user_info.username" disabled placeholder='用户名'></el-input>
+                </el-form-item>
+                <el-form-item class='edit-form'
+                              label="当前密码"
+                              prop='old_password'>
+                    <el-input
+                            type='password'
+                            placeholder='当前密码'
+                            auto-complete='off'
+                            v-model="dialog.user_info.old_password"></el-input>
+                </el-form-item>
+                <el-form-item class='edit-form'
+                              label="新密码"
+                              prop='password'>
+                    <el-input
+                            type='password'
+                            placeholder='新密码'
+                            auto-complete='off'
+                            v-model="dialog.user_info.password"></el-input>
+                </el-form-item>
+                <el-form-item class='edit-form'
+                              label="确认密码"
+                              prop='password_confirm'>
+                    <el-input
+                            type='password'
+                            placeholder='确认密码'
+                            auto-complete='off'
+                            v-model="dialog.user_info.password_confirm"></el-input>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="dialog.show_pass = false">取 消</el-button>
+                <el-button type="primary" @click="updUserPass('user_info')">确 定</el-button>
+            </span>
+        </el-dialog>
+
+
+        <el-dialog size="small" :title="dialog.title"
+                   v-model="dialog.show_set">
+            <el-form style="margin:20px;width:80%;"
+                     label-width="100px"
+                     v-model='dialog.set_info'
+                     ref='set_info'>
+                <el-form-item label="登录方式">
+                    <el-select placeholder="请选择登录方式"
+                               v-model='dialog.set_info.login_style'>
+                        <el-option label="单一登录" value="1"></el-option>
+                        <el-option label="多点登录" value="2"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="禁止修改密码">
+                    <el-select placeholder="请选择用户"
+                               multiple
+                               v-model='dialog.set_info.disabled_update_pass'>
+                        <!-- value的值的ID是number,disabled_update_pass的元素中的是字符串,
+                            所以在value上,需要拼装一个空串,来转化
+                            因为elementUI内部用了===
+                        -->
+                        <el-option
+                                v-for='user in dialog.set_info.select_users'
+                                :label='user.username'
+                                :value='user.id+""'></el-option>
+                    </el-select>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button @click="dialog.show_set = false">取 消</el-button>
+                <el-button type="primary" @click="onUpdateSetting">确 定</el-button>
+            </span>
+        </el-dialog>
+    </div>
+</template>
+
+<script>
+	import HeadNavJs from './HeadNav.js';
+	export default HeadNavJs;
+</script>
+
+<style scoped lang='less'>
+    .logo-container {
+        height: 60px;
+    }
+
+    .logo {
+        height: 26px;
+        width: auto;
+        margin-left: 20px;
+        margin-top: 17px;
+    }
+
+    .fa-user {
+        position: relative;
+        top: -2px;
+        margin-right: 4px;
+    }
+
+    .head-nav {
+        width: 100%;
+        height: 60px;
+        background: #324057;
+        position: fixed;
+        top: 0px;
+        left: 0px;
+        z-index: 99;
+        color: #FFF;
+        border-bottom: 1px solid #1F2D3D;
+
+    .logout {
+        width: 60px;
+        height: 60px;
+        line-height: 60px;
+        text-align: center;
+        float: right;
+        cursor: pointer;
+    }
+
+    }
+    .userinfo {
+        text-align: right;
+    }
+
+    .username {
+        height: 60px;
+        line-height: 60px;
+        cursor: pointer;
+
+    .el-dropdown {
+        color: #FFF;
+    }
+
+    }
+</style>

+ 54 - 0
src/components/Common/LeftMenu/LeftMenu.js

@@ -0,0 +1,54 @@
+    export default {
+        name: 'left-menu',
+        data() {
+            return {
+                menu_list: [],
+                win_size: {
+                    height: '',
+                }
+            }
+        },
+        methods: {
+            setSize() {
+                this.win_size.height = $(window).height() + "px";
+            },
+            toggleMenu() {
+                this.$store.dispatch(this.$store.state.leftmenu.menu_flag ? 'set_menu_close' : 'set_menu_open');
+            },
+            updateCurMenu(route) {
+                var route = route || this.$route;
+                if (route.matched.length) {
+                    var rootPath = route.matched[0].path,
+                        fullPath = route.path;
+                    this.$store.dispatch('set_cur_route', {
+                        rootPath,
+                        fullPath
+                    });
+                    var routes = this.$router.options.routes;
+                    for (var i = 0; i < routes.length; i++) {
+                        if (routes[i].path === rootPath && !routes[i].hidden) {
+                            this.menu_list = routes[i].children;
+                            break;
+                        }
+                    }
+                } else {
+                    this.$router.push('/404');
+                }
+            }
+        },
+        created() {
+            this.setSize();
+            $(window).resize(() => {
+                this.setSize();
+            });
+            this.updateCurMenu();
+        },
+        mounted() {
+            // console.log(this.$store.state.user.userinfo.access);
+        },
+        watch: {
+            $route(to, from) {
+                this.updateCurMenu(to);
+            }
+        }
+    }

+ 43 - 0
src/components/Common/LeftMenu/LeftMenu.less

@@ -0,0 +1,43 @@
+.fa {
+    margin-right: 8px;
+}
+.left-fixed-right-auto {
+    padding-top: 60px;
+}
+.left {
+    position: fixed;
+    float: left;
+    /*width:190px;
+    margin-right:-190px;*/
+    top: 60px;
+}
+.right-content {
+    float: right;
+    width: 100%;
+}
+#left-menu {
+    height: 100%;
+    background: #324057;
+    //position: relative;
+    overflow-x: hidden;
+}
+.toggle-menu {
+    width: 40px;
+    height: 40px;
+    background: #1f2f3d;
+    position: absolute;
+    top: 0px;
+    /*left: 190px;*/
+    z-index: 1000;
+    cursor: pointer;
+    line-height: 40px;
+    text-align: center;
+    color: #fff;
+    font-size: 14px;
+    opacity: 0.2;
+    transition: opacity .3s ease-out;
+    
+    &:hover {
+        opacity: 1;
+    }
+}

+ 75 - 0
src/components/Common/LeftMenu/LeftMenu.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="left" :style="{'height':win_size.height,'width':$store.state.leftmenu.width}" id='admin-left'>
+        <div id='left-menu'>
+            <el-row class='tac'
+                    v-for="(route,index) in $router.options.routes"
+                    v-if='!route.hidden && $route.matched.length && $route.matched[0].path===route.path'>
+                <el-col :span="24">
+                    <el-menu
+                            class="el-menu-vertical-demo"
+                            theme="dark"
+                            :default-active="$route.path"
+                            unique-opened
+                            router>
+                        <!-- v-if="!item.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[route.path+'/'+item.path]) || $store.state.user.userinfo.access_status!==1)" -->
+                        <template
+                                v-for="(item,index) in route.children"
+                                v-if="!item.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[route.path+'/'+item.path]) || $store.state.user.userinfo.access_status!==1)">
+                            <el-submenu
+                                    :index="item.path">
+                                <template
+                                        slot="title">
+                                    <el-tooltip
+                                            class="item"
+                                            effect="dark"
+                                            placement="right"
+                                            :disabled="$store.state.leftmenu.menu_flag"
+                                            :content="item.name">
+                                        <i :class="'fa fa-'+item.icon"></i>
+                                    </el-tooltip>
+                                    <span
+                                            class='menu-name'
+                                            v-if="$store.state.leftmenu.menu_flag">{{item.name}}
+                                        <!-- {{route.path+'/'+item.path}} --></span>
+                                </template>
+
+                                <!-- v-if="!child.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[route.path+'/'+item.path+'/'+child.path]) || $store.state.user.userinfo.access_status!==1)" -->
+                                <el-menu-item
+                                        v-for='(child,cindex) in item.children'
+                                        v-if="!child.hidden && (($store.state.user.userinfo.access_status===1 && $store.state.user.userinfo.web_routers[route.path+'/'+item.path+'/'+child.path]) || $store.state.user.userinfo.access_status!==1)"
+                                        :style="{'padding-left':$store.state.leftmenu.menu_flag? '40px' : '23px'}"
+                                        :index='$store.state.router.headerCurRouter+"/"+item.path+"/"+child.path'>
+                                    <el-tooltip
+                                            class="item"
+                                            effect="dark"
+                                            placement="right"
+                                            :disabled="$store.state.leftmenu.menu_flag"
+                                            :content="child.name">
+                                        <i :class="'fa fa-'+child.icon"></i>
+                                    </el-tooltip>
+                                    <span
+                                            class='menu-name'
+                                            v-if="$store.state.leftmenu.menu_flag">{{child.name}}
+                                        <!-- {{route.path+'/'+item.path+'/'+child.path}} --></span>
+                                </el-menu-item>
+                            </el-submenu>
+                        </template>
+                    </el-menu>
+                </el-col>
+            </el-row>
+            <div class="toggle-menu"
+                 @click='toggleMenu'
+                 :style='{left:$store.state.leftmenu.width}'>
+                <i :class='[{"el-icon-arrow-left":$store.state.leftmenu.menu_flag},{"el-icon-arrow-right":!$store.state.leftmenu.menu_flag}]'></i>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+	module.exports = require('./LeftMenu.js');
+</script>
+
+<style scoped lang='less'>
+    @import url(./LeftMenu.less);
+</style>

+ 255 - 0
src/components/Common/ListData/ListData.js

@@ -0,0 +1,255 @@
+module.exports = {
+    name: 'list-data',
+    data() {
+        return {
+            batch_flag: true, //符合批量删除为true,否则为false
+            batch_datas: [],
+            batch_ids: [],
+
+            image_host: this.ImageHost, //如果列表中有图片,并且不是绝对路径的话,可以传入这个参数
+
+            list: this.List, //列表数组
+            fields: this.FieldList, //字段数组
+            selection: this.Selection, //是否需要批量选择
+            expand:this.Expand,
+            btn_info: this.BtnInfo,
+
+            pagination: this.Pagination,
+        }
+    },
+    methods: {
+        /**
+         * 表格列表触发CheckBox的事件
+         * @param  {array} val 当前选中的用户信息数组,每个元素是用户信息对象
+         */
+        onSelectionChange(val) {
+            this.batch_datas = val;
+
+            this.batch_ids = [];
+            if (val.length) {
+                this.batch_flag = false;
+                for (var i = 0; i < val.length; i++) {
+                    this.batch_ids.push(val[i].id);
+                }
+            } else {
+                this.batch_flag = true;
+            }
+
+            /**
+             * 改变CheckBox事件,第一个参数是ID数组,第二个参数二维数组,每个数组是选中的对象
+             */
+            this.$emit('onSelectionChange', this.batch_ids, this.batch_datas);
+            this.$emit('onSelectionChangeObj', {
+                ids: this.batch_ids,
+                datas: this.batch_datas
+            });
+        },
+
+
+
+        /**
+         * 删除事件
+         * @param  {object || boolean} user  当前信息对象或者为布尔值,为布尔值时,代表是批量删除
+         * @param  {number} index 当前列表索引
+         */
+        onDelete(data, index) {
+            var opts = {};
+            if (data === true) {
+                opts.batch_ids = this.batch_ids;
+                opts.batch_datas = this.batch_datas;
+            } else {
+                opts.data = data;
+                opts.index = index;
+            }
+
+            /**
+             * 删除事件,参数为对象
+             * 分两种情况,一种是单个删除,一种是批量删除,属性分别如下
+             * 1:单个删除
+             *     opts.data 当前要删除的数据对象
+             *     opts.index 当前要删除的索引
+             *     opts.list 当前列表数组
+             * 2:批量删除
+             *     opts.batch_ids 一维数组,需要删除的ID数组
+             *     opts.batch_datas 二维数组,每个元素为对象(需要删除的数据对象)
+             */
+            this.$emit('onDelete', opts);
+        },
+
+        onAdd(){
+            this.$emit('onAdd', {});
+        },
+
+        /**
+         * 获取行信息事件
+         * @param  {object} row   当前行对象
+         * @param  {number} index 当前行索引
+         * @param  {array} list  当前列表数组
+         */
+        onGetInfo(row, index, list, type) {
+            this.$emit('onGetInfo', {
+                row,
+                index,
+                list,
+                type
+            });
+        },
+
+
+        onBtnEvent(type,row={},index=-1,list=[]){
+            this.$emit('onBtn'+type, {
+                row,
+                index,
+                list
+            });
+        },
+
+
+        onUpdateBtn(data, index, list) {
+            if (this.btn_info.update && this.btn_info.update.path) {
+                var path = this.btn_info.update.path,
+                    param_keys = this.btn_info.update.param_keys || [],
+                    query_keys = this.btn_info.update.query_keys || [],
+                    query = {};
+
+                for (var i = 0; i < param_keys.length; i++) {
+                    path += '/' + data[param_keys[i]];
+                }
+                for (var i = 0; i < query_keys.length; i++) {
+                    query[query_keys[i]] = data[query_keys[i]];
+                }
+
+                // console.log(path);
+                // console.log(query);
+
+                this.$router.push({
+                    path: path,
+                    query: query
+                });
+            } else {
+                this.onGetInfo(data, index, list, 'update');
+            }
+
+        },
+
+
+        /**
+         * 内置删除事件执行成功后,更新列表方法
+         * 分两种情况,一种是批量删除,一种是单个删除
+         * 1:单个删除
+         *     row 当前需要删除行的索引
+         * 2:批量删除
+         *     row 一维数组,需要删除的ID数组
+         */
+        onUpdateList(row) {
+            if (!Array.isArray(row)) {
+                this.list.splice(row, 1);
+            } else {
+                this.list = this.list.filter(function(item, idx) {
+                    return row.indexOf(item.id) === -1;
+                });
+            }
+        },
+
+
+        /**
+         * 改变当前页码事件
+         * @param  {number} page 当前页面
+         */
+        onChangeCurrentPage(page) {
+            this.$emit('onChangeCurrentPage', page);
+        },
+
+
+        /**
+         * 改变每页显示的数量事件
+         * @param  {number} page_size 每页显示的数量
+         */
+        onChangePageSize(page_size) {
+            this.$emit('onChangePageSize', page_size);
+        }
+    },
+
+    mounted() {
+        // console.log(this.list);
+    },
+
+    /**
+     * 接收参数
+     * @type {Object}
+     */
+    props: {
+        ImageHost: {
+            type: String,
+            default: ''
+        },
+        List: {
+            type: Array,
+            required: true
+        },
+        FieldList: {
+            type: Array,
+            required: true
+        },
+        BtnInfo: {
+            type: Object,
+            default () {
+                return {};
+            }
+        },
+        Selection: {
+            type: Boolean,
+            default: false
+        },
+        Expand: {
+            type: Object,
+            default(){
+                return {
+                    show:false,
+                    position:"left"
+                };
+            }
+        },
+        Pagination: {
+            type: Object,
+            default () {
+                return {};
+            }
+        }
+    },
+
+
+    /**
+     * 监控参数
+     * @type {Object}
+     */
+    watch: {
+        ImageHost(v) {
+            if (v) {
+                this.image_host = v;
+            }
+        },
+        List(v) {
+            if (v) {
+                this.list = v;
+            }
+        },
+        FieldList(v) {
+            if (v) {
+                this.fields = v;
+            }
+        },
+        Selection(v) {
+            this.selection = v;
+        },
+        Expand(v){
+            this.expand=v;
+        },
+        BtnInfo(v) {
+            this.btn_info = v;
+        },
+        Pagination(v) {
+            this.pagination = v;
+        }
+    }
+}

+ 173 - 0
src/components/Common/ListData/ListData.vue

@@ -0,0 +1,173 @@
+<template>
+    <div class="list">
+        <el-col :span="24" class='actions-top'>
+            <el-button
+                    type='danger'
+                    icon='delete'
+                    v-if='selection'
+                    :disabled='batch_flag'
+                    @click='onBtnEvent("BatchDelete")'>删除选中
+            </el-button>
+
+            <el-button
+                    type='primary'
+                    icon='add'
+                    @click='onBtnEvent("Add")'>添加
+            </el-button>
+
+            <div class='list-header'>
+                <slot name='list-header'></slot>
+            </div>
+        </el-col>
+
+        <el-table border style="width: 100%" align='center'
+                  :data="list"
+                  @selection-change='onSelectionChange'>
+
+
+            <el-table-column type="expand"
+                             v-if='expand && expand.show && expand.show===true && (!expand.position || expand.position==="left")'>
+                <template scope="props">
+                </template>
+            </el-table-column>
+
+            <el-table-column v-if='selection'
+                             type="selection"
+                             width="55">
+            </el-table-column>
+
+            <template
+                    v-for='field in fields'>
+                <el-table-column
+                        v-if='!field.type || field.type!=="image"'
+                        :prop="field.key"
+                        :label="field.label"
+                        :align="field.align || 'center'"
+                        :sortable="field.sort || false"
+                        :formatter='field.formatter'
+                        :filters='field.filter_list'
+                        :filter-method="field.filter_method"
+                        :filter-multiple="field.filter_multiple"
+                        :style='field.style'
+                        :width='field.width'>
+                </el-table-column>
+                <el-table-column
+                        v-if='field.type && field.type==="image"'
+                        :prop="field.key"
+                        :label="field.label"
+                        :align="field.align || 'center'"
+                        :width='field.width'>
+                    <template scope='scope'>
+                        <img :src="(image_host || '')+scope.row[field.key]" alt="">
+                    </template>
+                </el-table-column>
+            </template>
+
+
+            <el-table-column
+                    v-if='btn_info.show!==false'
+                    :label="btn_info.label || '操作'"
+                    :width="btn_info.width || 160"
+                    :context="_self">
+                <template scope='scope'>
+                    <!-- @click='onGetInfo(scope.row,scope.$index,list,"select")' -->
+                    <el-button
+                            v-if='btn_info.select!==false'
+                            type="info"
+                            icon='view'
+                            size="mini"
+                            @click='onBtnEvent("Select",scope.row,scope.$index,list)'></el-button>
+                    <el-button
+                            v-if='btn_info.update!==false'
+                            type="info"
+                            icon='edit'
+                            size="mini"
+                            @click='onBtnEvent("Update",scope.row,scope.$index,list)'></el-button>
+                    <el-button
+                            v-if='btn_info.delete!==false'
+                            type="danger"
+                            icon='delete'
+                            size="mini"
+                            @click='onBtnEvent("Delete",scope.row,scope.$index,list)'></el-button>
+
+
+                    <el-button
+                            v-if='btn_info.list'
+                            v-for='btn in btn_info.list'
+                            :type="btn.type || 'info'"
+                            size="mini"
+                            @click='onGetInfo(scope.row,scope.$index,list,btn.fn_type || btn.text)'>{{btn.text}}
+                    </el-button>
+                </template>
+            </el-table-column>
+
+
+            <el-table-column type="expand"
+                             :context="_self"
+                             v-if='expand && expand.show && expand.show===true && expand.position && expand.position==="right"'>
+                <!--<slot name="list-expand" index="index"></slot>-->
+                <template scope="scope">
+                    <slot name="list-expand"
+                          :data="scope.row"
+                          :index="scope.$index"></slot>
+                </template>
+            </el-table-column>
+        </el-table>
+        <el-col :span="24" class='btm-action'>
+            <!-- 
+     
+             -->
+            <el-pagination
+                    v-if='pagination  && pagination.total && pagination.total>0'
+                    class='pagination'
+                    :page-sizes="pagination.page_sizes"
+                    :page-size="pagination.page_size"
+                    :layout="pagination.layout"
+                    :total="pagination.total"
+                    :current-page='pagination.current_page'
+                    @current-change='onChangeCurrentPage'
+                    @size-change='onChangePageSize'>
+            </el-pagination>
+        </el-col>
+    </div>
+</template>
+
+<script>
+	import ListDataJs from './ListData.js';
+	module.exports = ListDataJs;
+</script>
+<style scoped lang='less'>
+    .demo-form-inline {
+        display: inline-block;
+        float: right;
+    }
+
+    .btm-action {
+        margin-top: 20px;
+        text-align: center;
+    }
+
+    .actions-top {
+        height: 46px;
+    }
+
+    .pagination {
+        display: inline-block;
+    }
+
+    .list {
+
+    table {
+
+    img {
+        max-width: 100%;
+        height: auto;
+    }
+
+    }
+    }
+
+    .list-header {
+        display: inline-block;
+    }
+</style>

+ 2 - 0
src/components/Common/ListData/index.js

@@ -0,0 +1,2 @@
+import ListData from './ListData.vue';
+module.exports = ListData;

+ 8 - 0
src/components/Common/index.js

@@ -0,0 +1,8 @@
+module.exports = {
+	Bread: require('./Bread/Bread.vue'),
+	HeadNav: require('./HeadNav/HeadNav.vue'),
+	LeftMenu: require('./LeftMenu/LeftMenu.vue'),
+	ListData: require('./ListData/'),
+	FormData: require('./FormData/'),
+	DialogInfo: require('./DialogInfo/'),
+};

+ 86 - 0
src/components/Init/Init.js

@@ -0,0 +1,86 @@
+module.exports = {
+  data() {
+    return {
+      winSize: {
+        width: '',
+        height: ''
+      },
+      offset: {
+        position: 'absolute',
+        left: '',
+        top: ''
+      },
+      client_data: {
+        init: false,
+        basePath: null
+      },
+      dialog: {
+        show: false,
+        client_rules: {
+          name   : [{
+            required: true,
+            message : '名称不能为空!',
+            trigger : 'blur'
+          }]
+        }
+      }
+    }
+  },
+  methods: {
+    setSize() {
+      this.winSize.width = $(window).width() + "px";
+      this.winSize.height = $(window).height() + "px";
+
+      this.offset.left = (parseInt(this.winSize.width) / 2 - 175) + 'px';
+      this.offset.top = (parseInt(this.winSize.height) / 2 - 178) + 'px';
+    },
+    onFastInitClick() {
+      this.dialog.show = true;
+    },
+    onInitClick() {
+      this.dialog.show = true;
+    },
+    save_client(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_client_saveClient(this[ref], data => {
+            this.$message.success('保存成功');
+            this.init_client();
+          });
+        }
+      });
+    },
+    init_client() {
+      // 客户端初始化设置
+      this.$$api_client_initClient({}, data => {
+        // 轮询服务初始化设置
+        this.$$api_schedule_saveSetting({
+          key: 'api.uas.outer.url',
+          description: 'UAS系统外网接口地址',
+          value: window.location.origin + '/ERP'
+        }, data => {
+          this.$message.success('初始化完成');
+          this.$router.push('/deployment');
+        });
+      });
+    },
+    getClient() {
+      this.$$api_client_getClient({}, (data) => {
+        if (data.content.init) {
+          this.$router.push('/deployment');
+        } else {
+          this.client_data.basePath = window.location.origin + '/uas_manage_client';
+        }
+      });
+    }
+  },
+  created() {
+    this.setSize();
+    $(window).resize(() => {
+      this.setSize();
+    });
+  },
+  mounted() {
+    this.getClient();
+  }
+}

+ 25 - 0
src/components/Init/Init.less

@@ -0,0 +1,25 @@
+.init {
+  background : #1F2D3D;
+  .card-box {
+    box-shadow            : 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);
+    -webkit-border-radius : 5px;
+    border-radius         : 5px;
+    -moz-border-radius    : 5px;
+    background-clip       : padding-box;
+    margin-bottom         : 20px;
+    background-color      : #F9FAFC;
+    border                : 2px solid #8492A6;
+  }
+  .content {
+    width   : 350px;
+    padding : 35px 35px 15px 35px;
+    text-align: center;
+  }
+  .title {
+    margin      : 0px auto 40px auto;
+    text-align  : center;
+    color       : #505458;
+    font-weight : normal;
+    font-size   : 16px;
+  }
+}

+ 46 - 0
src/components/Init/Init.vue

@@ -0,0 +1,46 @@
+<template>
+  <div class="init" :style="winSize">
+    <el-row>
+      <el-col :span='24'>
+        <div class="content card-box" :style="offset">
+          <h3 class="title">请先初始化相关设置</h3>
+          <el-button type="primary" @click='onFastInitClick()'>一键初始化</el-button>
+          <el-button @click='onInitClick()'>自定义初始化</el-button>
+        </div>
+      </el-col>
+    </el-row>
+    <el-dialog title="初始化" v-model="dialog.show" size="small">
+      <el-form style="padding:20px;width:100%;"
+               label-width="100px"
+               :rules="dialog.client_rules"
+               :model="client_data"
+               ref='client_data'>
+        <el-form-item class='edit-form'
+                      label="名称"
+                      prop='name'>
+          <el-input
+            v-model="client_data.name" placeholder='优软科技'></el-input>
+        </el-form-item>
+        <el-form-item class='edit-form'
+                      label="访问地址"
+                      prop='basePath'>
+          <el-input
+            v-model="client_data.basePath"
+            placeholder='外网地址,例如:http://www.example.com:8080/uas_manage_client'></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+          <el-button @click="dialog.show = false">取 消</el-button>
+          <el-button type="primary" @click='save_client("client_data")'>确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+  import InitJs from './Init.js';
+	module.exports = InitJs;
+</script>
+<style scoped lang='less'>
+  @import url(Init.less);
+</style>
+

+ 2 - 0
src/components/Init/index.js

@@ -0,0 +1,2 @@
+import Init from './Init.vue';
+module.exports = Init;

+ 132 - 0
src/components/Login/Login.js

@@ -0,0 +1,132 @@
+module.exports = {
+  name: 'login',
+  data() {
+    return {
+      winSize: {
+        width: '',
+        height: ''
+      },
+
+      formOffset: {
+        position: 'absolute',
+        left: '',
+        top: ''
+      },
+
+      remumber: this.$store.state.user.remumber,
+
+      login_actions: {
+        disabled: false
+      },
+
+      data: {
+        username: '',
+        password: ''
+      },
+
+      rule_data: {
+        username: [{
+          validator: (rule, value, callback) => {
+            if (value === '') {
+              callback(new Error('请输入用户名'));
+            } else {
+              if (/^[a-zA-Z0-9_-]{1,16}$/.test(value)) {
+                callback();
+              } else {
+                callback(new Error('用户名至少6位,由大小写字母和数字,-,_组成'));
+              }
+            }
+          },
+          trigger: 'blur'
+        }],
+        password: [{
+          validator: (rule, value, callback) => {
+            if (value === '') {
+              callback(new Error('请输入密码'));
+            } else {
+              if (!(/^[a-zA-Z0-9_-]{6,16}$/.test(value))) {
+                callback(new Error('密码至少6位,由大小写字母和数字,-,_组成'));
+              } else {
+                callback();
+              }
+            }
+          },
+          trigger: 'blur'
+        }]
+      }
+    }
+  },
+  methods: {
+    setSize() {
+      this.winSize.width = $(window).width() + "px";
+      this.winSize.height = $(window).height() + "px";
+
+      this.formOffset.left = (parseInt(this.winSize.width) / 2 - 175) + 'px';
+      this.formOffset.top = (parseInt(this.winSize.height) / 2 - 178) + 'px';
+    },
+
+    onLogin(ref, type) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.login_actions.disabled = true;
+          //如果记住密码,提交的信息包括真实token,密码则是假的
+          //服务端登录验证优先级:用户名必须,其次先取token,不存在时再取密码
+          this.$$api_user_login(this[ref], data => {
+            //登录成功之后,验证是否记住密码,如果记住密码,本地保存记住信息
+            //如果没有记住,就初始化本地记住信息
+            if (this.remumber.remumber_flag === true) {
+              this.$store.dispatch('update_remumber', {
+                remumber_flag: this.remumber.remumber_flag,
+                remumber_login_info: {
+                  username: this[ref].username,
+                  token: data.content.token
+                }
+              });
+            } else {
+              this.$store.dispatch('remove_remumber');
+            }
+            data.content.username = this[ref].username;
+            this.$store.dispatch('update_userinfo', {
+              userinfo: data.content
+            }).then(() => {
+              this.checkClient();
+            });
+          }, {
+            errFn: () => {
+              this.login_actions.disabled = false;
+            },
+            tokenFlag: true
+          });
+        }
+      });
+    },
+
+    checkClient() {
+      this.$$api_client_getClient({}, data => {
+        if (data.content.init) {
+          this.$router.push('/deployment');
+        } else {
+          this.$router.push('/init');
+        }
+      });
+    },
+
+    resetForm(ref) {
+      this.$refs[ref].resetFields();
+    }
+  },
+  created() {
+    this.setSize();
+    $(window).resize(() => {
+      this.setSize();
+    });
+  },
+  mounted() {
+    //如果上次登录选择的是记住密码并登录成功,则会保存状态,用户名以及token
+    if (this.remumber.remumber_flag === true) {
+      this.data.username = this.remumber.remumber_login_info.username;
+      this.data.password = this.remumber.remumber_login_info.token.substring(0, 16);
+      this.$set(this.data, 'token', this.remumber.remumber_login_info.token);
+    }
+  }
+}

+ 36 - 0
src/components/Login/Login.less

@@ -0,0 +1,36 @@
+.login {
+    background : #1F2D3D;
+
+    .card-box {
+        box-shadow            : 0 0px 8px 0 rgba(0, 0, 0, 0.06), 0 1px 0px 0 rgba(0, 0, 0, 0.02);
+        -webkit-border-radius : 5px;
+        border-radius         : 5px;
+        -moz-border-radius    : 5px;
+        background-clip       : padding-box;
+        margin-bottom         : 20px;
+        background-color      : #F9FAFC;
+        border                : 2px solid #8492A6;
+    }
+
+    .title {
+        margin      : 0px auto 40px auto;
+        text-align  : center;
+        color       : #505458;
+        font-weight : normal;
+        font-size   : 16px;
+
+        span {
+            cursor : pointer;
+
+            &.active {
+                font-weight : bold;
+                font-size   : 18px;
+            }
+        }
+    }
+
+    .loginform {
+        width   : 350px;
+        padding : 35px 35px 15px 35px;
+    }
+}

+ 51 - 0
src/components/Login/Login.vue

@@ -0,0 +1,51 @@
+<template>
+    <div class="login" :style="winSize">
+        <el-row>
+            <el-col :span='24'>
+                <div class="content">
+                    <el-form label-position="left" label-width="0px" class="demo-ruleForm card-box loginform"
+                             v-loading="login_actions.disabled"
+                             :element-loading-text="'正在登录...'"
+                             :style="formOffset"
+                             :model='data'
+                             :rules="rule_data"
+                             ref='data'>
+                        <h3 class="title">系统登录</h3>
+                        <el-form-item
+                                prop='username'>
+                            <el-input type="text" auto-complete="off" placeholder="账号"
+                                      v-model='data.username'></el-input>
+                        </el-form-item>
+
+                        <el-form-item
+                                prop='password'>
+                            <el-input type="password" auto-complete="off" placeholder="密码"
+                                      v-model='data.password'
+                                      @keyup.native.enter="onLogin('data',true)"></el-input>
+                        </el-form-item>
+
+                        <el-checkbox style="margin:0px 0px 35px 0px;"
+                                     :checked='remumber.remumber_flag'
+                                     v-model='remumber.remumber_flag'>记住密码
+                        </el-checkbox>
+                        <el-form-item style="width:100%;">
+                            <el-button type="primary"
+                                       @click='onLogin("data")'>登录
+                            </el-button>
+                            <el-button @click='resetForm("data")'>重置</el-button>
+                        </el-form-item>
+                    </el-form>
+                </div>
+            </el-col>
+        </el-row>
+    </div>
+</template>
+
+<script>
+	import LoginJs from './Login.js';
+	module.exports = LoginJs;
+</script>
+
+<style scoped lang='less'>
+    @import url(Login.less);
+</style>

+ 2 - 0
src/components/Login/index.js

@@ -0,0 +1,2 @@
+import Login from './Login.vue';
+module.exports = Login;

+ 27 - 0
src/components/Modules/Database/Master/List.js

@@ -0,0 +1,27 @@
+module.exports = {
+  data() {
+    return {
+      master_list : []
+    }
+  },
+  methods: {
+    getList(){
+      this.$$api_database_getMastersView({
+      }, (data) => {
+        this.master_list = data.content;
+      });
+    },
+    refresh(){
+      this.$$api_database_refreshMasters({}, () => {
+        this.getList();
+        // 同时刷新调度任务
+        this.$$api_schedule_refreshTaskConfigs({}, () => {
+          this.$message.success('刷新成功');
+        })
+      })
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 32 - 0
src/components/Modules/Database/Master/List.vue

@@ -0,0 +1,32 @@
+<template>
+  <div class="list">
+    <div style="margin-bottom: 20px">
+      <el-button type="primary" @click="refresh()">刷新</el-button>
+    </div>
+    <el-table border style="width: 100%" align='center'
+              :data="master_list">
+      <el-table-column
+        prop="name"
+        label="名称"
+        align="center"
+        width="200">
+      </el-table-column>
+      <el-table-column
+        prop="title"
+        label="描述">
+      </el-table-column>
+      <el-table-column
+        label="环境"
+        width="200">
+        <template scope="scope">
+          <span>{{ scope.row.env == 'prod' ? '正式' : '测试' }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+	import ListJs from './List.js';
+	module.exports = ListJs;
+</script>

+ 2 - 0
src/components/Modules/Database/Master/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 63 - 0
src/components/Modules/Database/Setting/DBA/List.js

@@ -0,0 +1,63 @@
+module.exports = {
+  data() {
+    return {
+      dba_list : [],
+      dba_data: {},
+      dialog: {
+        show: false,
+        dba_rules: {
+          username: [{
+            required: true,
+            message : '请填写属性值!',
+            trigger : 'blur'
+          }],
+          password: [{
+            required: true,
+            message : '请填写属性值!',
+            trigger : 'blur'
+          }],
+          url: [{
+            required: true,
+            message : '请填写属性值!',
+            trigger : 'blur'
+          }]
+        }
+      }
+    }
+  },
+  methods: {
+    getList(){
+      this.$$api_database_getDBAList({
+      }, (data) => {
+        this.dba_list = data.content;
+      });
+    },
+    onEditClick(dba) {
+      this.dba_data = dba || {username: 'system', password: '', url: 'jdbc:oracle:thin:@127.0.0.1:1521:orcl'};
+      this.dialog.show = true;
+    },
+    onDelClick(dba) {
+      this.$confirm('确认删除?').then(_ => {
+          this.$$api_database_deleteDBA({
+            id: dba.id
+          }, (data) => {
+            this.getList();
+          });
+        }).catch(_ => {});
+    },
+    onSaveClick(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_database_saveDBA(this[ref], data => {
+            this.$message.success('保存成功');
+            this.dialog.show = false;
+            this.getList();
+          });
+        }
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 75 - 0
src/components/Modules/Database/Setting/DBA/List.vue

@@ -0,0 +1,75 @@
+<template>
+  <div class="list">
+    <div style="margin-bottom: 20px">
+      <el-button type="primary" @click="onEditClick()">添加DBA</el-button>
+    </div>
+    <el-table border style="width: 100%" align='center'
+              :data="dba_list">
+      <el-table-column
+        prop="username"
+        label="用户"
+        align="center"
+        width="200">
+      </el-table-column>
+      <el-table-column
+        prop="password"
+        label="密码"
+        width="240">
+      </el-table-column>
+      <el-table-column
+        prop="url"
+        label="连接">
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        :width="100"
+        :context="_self">
+        <template scope='scope'>
+          <el-button
+            type="info"
+            icon='edit'
+            size="mini"
+            @click='onEditClick(scope.row)'></el-button>
+          <el-button
+            type="warning"
+            icon='delete'
+            size="mini"
+            @click='onDelClick(scope.row)'></el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog title="设置" v-model="dialog.show" size="tiny">
+      <el-form style="padding:20px;width:100%;"
+               label-width="60px"
+               :rules="dialog.dba_rules"
+               :model="dba_data"
+               ref='dba_data'>
+        <el-form-item class='edit-form'
+                      label="用户"
+                      prop='username'>
+          <el-input v-model="dba_data.username"></el-input>
+        </el-form-item>
+        <el-form-item class='edit-form'
+                      label="密码"
+                      prop='password'>
+          <el-input type="password" v-model="dba_data.password"></el-input>
+        </el-form-item>
+        <el-form-item class="edit-form"
+                      label='连接'
+                      prop='url'>
+          <el-input v-model="dba_data.url"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialog.show = false">取 消</el-button>
+        <el-button type="primary" @click='onSaveClick("dba_data")'>确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+	import ListJs from './List.js';
+	module.exports = ListJs;
+</script>

+ 2 - 0
src/components/Modules/Database/Setting/DBA/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 3 - 0
src/components/Modules/Database/Setting/index.js

@@ -0,0 +1,3 @@
+module.exports = {
+  DBA: require('./DBA/')
+};

+ 4 - 0
src/components/Modules/Database/index.js

@@ -0,0 +1,4 @@
+module.exports = {
+  Master: require('./Master/'),
+  Setting: require('./Setting/')
+};

+ 73 - 0
src/components/Modules/Deployment/Artifact/Edit/Edit.js

@@ -0,0 +1,73 @@
+module.exports = {
+  data() {
+    return {
+      artifact_data : {
+        autoUpdate: true,
+        repoType: 'DIFF',
+        packaging: 'war'
+      },
+      artifact_rules: {
+        groupId   : [{
+          required: true,
+          message : '组名不能为空!',
+          trigger : 'blur'
+        }],
+        artifactId: [{
+          required: true,
+          message : '软件名不能为空!',
+          trigger : 'blur'
+        }],
+        description: [{
+          required: true,
+          message : '软件描述不能为空!',
+          trigger : 'blur'
+        }],
+        repoType: [{
+          required: true,
+          message : '请选择软件仓库类型!',
+          trigger : 'blur'
+        }],
+        packaging: [{
+          required: true,
+          message : '请选择打包文件类型!',
+          trigger : 'blur'
+        }]
+      }
+    }
+  },
+  methods: {
+    save_artifact(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_deployment_saveArtifact(this[ref], data => {
+            this.$message.success('保存成功');
+            this.$router.push('/deployment/artifact/list');
+          });
+        }
+      });
+    },
+    reset_artifact(ref) {
+      this.$refs[ref].resetFields();
+    },
+    getView(){
+      if (this.$route.query.id) {
+        this.$$api_deployment_getArtifact({
+          id: this.$route.query.id
+        }, (data) => {
+          this.artifact_data = data.content;
+        });
+      } else{
+        this.$delete(this.artifact_data, 'id');
+        this.$refs.artifact_data.resetFields();
+      }
+    }
+  },
+  mounted() {
+    this.getView();
+  },
+  watch: {
+    $route(to, from){
+      this.getView();
+    }
+  }
+}

+ 65 - 0
src/components/Modules/Deployment/Artifact/Edit/Edit.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="form">
+    <el-form style="margin:20px;width:60%;min-width:600px;"
+             label-width="100px"
+             :model="artifact_data"
+             :rules="artifact_rules"
+             ref='artifact_data'>
+      <el-form-item class='edit-form'
+                    label="组名"
+                    prop='groupId'>
+        <el-input
+          v-model="artifact_data.groupId" placeholder='com.uas.erp'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="软件名"
+                    prop='artifactId'>
+        <el-input
+          v-model="artifact_data.artifactId"
+          placeholder='uas'></el-input>
+      </el-form-item>
+      <el-form-item class="edit-form"
+                    label='描述'
+                    prop='description'>
+        <el-input type='textarea'
+                  v-model="artifact_data.description"
+                  placeholder='软件描述'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="仓库类型"
+                    prop='repoType'>
+        <el-select v-model="artifact_data.repoType" placeholder="请选择">
+          <el-option label="Maven仓库" value="MAVEN"></el-option>
+          <el-option label="差异仓库" value="DIFF"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="打包类型"
+                    prop='packaging'>
+        <el-select v-model="artifact_data.packaging" placeholder="请选择">
+          <el-option label="jar" value="jar"></el-option>
+          <el-option label="war" value="war"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="自动更新">
+        <el-switch on-text="启用" off-text="禁用"
+                   v-model="artifact_data.autoUpdate"></el-switch>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click='save_artifact("artifact_data")'>{{artifact_data.id ? '修改' : '添加'}}</el-button>
+        <el-button type="default"
+                   v-if="!artifact_data.id"
+                   @click='reset_artifact("artifact_data")'>重置</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+<script>
+    import EditJs from './Edit.js';
+    module.exports=EditJs;
+</script>
+<style scoped>
+    .edit-form{
+        width:500px;
+    }
+</style>

+ 2 - 0
src/components/Modules/Deployment/Artifact/Edit/index.js

@@ -0,0 +1,2 @@
+import Edit from './Edit.vue';
+module.exports = Edit;

+ 26 - 0
src/components/Modules/Deployment/Artifact/Item/Item.js

@@ -0,0 +1,26 @@
+module.exports = {
+  data() {
+    return {
+      artifact_items : []
+    }
+  },
+  methods: {
+    getList(){
+      if (this.$route.query.id) {
+        this.$$api_deployment_getArtifactItems({
+          id: this.$route.query.id
+        }, (data) => {
+          this.artifact_items = data.content;
+        });
+      }
+    }
+  },
+  mounted() {
+    this.getList();
+  },
+  watch: {
+    $route(to, from){
+      this.getList();
+    }
+  }
+}

+ 32 - 0
src/components/Modules/Deployment/Artifact/Item/Item.vue

@@ -0,0 +1,32 @@
+<template>
+  <div class="list">
+    <el-table border style="width: 100%" align='center'
+              :data="artifact_items">
+      <el-table-column
+        prop="version"
+        label="版本"
+        align="center"
+        width="80">
+      </el-table-column>
+      <el-table-column
+        label="程序提交时间"
+        align="center">
+        <template scope="scope">
+          <span>{{ scope.row.versionTime | time }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="软件更新时间"
+        align="center">
+        <template scope="scope">
+          <span>{{ scope.row.updateTime | time }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+	import ItemJs from './Item.js';
+	module.exports = ItemJs;
+</script>

+ 2 - 0
src/components/Modules/Deployment/Artifact/Item/index.js

@@ -0,0 +1,2 @@
+import Item from './Item.vue';
+module.exports = Item;

+ 40 - 0
src/components/Modules/Deployment/Artifact/List/List.js

@@ -0,0 +1,40 @@
+module.exports = {
+  data() {
+    return {
+      artifact_list: []
+    }
+  },
+  methods: {
+    getList() {
+      this.$$api_deployment_getArtifacts({}, (data) => {
+        this.artifact_list = data.content;
+      });
+    },
+    onEditClick(artifact) {
+      var path = {path: '/deployment/artifact/edit'};
+      if (artifact && artifact.id) {
+        path.query = {id: artifact.id};
+      }
+      this.$router.push(path);
+    },
+    onUpdateClick(artifact) {
+      this.$$api_deployment_pullArtifact({
+        id: artifact.id
+      }, (data) => {
+        this.$message.success('已更新到最新版本');
+        this.getList();
+      });
+    },
+    onVersionItemsClick(artifact) {
+      this.$router.push({
+        path: '/deployment/artifact/items',
+        query: {
+          id: (artifact ? artifact.id : null)
+        }
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 48 - 0
src/components/Modules/Deployment/Artifact/List/List.vue

@@ -0,0 +1,48 @@
+<template>
+  <el-row :gutter="20">
+    <el-col :span="6" v-for="artifact in artifact_list">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body">
+          <span class="left icon"><i class="el-icon-document"></i></span>
+          <span>{{ artifact.description }}<el-tag type="gray" class="tag">{{ artifact.lastVersion || '未更新' }}</el-tag></span>
+          <div class="bottom clearfix">
+            <el-button type="text" class="button" @click="onEditClick(artifact)">编辑</el-button>
+            <el-button type="text" class="button" @click="onUpdateClick(artifact)">软件更新</el-button>
+            <el-button type="text" class="button" v-show="artifact.lastVersion" @click="onVersionItemsClick(artifact)">版本日志</el-button>
+          </div>
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="4">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body text-center">
+          <el-button type="text" class="button icon" icon="plus" size="large" @click="onEditClick()"></el-button>
+        </div>
+      </el-card>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+    import ListJs from './List.js';
+    module.exports=ListJs;
+</script>
+<style scoped>
+    .left {
+      float: left;
+      margin-right: 14px
+    }
+    .tag {
+      margin-left: 10px
+    }
+    .icon {
+      font-size: 36px;
+    }
+    .card-body {
+      height: 83px;
+      padding: 14px;
+    }
+    .text-center {
+      text-align: center;
+    }
+</style>

+ 2 - 0
src/components/Modules/Deployment/Artifact/List/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 5 - 0
src/components/Modules/Deployment/Artifact/index.js

@@ -0,0 +1,5 @@
+module.exports = {
+  Edit: require('./Edit/'),
+  List: require('./List/'),
+  Item: require('./Item/')
+};

+ 79 - 0
src/components/Modules/Deployment/Container/Edit/Edit.js

@@ -0,0 +1,79 @@
+module.exports = {
+  data() {
+    return {
+      container_data : {
+        host: 'localhost',
+        post: '8080',
+        type: 'OTHER'
+      },
+      container_rules: {
+        host   : [{
+          required: true,
+          message : '请填写容器所在服务器地址!',
+          trigger : 'blur'
+        }],
+        home   : [{
+          required: true,
+          message : '请填写容器根目录!',
+          trigger : 'blur'
+        }],
+        port: [{
+          required: true,
+          type: 'number',
+          message : '端口不能为空!',
+          trigger : 'blur'
+        }],
+        description: [{
+          required: true,
+          message : '描述不能为空!',
+          trigger : 'blur'
+        }]
+      }
+    }
+  },
+  methods: {
+    save_container(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_deployment_saveContainer(this[ref], data => {
+            this.$message.success('保存成功');
+            this.$router.push('/deployment/container/list');
+          });
+        }
+      });
+    },
+    reset_artifact(ref) {
+      this.$refs[ref].resetFields();
+    },
+    getView(){
+      if (this.$route.query.id) {
+        this.$$api_deployment_getContainer({
+          id: this.$route.query.id
+        }, (data) => {
+          this.container_data = data.content;
+        });
+      } else{
+        this.$delete(this.container_data, 'id');
+        this.$refs.container_data.resetFields();
+      }
+    },
+    onHomeFieldBlur(ref) {
+      if ('CLUSTER' != this[ref].type) {
+        this.$$api_deployment_checkContainer({
+          path: this[ref].home
+        }, (data) => {
+          data.content.host = 'localhost';
+          this.container_data = data.content;
+        });
+      }
+    }
+  },
+  mounted() {
+    this.getView();
+  },
+  watch: {
+    $route(to, from){
+      this.getView();
+    }
+  }
+}

+ 99 - 0
src/components/Modules/Deployment/Container/Edit/Edit.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="form">
+    <el-form style="margin:20px;width:80%;min-width:600px;"
+             label-width="100px"
+             :model="container_data"
+             :rules="container_rules"
+             ref='container_data'>
+      <el-form-item class='edit-form'
+                    label="IP地址"
+                    prop='host'>
+        <el-input
+          :disabled='container_data.id ? true : false'
+          v-model="container_data.host" placeholder='localhost'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="根目录"
+                    prop='home'>
+        <el-input
+          :disabled='container_data.id ? true : false'
+          v-model="container_data.home" placeholder='/home/uas/program/1'
+          @blur='onHomeFieldBlur("container_data")'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="类型"
+                    prop='type'>
+        <el-select :disabled='container_data.id ? true : false'
+                   v-model="container_data.type" placeholder="请选择容器类型">
+          <el-option label="Tomcat" value="TOMCAT"></el-option>
+          <el-option label="JBoss" value="JBOSS"></el-option>
+          <el-option label="Spring Boot" value="SPRING_BOOT"></el-option>
+          <el-option label="集群" value="CLUSTER"></el-option>
+          <el-option label="其他" value="OTHER"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="web应用目录"
+                    prop='webappsHome'>
+        <el-input
+          :disabled='container_data.id ? true : false'
+          v-model="container_data.webappsHome"></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="端口"
+                    prop='port'>
+        <el-input-number
+          :disabled='container_data.id ? true : false'
+          v-model="container_data.port"
+          placeholder='8080'></el-input-number>
+      </el-form-item>
+      <el-form-item class="edit-form"
+                    label='描述'
+                    prop='description'>
+        <el-input type='textarea'
+                  v-model="container_data.description"
+                  placeholder='容器描述'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="启动命令"
+                    prop='startCommand'>
+        <el-input v-model="container_data.startCommand"></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="关闭命令"
+                    prop='stopCommand'>
+        <el-input v-model="container_data.stopCommand"></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="查状态命令"
+                    prop='statusCommand'>
+        <el-input v-model="container_data.statusCommand"></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="查进程命令"
+                    prop='processCommand'>
+        <el-input v-model="container_data.processCommand"></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="部署命令"
+                    prop='deployCommand'>
+        <el-input v-model="container_data.deployCommand"></el-input>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click='save_container("container_data")'>{{container_data.id ? '修改' : '添加'}}</el-button>
+        <el-button type="default"
+                   v-if="!container_data.id"
+                   @click='reset_container("container_data")'>重置</el-button>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+<script>
+    import EditJs from './Edit.js';
+    module.exports=EditJs;
+</script>
+<style scoped>
+    .edit-form{
+        width:500px;
+    }
+</style>

+ 2 - 0
src/components/Modules/Deployment/Container/Edit/index.js

@@ -0,0 +1,2 @@
+import Edit from './Edit.vue';
+module.exports = Edit;

+ 80 - 0
src/components/Modules/Deployment/Container/List/List.js

@@ -0,0 +1,80 @@
+module.exports = {
+  data() {
+    return {
+      container_list: []
+    }
+  },
+  methods: {
+    getList() {
+      this.$$api_deployment_getContainers({}, (data) => {
+        this.container_list = data.content;
+      });
+    },
+    getStatusType(status){
+      switch(status){
+        case 'RUNNING':
+          return 'success';
+        case 'STOPPED':
+          return 'warning';
+        case 'UNRECOGNIZED':
+          return 'danger';
+        default:
+          return 'danger';
+      }
+    },
+    getStatusMessage(status){
+      switch(status){
+        case 'RUNNING':
+          return '运行中';
+        case 'STOPPED':
+          return '已停止';
+        case 'UNRECOGNIZED':
+          return '状态异常';
+        default:
+          return '状态异常';
+      }
+    },
+    onEditClick(container) {
+      var path = {path: '/deployment/container/edit'};
+      if (container && container.id) {
+        path.query = {id: container.id};
+      }
+      this.$router.push(path);
+    },
+    onStartClick(container) {
+      this.$$api_deployment_startContainer({
+        id: container.id
+      }, (data) => {
+        this.$message.success('启动成功');
+        this.getList();
+      });
+    },
+    onStopClick(container) {
+      this.$$api_deployment_stopContainer({
+        id: container.id
+      }, (data) => {
+        this.$message.success('关闭成功');
+        this.getList();
+      });
+    },
+    onRestartClick(container) {
+      this.$$api_deployment_restartContainer({
+        id: container.id
+      }, (data) => {
+        this.$message.success('重启成功');
+        this.getList();
+      });
+    },
+    onDeleteClick(container) {
+      this.$$api_deployment_deleteContainer({
+        id: container.id
+      }, (data) => {
+        this.$message.success('删除成功');
+        this.getList();
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 51 - 0
src/components/Modules/Deployment/Container/List/List.vue

@@ -0,0 +1,51 @@
+<template>
+  <el-row :gutter="20">
+    <el-col :span="6" v-for="container in container_list">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body">
+          <span class="left icon"><i class="fa fa-hdd-o"></i></span>
+          <span>{{ container.description }}<el-tag :type="getStatusType(container.status)" class="tag">{{ getStatusMessage(container.status) }}</el-tag></span>
+          <div class="bottom clearfix">
+            <el-button type="text" class="button" @click="onEditClick(container)">编辑</el-button>
+            <el-button type="text" class="button" v-show="'RUNNING'==container.status" @click="onStopClick(container)">关闭</el-button>
+            <el-button type="text" class="button" v-show="'RUNNING'==container.status" @click="onRestartClick(container)">重启</el-button>
+            <el-button type="text" class="button" v-show="'STOPPED'==container.status" @click="onStartClick(container)">启动</el-button>
+            <el-button type="text" class="button" v-show="!container.status || 'UNRECOGNIZED'==container.status" @click="onInstallClick(container)">安装</el-button>
+            <el-button type="text" class="button" @click="onDeleteClick(container)">删除</el-button>
+          </div>
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="4">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body text-center">
+          <el-button type="text" class="button icon" icon="plus" size="large" @click="onEditClick()"></el-button>
+        </div>
+      </el-card>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+    import ListJs from './List.js';
+    module.exports=ListJs;
+</script>
+<style scoped>
+    .left {
+      float: left;
+      margin-right: 14px
+    }
+    .tag {
+      margin-left: 10px
+    }
+    .icon {
+      font-size: 36px;
+    }
+    .card-body {
+      height: 83px;
+      padding: 14px;
+    }
+    .text-center {
+      text-align: center
+    }
+</style>

+ 2 - 0
src/components/Modules/Deployment/Container/List/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 4 - 0
src/components/Modules/Deployment/Container/index.js

@@ -0,0 +1,4 @@
+module.exports = {
+  Edit: require('./Edit/'),
+  List: require('./List/')
+};

+ 150 - 0
src/components/Modules/Deployment/Project/Edit/Edit.js

@@ -0,0 +1,150 @@
+module.exports = {
+  data() {
+    return {
+      project_data : {
+        contextName : '/',
+        replaces : [],
+        artifactId: null,
+        containerId: null
+      },
+      dialog: {
+        show: false,
+        resource_rules: {
+          path: [{
+            required: true,
+            message : '请输入文件路径!',
+            trigger : 'blur'
+          }],
+          resource: [{
+            required: true,
+            message : '请输入文件内容!',
+            trigger : 'blur'
+          }]
+        }
+      },
+      resource_index: 0,
+      resource_data: {},
+      artifact_list: [],
+      container_list: [],
+      project_rules: {
+        artifactId   : [{
+          required: true,
+          type: 'number',
+          message : '请选择运行软件!',
+          trigger : 'blur'
+        }],
+        containerId: [{
+          required: true,
+          type: 'number',
+          message : '请选择运行环境!',
+          trigger : 'blur'
+        }],
+        description: [{
+          required: true,
+          message : '项目描述不能为空!',
+          trigger : 'blur'
+        }],
+        contextName: [{
+          required: true,
+          message : '请求路径不能为空!',
+          trigger : 'blur'
+        }]
+      }
+    }
+  },
+  methods: {
+    save_project(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_deployment_saveProject(this[ref], data => {
+            this.$message.success('保存成功');
+            this.$router.push('/deployment/project/list');
+          });
+        }
+      });
+    },
+    reset_project(ref) {
+      this.$refs[ref].resetFields();
+    },
+    getView(){
+      if (this.$route.query.id) {
+        this.$$api_deployment_getProject({
+          id: this.$route.query.id
+        }, (data) => {
+          this.project_data = data.content;
+        });
+      } else{
+        this.$delete(this.project_data, 'id');
+        this.$refs.project_data.resetFields();
+      }
+    },
+    getArtifacts() {
+      this.$$api_deployment_getArtifacts({}, (data) => {
+        this.artifact_list = data.content;
+        if (this.artifact_list.length && !this.project_data.artifactId) {
+          this.project_data.artifactId = this.artifact_list[0].id;
+        }
+      });
+    },
+    getContainers() {
+      this.$$api_deployment_getContainers({}, (data) => {
+        this.container_list = data.content;
+        if (this.container_list.length && !this.project_data.containerId) {
+          this.project_data.containerId = this.container_list[0].id;
+        }
+      });
+    },
+    init() {
+      this.getView();
+      this.getArtifacts();
+      this.getContainers();
+    },
+    onAddResourceClick() {
+      this.project_data.replaces = this.project_data.replaces || [];
+      this.project_data.replaces.push({});
+      this.onEditResourceClick(this.project_data.replaces.length - 1);
+    },
+    onEditResourceClick(index) {
+      this.resource_index = index;
+      this.resource_data = this.project_data.replaces[index];
+      if (this.resource_data.filePath) {
+        this.$$api_resource_getResource({
+          path: this.resource_data.filePath
+        }, data => {
+          this.resource_data.resource = data.content;
+          this.dialog.show = true;
+        });
+      } else {
+        this.dialog.show = true;
+      }
+    },
+    onResourceSaveClick(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_resource_saveResource(this[ref], data => {
+            this.$message.success('保存成功');
+            this.project_data.replaces[this.resource_index] = {
+              path: this[ref].path,
+              filePath: data.content
+            };
+            this.dialog.show = false;
+          });
+        }
+      });
+    },
+    onResourceCancelClick(ref) {
+      if (!this[ref].path && !this[ref].filePath) {
+        this.project_data.replaces.splice(this.resource_index);
+      }
+      this.dialog.show = false;
+    }
+  },
+  mounted() {
+    this.init();
+  },
+  watch: {
+    $route(to, from){
+      this.init();
+    }
+  }
+}

+ 87 - 0
src/components/Modules/Deployment/Project/Edit/Edit.vue

@@ -0,0 +1,87 @@
+<template>
+  <div class="form">
+    <el-form style="margin:20px;width:60%;min-width:600px;"
+             label-width="100px"
+             :model="project_data"
+             :rules="project_rules"
+             ref='project_data'>
+      <el-form-item class='edit-form'
+                    label="软件"
+                    prop='artifactId'>
+        <el-select :disabled='project_data.id ? true : false'
+                   v-model="project_data.artifactId" placeholder="请选择运行软件">
+          <el-option v-for="artifact in artifact_list"
+                     :label="artifact.description"
+                     :value="artifact.id"
+                     :key="artifact.id"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="容器"
+                    prop='containerId'>
+        <el-select :disabled='project_data.id ? true : false'
+                   v-model="project_data.containerId" placeholder="请选择运行环境">
+          <el-option v-for="container in container_list"
+                     :label="container.description"
+                     :value="container.id"
+                     :key="container.id"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item class="edit-form"
+                    label='描述'
+                    prop='description'>
+        <el-input type='textarea'
+                  v-model="project_data.description"
+                  placeholder='项目描述'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="请求路径"
+                    prop='contextName'>
+        <el-input
+          :disabled='project_data.id ? true : false'
+          v-model="project_data.contextName"
+          placeholder='/ERP'></el-input>
+      </el-form-item>
+      <el-form-item class='edit-form'
+                    label="替换资源">
+        <template v-for="(r, index) in project_data.replaces">
+          <el-button type="text" icon="document" @click="onEditResourceClick(index)">{{r.path}}</el-button>
+        </template>
+        <el-button type="text" icon="plus" @click="onAddResourceClick()"></el-button>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click='save_project("project_data")'>{{project_data.id ? '修改' : '添加'}}</el-button>
+        <el-button type="default"
+                   v-if="!project_data.id"
+                   @click='reset_project("project_data")'>重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-dialog title="编辑资源" v-model="dialog.show" size="large">
+      <el-form style="padding:20px;width:100%;"
+               label-position="top"
+               :rules="dialog.resource_rules"
+               :model="resource_data"
+               ref='resource_data'>
+        <el-form-item class="edit-form"
+                      label='文件路径'
+                      prop='path'>
+          <el-input v-model="resource_data.path"></el-input>
+        </el-form-item>
+        <el-form-item class="edit-form"
+                      label='文件内容'
+                      prop='resource'>
+          <el-input type="textarea" v-model="resource_data.resource" :rows="10"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click='onResourceCancelClick("resource_data")'>取 消</el-button>
+        <el-button type="primary" @click='onResourceSaveClick("resource_data")'>确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+<script>
+    import EditJs from './Edit.js';
+    module.exports=EditJs;
+</script>

+ 2 - 0
src/components/Modules/Deployment/Project/Edit/index.js

@@ -0,0 +1,2 @@
+import Edit from './Edit.vue';
+module.exports = Edit;

+ 48 - 0
src/components/Modules/Deployment/Project/List/List.js

@@ -0,0 +1,48 @@
+module.exports = {
+  data() {
+    return {
+      project_list: []
+    }
+  },
+  methods: {
+    getList() {
+      this.$$api_deployment_getProjects({}, (data) => {
+        this.project_list = data.content;
+      });
+    },
+    onEditClick(project) {
+      var path = {path: '/deployment/project/edit'};
+      if (project && project.id) {
+        path.query = {id: project.id};
+      }
+      this.$router.push(path);
+    },
+    onDeployClick(project) {
+      this.$$api_deployment_deployProject({
+        id: project.id
+      }, (data) => {
+        this.$message.success('部署成功');
+        this.getList();
+      });
+    },
+    onLogClick(project) {
+      this.$router.push({
+        path: '/deployment/project/log',
+        query: {
+          id: project.id
+        }
+      });
+    },
+    onDeleteClick(project) {
+      this.$$api_deployment_deleteProject({
+        id: project.id
+      }, (data) => {
+        this.$message.success('删除成功');
+        this.getList();
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 49 - 0
src/components/Modules/Deployment/Project/List/List.vue

@@ -0,0 +1,49 @@
+<template>
+  <el-row :gutter="20">
+    <el-col :span="6" v-for="project in project_list">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body">
+          <span class="left icon"><i class="el-icon-share"></i></span>
+          <span>{{ project.description }}<el-tag type="gray" class="tag">{{ project.deployVersion || '未部署' }}</el-tag></span>
+          <div class="bottom clearfix">
+            <el-button type="text" class="button" @click="onEditClick(project)">编辑</el-button>
+            <el-button type="text" class="button" @click="onDeployClick(project)">部署</el-button>
+            <el-button type="text" class="button" v-show="project.deployVersion" @click="onLogClick(project)">查看日志</el-button>
+            <el-button type="text" class="button" @click="onDeleteClick(project)">删除</el-button>
+          </div>
+        </div>
+      </el-card>
+    </el-col>
+    <el-col :span="4">
+      <el-card :body-style="{ padding: '0px' }">
+        <div class="card-body text-center">
+          <el-button type="text" class="button icon" icon="plus" size="large" @click="onEditClick()"></el-button>
+        </div>
+      </el-card>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+    import ListJs from './List.js';
+    module.exports=ListJs;
+</script>
+<style scoped>
+    .left {
+      float: left;
+      margin-right: 14px
+    }
+    .tag {
+      margin-left: 10px
+    }
+    .icon {
+      font-size: 36px;
+    }
+    .card-body {
+      height: 83px;
+      padding: 14px;
+    }
+    .text-center {
+      text-align: center;
+    }
+</style>

+ 2 - 0
src/components/Modules/Deployment/Project/List/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 26 - 0
src/components/Modules/Deployment/Project/Log/Log.js

@@ -0,0 +1,26 @@
+module.exports = {
+  data() {
+    return {
+      deploy_logs : []
+    }
+  },
+  methods: {
+    getList(){
+      if (this.$route.query.id) {
+        this.$$api_deployment_getProjectDeployLogs({
+          id: this.$route.query.id
+        }, (data) => {
+          this.deploy_logs = data.content;
+        });
+      }
+    }
+  },
+  mounted() {
+    this.getList();
+  },
+  watch: {
+    $route(to, from){
+      this.getList();
+    }
+  }
+}

+ 25 - 0
src/components/Modules/Deployment/Project/Log/Log.vue

@@ -0,0 +1,25 @@
+<template>
+  <div class="list">
+    <el-table border style="width: 100%" align='center'
+              :data="deploy_logs">
+      <el-table-column
+        prop="deployVersion"
+        label="版本"
+        align="center"
+        width="80">
+      </el-table-column>
+      <el-table-column
+        label="部署时间"
+        align="center">
+        <template scope="scope">
+          <span>{{ scope.row.deployTime | time }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+	import LogJs from './Log.js';
+	module.exports = LogJs;
+</script>

+ 2 - 0
src/components/Modules/Deployment/Project/Log/index.js

@@ -0,0 +1,2 @@
+import Log from './Log.vue';
+module.exports = Log;

+ 5 - 0
src/components/Modules/Deployment/Project/index.js

@@ -0,0 +1,5 @@
+module.exports = {
+  Edit: require('./Edit/'),
+  List: require('./List/'),
+  Log: require('./Log/')
+};

+ 5 - 0
src/components/Modules/Deployment/index.js

@@ -0,0 +1,5 @@
+module.exports = {
+  Artifact: require('./Artifact/'),
+  Container: require('./Container/'),
+  Project: require('./Project/')
+};

+ 0 - 0
README.md → src/components/Modules/Monitor/Alarm/Edit/index.js


+ 0 - 0
src/components/Modules/Monitor/Alarm/List/index.js


+ 4 - 0
src/components/Modules/Monitor/Alarm/index.js

@@ -0,0 +1,4 @@
+module.exports = {
+  Edit: require('./Edit/'),
+  List: require('./List/')
+};

+ 0 - 0
src/components/Modules/Monitor/Backup/Edit/index.js


+ 0 - 0
src/components/Modules/Monitor/Backup/List/index.js


+ 4 - 0
src/components/Modules/Monitor/Backup/index.js

@@ -0,0 +1,4 @@
+module.exports = {
+  Edit: require('./Edit/'),
+  List: require('./List/')
+};

+ 4 - 0
src/components/Modules/Monitor/index.js

@@ -0,0 +1,4 @@
+module.exports = {
+  Alarm: require('./Alarm/'),
+  Backup: require('./Backup/')
+};

+ 242 - 0
src/components/Modules/Schedule/List/List.js

@@ -0,0 +1,242 @@
+let getTimeRange = (dayOffset) => {
+  const end = new Date()
+  end.setDate(end.getDate() - dayOffset)
+  end.setHours(23, 59, 59, 59)
+  const start = new Date(end.getTime())
+  start.setHours(0, 0, 0, 0)
+  return [start, end]
+}
+module.exports = {
+  data() {
+    return {
+      module: '',
+      masters: {
+        active: {
+          name: '',
+          title: ''
+        },
+        list: []
+      },
+      roles: {
+        active: {
+          name: 'BUYER',
+          title: '买家数据'
+        },
+        list: [{
+          name: 'BUYER',
+          title: '买家数据'
+        }, {
+          name: 'SELLER',
+          title: '卖家数据'
+        }, {
+          name: 'PLAIN',
+          title: '其他'
+        }]
+      },
+      tasks: {
+        active: {},
+        list: [],
+        checked: [],
+        getted: new Set(),
+        original: {},
+        modified: new Map()
+      }
+    }
+  },
+  methods: {
+    getMasterList() {
+      this.$$api_database_getMastersView({cloudEnabled: true}, (data) => {
+        this.masters.list = data.content
+        this.masters.active.name = this.masters.list[0].name
+        this.masters.active.title = this.masters.list[0].title
+      })
+    },
+    getTaskTree(){
+      let masterName = this.masters.active.name, role = this.roles.active.name,
+        key = masterName + '@' + role
+      if (!this.tasks.getted.has(key)) {
+        this.$$api_schedule_getTaskConfigs({
+          module: this.module,
+          masterName,
+          role
+        }, (data) => {
+          this.groupByBean(data.content)
+          this.tasks.getted.add(key)
+        });
+      }
+    },
+    groupByBean(tasks) {
+      let masterName = this.masters.active.name, role = this.roles.active.name,
+        prefix = masterName + '@' + role, map = new Map()
+      tasks.forEach( task => {
+        let children = map.get(prefix + '@' + task.beanName) || []
+        children.push(task)
+        map.set(prefix + '@' + task.beanName, children)
+      })
+      map.forEach( (value, key) => {
+        let children = value.map( task => {
+          let id = prefix + '@' + task.beanName + '@' + task.methodName
+          task.enabled && this.tasks.checked.push(id)
+          task.expType = this.getExpType(task)
+          this.tasks.original[id] = Object.assign({}, task)
+          return {
+            data: task,
+            id: id,
+            label: task.methodTitle,
+            checked: task.enabled
+          }
+        }), title = children[0].data.beanTitle
+        this.tasks.list.push({
+          data: {masterName, role, beanTitle: title},
+          id: key,
+          label: title,
+          children
+        })
+      })
+    },
+    getNodeIdByTask(task) {
+      return this.masters.active.name + '@' + this.roles.active.name +
+          '@' + task.beanName + '@' + task.methodName
+    },
+    onNodeClick(node) {
+      this.tasks.active = node.data
+    },
+    onNodeCheckChange(node, checked) {
+      if (!node.children) {
+        let index = this.tasks.checked.indexOf(node.id)
+        if (index > -1 && !checked) {
+          this.tasks.checked.splice(index, 1)
+        } else if (index == -1 && checked) {
+          this.tasks.checked.push(node.id)
+        }
+        node.data.enabled = checked
+        this.checkModify(node.data)
+      }
+    },
+    getExpType(task) {
+      let type = 'fixedDelay'
+      if (task.fixedRate > 0) {
+        type = 'fixedRate'
+      } else if(task.cron) {
+        type = 'cron'
+      }
+      return type
+    },
+    isTaskActive(task) {
+      return task.role == this.roles.active.name &&
+        task.masterName == this.masters.active.name
+    },
+    /**
+     * 检查是否修改
+     * @param latest
+     */
+    checkModify(latest) {
+      let key = this.getNodeIdByTask(latest), modified = this.tasks.modified.get(key),
+        original = this.tasks.original[key]
+      if (this.isTaskEquals(latest, original)) {
+        if (modified) {
+          this.tasks.modified.delete(key)
+        }
+      } else {
+        this.tasks.modified.set(key, latest)
+      }
+    },
+    /**
+     * 判断task配置是否相同
+     * @param m
+     * @param n
+     * @returns {boolean}
+     */
+    isTaskEquals(m, n) {
+      return m.enabled == n.enabled && m.expType == n.expType
+        && m[m.expType] == n[n.expType]
+    },
+    setTaskChecked(task) {
+      this.$refs['tree'].setChecked(this.getNodeIdByTask(task), task.enabled)
+    },
+    /**
+     * 保存
+     */
+    onTasksSave() {
+      let tasks = []
+      this.tasks.modified.forEach( (value, key) => {
+        tasks.push(Object.assign({}, value, {
+          fixedDelay: value.expType == 'fixedDelay' ? value.fixedDelay : -1,
+          fixedRate: value.expType == 'fixedRate' ? value.fixedRate : -1,
+          cron: value.expType == 'cron' ? value.cron : ''
+        }))
+      })
+      this.$$api_schedule_saveTaskConfigs(tasks, () => {
+        this.$message('修改成功')
+        this.reloadPage()
+      });
+    },
+    /**
+     * 重置
+     */
+    onTasksReset() {
+      this.tasks.modified.forEach( (value, key) => {
+        let original = this.tasks.original[key]
+        Object.assign(value, original)
+        this.setTaskChecked(value)
+      })
+    },
+    reloadPage() {
+      this.tasks = {
+        active: {},
+        list: [],
+        checked: [],
+        getted: new Set(),
+        original: {},
+        modified: new Map()
+      }
+      if (this.masters.list.length) {
+        this.getTaskTree()
+      }
+    }
+  },
+  watch: {
+    'module'(val) {
+      this.reloadPage()
+    },
+    'masters.active.name'(val){
+      this.tasks.active = {}
+      let masters = this.masters.list.filter( master => master.name == val)
+      if (masters.length) {
+        this.masters.active.title = masters[0].title
+        this.getTaskTree()
+      }
+    },
+    'roles.active.name'(val) {
+      this.tasks.active = {}
+      let roles = this.roles.list.filter( role => role.name == val)
+      if (roles.length) {
+        this.roles.active.title = roles[0].title
+        this.getTaskTree()
+      }
+    },
+    'tasks.active': {
+      handler(val, oldVal) {
+        if (val && val.methodName) {
+          this.checkModify(val)
+          this.setTaskChecked(val)
+        }
+      },
+      deep: true
+    },
+    $route(to, from){
+      this.module = this.$route.path.split('/')[2]
+    }
+  },
+  computed: {
+    activeTasks() {
+      return this.tasks.list.filter(node => {
+        return this.isTaskActive(node.data)
+      })
+    }
+  },
+  mounted() {
+    this.module = this.$route.path.split('/')[2]
+    this.getMasterList()
+  }
+}

+ 119 - 0
src/components/Modules/Schedule/List/List.vue

@@ -0,0 +1,119 @@
+<template>
+    <div>
+      <transition enter-active-class="animated slideInDown"
+                  leave-active-class="animated slideOutUp">
+        <div class="toolbar" v-show="tasks.modified.size > 0">
+          <el-badge :value="tasks.modified.size" class="item">
+            <el-button type="primary" @click="onTasksSave">保存修改</el-button>
+          </el-badge>
+          <el-button @click="onTasksReset">重置</el-button>
+        </div>
+      </transition>
+      <!--账套选项-->
+      <el-tabs v-model="masters.active.name">
+        <el-tab-pane v-for="master in masters.list"
+                     :label="master.title"
+                     :name="master.name" :key="master.name">
+        </el-tab-pane>
+      </el-tabs>
+      <el-row :gutter="20">
+        <el-col :span="8">
+          <div class="left-panel">
+            <!--任务类型-->
+            <el-tabs v-model="roles.active.name">
+              <el-tab-pane v-for="role in roles.list"
+                           :label="role.title"
+                           :name="role.name" :key="role.name">
+              </el-tab-pane>
+            </el-tabs>
+            <el-tree
+              ref="tree"
+              :data="activeTasks"
+              :default-checked-keys="tasks.checked"
+              :highlight-current="true"
+              show-checkbox
+              node-key="id"
+              accordion
+              @node-click="onNodeClick"
+              @check-change="onNodeCheckChange" class="tree-panel">
+            </el-tree>
+          </div>
+        </el-col>
+        <el-col :span="16">
+          <div class="panel">
+            <div class="breadcrumb">
+              <el-breadcrumb separator="/">
+                <el-breadcrumb-item>{{masters.active.title}}</el-breadcrumb-item>
+                <el-breadcrumb-item>{{roles.active.title}}</el-breadcrumb-item>
+                <el-breadcrumb-item v-show="tasks.active.beanTitle">{{tasks.active.beanTitle}}</el-breadcrumb-item>
+                <el-breadcrumb-item v-show="tasks.active.methodTitle">{{tasks.active.methodTitle}}</el-breadcrumb-item>
+              </el-breadcrumb>
+            </div>
+            <div class="block" v-if="tasks.active.methodName">
+              <el-form ref="form" :model="tasks.active" label-width="80px">
+                <el-form-item label="开启任务">
+                  <el-switch on-text="启" off-text="停" v-model="tasks.active.enabled"></el-switch>
+                </el-form-item>
+                <el-form-item label="执行时间">
+                  <el-radio-group v-model="tasks.active.expType">
+                    <el-radio label="fixedDelay">固定延迟</el-radio>
+                    <el-radio label="fixedRate">固定周期</el-radio>
+                    <el-radio label="cron">cron表达式</el-radio>
+                  </el-radio-group>
+                  <div style="width: 180px">
+                    <el-input v-if="tasks.active.expType=='fixedDelay'" v-model="tasks.active.fixedDelay">
+                      <template slot="append">毫秒</template>
+                    </el-input>
+                    <el-input v-if="tasks.active.expType=='fixedRate'" v-model="tasks.active.fixedRate">
+                      <template slot="append">毫秒</template>
+                    </el-input>
+                    <el-input v-if="tasks.active.expType=='cron'" v-model="tasks.active.cron"></el-input>
+                  </div>
+                </el-form-item>
+              </el-form>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </div>
+</template>
+
+<script>
+  import ListJs from './List.js';
+	module.exports = ListJs;
+</script>
+<style scoped>
+  .toolbar {
+    position: absolute;
+    width: 200px;
+    top: 0;
+    left: 50%;
+    margin-left: -100px;
+    padding: 10px 15px;
+    border: 1px solid #d1dbe5;
+    box-shadow: 0 2px 4px 0 rgba(0,0,0,.12), 0 0 6px 0 rgba(0,0,0,.04);
+    z-index: 10
+  }
+  .toolbar .item {
+    margin-right: 15px
+  }
+  .left-panel {
+    border: 1px solid #d1dbe5;
+  }
+  .left-panel .tree-panel {
+    border-width: 0
+  }
+  .panel {
+    border: 1px solid #d1dbe5;
+    border-radius: 4px;
+    transition: .2s;
+  }
+  .panel .breadcrumb {
+    padding: 15px;
+    padding-bottom: 14px
+  }
+  .panel .block {
+    padding: 15px;
+    border-top: 1px solid #d1dbe5;
+  }
+</style>

+ 2 - 0
src/components/Modules/Schedule/List/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 44 - 0
src/components/Modules/Schedule/Setting/List.js

@@ -0,0 +1,44 @@
+module.exports = {
+  data() {
+    return {
+      setting_list : [],
+      setting_data: {},
+      dialog: {
+        show: false,
+        setting_rules: {
+          value: [{
+            required: true,
+            message : '请填写属性值!',
+            trigger : 'blur'
+          }]
+        }
+      }
+    }
+  },
+  methods: {
+    getList(){
+      this.$$api_schedule_getSettings({
+      }, (data) => {
+        this.setting_list = data.content;
+      });
+    },
+    onEditClick(setting) {
+      this.setting_data = setting;
+      this.dialog.show = true;
+    },
+    onSaveClick(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_schedule_saveSetting(this[ref], data => {
+            this.$message.success('保存成功');
+            this.dialog.show = false;
+            this.getList();
+          });
+        }
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 67 - 0
src/components/Modules/Schedule/Setting/List.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="list">
+    <el-table border style="width: 100%" align='center'
+              :data="setting_list">
+      <el-table-column
+        prop="key"
+        label="名称"
+        align="center"
+        width="200">
+      </el-table-column>
+      <el-table-column
+        prop="description"
+        label="描述"
+        width="240">
+      </el-table-column>
+      <el-table-column
+        prop="value"
+        label="值">
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        :width="80"
+        :context="_self">
+        <template scope='scope'>
+          <el-button
+            type="info"
+            icon='edit'
+            size="mini"
+            @click='onEditClick(scope.row)'></el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog title="设置" v-model="dialog.show" size="tiny">
+      <el-form style="padding:20px;width:100%;"
+               label-width="60px"
+               :rules="dialog.setting_rules"
+               :model="setting_data"
+               ref='setting_data'>
+        <el-form-item class='edit-form'
+                      label="名称"
+                      prop='key'>
+          {{setting_data.key}}
+        </el-form-item>
+        <el-form-item class='edit-form'
+                      label="描述"
+                      prop='description'>
+          {{setting_data.description}}
+        </el-form-item>
+        <el-form-item class="edit-form"
+                      label='值'
+                      prop='value'>
+          <el-input v-model="setting_data.value"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialog.show = false">取 消</el-button>
+        <el-button type="primary" @click='onSaveClick("setting_data")'>确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+	import ListJs from './List.js';
+	module.exports = ListJs;
+</script>

+ 2 - 0
src/components/Modules/Schedule/Setting/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 173 - 0
src/components/Modules/Schedule/Stat/Stat.js

@@ -0,0 +1,173 @@
+module.exports = {
+  data() {
+    return {
+      module: '',
+      masterName: '',
+      stats: [],
+      tasks: [],
+      options: [{
+        value: 'BUYER',
+        label: '买家数据',
+        children: []
+      }, {
+        value: 'SELLER',
+        label: '卖家数据',
+        children: []
+      }, {
+        value: 'PLAIN',
+        label: '其他',
+        children: []
+      }],
+      logging: {},
+      searcher: {
+        value: 'all',
+        options: [{
+          value: 'all',
+          label: '全部日志',
+        },{
+          value: 'upload',
+          label: '上传成功',
+        },{
+          value: 'download',
+          label: '下载成功',
+        },{
+          value: 'error',
+          label: '执行失败',
+        }]
+      }
+    }
+  },
+  methods: {
+    /**
+     * 统计查询
+     */
+    getAllStats() {
+      this.$$api_schedule_getAllTaskStat({
+        module: this.module
+      }, (data) => {
+        this.stats = data.content
+        if (this.stats.length) {
+          this.masterName = this.stats[0].master
+          // init tasks select
+          this.getAllTasks()
+          // init logging
+          this.stats.forEach( stat => {
+            this.$set(this.logging, stat.master, {
+              taskFilter: [],
+              list: [],
+              pagination: {
+                current_page: 1,
+                total: 0,
+                page_size: 9,
+                page_sizes: [9, 12, 24],
+                layout: "total, sizes, prev, pager, next"
+              }
+            })
+          })
+        }
+      })
+    },
+    getAllTasks() {
+      this.$$api_schedule_getTaskConfigs({
+        module: this.module,
+        masterName: this.masterName
+      }, (data) => {
+        this.tasks = data.content
+        this.setTaskOption(data.content)
+      })
+    },
+    setTaskOption(tasks) {
+      this.options.forEach( (option) => {
+        option.children = this.groupByBean(tasks, option.value)
+      })
+    },
+    /**
+     * 选择任务
+     * @param val [role, beanName, methodName]
+     */
+    onTaskOptionChange(val) {
+      this.logging[this.masterName].taskFilter = val[2] ? val : []
+    },
+    groupByBean(tasks, role) {
+      let list = [], map = new Map(), spit = '!@#$%'
+      tasks.forEach( task => {
+        if (task.role == role) {
+          let key = task.beanName + spit + task.beanTitle, children = map.get(key) || []
+          children.push({
+            value: task.methodName,
+            label: task.methodTitle
+          })
+          map.set(key, children)
+        }
+      })
+      map.forEach( (value, key) => {
+        let s = key.split(spit)
+        list.push({
+          value: s[0],
+          label: s[1],
+          children: value
+        })
+      })
+      return list
+    },
+    /**
+     * 日志查询
+     */
+    getTaskLogging() {
+      let logging = this.logging[this.masterName], filter = logging.taskFilter || []
+      this.$$api_schedule_getTaskLogging({
+        // 接口里面page从0开始
+        page: logging.pagination.current_page - 1,
+        size: logging.pagination.page_size,
+        masterName: this.masterName,
+        beanName: filter[1],
+        methodName: filter[2],
+        level: this.searcher.value == 'error' ? 'ERROR' : null
+      }, (data) => {
+        logging.list = data.content.content.map( (log) => {
+          const tasks = this.tasks.filter( task => {
+            return task.beanName == log.beanName && task.methodName == log.methodName
+          })
+          if (tasks.length) {
+            log.beanTitle = tasks[0].beanTitle
+            log.methodTitle = tasks[0].methodTitle
+          }
+          return log
+        })
+        logging.pagination.total = data.content.totalElements
+      })
+    },
+    onChangeCurrentPage(page) {
+      this.logging[this.masterName].pagination.current_page = page
+      this.getTaskLogging()
+    },
+    onChangePageSize(page_size) {
+      this.logging[this.masterName].pagination.page_size = page_size
+      this.getTaskLogging()
+    },
+    refreshStat() {
+      const master = this.masterName
+      this.$$api_schedule_getTaskStat({
+        module: this.module,
+        masterName: master
+      }, (data) => {
+        const stat = this.stats.filter( stat => stat.master == master)
+        Object.assign(stat[0], data.content)
+      })
+    }
+  },
+  mounted() {
+    this.module = this.$route.path.split('/')[2]
+    this.getAllStats()
+  },
+  watch: {
+    $route(to, from){
+      this.module = this.$route.path.split('/')[2]
+    }
+  },
+  computed: {
+    myLogging() {
+      return this.logging[this.masterName]
+    }
+  }
+}

+ 220 - 0
src/components/Modules/Schedule/Stat/Stat.vue

@@ -0,0 +1,220 @@
+<template>
+  <div>
+    <el-tabs v-model="masterName">
+      <el-tab-pane v-for="stat in stats"
+                   :label="stat.title"
+                   :name="stat.master"
+                   :key="stat.master">
+        <el-tabs>
+          <el-tab-pane label="任务统计">
+            <p class="text header-block">
+              <span v-if="stat.execution.startTime">
+                {{stat.execution.startTime | time}} 到 {{stat.execution.endTime | time}}
+              </span>
+              <span v-else="">暂无日志</span>
+              <el-button type="text" class="header-tool" title="刷新" @click="refreshStat">
+                <i class="fa fa-refresh fa-fw"></i>
+              </el-button>
+            </p>
+            <div class="stat-list">
+              <el-row :gutter="20">
+                <el-col :span="8">
+                  <el-card class="box-card stat-box">
+                    <el-row>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3>{{stat.size}}</h3>
+                          <span>任务总数</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3 class="text-success">{{stat.enabledSize}}</h3>
+                          <span>已启用</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3 class="text-warning">{{stat.disabledSize}}</h3>
+                          <span>未启用</span>
+                        </div>
+                      </el-col>
+                    </el-row>
+                  </el-card>
+                </el-col>
+                <el-col :span="8">
+                  <el-card class="box-card stat-box">
+                    <el-row :gutter="20">
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3>{{stat.execution.total}}</h3>
+                          <span>执行次数</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3 class="text-success">{{stat.execution.success}}</h3>
+                          <span>成功</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3 :class="stat.execution.error>0?'text-error':''">{{stat.execution.error}}</h3>
+                          <span>异常</span>
+                        </div>
+                      </el-col>
+                    </el-row>
+                  </el-card>
+                </el-col>
+                <el-col :span="8">
+                  <el-card class="box-card stat-box">
+                    <el-row :gutter="20">
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3>{{stat.execution.uploadSize}}</h3>
+                          <span>上传数据量</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3>{{stat.execution.downloadSize}}</h3>
+                          <span>下载数据量</span>
+                        </div>
+                      </el-col>
+                      <el-col :span="8">
+                        <div class="item">
+                          <h3 :class="stat.execution.avgUsage>1000?'text-warning':'text-success'">
+                            {{(stat.execution.avgUsage/1000).toFixed(2)}}
+                          </h3>
+                          <span>平均耗时/秒</span>
+                        </div>
+                      </el-col>
+                    </el-row>
+                  </el-card>
+                </el-col>
+              </el-row>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="任务日志">
+            <div>
+              <el-row :gutter="20" class="logging-searchbar">
+                <el-col :span="12">
+                  <el-cascader placeholder="请选择任务"
+                               :options="options"
+                               :clearable="true"
+                               expand-trigger="hover"
+                               @change="onTaskOptionChange"></el-cascader>
+                </el-col>
+                <el-col :span="12">
+                  <el-select v-model="searcher.value" clearable placeholder="请选择">
+                    <el-option
+                      v-for="item in searcher.options"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value" @change="getTaskLogging">
+                    </el-option>
+                  </el-select>
+                  <el-button @click="getTaskLogging">查找</el-button>
+                </el-col>
+              </el-row>
+              <el-table :data="myLogging.list" class="logging-list">
+                <el-table-column
+                  label="时间"
+                  width="200">
+                  <template scope="scope">
+                    <span>{{ scope.row.startTime | time }}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="任务"
+                  width="200">
+                  <template scope="scope">
+                    <span>{{ scope.row.beanTitle}} / {{ scope.row.methodTitle}}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="耗时/ms"
+                  width="100">
+                  <template scope="scope">
+                    <span>{{ scope.row.endTime - scope.row.startTime }}</span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="结果">
+                  <template scope="scope">
+                    <span v-if="scope.row.level=='INFO'">
+                      <span class="logging-icon text-success">
+                        <i class="el-icon-circle-check"></i>
+                      </span>
+                      {{scope.row.message || 0}} 条数据
+                    </span>
+                    <span v-else>
+                      <span class="logging-icon text-error">
+                        <i class="el-icon-circle-close"></i>
+                      </span>
+                      {{scope.row.message}}
+                    </span>
+                  </template>
+                </el-table-column>
+              </el-table>
+              <el-col :span="24">
+                <el-pagination
+                  v-if='myLogging.pagination.total>0'
+                  class='pagination'
+                  :page-sizes="myLogging.pagination.page_sizes"
+                  :page-size="myLogging.pagination.page_size"
+                  :layout="myLogging.pagination.layout"
+                  :total="myLogging.pagination.total"
+                  :current-page='myLogging.pagination.current_page'
+                  @current-change='onChangeCurrentPage'
+                  @size-change='onChangePageSize'>
+                </el-pagination>
+              </el-col>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+<script>
+  import StatJs from './Stat.js';
+	module.exports = StatJs;
+</script>
+<style scoped>
+  .stat-box .item{
+    text-align: center
+  }
+  .stat-box .item h3 {
+    font-size: 28px;
+    font-weight: normal;
+  }
+  .text, .stat-box .item span {
+    font-size: 14px;
+    color: #8391a5
+  }
+  .header-block {
+    border-left: 3px solid #d1dbe5;
+    margin-bottom: 15px;
+    padding-left: 10px;
+  }
+  .header-tool {
+    position: absolute;
+    right: 5px;
+    top: 5px;
+    padding: 0;
+    margin: 0;
+  }
+  .logging-searchbar, .logging-list {
+    margin-bottom: 15px;
+  }
+  .logging-searchbar .el-cascader {
+    width: 100%;
+  }
+  .logging-icon {
+    margin-right: 5px
+  }
+  .pagination {
+    text-align: center
+  }
+</style>

+ 2 - 0
src/components/Modules/Schedule/Stat/index.js

@@ -0,0 +1,2 @@
+import Stat from './Stat.vue';
+module.exports = Stat;

+ 5 - 0
src/components/Modules/Schedule/index.js

@@ -0,0 +1,5 @@
+module.exports = {
+  List: require('./List/'),
+  Stat: require('./Stat/'),
+  Setting: require('./Setting/')
+};

+ 45 - 0
src/components/Modules/Setting/Api/List/List.js

@@ -0,0 +1,45 @@
+module.exports = {
+  data() {
+    return {
+      setting_list : [],
+      setting_data: {},
+      dialog: {
+        show: false,
+        setting_rules: {
+          value: [{
+            required: true,
+            message : '请填写属性值!',
+            trigger : 'blur'
+          }]
+        }
+      }
+    }
+  },
+  methods: {
+    getList(){
+      this.$$api_setting_getSettings({
+        group: 'api'
+      }, (data) => {
+        this.setting_list = data.content;
+      });
+    },
+    onEditClick(setting) {
+      this.setting_data = setting;
+      this.dialog.show = true;
+    },
+    onSaveClick(ref) {
+      this.$refs[ref].validate((valid) => {
+        if (valid) {
+          this.$$api_setting_saveSetting(this[ref], data => {
+            this.$message.success('保存成功');
+            this.dialog.show = false;
+            this.getList();
+          });
+        }
+      });
+    }
+  },
+  mounted() {
+    this.getList();
+  }
+}

+ 67 - 0
src/components/Modules/Setting/Api/List/List.vue

@@ -0,0 +1,67 @@
+<template>
+  <div class="list">
+    <el-table border style="width: 100%" align='center'
+              :data="setting_list">
+      <el-table-column
+        prop="key"
+        label="名称"
+        align="center"
+        width="200">
+      </el-table-column>
+      <el-table-column
+        prop="description"
+        label="描述"
+        width="240">
+      </el-table-column>
+      <el-table-column
+        prop="value"
+        label="值">
+      </el-table-column>
+      <el-table-column
+        label="操作"
+        :width="80"
+        :context="_self">
+        <template scope='scope'>
+          <el-button
+            type="info"
+            icon='edit'
+            size="mini"
+            @click='onEditClick(scope.row)'></el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-dialog title="设置" v-model="dialog.show" size="tiny">
+      <el-form style="padding:20px;width:100%;"
+               label-width="60px"
+               :rules="dialog.setting_rules"
+               :model="setting_data"
+               ref='setting_data'>
+        <el-form-item class='edit-form'
+                      label="名称"
+                      prop='key'>
+          {{setting_data.key}}
+        </el-form-item>
+        <el-form-item class='edit-form'
+                      label="描述"
+                      prop='description'>
+          {{setting_data.description}}
+        </el-form-item>
+        <el-form-item class="edit-form"
+                      label='值'
+                      prop='value'>
+          <el-input v-model="setting_data.value"></el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialog.show = false">取 消</el-button>
+        <el-button type="primary" @click='onSaveClick("setting_data")'>确 定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+	import ListJs from './List.js';
+	module.exports = ListJs;
+</script>

+ 2 - 0
src/components/Modules/Setting/Api/List/index.js

@@ -0,0 +1,2 @@
+import List from './List.vue';
+module.exports = List;

+ 3 - 0
src/components/Modules/Setting/Api/index.js

@@ -0,0 +1,3 @@
+module.exports = {
+  List: require('./List/')
+};

部分文件因文件數量過多而無法顯示