From 856c467d127e5a2e8a61a84552144066ea21a169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=86=8A=E6=88=90=E5=BC=BA?= Date: Tue, 13 Oct 2020 18:13:44 +0800 Subject: [PATCH] Initial commit --- .babelrc | 27 ++++ .editorconfig | 9 ++ .eslintignore | 5 + .eslintrc.js | 29 ++++ .gitignore | 17 +++ .postcssrc.js | 10 ++ README.md | 30 +++++ build/build.js | 41 ++++++ build/check-versions.js | 54 ++++++++ build/logo.png | Bin 0 -> 6849 bytes build/utils.js | 101 ++++++++++++++ build/vue-loader.conf.js | 22 +++ build/webpack.base.conf.js | 96 +++++++++++++ build/webpack.dev.conf.js | 95 +++++++++++++ build/webpack.prod.conf.js | 149 +++++++++++++++++++++ config/dev.env.js | 7 + config/index.js | 76 +++++++++++ config/prod.env.js | 4 + config/test.env.js | 7 + index.html | 12 ++ package.json | 94 +++++++++++++ src/App.vue | 16 +++ src/assets/logo.png | Bin 0 -> 6849 bytes src/components/layout/AppMain.vue | 33 +++++ src/components/layout/NavBar.vue | 75 +++++++++++ src/components/layout/index.vue | 38 ++++++ src/main.js | 20 +++ src/router/index.js | 14 ++ src/router/kpi_route.js | 48 +++++++ src/style/public.css | 55 ++++++++ src/views/kpi/assessment/index.vue | 27 ++++ src/views/kpi/okr/index.vue | 29 ++++ src/views/kpi/report/index.vue | 27 ++++ src/views/kpi/set/index.vue | 27 ++++ src/views/kpi/workbench/index.vue | 33 +++++ static/.gitkeep | 0 test/e2e/custom-assertions/elementCount.js | 27 ++++ test/e2e/nightwatch.conf.js | 46 +++++++ test/e2e/runner.js | 48 +++++++ test/e2e/specs/test.js | 19 +++ test/unit/.eslintrc | 7 + test/unit/jest.conf.js | 30 +++++ test/unit/setup.js | 3 + test/unit/specs/HelloWorld.spec.js | 11 ++ 44 files changed, 1518 insertions(+) create mode 100644 .babelrc create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .postcssrc.js create mode 100644 README.md create mode 100644 build/build.js create mode 100644 build/check-versions.js create mode 100644 build/logo.png create mode 100644 build/utils.js create mode 100644 build/vue-loader.conf.js create mode 100644 build/webpack.base.conf.js create mode 100644 build/webpack.dev.conf.js create mode 100644 build/webpack.prod.conf.js create mode 100644 config/dev.env.js create mode 100644 config/index.js create mode 100644 config/prod.env.js create mode 100644 config/test.env.js create mode 100644 index.html create mode 100644 package.json create mode 100644 src/App.vue create mode 100644 src/assets/logo.png create mode 100644 src/components/layout/AppMain.vue create mode 100644 src/components/layout/NavBar.vue create mode 100644 src/components/layout/index.vue create mode 100644 src/main.js create mode 100644 src/router/index.js create mode 100644 src/router/kpi_route.js create mode 100644 src/style/public.css create mode 100644 src/views/kpi/assessment/index.vue create mode 100644 src/views/kpi/okr/index.vue create mode 100644 src/views/kpi/report/index.vue create mode 100644 src/views/kpi/set/index.vue create mode 100644 src/views/kpi/workbench/index.vue create mode 100644 static/.gitkeep create mode 100644 test/e2e/custom-assertions/elementCount.js create mode 100644 test/e2e/nightwatch.conf.js create mode 100644 test/e2e/runner.js create mode 100644 test/e2e/specs/test.js create mode 100644 test/unit/.eslintrc create mode 100644 test/unit/jest.conf.js create mode 100644 test/unit/setup.js create mode 100644 test/unit/specs/HelloWorld.spec.js diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..873ca1b --- /dev/null +++ b/.babelrc @@ -0,0 +1,27 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2", + ["es2015", { + "modules": false + }] + ], + "plugins": ["transform-vue-jsx", "transform-runtime",[ + "component", + { + "libraryName": "element-ui", + "styleLibraryName": "theme-chalk" + } + ]], + "env": { + "test": { + "presets": ["env", "stage-2"], + "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9d08a1a --- /dev/null +++ b/.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/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e2192c5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +/build/ +/config/ +/dist/ +/*.js +/test/unit/coverage/ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..22fdce8 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,29 @@ +// https://eslint.org/docs/user-guide/configuring + +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint' + }, + env: { + browser: true, + }, + extends: [ + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + 'plugin:vue/essential', + // https://github.com/standard/standard/blob/master/docs/RULES-en.md + 'standard' + ], + // required to lint *.vue files + plugins: [ + 'vue' + ], + // add your custom rules here + rules: { + // allow async-await + 'generator-star-spacing': 'off', + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfb4167 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +/test/unit/coverage/ +/test/e2e/reports/ +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 0000000..eee3e92 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c7e95ae --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# linzi_kpi + +> A Vue.js project + +## 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 + +# run unit tests +npm run unit + +# run e2e tests +npm run e2e + +# run all tests +npm test +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/build/build.js b/build/build.js new file mode 100644 index 0000000..8f2ad8a --- /dev/null +++ b/build/build.js @@ -0,0 +1,41 @@ +'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/build/check-versions.js b/build/check-versions.js new file mode 100644 index 0000000..3ef972a --- /dev/null +++ b/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/build/logo.png b/build/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d2503fc2a44b5053b0837ebea6e87a2d339a43 GIT binary patch literal 6849 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 { + 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/build/vue-loader.conf.js b/build/vue-loader.conf.js new file mode 100644 index 0000000..33ed58b --- /dev/null +++ b/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js new file mode 100644 index 0000000..97cd6e2 --- /dev/null +++ b/build/webpack.base.conf.js @@ -0,0 +1,96 @@ +'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: /\.less$/, + loader: "style-loader!css-loader!less-loader" + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + 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/build/webpack.dev.conf.js b/build/webpack.dev.conf.js new file mode 100644 index 0000000..070ae22 --- /dev/null +++ b/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'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 HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + 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 webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // 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/build/webpack.prod.conf.js b/build/webpack.prod.conf.js new file mode 100644 index 0000000..2f17259 --- /dev/null +++ b/build/webpack.prod.conf.js @@ -0,0 +1,149 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = process.env.NODE_ENV === 'testing' + ? require('../config/test.env') + : require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: process.env.NODE_ENV === 'testing' + ? 'index.html' + : 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' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code splitted chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // 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/config/dev.env.js b/config/dev.env.js new file mode 100644 index 0000000..1e22973 --- /dev/null +++ b/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..926ab36 --- /dev/null +++ b/config/index.js @@ -0,0 +1,76 @@ +'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: {}, + + // 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: true + }, + + 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/config/prod.env.js b/config/prod.env.js new file mode 100644 index 0000000..a6f9976 --- /dev/null +++ b/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/config/test.env.js b/config/test.env.js new file mode 100644 index 0000000..c2824a3 --- /dev/null +++ b/config/test.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const devEnv = require('./dev.env') + +module.exports = merge(devEnv, { + NODE_ENV: '"testing"' +}) diff --git a/index.html b/index.html new file mode 100644 index 0000000..bdff4c1 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + linzi_kpi + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..004c4e5 --- /dev/null +++ b/package.json @@ -0,0 +1,94 @@ +{ + "name": "linzi_kpi", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "xiongchengqiang ", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "unit": "jest --config test/unit/jest.conf.js --coverage", + "e2e": "node test/e2e/runner.js", + "test": "npm run unit && npm run e2e", + "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs", + "build": "node build/build.js" + }, + "dependencies": { + "element-ui": "^2.13.2", + "less": "3.9", + "less-loader": "5.0.0", + "vue": "^2.5.2", + "vue-router": "^3.0.1" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-eslint": "^8.2.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-jest": "^21.0.2", + "babel-loader": "^7.1.1", + "babel-plugin-component": "^1.1.1", + "babel-plugin-dynamic-import-node": "^1.2.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-es2015": "^6.24.1", + "babel-preset-stage-2": "^6.22.0", + "babel-register": "^6.22.0", + "chalk": "^2.0.1", + "chromedriver": "^2.27.2", + "copy-webpack-plugin": "^4.0.1", + "cross-spawn": "^5.0.1", + "css-loader": "^0.28.0", + "eslint": "^4.15.0", + "eslint-config-standard": "^10.2.1", + "eslint-friendly-formatter": "^3.0.0", + "eslint-loader": "^1.7.1", + "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", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^2.30.1", + "jest": "^22.0.4", + "jest-serializer-vue": "^0.3.0", + "nightwatch": "^0.9.12", + "node-notifier": "^5.1.2", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "prettier": "^1.12.1", + "rimraf": "^2.6.0", + "selenium-server": "^3.0.1", + "semver": "^5.3.0", + "shelljs": "^0.7.6", + "style-loader": "^2.0.0", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-jest": "^1.0.2", + "vue-loader": "^13.3.0", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.5.2", + "webpack": "^3.6.0", + "webpack-bundle-analyzer": "^2.9.0", + "webpack-dev-server": "^2.9.1", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..634532a --- /dev/null +++ b/src/App.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d2503fc2a44b5053b0837ebea6e87a2d339a43 GIT binary patch literal 6849 zcmaKRcUV(fvo}bjDT-7nLI_nlK}sT_69H+`qzVWDA|yaU?}j417wLi^B1KB1SLsC& zL0ag7$U(XW5YR7p&Ux?sP$d4lvMt8C^+TcQu4F zQqv!UF!I+kw)c0jhd6+g6oCr9P?7)?!qX1ui*iL{p}sKCAGuJ{{W)0z1pLF|=>h}& zt(2Lr0Z`2ig8<5i%Zk}cO5Fm=LByqGWaS`oqChZdEFmc`0hSb#gg|Aap^{+WKOYcj zHjINK)KDG%&s?Mt4CL(T=?;~U@bU2x_mLKN!#GJuK_CzbNw5SMEJorG!}_5;?R>@1 zSl)jns3WlU7^J%=(hUtfmuUCU&C3%8B5C^f5>W2Cy8jW3#{Od{lF1}|?c61##3dzA zsPlFG;l_FzBK}8>|H_Ru_H#!_7$UH4UKo3lKOA}g1(R&|e@}GINYVzX?q=_WLZCgh z)L|eJMce`D0EIwgRaNETDsr+?vQknSGAi=7H00r`QnI%oQnFxm`G2umXso9l+8*&Q z7WqF|$p49js$mdzo^BXpH#gURy=UO;=IMrYc5?@+sR4y_?d*~0^YP7d+y0{}0)zBM zIKVM(DBvICK#~7N0a+PY6)7;u=dutmNqK3AlsrUU9U`d;msiucB_|8|2kY=(7XA;G zwDA8AR)VCA#JOkxm#6oHNS^YVuOU;8p$N)2{`;oF|rQ?B~K$%rHDxXs+_G zF5|-uqHZvSzq}L;5Kcy_P+x0${33}Ofb6+TX&=y;;PkEOpz%+_bCw_{<&~ zeLV|!bP%l1qxywfVr9Z9JI+++EO^x>ZuCK);=$VIG1`kxK8F2M8AdC$iOe3cj1fo(ce4l-9 z7*zKy3={MixvUk=enQE;ED~7tv%qh&3lR<0m??@w{ILF|e#QOyPkFYK!&Up7xWNtL zOW%1QMC<3o;G9_S1;NkPB6bqbCOjeztEc6TsBM<(q9((JKiH{01+Ud=uw9B@{;(JJ z-DxI2*{pMq`q1RQc;V8@gYAY44Z!%#W~M9pRxI(R?SJ7sy7em=Z5DbuDlr@*q|25V)($-f}9c#?D%dU^RS<(wz?{P zFFHtCab*!rl(~j@0(Nadvwg8q|4!}L^>d?0al6}Rrv9$0M#^&@zjbfJy_n!%mVHK4 z6pLRIQ^Uq~dnyy$`ay51Us6WaP%&O;@49m&{G3z7xV3dLtt1VTOMYl3UW~Rm{Eq4m zF?Zl_v;?7EFx1_+#WFUXxcK78IV)FO>42@cm@}2I%pVbZqQ}3;p;sDIm&knay03a^ zn$5}Q$G!@fTwD$e(x-~aWP0h+4NRz$KlnO_H2c< z(XX#lPuW_%H#Q+c&(nRyX1-IadKR-%$4FYC0fsCmL9ky3 zKpxyjd^JFR+vg2!=HWf}2Z?@Td`0EG`kU?{8zKrvtsm)|7>pPk9nu@2^z96aU2<#` z2QhvH5w&V;wER?mopu+nqu*n8p~(%QkwSs&*0eJwa zMXR05`OSFpfyRb!Y_+H@O%Y z0=K^y6B8Gcbl?SA)qMP3Z+=C(?8zL@=74R=EVnE?vY!1BQy2@q*RUgRx4yJ$k}MnL zs!?74QciNb-LcG*&o<9=DSL>1n}ZNd)w1z3-0Pd^4ED1{qd=9|!!N?xnXjM!EuylY z5=!H>&hSofh8V?Jofyd!h`xDI1fYAuV(sZwwN~{$a}MX^=+0TH*SFp$vyxmUv7C*W zv^3Gl0+eTFgBi3FVD;$nhcp)ka*4gSskYIqQ&+M}xP9yLAkWzBI^I%zR^l1e?bW_6 zIn{mo{dD=)9@V?s^fa55jh78rP*Ze<3`tRCN4*mpO$@7a^*2B*7N_|A(Ve2VB|)_o z$=#_=aBkhe(ifX}MLT()@5?OV+~7cXC3r!%{QJxriXo9I%*3q4KT4Xxzyd{ z9;_%=W%q!Vw$Z7F3lUnY+1HZ*lO;4;VR2+i4+D(m#01OYq|L_fbnT;KN<^dkkCwtd zF7n+O7KvAw8c`JUh6LmeIrk4`F3o|AagKSMK3))_5Cv~y2Bb2!Ibg9BO7Vkz?pAYX zoI=B}+$R22&IL`NCYUYjrdhwjnMx_v=-Qcx-jmtN>!Zqf|n1^SWrHy zK|MwJ?Z#^>)rfT5YSY{qjZ&`Fjd;^vv&gF-Yj6$9-Dy$<6zeP4s+78gS2|t%Z309b z0^fp~ue_}i`U9j!<|qF92_3oB09NqgAoehQ`)<)dSfKoJl_A6Ec#*Mx9Cpd-p#$Ez z={AM*r-bQs6*z$!*VA4|QE7bf@-4vb?Q+pPKLkY2{yKsw{&udv_2v8{Dbd zm~8VAv!G~s)`O3|Q6vFUV%8%+?ZSVUa(;fhPNg#vab@J*9XE4#D%)$UU-T5`fwjz! z6&gA^`OGu6aUk{l*h9eB?opVdrHK>Q@U>&JQ_2pR%}TyOXGq_6s56_`U(WoOaAb+K zXQr#6H}>a-GYs9^bGP2Y&hSP5gEtW+GVC4=wy0wQk=~%CSXj=GH6q z-T#s!BV`xZVxm{~jr_ezYRpqqIcXC=Oq`b{lu`Rt(IYr4B91hhVC?yg{ol4WUr3v9 zOAk2LG>CIECZ-WIs0$N}F#eoIUEtZudc7DPYIjzGqDLWk_A4#(LgacooD z2K4IWs@N`Bddm-{%oy}!k0^i6Yh)uJ1S*90>|bm3TOZxcV|ywHUb(+CeX-o1|LTZM zwU>dY3R&U)T(}5#Neh?-CWT~@{6Ke@sI)uSuzoah8COy)w)B)aslJmp`WUcjdia-0 zl2Y}&L~XfA`uYQboAJ1;J{XLhYjH){cObH3FDva+^8ioOQy%Z=xyjGLmWMrzfFoH; zEi3AG`_v+%)&lDJE;iJWJDI@-X9K5O)LD~j*PBe(wu+|%ar~C+LK1+-+lK=t# z+Xc+J7qp~5q=B~rD!x78)?1+KUIbYr^5rcl&tB-cTtj+e%{gpZZ4G~6r15+d|J(ky zjg@@UzMW0k9@S#W(1H{u;Nq(7llJbq;;4t$awM;l&(2s+$l!Ay9^Ge|34CVhr7|BG z?dAR83smef^frq9V(OH+a+ki#q&-7TkWfFM=5bsGbU(8mC;>QTCWL5ydz9s6k@?+V zcjiH`VI=59P-(-DWXZ~5DH>B^_H~;4$)KUhnmGo*G!Tq8^LjfUDO)lASN*=#AY_yS zqW9UX(VOCO&p@kHdUUgsBO0KhXxn1sprK5h8}+>IhX(nSXZKwlNsjk^M|RAaqmCZB zHBolOHYBas@&{PT=R+?d8pZu zUHfyucQ`(umXSW7o?HQ3H21M`ZJal+%*)SH1B1j6rxTlG3hx1IGJN^M7{$j(9V;MZ zRKybgVuxKo#XVM+?*yTy{W+XHaU5Jbt-UG33x{u(N-2wmw;zzPH&4DE103HV@ER86 z|FZEmQb|&1s5#`$4!Cm}&`^{(4V}OP$bk`}v6q6rm;P!H)W|2i^e{7lTk2W@jo_9q z*aw|U7#+g59Fv(5qI`#O-qPj#@_P>PC#I(GSp3DLv7x-dmYK=C7lPF8a)bxb=@)B1 zUZ`EqpXV2dR}B&r`uM}N(TS99ZT0UB%IN|0H%DcVO#T%L_chrgn#m6%x4KE*IMfjX zJ%4veCEqbXZ`H`F_+fELMC@wuy_ch%t*+Z+1I}wN#C+dRrf2X{1C8=yZ_%Pt6wL_~ zZ2NN-hXOT4P4n$QFO7yYHS-4wF1Xfr-meG9Pn;uK51?hfel`d38k{W)F*|gJLT2#T z<~>spMu4(mul-8Q3*pf=N4DcI)zzjqAgbE2eOT7~&f1W3VsdD44Ffe;3mJp-V@8UC z)|qnPc12o~$X-+U@L_lWqv-RtvB~%hLF($%Ew5w>^NR82qC_0FB z)=hP1-OEx?lLi#jnLzH}a;Nvr@JDO-zQWd}#k^an$Kwml;MrD&)sC5b`s0ZkVyPkb zt}-jOq^%_9>YZe7Y}PhW{a)c39G`kg(P4@kxjcYfgB4XOOcmezdUI7j-!gs7oAo2o zx(Ph{G+YZ`a%~kzK!HTAA5NXE-7vOFRr5oqY$rH>WI6SFvWmahFav!CfRMM3%8J&c z*p+%|-fNS_@QrFr(at!JY9jCg9F-%5{nb5Bo~z@Y9m&SHYV`49GAJjA5h~h4(G!Se zZmK{Bo7ivCfvl}@A-ptkFGcWXAzj3xfl{evi-OG(TaCn1FAHxRc{}B|x+Ua1D=I6M z!C^ZIvK6aS_c&(=OQDZfm>O`Nxsw{ta&yiYPA~@e#c%N>>#rq)k6Aru-qD4(D^v)y z*>Rs;YUbD1S8^D(ps6Jbj0K3wJw>L4m)0e(6Pee3Y?gy9i0^bZO?$*sv+xKV?WBlh zAp*;v6w!a8;A7sLB*g-^<$Z4L7|5jXxxP1}hQZ<55f9<^KJ>^mKlWSGaLcO0=$jem zWyZkRwe~u{{tU63DlCaS9$Y4CP4f?+wwa(&1ou)b>72ydrFvm`Rj-0`kBJgK@nd(*Eh!(NC{F-@=FnF&Y!q`7){YsLLHf0_B6aHc# z>WIuHTyJwIH{BJ4)2RtEauC7Yq7Cytc|S)4^*t8Va3HR zg=~sN^tp9re@w=GTx$;zOWMjcg-7X3Wk^N$n;&Kf1RgVG2}2L-(0o)54C509C&77i zrjSi{X*WV=%C17((N^6R4Ya*4#6s_L99RtQ>m(%#nQ#wrRC8Y%yxkH;d!MdY+Tw@r zjpSnK`;C-U{ATcgaxoEpP0Gf+tx);buOMlK=01D|J+ROu37qc*rD(w`#O=3*O*w9?biwNoq3WN1`&Wp8TvKj3C z3HR9ssH7a&Vr<6waJrU zdLg!ieYz%U^bmpn%;(V%%ugMk92&?_XX1K@mwnVSE6!&%P%Wdi7_h`CpScvspMx?N zQUR>oadnG17#hNc$pkTp+9lW+MBKHRZ~74XWUryd)4yd zj98$%XmIL4(9OnoeO5Fnyn&fpQ9b0h4e6EHHw*l68j;>(ya`g^S&y2{O8U>1*>4zR zq*WSI_2o$CHQ?x0!wl9bpx|Cm2+kFMR)oMud1%n2=qn5nE&t@Fgr#=Zv2?}wtEz^T z9rrj=?IH*qI5{G@Rn&}^Z{+TW}mQeb9=8b<_a`&Cm#n%n~ zU47MvCBsdXFB1+adOO)03+nczfWa#vwk#r{o{dF)QWya9v2nv43Zp3%Ps}($lA02*_g25t;|T{A5snSY?3A zrRQ~(Ygh_ebltHo1VCbJb*eOAr;4cnlXLvI>*$-#AVsGg6B1r7@;g^L zFlJ_th0vxO7;-opU@WAFe;<}?!2q?RBrFK5U{*ai@NLKZ^};Ul}beukveh?TQn;$%9=R+DX07m82gP$=}Uo_%&ngV`}Hyv8g{u z3SWzTGV|cwQuFIs7ZDOqO_fGf8Q`8MwL}eUp>q?4eqCmOTcwQuXtQckPy|4F1on8l zP*h>d+cH#XQf|+6c|S{7SF(Lg>bR~l(0uY?O{OEVlaxa5@e%T&xju=o1`=OD#qc16 zSvyH*my(dcp6~VqR;o(#@m44Lug@~_qw+HA=mS#Z^4reBy8iV?H~I;{LQWk3aKK8$bLRyt$g?- + + + + + diff --git a/src/components/layout/NavBar.vue b/src/components/layout/NavBar.vue new file mode 100644 index 0000000..5d8ed62 --- /dev/null +++ b/src/components/layout/NavBar.vue @@ -0,0 +1,75 @@ + + + + + + diff --git a/src/components/layout/index.vue b/src/components/layout/index.vue new file mode 100644 index 0000000..d5d030f --- /dev/null +++ b/src/components/layout/index.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..ca83cce --- /dev/null +++ b/src/main.js @@ -0,0 +1,20 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import { Menu, MenuItem, Submenu } from 'element-ui' +import './style/public.css' + +Vue.use(Menu) +Vue.use(MenuItem) +Vue.use(Submenu) + +Vue.config.productionTip = false +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + components: { App }, + template: '' +}) diff --git a/src/router/index.js b/src/router/index.js new file mode 100644 index 0000000..2d0a1cc --- /dev/null +++ b/src/router/index.js @@ -0,0 +1,14 @@ +import Vue from 'vue' +import Router from 'vue-router' +import kpiRouter from './kpi_route.js' +Vue.use(Router) + +const routes = [ + { + path: '/', + redirect: kpiRouter.length > 0 ? kpiRouter[0].redirect : '/' + } +].concat(kpiRouter) +export default new Router({ + routes +}) diff --git a/src/router/kpi_route.js b/src/router/kpi_route.js new file mode 100644 index 0000000..b35e7f2 --- /dev/null +++ b/src/router/kpi_route.js @@ -0,0 +1,48 @@ +export default [ + { + path: '/kpi', + name: 'kpi', + component: (resolve) => require(['@/components/layout/index.vue'], resolve), + redirect: 'kpi/workbench', + meta: { + title: '智能绩效', + isNav: true + }, + children: [{ + path: 'workbench', + name: 'workbench', + component: (resolve) => require(['@/views/kpi/workbench/index.vue'], resolve), + meta: { + title: '工作台' + } + }, { + path: 'assessment', + name: 'assessment', + component: (resolve) => require(['@/views/kpi/assessment/index.vue'], resolve), + meta: { + title: '考核管理' + } + }, { + path: 'okr', + name: 'okr', + component: (resolve) => require(['@/views/kpi/okr/index.vue'], resolve), + meta: { + title: 'OKR' + } + }, { + path: 'report', + name: 'report', + component: (resolve) => require(['@/views/kpi/report/index.vue'], resolve), + meta: { + title: '绩效报表' + } + }, { + path: 'set', + name: 'set', + component: (resolve) => require(['@/views/kpi/set/index.vue'], resolve), + meta: { + title: '设置' + } + }] + } +] diff --git a/src/style/public.css b/src/style/public.css new file mode 100644 index 0000000..993ccd1 --- /dev/null +++ b/src/style/public.css @@ -0,0 +1,55 @@ +@charset "utf-8"; +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + /*background: transparent;*/ +} +table { + border-collapse:collapse; + border-spacing:0; +} +fieldset, img { border:0;} +address, caption, cite, code, dfn, em, strong, th, var { + font-style:normal; + font-weight:normal; +} +ol, ul { list-style:none; } +caption, th { text-align:left; } +h1, h2, h3, h4, h5, h6 { + font-size:200%; + font-weight:normal; +} +:focus { outline: 0;} +a{ text-decoration:none;} +a:hover img{ border:none;} + +a:active{noOutline:expression(this.onFocus=this.blur());} +/*清除浮动*/ +.clearfix:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +.clearfix {display: inline-block;} +/* Hides from IE-mac \*/ +* html .clearfix { height: 1%;} +.clearfix {display: block;} +/* End hide from IE-mac */ + +/*png css hack for ie6*/ +*html img.png{ + _background-image: expression(this.runtimeStyle.backgroundImage = "none",this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "', sizingMethod='image')",this.src = "http://i0.itc.cn/20101019/848_0a785a7b_1118_4825_85dc_e8696988c94b_0.gif"); +} \ No newline at end of file diff --git a/src/views/kpi/assessment/index.vue b/src/views/kpi/assessment/index.vue new file mode 100644 index 0000000..59020c5 --- /dev/null +++ b/src/views/kpi/assessment/index.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/views/kpi/okr/index.vue b/src/views/kpi/okr/index.vue new file mode 100644 index 0000000..0bc08f1 --- /dev/null +++ b/src/views/kpi/okr/index.vue @@ -0,0 +1,29 @@ + + + + + + diff --git a/src/views/kpi/report/index.vue b/src/views/kpi/report/index.vue new file mode 100644 index 0000000..b5848ca --- /dev/null +++ b/src/views/kpi/report/index.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/views/kpi/set/index.vue b/src/views/kpi/set/index.vue new file mode 100644 index 0000000..2deeb4a --- /dev/null +++ b/src/views/kpi/set/index.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/views/kpi/workbench/index.vue b/src/views/kpi/workbench/index.vue new file mode 100644 index 0000000..75ab06b --- /dev/null +++ b/src/views/kpi/workbench/index.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/e2e/custom-assertions/elementCount.js b/test/e2e/custom-assertions/elementCount.js new file mode 100644 index 0000000..818e602 --- /dev/null +++ b/test/e2e/custom-assertions/elementCount.js @@ -0,0 +1,27 @@ +// A custom Nightwatch assertion. +// The assertion name is the filename. +// Example usage: +// +// browser.assert.elementCount(selector, count) +// +// For more information on custom assertions see: +// http://nightwatchjs.org/guide#writing-custom-assertions + +exports.assertion = function (selector, count) { + this.message = 'Testing if element <' + selector + '> has count: ' + count + this.expected = count + this.pass = function (val) { + return val === this.expected + } + this.value = function (res) { + return res.value + } + this.command = function (cb) { + var self = this + return this.api.execute(function (selector) { + return document.querySelectorAll(selector).length + }, [selector], function (res) { + cb.call(self, res) + }) + } +} diff --git a/test/e2e/nightwatch.conf.js b/test/e2e/nightwatch.conf.js new file mode 100644 index 0000000..f019c0a --- /dev/null +++ b/test/e2e/nightwatch.conf.js @@ -0,0 +1,46 @@ +require('babel-register') +var config = require('../../config') + +// http://nightwatchjs.org/gettingstarted#settings-file +module.exports = { + src_folders: ['test/e2e/specs'], + output_folder: 'test/e2e/reports', + custom_assertions_path: ['test/e2e/custom-assertions'], + + selenium: { + start_process: true, + server_path: require('selenium-server').path, + host: '127.0.0.1', + port: 4444, + cli_args: { + 'webdriver.chrome.driver': require('chromedriver').path + } + }, + + test_settings: { + default: { + selenium_port: 4444, + selenium_host: 'localhost', + silent: true, + globals: { + devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) + } + }, + + chrome: { + desiredCapabilities: { + browserName: 'chrome', + javascriptEnabled: true, + acceptSslCerts: true + } + }, + + firefox: { + desiredCapabilities: { + browserName: 'firefox', + javascriptEnabled: true, + acceptSslCerts: true + } + } + } +} diff --git a/test/e2e/runner.js b/test/e2e/runner.js new file mode 100644 index 0000000..2722032 --- /dev/null +++ b/test/e2e/runner.js @@ -0,0 +1,48 @@ +// 1. start the dev server using production config +process.env.NODE_ENV = 'testing' + +const webpack = require('webpack') +const DevServer = require('webpack-dev-server') + +const webpackConfig = require('../../build/webpack.prod.conf') +const devConfigPromise = require('../../build/webpack.dev.conf') + +let server + +devConfigPromise.then(devConfig => { + const devServerOptions = devConfig.devServer + const compiler = webpack(webpackConfig) + server = new DevServer(compiler, devServerOptions) + const port = devServerOptions.port + const host = devServerOptions.host + return server.listen(port, host) +}) +.then(() => { + // 2. run the nightwatch test suite against it + // to run in additional browsers: + // 1. add an entry in test/e2e/nightwatch.conf.js under "test_settings" + // 2. add it to the --env flag below + // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` + // For more information on Nightwatch's config file, see + // http://nightwatchjs.org/guide#settings-file + let opts = process.argv.slice(2) + if (opts.indexOf('--config') === -1) { + opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) + } + if (opts.indexOf('--env') === -1) { + opts = opts.concat(['--env', 'chrome']) + } + + const spawn = require('cross-spawn') + const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) + + runner.on('exit', function (code) { + server.close() + process.exit(code) + }) + + runner.on('error', function (err) { + server.close() + throw err + }) +}) diff --git a/test/e2e/specs/test.js b/test/e2e/specs/test.js new file mode 100644 index 0000000..a7b1bd9 --- /dev/null +++ b/test/e2e/specs/test.js @@ -0,0 +1,19 @@ +// For authoring Nightwatch tests, see +// http://nightwatchjs.org/guide#usage + +module.exports = { + 'default e2e tests': function (browser) { + // automatically uses dev Server port from /config.index.js + // default: http://localhost:8080 + // see nightwatch.conf.js + const devServer = browser.globals.devServerURL + + browser + .url(devServer) + .waitForElementVisible('#app', 5000) + .assert.elementPresent('.hello') + .assert.containsText('h1', 'Welcome to Your Vue.js App') + .assert.elementCount('img', 1) + .end() + } +} diff --git a/test/unit/.eslintrc b/test/unit/.eslintrc new file mode 100644 index 0000000..4d5d98f --- /dev/null +++ b/test/unit/.eslintrc @@ -0,0 +1,7 @@ +{ + "env": { + "jest": true + }, + "globals": { + } +} diff --git a/test/unit/jest.conf.js b/test/unit/jest.conf.js new file mode 100644 index 0000000..4f7da15 --- /dev/null +++ b/test/unit/jest.conf.js @@ -0,0 +1,30 @@ +const path = require('path') + +module.exports = { + rootDir: path.resolve(__dirname, '../../'), + moduleFileExtensions: [ + 'js', + 'json', + 'vue' + ], + moduleNameMapper: { + '^@/(.*)$': '/src/$1' + }, + transform: { + '^.+\\.js$': '/node_modules/babel-jest', + '.*\\.(vue)$': '/node_modules/vue-jest' + }, + testPathIgnorePatterns: [ + '/test/e2e' + ], + snapshotSerializers: ['/node_modules/jest-serializer-vue'], + setupFiles: ['/test/unit/setup'], + mapCoverage: true, + coverageDirectory: '/test/unit/coverage', + collectCoverageFrom: [ + 'src/**/*.{js,vue}', + '!src/main.js', + '!src/router/index.js', + '!**/node_modules/**' + ] +} diff --git a/test/unit/setup.js b/test/unit/setup.js new file mode 100644 index 0000000..edbdbae --- /dev/null +++ b/test/unit/setup.js @@ -0,0 +1,3 @@ +import Vue from 'vue' + +Vue.config.productionTip = false diff --git a/test/unit/specs/HelloWorld.spec.js b/test/unit/specs/HelloWorld.spec.js new file mode 100644 index 0000000..cfb0453 --- /dev/null +++ b/test/unit/specs/HelloWorld.spec.js @@ -0,0 +1,11 @@ +import Vue from 'vue' +import HelloWorld from '@/components/HelloWorld' + +describe('HelloWorld.vue', () => { + it('should render correct contents', () => { + const Constructor = Vue.extend(HelloWorld) + const vm = new Constructor().$mount() + expect(vm.$el.querySelector('.hello h1').textContent) + .toEqual('Welcome to Your Vue.js App') + }) +})