You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2019/09/29 10:38:34 UTC

[incubator-shardingsphere-benchmark] 34/49: feature:add ui-frontend project

This is an automated email from the ASF dual-hosted git repository.

zhaoyanan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shardingsphere-benchmark.git

commit e6e1fb4127ac3ae69da6eb74e84970dc76fbf69c
Author: wqzwh <wa...@163.com>
AuthorDate: Thu Sep 26 15:28:22 2019 +0800

    feature:add ui-frontend project
---
 web/.babelrc                                       |  27 ++
 web/.editorconfig                                  |   9 +
 web/.eslintignore                                  |   4 +
 web/.eslintrc.js                                   | 297 +++++++++++++++++++++
 web/.gitignore                                     |  17 ++
 web/.postcssrc.js                                  |  27 ++
 web/README.md                                      |  17 ++
 web/build/build.js                                 |  58 ++++
 web/build/check-versions.js                        |  71 +++++
 web/build/utils.js                                 | 125 +++++++++
 web/build/vue-loader.conf.js                       |  22 ++
 web/build/webpack.base.conf.js                     | 119 +++++++++
 web/build/webpack.dev.conf.js                      | 131 +++++++++
 web/build/webpack.prod.conf.js                     | 152 +++++++++++
 web/config/dev.env.js                              |  24 ++
 web/config/index.js                                | 100 +++++++
 web/config/prod.env.js                             |  21 ++
 web/favicon.ico                                    | Bin 0 -> 9662 bytes
 web/favicon.png                                    | Bin 0 -> 24930 bytes
 web/index.html                                     |  32 +++
 web/package.json                                   |  87 ++++++
 web/src/App.vue                                    |  18 ++
 web/src/assets/logo.png                            | Bin 0 -> 24930 bytes
 web/src/assets/logo_top.png                        | Bin 0 -> 8832 bytes
 web/src/components/Container/index.vue             | 185 +++++++++++++
 web/src/components/Footer/index.vue                |  24 ++
 web/src/main.js                                    |  15 ++
 web/src/router/index.js                            |  37 +++
 web/src/utils/line.js                              |  50 ++++
 web/src/utils/mixin.js                             |  69 +++++
 web/src/utils/utils.js                             |  11 +
 web/src/views/mysql-vs-sharding/index.vue          |  64 +++++
 .../sharding-proxy-master-slave-sharding/index.vue |  64 +++++
 .../views/sharding-proxy-master-slave/index.vue    |  64 +++++
 .../index.vue                                      |  64 +++++
 web/static/.gitkeep                                |   0
 web/static/404.html                                |  31 +++
 37 files changed, 2036 insertions(+)

diff --git a/web/.babelrc b/web/.babelrc
new file mode 100644
index 0000000..f261300
--- /dev/null
+++ b/web/.babelrc
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+{
+  "presets": [
+    "@babel/preset-env"
+  ],
+  "plugins": ["transform-vue-jsx", "@babel/plugin-transform-runtime", "@babel/plugin-syntax-dynamic-import"],
+  "env": {
+    "test": {
+      "plugins": ["istanbul"]
+    }
+  }
+}
diff --git a/web/.editorconfig b/web/.editorconfig
new file mode 100644
index 0000000..9d08a1a
--- /dev/null
+++ b/web/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/web/.eslintignore b/web/.eslintignore
new file mode 100644
index 0000000..e1fcc9c
--- /dev/null
+++ b/web/.eslintignore
@@ -0,0 +1,4 @@
+/build/
+/config/
+/dist/
+/*.js
diff --git a/web/.eslintrc.js b/web/.eslintrc.js
new file mode 100644
index 0000000..a588840
--- /dev/null
+++ b/web/.eslintrc.js
@@ -0,0 +1,297 @@
+module.exports = {
+  root: true,
+  parserOptions: {
+    parser: 'babel-eslint',
+    sourceType: 'module'
+  },
+  env: {
+    browser: true,
+    node: true,
+    es6: true
+  },
+  extends: ['plugin:vue/recommended', 'eslint:recommended'],
+  // add your custom rules here
+  //it is base on https://github.com/vuejs/eslint-config-vue
+  rules: {
+    'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }],
+    'vue/html-indent': [
+      'off',
+      2,
+      {
+        attribute: 1,
+        baseIndent: 1,
+        closeBracket: 0,
+        alignAttributesVertically: true,
+        ignores: []
+      }
+    ],
+    'no-tabs': 'off',
+    'vue/max-attributes-per-line': [
+      2,
+      {
+        singleline: 10,
+        multiline: {
+          max: 1,
+          allowFirstLine: false
+        }
+      }
+    ],
+    'vue/html-self-closing': [
+      'error',
+      {
+        html: {
+          void: 'any',
+          normal: 'any',
+          component: 'any'
+        },
+        svg: 'any',
+        math: 'any'
+      }
+    ],
+    'vue/name-property-casing': ['error', 'PascalCase'],
+    'accessor-pairs': 2,
+    'arrow-spacing': [
+      2,
+      {
+        before: true,
+        after: true
+      }
+    ],
+    'block-spacing': [2, 'always'],
+    'brace-style': [
+      2,
+      '1tbs',
+      {
+        allowSingleLine: true
+      }
+    ],
+    camelcase: [
+      0,
+      {
+        properties: 'always'
+      }
+    ],
+    'comma-dangle': [2, 'never'],
+    'comma-spacing': [
+      2,
+      {
+        before: false,
+        after: true
+      }
+    ],
+    'comma-style': [2, 'last'],
+    'constructor-super': 2,
+    curly: [2, 'multi-line'],
+    'dot-location': [2, 'property'],
+    'eol-last': 0,
+    eqeqeq: [2, 'allow-null'],
+    'generator-star-spacing': [
+      2,
+      {
+        before: true,
+        after: true
+      }
+    ],
+    'handle-callback-err': [2, '^(err|error)$'],
+    indent: [0, 2],
+    // indent: [
+    //   2,
+    //   2,
+    //   {
+    //     SwitchCase: 1
+    //   }
+    // ],
+    'jsx-quotes': [2, 'prefer-single'],
+    'key-spacing': [
+      2,
+      {
+        beforeColon: false,
+        afterColon: true
+      }
+    ],
+    'keyword-spacing': [
+      2,
+      {
+        before: true,
+        after: true
+      }
+    ],
+    'new-cap': [
+      2,
+      {
+        newIsCap: true,
+        capIsNew: false
+      }
+    ],
+    'new-parens': 2,
+    'no-array-constructor': 2,
+    'no-caller': 2,
+    'no-console': 'off',
+    'no-class-assign': 2,
+    'no-cond-assign': 2,
+    'no-const-assign': 2,
+    'no-control-regex': 2,
+    'no-delete-var': 2,
+    'no-dupe-args': 2,
+    'no-dupe-class-members': 2,
+    'no-dupe-keys': 2,
+    'no-duplicate-case': 2,
+    'no-empty-character-class': 2,
+    'no-empty-pattern': 2,
+    'no-eval': 2,
+    'no-ex-assign': 2,
+    'no-extend-native': 2,
+    'no-extra-bind': 2,
+    'no-extra-boolean-cast': 2,
+    'no-extra-parens': [2, 'functions'],
+    'no-fallthrough': 2,
+    'no-floating-decimal': 2,
+    'no-func-assign': 2,
+    'no-implied-eval': 2,
+    'no-inner-declarations': [2, 'functions'],
+    'no-invalid-regexp': 2,
+    'no-irregular-whitespace': 2,
+    'no-iterator': 2,
+    'no-label-var': 2,
+    'no-labels': [
+      2,
+      {
+        allowLoop: false,
+        allowSwitch: false
+      }
+    ],
+    'no-lone-blocks': 2,
+    'no-mixed-spaces-and-tabs': 2,
+    'no-multi-spaces': 2,
+    'no-multi-str': 2,
+    'no-multiple-empty-lines': [
+      2,
+      {
+        max: 1
+      }
+    ],
+    'no-native-reassign': 2,
+    'no-negated-in-lhs': 2,
+    'no-new-object': 2,
+    'no-new-require': 2,
+    'no-new-symbol': 2,
+    'no-new-wrappers': 2,
+    'no-obj-calls': 2,
+    'no-octal': 2,
+    'no-octal-escape': 2,
+    'no-path-concat': 2,
+    'no-proto': 2,
+    'no-redeclare': 2,
+    'no-regex-spaces': 2,
+    'no-return-assign': [2, 'except-parens'],
+    'no-self-assign': 2,
+    'no-self-compare': 2,
+    'no-sequences': 2,
+    'no-shadow-restricted-names': 2,
+    'no-spaced-func': 2,
+    'no-sparse-arrays': 2,
+    'no-this-before-super': 2,
+    'no-throw-literal': 2,
+    'no-trailing-spaces': 2,
+    'no-undef': 2,
+    'no-undef-init': 2,
+    'no-unexpected-multiline': 2,
+    'no-unmodified-loop-condition': 2,
+    'no-unneeded-ternary': [
+      2,
+      {
+        defaultAssignment: false
+      }
+    ],
+    'no-unreachable': 2,
+    'no-unsafe-finally': 2,
+    'no-unused-vars': [
+      2,
+      {
+        vars: 'all',
+        args: 'none'
+      }
+    ],
+    'no-useless-call': 2,
+    'no-useless-computed-key': 2,
+    'no-useless-constructor': 2,
+    'no-useless-escape': 0,
+    'no-whitespace-before-property': 2,
+    'no-with': 2,
+    'one-var': [
+      2,
+      {
+        initialized: 'never'
+      }
+    ],
+    'operator-linebreak': [
+      2,
+      'after',
+      {
+        overrides: {
+          '?': 'before',
+          ':': 'before'
+        }
+      }
+    ],
+    'padded-blocks': [2, 'never'],
+    quotes: [
+      2,
+      'single',
+      {
+        avoidEscape: true,
+        allowTemplateLiterals: true
+      }
+    ],
+    semi: [2, 'never'],
+    'semi-spacing': [
+      2,
+      {
+        before: false,
+        after: true
+      }
+    ],
+    'space-before-blocks': [2, 'always'],
+    'space-before-function-paren': [2, 'never'],
+    'space-in-parens': [2, 'never'],
+    'space-infix-ops': 2,
+    'space-unary-ops': [
+      2,
+      {
+        words: true,
+        nonwords: false
+      }
+    ],
+    'spaced-comment': [
+      2,
+      'always',
+      {
+        markers: [
+          'global',
+          'globals',
+          'eslint',
+          'eslint-disable',
+          '*package',
+          '!',
+          ','
+        ]
+      }
+    ],
+    'template-curly-spacing': [2, 'never'],
+    'use-isnan': 2,
+    'valid-typeof': 2,
+    'wrap-iife': [2, 'any'],
+    'yield-star-spacing': [2, 'both'],
+    yoda: [2, 'never'],
+    'prefer-const': 2,
+    'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
+    'object-curly-spacing': [
+      2,
+      'always',
+      {
+        objectsInObjects: false
+      }
+    ],
+    'array-bracket-spacing': [2, 'never']
+  }
+}
diff --git a/web/.gitignore b/web/.gitignore
new file mode 100644
index 0000000..6bc0c43
--- /dev/null
+++ b/web/.gitignore
@@ -0,0 +1,17 @@
+.DS_Store
+node_modules/
+node/
+/dist/
+/coverage/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+package-lock.json
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
diff --git a/web/.postcssrc.js b/web/.postcssrc.js
new file mode 100644
index 0000000..dc9c58f
--- /dev/null
+++ b/web/.postcssrc.js
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    "postcss-import": {},
+    "postcss-url": {},
+    // to edit target browsers: use "browserslist" field in package.json
+    "autoprefixer": {}
+  }
+}
diff --git a/web/README.md b/web/README.md
new file mode 100644
index 0000000..be27161
--- /dev/null
+++ b/web/README.md
@@ -0,0 +1,17 @@
+# incubator-shardingsphere-benchmark-ui-frontend"
+
+## Build Setup
+
+```bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
diff --git a/web/build/build.js b/web/build/build.js
new file mode 100644
index 0000000..b6d0ada
--- /dev/null
+++ b/web/build/build.js
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
+
+const spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})
diff --git a/web/build/check-versions.js b/web/build/check-versions.js
new file mode 100644
index 0000000..b07e6e8
--- /dev/null
+++ b/web/build/check-versions.js
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const chalk = require('chalk')
+const semver = require('semver')
+const packageConfig = require('../package.json')
+const shell = require('shelljs')
+
+function exec (cmd) {
+  return require('child_process').execSync(cmd).toString().trim()
+}
+
+const versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  }
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  })
+}
+
+module.exports = function () {
+  const warnings = []
+
+  for (let i = 0; i < versionRequirements.length; i++) {
+    const mod = versionRequirements[i]
+
+    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
+      warnings.push(mod.name + ': ' +
+        chalk.red(mod.currentVersion) + ' should be ' +
+        chalk.green(mod.versionRequirement)
+      )
+    }
+  }
+
+  if (warnings.length) {
+    console.log('')
+    console.log(chalk.yellow('To use this template, you must update following to modules:'))
+    console.log()
+
+    for (let i = 0; i < warnings.length; i++) {
+      const warning = warnings[i]
+      console.log('  ' + warning)
+    }
+
+    console.log()
+    process.exit(1)
+  }
+}
diff --git a/web/build/utils.js b/web/build/utils.js
new file mode 100644
index 0000000..5c6cf79
--- /dev/null
+++ b/web/build/utils.js
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const path = require('path')
+const config = require('../config')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
+const packageConfig = require('../package.json')
+
+exports.assetsPath = function(_path) {
+  const assetsSubDirectory =
+    process.env.NODE_ENV === 'production'
+      ? config.build.assetsSubDirectory
+      : config.dev.assetsSubDirectory
+
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function(options) {
+  options = options || {}
+
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  const postcssLoader = {
+    loader: 'postcss-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders(loader, loaderOptions) {
+    const loaders = options.usePostCSS
+      ? [cssLoader, postcssLoader]
+      : [cssLoader]
+
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return [
+        {
+          loader: MiniCssExtractPlugin.loader,
+          options: {
+            publicPath: '../../'
+          }
+        }
+      ].concat(loaders)
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function(options) {
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+
+  return output
+}
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}
diff --git a/web/build/vue-loader.conf.js b/web/build/vue-loader.conf.js
new file mode 100644
index 0000000..9c30005
--- /dev/null
+++ b/web/build/vue-loader.conf.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+
+module.exports = {
+
+}
diff --git a/web/build/webpack.base.conf.js b/web/build/webpack.base.conf.js
new file mode 100644
index 0000000..76b5575
--- /dev/null
+++ b/web/build/webpack.base.conf.js
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve(dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const createLintingRule = () => ({
+  test: /\.(js|vue)$/,
+  loader: 'eslint-loader',
+  enforce: 'pre',
+  include: [resolve('src'), resolve('test')],
+  options: {
+    formatter: require('eslint-friendly-formatter'),
+    emitWarning: !config.dev.showEslintErrorsInOverlay
+  }
+})
+
+module.exports = {
+  context: path.resolve(__dirname, '../'),
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath:
+      process.env.NODE_ENV === 'production'
+        ? config.build.assetsPublicPath
+        : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      vue$: 'vue/dist/vue.esm.js',
+      '@': resolve('src')
+    }
+  },
+  module: {
+    rules: [
+      ...(config.dev.useEslint ? [createLintingRule()] : []),
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test')]
+      },
+      {
+        test: /\.svg$/,
+        loader: 'svg-sprite-loader',
+        include: [resolve('src/icons')],
+        options: {
+          symbolId: 'icon-[name]'
+        }
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        exclude: [resolve('src/icons')],
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
+}
diff --git a/web/build/webpack.dev.conf.js b/web/build/webpack.dev.conf.js
new file mode 100644
index 0000000..3dd767e
--- /dev/null
+++ b/web/build/webpack.dev.conf.js
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+const { VueLoaderPlugin } = require('vue-loader')
+
+function resolve(dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  mode: 'development',
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.dev.cssSourceMap,
+      usePostCSS: true
+    })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        {
+          from: /.*/,
+          to: path.posix.join(config.dev.assetsPublicPath, 'index.html')
+        }
+      ]
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }),
+    new VueLoaderPlugin(),
+    new webpack.HotModuleReplacementPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true,
+      favicon: resolve('favicon.png'),
+      title: 'sharding-ui'
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(
+        new FriendlyErrorsPlugin({
+          compilationSuccessInfo: {
+            messages: [
+              `Your application is running here: http://${
+                devWebpackConfig.devServer.host
+              }:${port}`
+            ]
+          },
+          onErrors: config.dev.notifyOnErrors
+            ? utils.createNotifierCallback()
+            : undefined
+        })
+      )
+
+      resolve(devWebpackConfig)
+    }
+  })
+})
diff --git a/web/build/webpack.prod.conf.js b/web/build/webpack.prod.conf.js
new file mode 100644
index 0000000..58b5373
--- /dev/null
+++ b/web/build/webpack.prod.conf.js
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const MiniCssExtractPlugin = require('mini-css-extract-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const { VueLoaderPlugin } = require('vue-loader')
+
+function resolve(dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+const env = require('../config/prod.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  mode: 'production',
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  optimization: {
+    providedExports: true,
+    usedExports: true, // tree shaking
+    sideEffects: true,
+    concatenateModules: true,
+    noEmitOnErrors: true,
+    splitChunks: {
+      chunks: 'all',
+      minSize: 30000,
+      maxSize: 0,
+      minChunks: 1,
+      maxAsyncRequests: 5,
+      maxInitialRequests: 3,
+      automaticNameDelimiter: '~',
+      name: true,
+      cacheGroups: {
+        vendors: {
+          test: /[\\/]node_modules[\\/]/,
+          priority: -10
+        },
+        default: {
+          priority: -20,
+          reuseExistingChunk: true
+        }
+      }
+    },
+    runtimeChunk: {
+      name: 'manifest'
+    }
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new VueLoaderPlugin(),
+    new MiniCssExtractPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css')
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false }}
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: config.build.index,
+      template: 'index.html',
+      inject: true,
+      favicon: resolve('favicon.png'),
+      title: 'sharding-ui',
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
+    .BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig
diff --git a/web/config/dev.env.js b/web/config/dev.env.js
new file mode 100644
index 0000000..58340ed
--- /dev/null
+++ b/web/config/dev.env.js
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+  NODE_ENV: process.env.NODE_ENV !== 'mock' ? '"development"' : '"mock"'
+})
diff --git a/web/config/index.js b/web/config/index.js
new file mode 100644
index 0000000..ceb6f12
--- /dev/null
+++ b/web/config/index.js
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+// Template version: 1.3.1
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+  dev: {
+    // Paths
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {
+      '/api': {
+        target: 'http://localhost:8088',
+        changeOrigin: true,
+        pathRewrite: {
+          '^/api': '/api'
+        }
+      }
+    },
+
+    // Various Dev Server settings
+    host: 'localhost', // can be overwritten by process.env.HOST
+    port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+    autoOpenBrowser: false,
+    errorOverlay: true,
+    notifyOnErrors: true,
+    poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+    // Use Eslint Loader?
+    // If true, your code will be linted during bundling and
+    // linting errors and warnings will be shown in the console.
+    useEslint: true,
+    // If true, eslint errors and warnings will also be shown in the error overlay
+    // in the browser.
+    showEslintErrorsInOverlay: false,
+
+    /**
+     * Source Maps
+     */
+
+    // https://webpack.js.org/configuration/devtool/#development
+    devtool: 'cheap-module-eval-source-map',
+
+    // If you have problems debugging vue-files in devtools,
+    // set this to false - it *may* help
+    // https://vue-loader.vuejs.org/en/options.html#cachebusting
+    cacheBusting: true,
+
+    cssSourceMap: false
+  },
+
+  build: {
+    // Template for index.html
+    index: path.resolve(__dirname, '../dist/index.html'),
+
+    // Paths
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+
+    /**
+     * Source Maps
+     */
+
+    productionSourceMap: true,
+    // https://webpack.js.org/configuration/devtool/#production
+    devtool: '#source-map',
+
+    // Gzip off by default as many popular static hosts such as
+    // Surge or Netlify already gzip all static assets for you.
+    // Before setting to `true`, make sure to:
+    // npm install --save-dev compression-webpack-plugin
+    productionGzip: false,
+    productionGzipExtensions: ['js', 'css'],
+
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  }
+}
diff --git a/web/config/prod.env.js b/web/config/prod.env.js
new file mode 100644
index 0000000..00279d4
--- /dev/null
+++ b/web/config/prod.env.js
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"'
+}
diff --git a/web/favicon.ico b/web/favicon.ico
new file mode 100644
index 0000000..83655b2
Binary files /dev/null and b/web/favicon.ico differ
diff --git a/web/favicon.png b/web/favicon.png
new file mode 100644
index 0000000..209bb49
Binary files /dev/null and b/web/favicon.png differ
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..364ecc1
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,32 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~  he License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width,initial-scale=1.0">
+  <title>Sharding Performance monitoring</title>
+</head>
+
+<body>
+  <div id="app"></div>
+  <!-- built files will be auto injected -->
+</body>
+
+</html>
diff --git a/web/package.json b/web/package.json
new file mode 100644
index 0000000..c237062
--- /dev/null
+++ b/web/package.json
@@ -0,0 +1,87 @@
+{
+  "name": "incubator-shardingsphere-benchmark-ui-frontend",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "ShardingSphere",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "start": "npm run dev",
+    "lint": "eslint --ext .js,.vue src",
+    "build": "node build/build.js"
+  },
+  "dependencies": {
+    "echarts": "^4.3.0",
+    "vue-echarts": "^4.0.4",
+    "iview": "^3.5.1",
+    "vue": "^2.5.2",
+    "vue-i18n": "^8.4.0",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.0.1"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.6.0",
+    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+    "@babel/plugin-transform-runtime": "^7.6.0",
+    "@babel/preset-env": "^7.6.0",
+    "@babel/runtime": "^7.6.0",
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^7.0.0-bridge.0",
+    "babel-eslint": "^8.2.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-dynamic-import-webpack": "^1.1.0",
+    "babel-plugin-istanbul": "^5.2.0",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.7.0",
+    "chalk": "^2.0.1",
+    "copy-webpack-plugin": "^5.0.3",
+    "cross-env": "^5.2.1",
+    "css-loader": "^2.1.1",
+    "eslint": "^4.15.0",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^2.1.2",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.2.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "eslint-plugin-vue": "^4.0.0",
+    "file-loader": "^3.0.1",
+    "friendly-errors-webpack-plugin": "^1.7.0",
+    "html-webpack-plugin": "^3.2.0",
+    "mini-css-extract-plugin": "^0.6.0",
+    "node-notifier": "^5.1.2",
+    "node-sass": "^4.10.0",
+    "optimize-css-assets-webpack-plugin": "^5.0.1",
+    "ora": "^1.2.0",
+    "portfinder": "^1.0.13",
+    "postcss-import": "^11.0.0",
+    "postcss-loader": "^2.0.8",
+    "postcss-url": "^7.2.1",
+    "rimraf": "^2.6.0",
+    "sass-loader": "^7.1.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "svg-sprite-loader": "^4.1.3",
+    "url-loader": "^1.1.2",
+    "vue-loader": "^15.7.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^4.31.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-cli": "^3.3.2",
+    "webpack-dev-server": "^3.4.1",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 6.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}
diff --git a/web/src/App.vue b/web/src/App.vue
new file mode 100644
index 0000000..28de323
--- /dev/null
+++ b/web/src/App.vue
@@ -0,0 +1,18 @@
+<template>
+  <div id="app">
+    <Container>
+      <router-view />
+    </Container>
+  </div>
+</template>
+
+<script>
+import Container from '@/components/Container'
+
+export default {
+  name: 'App',
+  components: {
+    Container
+  }
+}
+</script>
diff --git a/web/src/assets/logo.png b/web/src/assets/logo.png
new file mode 100644
index 0000000..209bb49
Binary files /dev/null and b/web/src/assets/logo.png differ
diff --git a/web/src/assets/logo_top.png b/web/src/assets/logo_top.png
new file mode 100644
index 0000000..ff22614
Binary files /dev/null and b/web/src/assets/logo_top.png differ
diff --git a/web/src/components/Container/index.vue b/web/src/components/Container/index.vue
new file mode 100644
index 0000000..1b2e15a
--- /dev/null
+++ b/web/src/components/Container/index.vue
@@ -0,0 +1,185 @@
+<template>
+  <div class="layout">
+    <Layout>
+      <Sider
+        ref="side1"
+        v-model="isCollapsed"
+        :collapsed-width="78"
+        class="layout-sider"
+        hide-trigger
+        collapsible
+      >
+        <div class="i-layout-sider-logo i-layout-sider-logo-dark">
+          <a href="/" target="_self" class="i-link i-link-color">
+            <img class="logo" src="../../assets/logo_top.png" />
+            <img class="collapse-logo" src="../../assets/logo.png" alt="logo" />
+          </a>
+        </div>
+        <Menu :class="menuitemClasses" active-name="1-2" theme="dark" width="auto">
+          <router-link to="mysql-vs-sharding">
+            <MenuItem name="1-2">
+              <Tooltip content="mysqlVsSharding" placement="right">
+                <span>mysqlVsSharding</span>
+              </Tooltip>
+            </MenuItem>
+          </router-link>
+          <router-link to="sharding-proxy-master-slave">
+            <MenuItem name="1-3">
+              <Tooltip content="shardingProxyMasterSlave" placement="right">
+                <span>shardingProxyMasterSlave</span>
+              </Tooltip>
+            </MenuItem>
+          </router-link>
+          <router-link to="sharding-proxy-master-slave-sharding">
+            <MenuItem name="1-4">
+              <Tooltip content="shardingProxyMasterSlaveSharding" placement="right">
+                <span>shardingProxyMasterSlaveSharding</span>
+              </Tooltip>
+            </MenuItem>
+          </router-link>
+          <router-link to="sharding-proxy-single-database-single-table">
+            <MenuItem name="1-5">
+              <Tooltip content="shardingProxySingleDatabaseSingleTable" placement="right">
+                <span>shardingProxySingleDatabaseSingleTable</span>
+              </Tooltip>
+            </MenuItem>
+          </router-link>
+        </Menu>
+      </Sider>
+      <Layout style="margin-left: 200px;">
+        <Header :style="{padding: 0}" class="layout-header-bar">
+          <!-- <Icon
+            :class="rotateIcon"
+            :style="{margin: '0 20px', color: '#f6ca9d', cursor: 'pointer'}"
+            size="24"
+            type="md-menu"
+            @click.native="collapsedSider"
+          />-->
+        </Header>
+        <Content>
+          <slot />
+        </Content>
+        <v-footer />
+      </Layout>
+    </Layout>
+  </div>
+</template>
+<script>
+import Footer from '../Footer/index.vue'
+export default {
+  name: 'Container',
+  components: {
+    'v-footer': Footer
+  },
+  data() {
+    return {
+      isCollapsed: false
+    }
+  },
+  computed: {
+    rotateIcon() {
+      return ['menu-icon', this.isCollapsed ? 'rotate-icon' : '']
+    },
+    menuitemClasses() {
+      return ['menu-item', this.isCollapsed ? 'collapsed-menu' : '']
+    }
+  },
+  methods: {
+    collapsedSider() {
+      // this.$refs.side1.toggleCollapse()
+    }
+  }
+}
+</script>
+<style scoped>
+.layout {
+  background: #f5f7f9;
+  position: relative;
+  overflow: hidden;
+}
+a {
+  color: #fff;
+}
+.layout-header-bar {
+  background: #fff;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
+}
+.layout-logo-left {
+  width: 90%;
+  height: 30px;
+  background: #5b6270;
+  border-radius: 3px;
+  margin: 15px auto;
+}
+.menu-icon {
+  transition: all 0.3s;
+}
+.rotate-icon {
+  transform: rotate(-90deg);
+}
+.menu-item {
+  background: #1d1e23;
+}
+.menu-item span {
+  display: inline-block;
+  width: 130px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  vertical-align: bottom;
+  transition: width 0.2s ease 0.2s;
+}
+.menu-item i {
+  transform: translateX(0px);
+  transition: font-size 0.2s ease, transform 0.2s ease;
+  vertical-align: middle;
+  font-size: 16px;
+}
+.collapsed-menu span {
+  width: 0px;
+  transition: width 0.2s ease;
+}
+.collapsed-menu i {
+  transform: translateX(5px);
+  transition: font-size 0.2s ease 0.2s, transform 0.2s ease 0.2s;
+  vertical-align: middle;
+  font-size: 22px;
+}
+.i-layout-sider-logo {
+  height: 63px;
+  line-height: 63px;
+  text-align: center;
+  border-bottom: 1px solid #f8f8f9;
+}
+.i-layout-sider-logo-dark {
+  border-bottom: 1px solid #101117;
+  background: #1d1e23;
+}
+.i-layout-sider-logo img {
+  height: 54%;
+  vertical-align: middle;
+}
+.layout-header-bar {
+  background: #1d1e23;
+  background: linear-gradient(90deg, #1d1e23, #3f4045);
+}
+.layout-sider {
+  background: #1d1e23;
+  z-index: 99;
+  position: fixed;
+  left: 0;
+  height: 100%;
+}
+.layout-sider .ivu-layout-sider-children {
+  background: #1d1e23 !important;
+}
+.ivu-layout-sider-collapsed .logo {
+  display: none;
+}
+.ivu-layout-sider-collapsed .collapse-logo {
+  display: inline-block;
+}
+.collapse-logo {
+  display: none;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/components/Footer/index.vue b/web/src/components/Footer/index.vue
new file mode 100644
index 0000000..0c4b903
--- /dev/null
+++ b/web/src/components/Footer/index.vue
@@ -0,0 +1,24 @@
+<template>
+  <div class="footer-wrapper">
+    Copyright
+    <i class="icon-copyright iconfont" /> 2016 - 2018 The Apache Software Foundation, Licensed under the Apache License, Version 2.0.
+  </div>
+</template>
+<script>
+export default {
+  name: 'Footer'
+}
+</script>
+<style rel="stylesheet/scss" lang="scss" scoped>
+$bg: #2d3a4b;
+$dark_gray: #889aa4;
+$light_gray: #eee;
+
+.footer-wrapper {
+  color: $dark_gray;
+  width: 100%;
+  font-size: 14px;
+  line-height: 60px;
+  text-align: center;
+}
+</style>
diff --git a/web/src/main.js b/web/src/main.js
new file mode 100644
index 0000000..f6104a4
--- /dev/null
+++ b/web/src/main.js
@@ -0,0 +1,15 @@
+import Vue from 'vue'
+import App from './App'
+import router from './router'
+
+import iView from 'iview'
+import 'iview/dist/styles/iview.css'
+
+Vue.config.productionTip = false
+
+Vue.use(iView)
+
+new Vue({
+  router,
+  render: h => h(App)
+}).$mount('#app')
diff --git a/web/src/router/index.js b/web/src/router/index.js
new file mode 100644
index 0000000..7441fce
--- /dev/null
+++ b/web/src/router/index.js
@@ -0,0 +1,37 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+Vue.use(Router)
+
+export const constantRouterMap = [
+  {
+    path: '*',
+    redirect: '/mysql-vs-sharding'
+  },
+  {
+    path: '/mysql-vs-sharding',
+    component: () => import('@/views/mysql-vs-sharding/index'),
+    hidden: true
+  },
+  {
+    path: '/sharding-proxy-master-slave',
+    component: () => import('@/views/sharding-proxy-master-slave/index'),
+    hidden: true
+  },
+  {
+    path: '/sharding-proxy-master-slave-sharding',
+    component: () =>
+      import('@/views/sharding-proxy-master-slave-sharding/index'),
+    hidden: true
+  },
+  {
+    path: '/sharding-proxy-single-database-single-table',
+    component: () =>
+      import('@/views/sharding-proxy-single-database-single-table/index'),
+    hidden: true
+  }
+]
+
+export default new Router({
+  scrollBehavior: () => ({ y: 0 }),
+  routes: constantRouterMap
+})
diff --git a/web/src/utils/line.js b/web/src/utils/line.js
new file mode 100644
index 0000000..bf30a09
--- /dev/null
+++ b/web/src/utils/line.js
@@ -0,0 +1,50 @@
+const color = ['#2D8CF0', '#9A66E4', '#19BE6B', '#FF9900', '#E46CBB']
+const getLineOptions = (name, xAxis, legend, series) => {
+  return {
+    color,
+    grid: {
+      left: '2%',
+      right: '2%',
+      bottom: '6%',
+      containLabel: true
+    },
+    tooltip: {
+      trigger: 'axis',
+      formatter(d) {
+        let html = ``
+        for (const v of d) {
+          html += `<div style="display:inline-block;margin: 10px;font-size: 14px;">
+          <p><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
+            v.color
+          };"></span>${v.seriesName}</p> 
+          <p>Throughtput: ${v.data.Throughout}</p>
+          <p>Samples: ${v.data.Samples}</p>
+          <p>Err: ${v.data.Err}</p>
+          <p>Max: ${v.data.Max}</p>
+          <p>Min: ${v.data.Min}</p>
+        </div>`
+        }
+        return html
+      }
+    },
+    xAxis: {
+      name: '(TEST TIMES)/DAY',
+      nameLocation: 'middle',
+      nameGap: 30,
+      type: 'category',
+      boundaryGap: false,
+      data: xAxis[name]
+    },
+    yAxis: {
+      name: 'TPS',
+      type: 'value'
+    },
+    legend: {
+      data: legend[name],
+      right: '2%'
+    },
+    series: series[name]
+  }
+}
+
+export { getLineOptions }
diff --git a/web/src/utils/mixin.js b/web/src/utils/mixin.js
new file mode 100644
index 0000000..1100e77
--- /dev/null
+++ b/web/src/utils/mixin.js
@@ -0,0 +1,69 @@
+const mountedMixin = {
+  data() {
+    return {
+      legend: {},
+      series: {},
+      desc: {},
+      xAxis: {}
+    }
+  },
+  mounted() {
+    const map = []
+    const legend = {}
+    const series = {}
+    const desc = {}
+    const xAxis = {}
+    for (const v in this.data) {
+      map.push(v)
+      if (v !== 'DESC') {
+        legend[v] = []
+        series[v] = []
+        xAxis[v] = []
+      }
+    }
+    for (const m of map) {
+      if (Object.prototype.toString.call(this.data[m]) === '[object Array]') {
+        for (const mm of this.data[m]) {
+          legend[m].push(mm.type)
+          const data = []
+          for (const mmm of Object.keys(mm.data)) {
+            if (xAxis[m].length !== mm.data.length) {
+              xAxis[m].push(mmm)
+            }
+            data.push({
+              ...mm.data[mmm],
+              value: mm.data[mmm].Throughout
+            })
+          }
+          series[m].push({
+            name: mm.type,
+            type: 'line',
+            data
+          })
+        }
+      } else {
+        for (const k of map) {
+          if (k !== m) {
+            desc[k] = {
+              mysqlVerison: this.data[m]['mysqlVerison'],
+              // 'ShardingSphere-proxy': this.data[m]['ShardingSphere-proxy'],
+              tableNumber: this.data[m]['tableNumber'],
+              // DataVolume: this.data[m]['DataVolume'],
+              // X: this.data[m]['X'],
+              // Y: this.data[m]['Y'],
+              sceneDescription: this.data[m]['sceneDescription'],
+              sqlExample: this.data[m][k].SqlExample
+              // ComparativeType: this.data[m][k].ComparativeType
+            }
+          }
+        }
+      }
+    }
+    this.legend = legend
+    this.series = series
+    this.desc = desc
+    this.xAxis = xAxis
+  }
+}
+
+export { mountedMixin }
diff --git a/web/src/utils/utils.js b/web/src/utils/utils.js
new file mode 100644
index 0000000..1f7851a
--- /dev/null
+++ b/web/src/utils/utils.js
@@ -0,0 +1,11 @@
+import mysqlVsShardingproxy from '../../../report/data_json/mysql_vs_shardingproxy.json'
+import shardingProxyMasterSlaveSharding from '../../../report/data_json/sharding_proxy_master_slave_sharding.json'
+import shardingProxyMasterSlave from '../../../report/data_json/sharding_proxy_master_slave.json'
+import shardingProxySingleDatabaseSingleTable from '../../../report/data_json/sharding_proxy_single_database_single_table.json'
+
+export {
+  mysqlVsShardingproxy,
+  shardingProxyMasterSlaveSharding,
+  shardingProxyMasterSlave,
+  shardingProxySingleDatabaseSingleTable
+}
diff --git a/web/src/views/mysql-vs-sharding/index.vue b/web/src/views/mysql-vs-sharding/index.vue
new file mode 100644
index 0000000..5125c39
--- /dev/null
+++ b/web/src/views/mysql-vs-sharding/index.vue
@@ -0,0 +1,64 @@
+<template>
+  <Row :gutter="16" style="background:#eee; padding:20px">
+    <Col v-for="(value, name, index) in series" :key="index" class="col-item" span="24">
+      <Card :bordered="false">
+        <p slot="title" style="font-size: 18px">{{ name.toLowerCase() }} throughtout compare</p>
+        <v-chart :options="getOptions(name)" />
+        <Form :label-width="140">
+          <FormItem
+            v-for="(value, name,indx) in desc[name]"
+            :label="`${name}:`"
+            :key="indx"
+          >{{ value }}</FormItem>
+        </Form>
+      </Card>
+    </Col>
+  </Row>
+</template>
+
+<script>
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/line'
+import 'echarts/lib/component/tooltip'
+import 'echarts/lib/component/legend'
+import 'echarts/lib/component/title'
+import { mysqlVsShardingproxy } from '../../utils/utils'
+import { mountedMixin } from '../../utils/mixin'
+import { getLineOptions } from '../../utils/line'
+
+export default {
+  name: 'Home',
+  components: {
+    'v-chart': ECharts
+  },
+  mixins: [mountedMixin],
+  data() {
+    return {
+      data: mysqlVsShardingproxy
+    }
+  },
+  methods: {
+    getOptions(name) {
+      return getLineOptions(name, this.xAxis, this.legend, this.series)
+    }
+  }
+}
+</script>
+<style>
+.echarts {
+  width: 100%;
+  height: 400px;
+}
+.col-item {
+  margin-bottom: 16px;
+}
+.ivu-form-item {
+  margin-bottom: 10px !important;
+}
+.ivu-form-item-label {
+  font-size: 15px !important;
+}
+.ivu-form-item-content {
+  font-size: 14px !important;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/sharding-proxy-master-slave-sharding/index.vue b/web/src/views/sharding-proxy-master-slave-sharding/index.vue
new file mode 100644
index 0000000..19cbef9
--- /dev/null
+++ b/web/src/views/sharding-proxy-master-slave-sharding/index.vue
@@ -0,0 +1,64 @@
+<template>
+  <Row :gutter="16" style="background:#eee; padding:20px">
+    <Col v-for="(value, name, index) in series" :key="index" class="col-item" span="24">
+      <Card :bordered="false">
+        <p slot="title" style="font-size: 18px">{{ name.toLowerCase() }} throughtout compare</p>
+        <v-chart :options="getOptions(name)" />
+        <Form :label-width="140">
+          <FormItem
+            v-for="(value, name,indx) in desc[name]"
+            :label="`${name}:`"
+            :key="indx"
+          >{{ value }}</FormItem>
+        </Form>
+      </Card>
+    </Col>
+  </Row>
+</template>
+
+<script>
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/line'
+import 'echarts/lib/component/tooltip'
+import 'echarts/lib/component/legend'
+import 'echarts/lib/component/title'
+import { shardingProxyMasterSlaveSharding } from '../../utils/utils'
+import { mountedMixin } from '../../utils/mixin'
+import { getLineOptions } from '../../utils/line'
+
+export default {
+  name: 'Home',
+  components: {
+    'v-chart': ECharts
+  },
+  mixins: [mountedMixin],
+  data() {
+    return {
+      data: shardingProxyMasterSlaveSharding
+    }
+  },
+  methods: {
+    getOptions(name) {
+      return getLineOptions(name, this.xAxis, this.legend, this.series)
+    }
+  }
+}
+</script>
+<style>
+.echarts {
+  width: 100%;
+  height: 400px;
+}
+.col-item {
+  margin-bottom: 16px;
+}
+.ivu-form-item {
+  margin-bottom: 10px !important;
+}
+.ivu-form-item-label {
+  font-size: 15px !important;
+}
+.ivu-form-item-content {
+  font-size: 14px !important;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/sharding-proxy-master-slave/index.vue b/web/src/views/sharding-proxy-master-slave/index.vue
new file mode 100644
index 0000000..1be9b47
--- /dev/null
+++ b/web/src/views/sharding-proxy-master-slave/index.vue
@@ -0,0 +1,64 @@
+<template>
+  <Row :gutter="16" style="background:#eee; padding:20px">
+    <Col v-for="(value, name, index) in series" :key="index" class="col-item" span="24">
+      <Card :bordered="false">
+        <p slot="title" style="font-size: 18px">{{ name.toLowerCase() }} throughtout compare</p>
+        <v-chart :options="getOptions(name)" />
+        <Form :label-width="140">
+          <FormItem
+            v-for="(value, name,indx) in desc[name]"
+            :label="`${name}:`"
+            :key="indx"
+          >{{ value }}</FormItem>
+        </Form>
+      </Card>
+    </Col>
+  </Row>
+</template>
+
+<script>
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/line'
+import 'echarts/lib/component/tooltip'
+import 'echarts/lib/component/legend'
+import 'echarts/lib/component/title'
+import { shardingProxyMasterSlave } from '../../utils/utils'
+import { mountedMixin } from '../../utils/mixin'
+import { getLineOptions } from '../../utils/line'
+
+export default {
+  name: 'Home',
+  components: {
+    'v-chart': ECharts
+  },
+  mixins: [mountedMixin],
+  data() {
+    return {
+      data: shardingProxyMasterSlave
+    }
+  },
+  methods: {
+    getOptions(name) {
+      return getLineOptions(name, this.xAxis, this.legend, this.series)
+    }
+  }
+}
+</script>
+<style>
+.echarts {
+  width: 100%;
+  height: 400px;
+}
+.col-item {
+  margin-bottom: 16px;
+}
+.ivu-form-item {
+  margin-bottom: 10px !important;
+}
+.ivu-form-item-label {
+  font-size: 15px !important;
+}
+.ivu-form-item-content {
+  font-size: 14px !important;
+}
+</style>
\ No newline at end of file
diff --git a/web/src/views/sharding-proxy-single-database-single-table/index.vue b/web/src/views/sharding-proxy-single-database-single-table/index.vue
new file mode 100644
index 0000000..ae7b588
--- /dev/null
+++ b/web/src/views/sharding-proxy-single-database-single-table/index.vue
@@ -0,0 +1,64 @@
+<template>
+  <Row :gutter="16" style="background:#eee; padding:20px">
+    <Col v-for="(value, name, index) in series" :key="index" class="col-item" span="24">
+      <Card :bordered="false">
+        <p slot="title" style="font-size: 18px">{{ name.toLowerCase() }} throughtout compare</p>
+        <v-chart :options="getOptions(name)" />
+        <Form :label-width="140">
+          <FormItem
+            v-for="(value, name,indx) in desc[name]"
+            :label="`${name}:`"
+            :key="indx"
+          >{{ value }}</FormItem>
+        </Form>
+      </Card>
+    </Col>
+  </Row>
+</template>
+
+<script>
+import ECharts from 'vue-echarts'
+import 'echarts/lib/chart/line'
+import 'echarts/lib/component/tooltip'
+import 'echarts/lib/component/legend'
+import 'echarts/lib/component/title'
+import { shardingProxySingleDatabaseSingleTable } from '../../utils/utils'
+import { mountedMixin } from '../../utils/mixin'
+import { getLineOptions } from '../../utils/line'
+
+export default {
+  name: 'Home',
+  components: {
+    'v-chart': ECharts
+  },
+  mixins: [mountedMixin],
+  data() {
+    return {
+      data: shardingProxySingleDatabaseSingleTable
+    }
+  },
+  methods: {
+    getOptions(name) {
+      return getLineOptions(name, this.xAxis, this.legend, this.series)
+    }
+  }
+}
+</script>
+<style>
+.echarts {
+  width: 100%;
+  height: 400px;
+}
+.col-item {
+  margin-bottom: 16px;
+}
+.ivu-form-item {
+  margin-bottom: 10px !important;
+}
+.ivu-form-item-label {
+  font-size: 15px !important;
+}
+.ivu-form-item-content {
+  font-size: 14px !important;
+}
+</style>
\ No newline at end of file
diff --git a/web/static/.gitkeep b/web/static/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/web/static/404.html b/web/static/404.html
new file mode 100644
index 0000000..5a0ea23
--- /dev/null
+++ b/web/static/404.html
@@ -0,0 +1,31 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~  he License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~  Unless required by applicable law or agreed to in writing, software
+  ~  distributed under the License is distributed on an "AS IS" BASIS,
+  ~  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~  See the License for the specific language governing permissions and
+  ~  limitations under the License.
+  -->
+
+<!DOCTYPE html>
+<html>
+
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width,initial-scale=1.0">
+  <title>sharding-ui</title>
+</head>
+
+<body>
+  <div>404</div>
+</body>
+
+</html>