├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── README.md ├── build.old ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js ├── webpack.prod.conf.js └── webpack.test.conf.js ├── build ├── setup-dev-server.js ├── utils.js ├── vue-loader.config.js ├── webpack.base.config.js ├── webpack.client.config.js └── webpack.server.config.js ├── config ├── dev.env.js ├── prod.env.js └── test.env.js ├── package-lock.json ├── package.json ├── public └── logo-48.png ├── server.js ├── src ├── App.vue ├── app.js ├── assets │ ├── css │ │ ├── index.css │ │ ├── marginPadding.css │ │ ├── project.css │ │ ├── projectVar.css │ │ ├── reset.css │ │ └── var.css │ ├── iconfont │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ └── iconfont.woff │ └── images │ │ ├── erweima.jpg │ │ ├── google.png │ │ ├── left_bg.jpg │ │ ├── logo.png │ │ ├── logo_icon.png │ │ ├── logo_white.png │ │ └── safe.png ├── components │ ├── BankSelect.vue │ ├── DatePicker.vue │ ├── Filtrate.vue │ ├── SearchTable.vue │ └── Tab.vue ├── config │ ├── api.js │ └── index.js ├── entry-client.js ├── entry-server.js ├── index.template.html ├── main.js ├── mock.js ├── pages │ ├── 404.vue │ ├── Deal.vue │ ├── Login.vue │ ├── Main.vue │ ├── News.vue │ ├── Notices.vue │ ├── Other.vue │ ├── Register.vue │ ├── Reset.vue │ ├── UserCenter.vue │ ├── deal │ │ └── Order.vue │ ├── finance │ │ ├── CapitalAccount.vue │ │ ├── CarbonAccount.vue │ │ └── Pandect.vue │ ├── message │ │ └── message.vue │ ├── other │ │ ├── AccountGuide.vue │ │ ├── Authentication.vue │ │ ├── DealGuide.vue │ │ ├── Help.vue │ │ ├── Illustrate.vue │ │ ├── Recharge.vue │ │ └── lawer.vue │ └── security │ │ ├── CapitalPwd.vue │ │ ├── Center.vue │ │ ├── Company.vue │ │ ├── Email.vue │ │ ├── Google.vue │ │ ├── Identity.vue │ │ ├── LoginPwd.vue │ │ └── Phone.vue ├── router │ └── index.js ├── store │ ├── index.js │ └── modules │ │ ├── account.js │ │ ├── assets.js │ │ ├── deal.js │ │ └── user.js └── util │ ├── api.js │ ├── common.js │ ├── getData.js │ ├── inter.js │ ├── pageData.js │ └── title.js ├── static └── .gitkeep └── test ├── e2e ├── custom-assertions │ └── elementCount.js ├── nightwatch.conf.js ├── runner.js └── specs │ └── test.js └── unit ├── .eslintrc ├── index.js ├── karma.conf.js └── specs └── Hello.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // http://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | test/unit/coverage 8 | test/e2e/reports 9 | selenium-debug.log 10 | config/index.js 11 | 12 | # Editor directories and files 13 | .idea 14 | *.suo 15 | *.ntvs* 16 | *.njsproj 17 | *.sln 18 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "postcss-for": {}, 8 | "postcss-cssnext": {}, 9 | // "postcss-nested": {}, 10 | "postcss-remove-root": {} 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # carbon 2 | 3 | > 碳交所 4 | 5 | ## 构建步骤 6 | 7 | ``` bash 8 | # 安装依赖 9 | npm install 10 | 11 | # 启动开发环境(服务运行在localhost:8080,配有热更新) 12 | npm run dev 13 | 14 | # 构建生产环境 15 | npm run build 16 | 17 | # 运行项目 18 | npm run start 19 | ``` 20 | 21 | ## 目录结构 22 | 23 | ``` bash 24 | build # 构建目录 25 | setup-dev-server.js # 配置构建开发环境 26 | utils.js # 封装的工具方法 27 | vue-loader.config.js # vue文件的loader配置 28 | webpack.base.config.js # 公用的webpack配置 29 | webpack.client.config.js # 客户端的webpack配置 30 | webpack.server.config.js # 服务器端的webpack配置 31 | src # 开发目录 32 | assets # 静态资源目录 33 | css 34 | index.css # main.js中引入 35 | var.css # 多项目变量 36 | project.css # 该项目复用样式 37 | projectVar.css # 该项目变量 38 | reset.css # reset样式 39 | ... # 多项目复用样式 40 | iconfont # 字体图标(阿里图标库http://www.iconfont.cn) 41 | images # 图片 42 | components # 可复用组件 43 | config # 项目自定义配置目录 44 | api.js # 接口地址 45 | index.js # 配置 46 | pages # 页面级组件(路由级组件) 47 | Login.vue... # 登录页面 48 | router # 路由目录 49 | index.js # 路由配置 50 | store # vuex目录 51 | modules # store模块 52 | index.js # 根store(app.js中引入,文件太大时,可将 action、mutation 和 getter 分割到单独的文件) 53 | util # 工具目录 54 | api.js # axios发送请求(发送和返回数据处理,状态码处理...) 55 | common.js # 封装的函数 56 | getData.js # get请求的封装(是否取用store中的数据...) 57 | inter.js # 不需要保存在store中的数据的接口 58 | pageData.js # 页面需要存储的数据(分页等,可选localStorage和query) 59 | title.js # 页面title控制 60 | App.vue # 根组件 61 | app.js # 公用入口 62 | entry-client.js # 客户端入口 63 | entry-server.js # 服务器端入口 64 | index.template.html # 页面模板 65 | .babelrc # babel配置 66 | .eslintignore # eslint忽略项配置 67 | .eslintrc.js # eslint配置(使用standard标准) 68 | .gitignore # git忽略项配置 69 | .postcssrc.js # postcss配置 70 | package.json # 项目配置 71 | server.js # 项目启动文件 72 | ``` -------------------------------------------------------------------------------- /build.old/build.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | process.env.NODE_ENV = 'production' 4 | 5 | var ora = require('ora') 6 | var rm = require('rimraf') 7 | var path = require('path') 8 | var chalk = require('chalk') 9 | var webpack = require('webpack') 10 | var config = require('../config') 11 | var webpackConfig = require('./webpack.prod.conf') 12 | 13 | var spinner = ora('building for production...') 14 | spinner.start() 15 | 16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 17 | if (err) throw err 18 | webpack(webpackConfig, function (err, stats) { 19 | spinner.stop() 20 | if (err) throw err 21 | process.stdout.write(stats.toString({ 22 | colors: true, 23 | modules: false, 24 | children: false, 25 | chunks: false, 26 | chunkModules: false 27 | }) + '\n\n') 28 | 29 | if (stats.hasErrors()) { 30 | console.log(chalk.red(' Build failed with errors.\n')) 31 | process.exit(1) 32 | } 33 | 34 | console.log(chalk.cyan(' Build complete.\n')) 35 | console.log(chalk.yellow( 36 | ' Tip: built files are meant to be served over an HTTP server.\n' + 37 | ' Opening index.html over file:// won\'t work.\n' 38 | )) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /build.old/check-versions.js: -------------------------------------------------------------------------------- 1 | var chalk = require('chalk') 2 | var semver = require('semver') 3 | var packageConfig = require('../package.json') 4 | var shell = require('shelljs') 5 | function exec (cmd) { 6 | return require('child_process').execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | } 15 | ] 16 | 17 | if (shell.which('npm')) { 18 | versionRequirements.push({ 19 | name: 'npm', 20 | currentVersion: exec('npm --version'), 21 | versionRequirement: packageConfig.engines.npm 22 | }) 23 | } 24 | 25 | module.exports = function () { 26 | var warnings = [] 27 | for (var i = 0; i < versionRequirements.length; i++) { 28 | var mod = versionRequirements[i] 29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 30 | warnings.push(mod.name + ': ' + 31 | chalk.red(mod.currentVersion) + ' should be ' + 32 | chalk.green(mod.versionRequirement) 33 | ) 34 | } 35 | } 36 | 37 | if (warnings.length) { 38 | console.log('') 39 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 40 | console.log() 41 | for (var i = 0; i < warnings.length; i++) { 42 | var warning = warnings[i] 43 | console.log(' ' + warning) 44 | } 45 | console.log() 46 | process.exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /build.old/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | require('eventsource-polyfill') 3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 4 | 5 | hotClient.subscribe(function (event) { 6 | if (event.action === 'reload') { 7 | window.location.reload() 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /build.old/dev-server.js: -------------------------------------------------------------------------------- 1 | require('./check-versions')() 2 | 3 | var config = require('../config') 4 | if (!process.env.NODE_ENV) { 5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 6 | } 7 | 8 | var opn = require('opn') 9 | var path = require('path') 10 | var express = require('express') 11 | var webpack = require('webpack') 12 | var proxyMiddleware = require('http-proxy-middleware') 13 | var webpackConfig = (process.env.NODE_ENV === 'testing' || process.env.NODE_ENV === 'production') 14 | ? require('./webpack.prod.conf') 15 | : require('./webpack.dev.conf') 16 | 17 | // default port where dev server listens for incoming traffic 18 | var port = process.env.PORT || config.dev.port 19 | // automatically open browser, if not set will be false 20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser 21 | // Define HTTP proxies to your custom API backend 22 | // https://github.com/chimurai/http-proxy-middleware 23 | var proxyTable = config.dev.proxyTable 24 | 25 | var app = express() 26 | var compiler = webpack(webpackConfig) 27 | 28 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 29 | publicPath: webpackConfig.output.publicPath, 30 | quiet: true 31 | }) 32 | 33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, { 34 | log: false, 35 | heartbeat: 2000 36 | }) 37 | // force page reload when html-webpack-plugin template changes 38 | compiler.plugin('compilation', function (compilation) { 39 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 40 | hotMiddleware.publish({ action: 'reload' }) 41 | cb() 42 | }) 43 | }) 44 | 45 | // proxy api requests 46 | Object.keys(proxyTable).forEach(function (context) { 47 | var options = proxyTable[context] 48 | if (typeof options === 'string') { 49 | options = { target: options } 50 | } 51 | app.use(proxyMiddleware(options.filter || context, options)) 52 | }) 53 | 54 | // handle fallback for HTML5 history API 55 | app.use(require('connect-history-api-fallback')()) 56 | 57 | // serve webpack bundle output 58 | app.use(devMiddleware) 59 | 60 | // enable hot-reload and state-preserving 61 | // compilation error display 62 | app.use(hotMiddleware) 63 | 64 | // serve pure static assets 65 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 66 | app.use(staticPath, express.static('./static')) 67 | 68 | var uri = 'http://localhost:' + port 69 | 70 | var _resolve 71 | var readyPromise = new Promise(resolve => { 72 | _resolve = resolve 73 | }) 74 | 75 | console.log('> Starting dev server...') 76 | devMiddleware.waitUntilValid(() => { 77 | console.log('> Listening at ' + uri + '\n') 78 | // when env is testing, don't need open it 79 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 80 | opn(uri) 81 | } 82 | _resolve() 83 | }) 84 | 85 | var server = app.listen(port) 86 | 87 | module.exports = { 88 | ready: readyPromise, 89 | close: () => { 90 | server.close() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /build.old/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | 15 | var cssLoader = { 16 | loader: 'css-loader', 17 | options: { 18 | minimize: process.env.NODE_ENV === 'production', 19 | sourceMap: options.sourceMap 20 | } 21 | } 22 | 23 | // generate loader string to be used with extract text plugin 24 | function generateLoaders (loader, loaderOptions) { 25 | var loaders = [cssLoader] 26 | if (loader) { 27 | loaders.push({ 28 | loader: loader + '-loader', 29 | options: Object.assign({}, loaderOptions, { 30 | sourceMap: options.sourceMap 31 | }) 32 | }) 33 | } 34 | 35 | // Extract CSS when that option is specified 36 | // (which is the case during production build) 37 | if (options.extract) { 38 | return ExtractTextPlugin.extract({ 39 | use: loaders, 40 | fallback: 'vue-style-loader' 41 | }) 42 | } else { 43 | return ['vue-style-loader'].concat(loaders) 44 | } 45 | } 46 | 47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 48 | return { 49 | css: generateLoaders(), 50 | postcss: generateLoaders(), 51 | less: generateLoaders('less'), 52 | sass: generateLoaders('sass', { indentedSyntax: true }), 53 | scss: generateLoaders('sass'), 54 | stylus: generateLoaders('stylus'), 55 | styl: generateLoaders('stylus') 56 | } 57 | } 58 | 59 | // Generate loaders for standalone style files (outside of .vue) 60 | exports.styleLoaders = function (options) { 61 | var output = [] 62 | var loaders = exports.cssLoaders(options) 63 | for (var extension in loaders) { 64 | var loader = loaders[extension] 65 | if (extension === 'css') { 66 | loader.push('postcss-loader') 67 | } 68 | output.push({ 69 | test: new RegExp('\\.' + extension + '$'), 70 | use: loader 71 | }) 72 | } 73 | return output 74 | } 75 | -------------------------------------------------------------------------------- /build.old/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var config = require('../config') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | loaders: utils.cssLoaders({ 7 | sourceMap: isProduction 8 | ? config.build.productionSourceMap 9 | : config.dev.cssSourceMap, 10 | extract: isProduction 11 | }), 12 | transformToRequire: { 13 | video: 'src', 14 | source: 'src', 15 | img: 'src', 16 | image: 'xlink:href' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build.old/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var config = require('../config') 4 | var vueLoaderConfig = require('./vue-loader.conf') 5 | 6 | function resolve (dir) { 7 | return path.join(__dirname, '..', dir) 8 | } 9 | 10 | module.exports = { 11 | entry: { 12 | app: './src/main.js' 13 | }, 14 | output: { 15 | path: config.build.assetsRoot, 16 | filename: '[name].js', 17 | publicPath: process.env.NODE_ENV === 'production' 18 | ? config.build.assetsPublicPath 19 | : config.dev.assetsPublicPath 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.vue', '.json'], 23 | alias: { 24 | 'vue$': 'vue/dist/vue.esm.js', 25 | '@': resolve('src') 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.(js|vue)$/, 32 | loader: 'eslint-loader', 33 | enforce: 'pre', 34 | include: [resolve('src'), resolve('test')], 35 | options: { 36 | formatter: require('eslint-friendly-formatter') 37 | } 38 | }, 39 | { 40 | test: /\.vue$/, 41 | loader: 'vue-loader', 42 | options: vueLoaderConfig 43 | }, 44 | { 45 | test: /\.js$/, 46 | loader: 'babel-loader', 47 | include: [resolve('src'), resolve('test')] 48 | }, 49 | { 50 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 51 | loader: 'url-loader', 52 | options: { 53 | limit: 10000, 54 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 55 | } 56 | }, 57 | { 58 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 59 | loader: 'url-loader', 60 | options: { 61 | limit: 10000, 62 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 63 | } 64 | }, 65 | { 66 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 67 | loader: 'url-loader', 68 | options: { 69 | limit: 10000, 70 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 71 | } 72 | } 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /build.old/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var webpack = require('webpack') 3 | var config = require('../config') 4 | var merge = require('webpack-merge') 5 | var baseWebpackConfig = require('./webpack.base.conf') 6 | var HtmlWebpackPlugin = require('html-webpack-plugin') 7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 8 | 9 | // add hot-reload related code to entry chunks 10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 12 | }) 13 | 14 | module.exports = merge(baseWebpackConfig, { 15 | module: { 16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 17 | }, 18 | // cheap-module-eval-source-map is faster for development 19 | devtool: '#cheap-module-eval-source-map', 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env': config.dev.env 23 | }), 24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }), 33 | new FriendlyErrorsPlugin() 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /build.old/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var utils = require('./utils') 3 | var webpack = require('webpack') 4 | var config = require('../config') 5 | var merge = require('webpack-merge') 6 | var baseWebpackConfig = require('./webpack.base.conf') 7 | var CopyWebpackPlugin = require('copy-webpack-plugin') 8 | var HtmlWebpackPlugin = require('html-webpack-plugin') 9 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 11 | const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') 12 | 13 | var env = process.env.NODE_ENV === 'testing' 14 | ? require('../config/test.env') 15 | : config.build.env 16 | 17 | var webpackConfig = merge(baseWebpackConfig, { 18 | module: { 19 | rules: utils.styleLoaders({ 20 | sourceMap: config.build.productionSourceMap, 21 | extract: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? '#source-map' : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new webpack.optimize.UglifyJsPlugin({ 36 | compress: { 37 | warnings: false 38 | }, 39 | sourceMap: true 40 | }), 41 | // extract css into its own file 42 | new ExtractTextPlugin({ 43 | filename: utils.assetsPath('css/[name].[contenthash].css') 44 | }), 45 | // Compress extracted CSS. We are using this plugin so that possible 46 | // duplicated CSS from different components can be deduped. 47 | new OptimizeCSSPlugin({ 48 | cssProcessorOptions: { 49 | safe: true 50 | } 51 | }), 52 | // generate dist index.html with correct asset hash for caching. 53 | // you can customize output by editing /index.html 54 | // see https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: process.env.NODE_ENV === 'testing' 57 | ? 'index.html' 58 | : config.build.index, 59 | template: 'index.html', 60 | inject: true, 61 | minify: { 62 | removeComments: true, 63 | collapseWhitespace: true, 64 | removeAttributeQuotes: true 65 | // more options: 66 | // https://github.com/kangax/html-minifier#options-quick-reference 67 | }, 68 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 69 | chunksSortMode: 'dependency' 70 | }), 71 | // keep module.id stable when vender modules does not change 72 | new webpack.HashedModuleIdsPlugin(), 73 | // split vendor js into its own file 74 | new webpack.optimize.CommonsChunkPlugin({ 75 | name: 'vendor', 76 | minChunks: function (module, count) { 77 | // any required modules inside node_modules are extracted to vendor 78 | return ( 79 | module.resource && 80 | /\.js$/.test(module.resource) && 81 | module.resource.indexOf( 82 | path.join(__dirname, '../node_modules') 83 | ) === 0 84 | ) 85 | } 86 | }), 87 | // extract webpack runtime and module manifest to its own file in order to 88 | // prevent vendor hash from being updated whenever app bundle is updated 89 | new webpack.optimize.CommonsChunkPlugin({ 90 | name: 'manifest', 91 | chunks: Infinity 92 | }), 93 | new VueSSRClientPlugin(), 94 | // copy custom static assets 95 | new CopyWebpackPlugin([ 96 | { 97 | from: path.resolve(__dirname, '../static'), 98 | to: config.build.assetsSubDirectory, 99 | ignore: ['.*'] 100 | } 101 | ]) 102 | ] 103 | }) 104 | 105 | if (config.build.productionGzip) { 106 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 107 | 108 | webpackConfig.plugins.push( 109 | new CompressionWebpackPlugin({ 110 | asset: '[path].gz[query]', 111 | algorithm: 'gzip', 112 | test: new RegExp( 113 | '\\.(' + 114 | config.build.productionGzipExtensions.join('|') + 115 | ')$' 116 | ), 117 | threshold: 10240, 118 | minRatio: 0.8 119 | }) 120 | ) 121 | } 122 | 123 | if (config.build.bundleAnalyzerReport) { 124 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 125 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 126 | } 127 | 128 | module.exports = webpackConfig 129 | -------------------------------------------------------------------------------- /build.old/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | // This is the webpack config used for unit tests. 2 | 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseConfig = require('./webpack.base.conf') 7 | 8 | var webpackConfig = merge(baseConfig, { 9 | // use inline sourcemap for karma-sourcemap-loader 10 | module: { 11 | rules: utils.styleLoaders() 12 | }, 13 | devtool: '#inline-source-map', 14 | resolveLoader: { 15 | alias: { 16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 18 | 'scss-loader': 'sass-loader' 19 | } 20 | }, 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': require('../config/test.env') 24 | }) 25 | ] 26 | }) 27 | 28 | // no need for app entry during tests 29 | delete webpackConfig.entry 30 | 31 | module.exports = webpackConfig 32 | -------------------------------------------------------------------------------- /build/setup-dev-server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const MFS = require('memory-fs') 4 | const webpack = require('webpack') 5 | const chokidar = require('chokidar') 6 | const clientConfig = require('./webpack.client.config') 7 | const serverConfig = require('./webpack.server.config') 8 | 9 | const readFile = (fs, file) => { 10 | try { 11 | return fs.readFileSync(path.join(clientConfig.output.path, file), 'utf-8') 12 | } catch (e) {} 13 | } 14 | 15 | module.exports = function setupDevServer (app, templatePath, cb) { 16 | let bundle 17 | let template 18 | let clientManifest 19 | 20 | let ready 21 | const readyPromise = new Promise(r => { ready = r }) 22 | const update = () => { 23 | if (bundle && clientManifest) { 24 | ready() 25 | cb(bundle, { 26 | template, 27 | clientManifest 28 | }) 29 | } 30 | } 31 | 32 | // read template from disk and watch 33 | template = fs.readFileSync(templatePath, 'utf-8') 34 | chokidar.watch(templatePath).on('change', () => { 35 | template = fs.readFileSync(templatePath, 'utf-8') 36 | console.log('index.html template updated.') 37 | update() 38 | }) 39 | 40 | // modify client config to work with hot middleware 41 | clientConfig.entry.app = ['webpack-hot-middleware/client', clientConfig.entry.app] 42 | clientConfig.output.filename = '[name].js' 43 | clientConfig.plugins.push( 44 | new webpack.HotModuleReplacementPlugin(), 45 | // new webpack.NoEmitOnErrorsPlugin() 46 | ) 47 | 48 | // dev middleware 49 | const clientCompiler = webpack(clientConfig) 50 | const devMiddleware = require('webpack-dev-middleware')(clientCompiler, { 51 | publicPath: clientConfig.output.publicPath, 52 | noInfo: true 53 | }) 54 | app.use(devMiddleware) 55 | clientCompiler.plugin('done', stats => { 56 | stats = stats.toJson() 57 | stats.errors.forEach(err => console.error(err)) 58 | stats.warnings.forEach(err => console.warn(err)) 59 | if (stats.errors.length) return 60 | clientManifest = JSON.parse(readFile( 61 | devMiddleware.fileSystem, 62 | 'vue-ssr-client-manifest.json' 63 | )) 64 | update() 65 | }) 66 | 67 | // hot middleware 68 | app.use(require('webpack-hot-middleware')(clientCompiler, { heartbeat: 5000 })) 69 | 70 | // watch and update server renderer 71 | const serverCompiler = webpack(serverConfig) 72 | const mfs = new MFS() 73 | serverCompiler.outputFileSystem = mfs 74 | serverCompiler.watch({}, (err, stats) => { 75 | if (err) throw err 76 | stats = stats.toJson() 77 | if (stats.errors.length) return 78 | 79 | // read bundle generated by vue-ssr-webpack-plugin 80 | bundle = JSON.parse(readFile(mfs, 'vue-ssr-server-bundle.json')) 81 | update() 82 | }) 83 | 84 | return readyPromise 85 | } 86 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | var path = require('path') 2 | var config = require('../config') 3 | var ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | 5 | exports.assetsPath = function (_path) { 6 | var assetsSubDirectory = process.env.NODE_ENV === 'production' 7 | ? config.build.assetsSubDirectory 8 | : config.dev.assetsSubDirectory 9 | return path.posix.join(assetsSubDirectory, _path) 10 | } 11 | 12 | exports.cssLoaders = function (options) { 13 | options = options || {} 14 | 15 | var cssLoader = { 16 | loader: 'css-loader', 17 | options: { 18 | minimize: process.env.NODE_ENV === 'production', 19 | sourceMap: options.sourceMap 20 | } 21 | } 22 | 23 | // generate loader string to be used with extract text plugin 24 | function generateLoaders (loader, loaderOptions) { 25 | var loaders = [cssLoader] 26 | if (loader) { 27 | loaders.push({ 28 | loader: loader + '-loader', 29 | options: Object.assign({}, loaderOptions, { 30 | sourceMap: options.sourceMap 31 | }) 32 | }) 33 | } 34 | 35 | // Extract CSS when that option is specified 36 | // (which is the case during production build) 37 | if (options.extract) { 38 | return ExtractTextPlugin.extract({ 39 | use: loaders, 40 | fallback: 'vue-style-loader' 41 | }) 42 | } else { 43 | return ['vue-style-loader'].concat(loaders) 44 | } 45 | } 46 | 47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 48 | return { 49 | css: generateLoaders(), 50 | postcss: generateLoaders(), 51 | less: generateLoaders('less'), 52 | sass: generateLoaders('sass', { indentedSyntax: true }), 53 | scss: generateLoaders('sass'), 54 | stylus: generateLoaders('stylus'), 55 | styl: generateLoaders('stylus') 56 | } 57 | } 58 | 59 | // Generate loaders for standalone style files (outside of .vue) 60 | exports.styleLoaders = function (options) { 61 | var output = [] 62 | var loaders = exports.cssLoaders(options) 63 | for (var extension in loaders) { 64 | var loader = loaders[extension] 65 | if (extension === 'css') { 66 | loader.push('postcss-loader') 67 | } 68 | output.push({ 69 | test: new RegExp('\\.' + extension + '$'), 70 | use: loader 71 | }) 72 | } 73 | return output 74 | } 75 | -------------------------------------------------------------------------------- /build/vue-loader.config.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils') 2 | var config = require('../config') 3 | var isProduction = process.env.NODE_ENV === 'production' 4 | 5 | module.exports = { 6 | loaders: utils.cssLoaders({ 7 | sourceMap: isProduction 8 | ? config.build.productionSourceMap 9 | : config.dev.cssSourceMap, 10 | extract: isProduction 11 | }), 12 | transformToRequire: { 13 | video: 'src', 14 | source: 'src', 15 | img: 'src', 16 | image: 'xlink:href' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build/webpack.base.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const vueConfig = require('./vue-loader.config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 6 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin') 7 | const utils = require('./utils') 8 | 9 | const isProd = process.env.NODE_ENV === 'production' 10 | 11 | module.exports = { 12 | devtool: isProd 13 | ? false 14 | : '#cheap-module-source-map', 15 | output: { 16 | path: path.resolve(__dirname, '../dist'), 17 | publicPath: '/dist/', 18 | filename: '[name].[chunkhash].js' 19 | }, 20 | resolve: { 21 | extensions: ['.js', '.vue', '.json'], 22 | alias: { 23 | 'public': path.resolve(__dirname, '../public'), 24 | '@': path.resolve('src') 25 | } 26 | }, 27 | module: { 28 | noParse: /es6-promise\.js$/, // avoid webpack shimming process 29 | rules: [ 30 | { 31 | test: /\.vue$/, 32 | loader: 'vue-loader', 33 | options: vueConfig 34 | }, 35 | { 36 | test: /\.js$/, 37 | loader: 'babel-loader', 38 | exclude: /node_modules/ 39 | }, 40 | { 41 | test: /\.(png|jpg|gif|svg)$/, 42 | loader: 'url-loader', 43 | options: { 44 | limit: 10000, 45 | name: '[name].[ext]?[hash]' 46 | } 47 | }, 48 | { 49 | test: /\.css$/, 50 | use: isProd 51 | ? ExtractTextPlugin.extract({ 52 | use: ['css-loader?minimize', 'postcss-loader'], 53 | fallback: 'vue-style-loader' 54 | }) 55 | : ['vue-style-loader', 'css-loader', 'postcss-loader'] 56 | }, 57 | { 58 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 59 | loader: 'url-loader', 60 | options: { 61 | limit: 10000, 62 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 63 | } 64 | } 65 | ] 66 | }, 67 | performance: { 68 | maxEntrypointSize: 300000, 69 | hints: isProd ? 'warning' : false 70 | }, 71 | plugins: isProd 72 | ? [ 73 | // new webpack.optimize.UglifyJsPlugin({ 74 | // compress: { warnings: false } 75 | // }), 76 | // new webpack.optimize.AggressiveSplittingPlugin({ 77 | // minSize: 30000, 78 | // maxSize: 50000, 79 | // chunkOverhead: 0, 80 | // entryChunkMultiplicator: 2 81 | // }), 82 | new UglifyJSPlugin(), 83 | new webpack.optimize.ModuleConcatenationPlugin(), 84 | new ExtractTextPlugin({ 85 | filename: 'common.[chunkhash].css', 86 | allChunks: true 87 | }) 88 | ] 89 | : [ 90 | new FriendlyErrorsPlugin() 91 | ] 92 | } -------------------------------------------------------------------------------- /build/webpack.client.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const base = require('./webpack.base.config') 4 | const SWPrecachePlugin = require('sw-precache-webpack-plugin') 5 | const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') 6 | 7 | const config = merge(base, { 8 | entry: { 9 | app: './src/entry-client.js' 10 | }, 11 | resolve: { 12 | alias: { 13 | 'create-api': './create-api-client.js' 14 | } 15 | }, 16 | plugins: [ 17 | // strip dev-only code in Vue source 18 | new webpack.DefinePlugin({ 19 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 20 | 'process.env.VUE_ENV': '"client"' 21 | }), 22 | // extract vendor chunks for better caching 23 | new webpack.optimize.CommonsChunkPlugin({ 24 | name: 'vendor', 25 | minChunks: function (module) { 26 | // a module is extracted into the vendor chunk if... 27 | return ( 28 | // it's inside node_modules 29 | /node_modules/.test(module.context) && 30 | // and not a CSS file (due to extract-text-webpack-plugin limitation) 31 | !/\.css$/.test(module.request) 32 | ) 33 | } 34 | }), 35 | // extract webpack runtime & manifest to avoid vendor chunk hash changing 36 | // on every build. 37 | new webpack.optimize.CommonsChunkPlugin({ 38 | name: 'manifest' 39 | }), 40 | new VueSSRClientPlugin() 41 | ] 42 | }) 43 | 44 | if (process.env.NODE_ENV === 'production') { 45 | //TODO: 46 | config.plugins.push( 47 | // auto generate service worker 48 | new SWPrecachePlugin({ 49 | cacheId: 'vue-hn', 50 | filename: 'service-worker.js', 51 | minify: true, 52 | dontCacheBustUrlsMatching: /./, 53 | staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/], 54 | runtimeCaching: [ 55 | { 56 | urlPattern: '/', 57 | handler: 'networkFirst' 58 | }, 59 | { 60 | urlPattern: /\/(top|new|show|ask|jobs)/, 61 | handler: 'networkFirst' 62 | }, 63 | { 64 | urlPattern: '/item/:id', 65 | handler: 'networkFirst' 66 | }, 67 | { 68 | urlPattern: '/user/:id', 69 | handler: 'networkFirst' 70 | } 71 | ] 72 | }) 73 | ) 74 | } 75 | 76 | module.exports = config -------------------------------------------------------------------------------- /build/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const merge = require('webpack-merge') 3 | const base = require('./webpack.base.config') 4 | const nodeExternals = require('webpack-node-externals') 5 | const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') 6 | 7 | module.exports = merge(base, { 8 | target: 'node', 9 | devtool: '#source-map', 10 | entry: './src/entry-server.js', 11 | output: { 12 | filename: 'server-bundle.js', 13 | libraryTarget: 'commonjs2' 14 | }, 15 | resolve: { 16 | alias: { 17 | 'create-api': './create-api-server.js' 18 | } 19 | }, 20 | // https://webpack.js.org/configuration/externals/#externals 21 | // https://github.com/liady/webpack-node-externals 22 | externals: nodeExternals({ 23 | // do not externalize CSS files in case we need to import it from a dep 24 | whitelist: [/\.css$/, /\.vue$/] 25 | }), 26 | plugins: [ 27 | new webpack.DefinePlugin({ 28 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 29 | 'process.env.VUE_ENV': '"server"' 30 | }), 31 | new VueSSRServerPlugin() 32 | ] 33 | }) -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carbon", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "node server", 7 | "start": "cross-env NODE_ENV=production node server", 8 | "build": "rimraf dist && npm run build:client && npm run build:server", 9 | "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", 10 | "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.16.2", 14 | "compression": "^1.7.1", 15 | "element-ui": "^1.4.7", 16 | "iview": "^2.4.0", 17 | "route-cache": "^0.4.3", 18 | "serve-favicon": "^2.4.5", 19 | "sw-precache-webpack-plugin": "^0.11.4", 20 | "vue": "^2.5.2", 21 | "vue-router": "^3.0.1", 22 | "vue-server-renderer": "^2.5.2", 23 | "vuex": "^3.0.0", 24 | "vuex-router-sync": "^5.0.0" 25 | }, 26 | "devDependencies": { 27 | "autoprefixer": "^7.1.2", 28 | "axios-mock-adapter": "^1.9.0", 29 | "babel-core": "^6.22.1", 30 | "babel-eslint": "^7.1.1", 31 | "babel-loader": "^7.1.1", 32 | "babel-plugin-istanbul": "^4.1.1", 33 | "babel-plugin-transform-runtime": "^6.22.0", 34 | "babel-preset-env": "^1.3.2", 35 | "babel-preset-stage-2": "^6.22.0", 36 | "babel-register": "^6.22.0", 37 | "chai": "^3.5.0", 38 | "chalk": "^2.0.1", 39 | "chromedriver": "^2.33.1", 40 | "connect-history-api-fallback": "^1.3.0", 41 | "copy-webpack-plugin": "^4.0.1", 42 | "cross-env": "^5.0.1", 43 | "cross-spawn": "^5.0.1", 44 | "css-loader": "^0.28.0", 45 | "cssnano": "^3.10.0", 46 | "eslint": "^3.19.0", 47 | "eslint-config-standard": "^6.2.1", 48 | "eslint-friendly-formatter": "^3.0.0", 49 | "eslint-loader": "^1.7.1", 50 | "eslint-plugin-html": "^3.0.0", 51 | "eslint-plugin-promise": "^3.4.0", 52 | "eslint-plugin-standard": "^2.0.1", 53 | "eventsource-polyfill": "^0.9.6", 54 | "express": "^4.16.2", 55 | "extract-text-webpack-plugin": "^3.0.1", 56 | "file-loader": "^0.11.1", 57 | "friendly-errors-webpack-plugin": "^1.1.3", 58 | "html-webpack-plugin": "^2.28.0", 59 | "http-proxy-middleware": "^0.17.3", 60 | "inject-loader": "^3.0.0", 61 | "karma": "^1.4.1", 62 | "karma-coverage": "^1.1.1", 63 | "karma-mocha": "^1.3.0", 64 | "karma-phantomjs-launcher": "^1.0.2", 65 | "karma-phantomjs-shim": "^1.4.0", 66 | "karma-sinon-chai": "^1.3.1", 67 | "karma-sourcemap-loader": "^0.3.7", 68 | "karma-spec-reporter": "0.0.31", 69 | "karma-webpack": "^2.0.2", 70 | "mocha": "^3.2.0", 71 | "mockjs": "^1.0.1-beta3", 72 | "nightwatch": "^0.9.12", 73 | "opn": "^5.1.0", 74 | "optimize-css-assets-webpack-plugin": "^3.2.0", 75 | "ora": "^1.2.0", 76 | "phantomjs-prebuilt": "^2.1.14", 77 | "postcss-cssnext": "^3.0.2", 78 | "postcss-for": "^2.1.1", 79 | "postcss-import": "^11.0.0", 80 | "postcss-loader": "^2.0.6", 81 | "postcss-remove-root": "0.0.2", 82 | "rimraf": "^2.6.0", 83 | "selenium-server": "^3.0.1", 84 | "semver": "^5.3.0", 85 | "shelljs": "^0.7.6", 86 | "sinon": "^2.1.0", 87 | "sinon-chai": "^2.8.0", 88 | "uglifyjs-webpack-plugin": "^1.0.0-beta.3", 89 | "url-loader": "^0.5.8", 90 | "vue-loader": "^13.0.4", 91 | "vue-style-loader": "^3.0.1", 92 | "vue-template-compiler": "^2.5.2", 93 | "webpack": "^3.8.1", 94 | "webpack-bundle-analyzer": "^2.9.0", 95 | "webpack-dev-middleware": "^1.12.0", 96 | "webpack-hot-middleware": "^2.20.0", 97 | "webpack-merge": "^4.1.0", 98 | "webpack-node-externals": "^1.6.0" 99 | }, 100 | "engines": { 101 | "node": ">= 4.0.0", 102 | "npm": ">= 3.0.0" 103 | }, 104 | "browserslist": [ 105 | "> 1%", 106 | "last 2 versions", 107 | "not ie <= 8" 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /public/logo-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/public/logo-48.png -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const LRU = require('lru-cache') 4 | const express = require('express') 5 | const favicon = require('serve-favicon') 6 | const compression = require('compression') 7 | const microcache = require('route-cache') 8 | const resolve = file => path.resolve(__dirname, file) 9 | const { createBundleRenderer } = require('vue-server-renderer') 10 | 11 | const isProd = process.env.NODE_ENV === 'production' 12 | const useMicroCache = process.env.MICRO_CACHE !== 'false' 13 | const serverInfo = 14 | `express/${require('express/package.json').version} ` + 15 | `vue-server-renderer/${require('vue-server-renderer/package.json').version}` 16 | 17 | const app = express() 18 | 19 | function createRenderer (bundle, options) { 20 | // https://github.com/vuejs/vue/blob/dev/packages/vue-server-renderer/README.md#why-use-bundlerenderer 21 | return createBundleRenderer(bundle, Object.assign(options, { 22 | // for component caching 23 | cache: LRU({ 24 | max: 1000, 25 | maxAge: 1000 * 60 * 15 26 | }), 27 | // this is only needed when vue-server-renderer is npm-linked 28 | basedir: resolve('./dist'), 29 | // recommended for performance 30 | runInNewContext: false 31 | })) 32 | } 33 | 34 | let renderer 35 | let readyPromise 36 | const templatePath = resolve('./src/index.template.html') 37 | if (isProd) { 38 | // In production: create server renderer using template and built server bundle. 39 | // The server bundle is generated by vue-ssr-webpack-plugin. 40 | const template = fs.readFileSync(templatePath, 'utf-8') 41 | const bundle = require('./dist/vue-ssr-server-bundle.json') 42 | // The client manifests are optional, but it allows the renderer 43 | // to automatically infer preload/prefetch links and directly add 92 | 93 | 226 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import ElementUI from 'element-ui' 4 | import 'element-ui/lib/theme-default/index.css' 5 | import { sync } from 'vuex-router-sync' 6 | import titleMixin from './util/title' 7 | import './assets/css/index.css' 8 | import './assets/iconfont/iconfont.css' 9 | import { createStore } from './store' 10 | import { createRouter } from './router' 11 | 12 | Vue.use(ElementUI) 13 | 14 | Vue.directive('number', { 15 | bind (el, binding, vnode) { 16 | let key = binding.expression 17 | let num = parseInt(binding.arg) > 0 ? parseInt(binding.arg) : 0 18 | let value = el.value 19 | let flag = true 20 | el.addEventListener('input', () => { 21 | let nowValue = el.value 22 | if (nowValue !== '') { 23 | if (num === 0) { 24 | if (nowValue.search(/^[0-9]+$/) === -1 || nowValue === '') { 25 | el.value = value 26 | } 27 | } else { 28 | let re = new RegExp('^[0-9]+(.[0-9]{0,' + num + '})?$') 29 | if (nowValue.search(re) === -1) { 30 | el.value = value 31 | } 32 | } 33 | } 34 | value = el.value 35 | flag = false 36 | vnode.context[key] = el.value 37 | }) 38 | 39 | vnode.context.$watch(key, (val, oldVal) => { 40 | if (!flag) { 41 | flag = true 42 | return false 43 | } 44 | if (!Number.isNaN(val)) { 45 | if (num === 0) { 46 | el.value = parseInt(val) 47 | } else { 48 | el.value = parseFloat(val).toFixed(2) 49 | } 50 | } 51 | }) 52 | } 53 | }) 54 | 55 | // mixin for handling title 56 | Vue.mixin(titleMixin) 57 | 58 | // Expose a factory function that creates a fresh set of store, router, 59 | // app instances on each call (which is called for each SSR request) 60 | export function createApp () { 61 | // create store and router instances 62 | const store = createStore() 63 | const router = createRouter() 64 | 65 | // sync the router with the vuex store. 66 | // this registers `store.state.route` 67 | sync(store, router) 68 | 69 | // create the app instance. 70 | // here we inject the router, store and ssr context to all child components, 71 | // making them available everywhere as `this.$router` and `this.$store`. 72 | const app = new Vue({ 73 | router, 74 | store, 75 | render: h => h(App) 76 | }) 77 | 78 | // expose the app, the router and the store. 79 | // note we are not mounting the app here, since bootstrapping will be 80 | // different depending on whether we are in a browser or on the server. 81 | return { app, router, store } 82 | } 83 | -------------------------------------------------------------------------------- /src/assets/css/index.css: -------------------------------------------------------------------------------- 1 | @import './var.css'; 2 | @import './reset.css'; 3 | @import './marginPadding.css'; 4 | @import './project.css'; 5 | 6 | html{ 7 | overflow-x: hidden; 8 | } 9 | 10 | body{ 11 | min-width: var(--contentWidth); 12 | font-size: 14px; 13 | margin-right: calc(100% - 100vw); 14 | font-family: '微软雅黑'; 15 | } 16 | 17 | .container{ 18 | width: var(--contentWidth); 19 | margin: 0 auto; 20 | } 21 | 22 | a{ 23 | text-decoration: none; 24 | } 25 | 26 | ::-webkit-scrollbar { 27 | width: 6px; 28 | } 29 | 30 | ::-webkit-scrollbar-thumb { 31 | background-color: #6e6e6e; 32 | outline: 1px solid #333; 33 | } 34 | 35 | .overflow{ 36 | // overflow: hidden; 37 | &:after{ 38 | clear: both; 39 | display: block; 40 | content: '' 41 | } 42 | } 43 | 44 | .float-left{ 45 | float: left; 46 | } 47 | 48 | .float-right{ 49 | float: right; 50 | } 51 | 52 | .cursor-pointer{ 53 | cursor: pointer; 54 | } 55 | 56 | .bold{ 57 | font-weight: bold; 58 | } 59 | 60 | .relative{ 61 | position: relative; 62 | } 63 | 64 | .inline-block{ 65 | display: inline-block; 66 | } 67 | 68 | .ivu-loading-bar { 69 | width: 100%; 70 | position: fixed; 71 | top: 0; 72 | left: 0; 73 | right: 0; 74 | z-index: 2000; 75 | } 76 | 77 | .ivu-loading-bar-inner { 78 | transition: width .2s linear; 79 | } 80 | 81 | .ivu-loading-bar-inner-color-primary { 82 | background-color: #2d8cf0; 83 | } 84 | 85 | .ivu-loading-bar-inner-failed-color-error { 86 | background-color: #ed3f14; 87 | } 88 | -------------------------------------------------------------------------------- /src/assets/css/marginPadding.css: -------------------------------------------------------------------------------- 1 | @for $i from 10 to 60 by 5{ 2 | .mt-$i { margin-top: $(i)px; } 3 | .ml-$i { margin-left: $(i)px; } 4 | .mb-$i { margin-bottom: $(i)px; } 5 | .mr-$i { margin-right: $(i)px; } 6 | } -------------------------------------------------------------------------------- /src/assets/css/project.css: -------------------------------------------------------------------------------- 1 | .el-table, 2 | /* .el-table th, 3 | .el-table th>.cell, */ 4 | .el-table td, 5 | .el-table th.is-leaf, 6 | .el-table::after, 7 | .el-table::before{ 8 | background-color: #fff; 9 | border: none; 10 | } 11 | 12 | .el-table th>.cell{ 13 | padding-top: 20px; 14 | padding-bottom: 20px; 15 | } 16 | 17 | .el-table td>.cell{ 18 | white-space: nowrap; 19 | overflow: hidden; 20 | text-overflow: ellipsis; 21 | } 22 | 23 | .el-table td{ 24 | padding-bottom: 10px; 25 | padding-top: 10px; 26 | border-bottom: 1px solid #eee; 27 | } 28 | 29 | .el-table--enable-row-hover .el-table__body tr:hover>td{ 30 | /* background-color: rgb(251,251,252); */ 31 | background-color: #f5f5f5; 32 | } 33 | 34 | .el-menu{ 35 | background-color: #fff; 36 | } 37 | 38 | .el-button{ 39 | min-width: 65px; 40 | } 41 | 42 | .el-button--success{ 43 | background-color: var(--mainColor); 44 | color: #fff; 45 | border: none; 46 | &:focus, 47 | &:hover{ 48 | background-color: #3BD89C; 49 | color: #fff; 50 | } 51 | &:active{ 52 | background-color: #1CB57A; 53 | color: #fff; 54 | } 55 | } 56 | 57 | .el-button--danger{ 58 | background-color: var(--redColor); 59 | color: #fff; 60 | &:focus, 61 | &:hover{ 62 | background-color: #F94553; 63 | color: #fff; 64 | } 65 | &:active{ 66 | background-color: #DD1423; 67 | color: #fff; 68 | } 69 | } 70 | 71 | .el-button--success.is-plain.is-hover { 72 | background: #fff; 73 | border-color: #13ce66; 74 | color: #13ce66; 75 | } 76 | 77 | .el-button.btn-block{ 78 | display: block; 79 | width: 100%; 80 | } 81 | 82 | .el-dialog__header{ 83 | padding: 23px 32px 16px; 84 | background-color: #f9f9f9; 85 | & .el-dialog__close{ 86 | color: #333; 87 | font-size: 12px; 88 | } 89 | } 90 | 91 | .el-pagination{ 92 | text-align: right; 93 | margin-top: 22px; 94 | } 95 | 96 | .el-pagination__total, 97 | .el-pagination__sizes{ 98 | float: left; 99 | } 100 | 101 | .el-form-item__label:before{ 102 | display: none; 103 | } 104 | 105 | .el-form{ 106 | & .el-input, 107 | & .el-select, 108 | & .el-input-number{ 109 | width: 260px; 110 | } 111 | } 112 | 113 | .code-input{ 114 | width: 164px !important; 115 | & .el-input__inner{ 116 | border-top-right-radius: 0; 117 | border-bottom-right-radius: 0; 118 | border-right: none; 119 | } 120 | } 121 | 122 | #app .send-code{ 123 | width: 97px; 124 | font-size: 12px; 125 | height: 36px; 126 | border-top-left-radius: 0; 127 | border-bottom-left-radius: 0; 128 | vertical-align: middle; 129 | } 130 | 131 | .bank-select{ 132 | & .el-select-dropdown__list{ 133 | padding: 0; 134 | } 135 | & .el-select-dropdown__item{ 136 | height: auto; 137 | } 138 | & .el-scrollbar__wrap{ 139 | overflow: auto; 140 | } 141 | } 142 | 143 | html, 144 | body, 145 | #app{ 146 | height: 100%; 147 | } 148 | 149 | body{ 150 | background-color: #f2f2f2; 151 | padding-right: 0 !important; 152 | } 153 | 154 | .title1{ 155 | font-size: 18px; 156 | margin-bottom: 20px; 157 | border-bottom: 2px solid rgb(238,238,238); 158 | &.normal{ 159 | font-size: 16px; 160 | } 161 | & .right{ 162 | float: right; 163 | font-size: 14px; 164 | color: #999; 165 | font-weight: normal; 166 | margin-top: 11px; 167 | } 168 | &>span{ 169 | display: inline-block; 170 | padding: 11px 0; 171 | border-bottom: 2px solid var(--mainColor); 172 | margin-bottom: -2px; 173 | } 174 | } 175 | 176 | .title2{ 177 | font-size: 16px; 178 | font-weight: bold; 179 | padding: 23px 0 20px; 180 | border-bottom: 1px solid #ddd; 181 | & .right{ 182 | float: right; 183 | } 184 | &.normal{ 185 | font-weight: normal; 186 | } 187 | } 188 | 189 | .title3{ 190 | border-bottom: 1px solid #ddd; 191 | font-size: 16px; 192 | padding-bottom: 10px; 193 | &>span:first-child{ 194 | border-left: 2px solid var(--mainColor); 195 | padding-left: 8px; 196 | font-weight: bold; 197 | } 198 | } 199 | 200 | .title4{ 201 | font-size: 18px; 202 | padding-left: 10px; 203 | border-left: 2px solid var(--mainColor); 204 | } 205 | 206 | .content{ 207 | background-color: #f0f2f5; 208 | min-height: calc(100vh - 386px); 209 | overflow: hidden; 210 | } 211 | 212 | .tab1{ 213 | overflow: visible; 214 | background-color: #f9f9f9; 215 | border-bottom: 1px solid #e1e1e1; 216 | border-top: 1px solid #e1e1e1; 217 | & .tab-item{ 218 | cursor: pointer; 219 | display: inline-block; 220 | vertical-align: top; 221 | padding: 17px 24px; 222 | color: #333; 223 | font-size: 16px; 224 | border: 1px solid transparent; 225 | border-bottom: none; 226 | border-top-width: 3px; 227 | &.active{ 228 | font-weight: bold; 229 | border-top-color: var(--mainColor); 230 | border-left-color: #e1e1e1; 231 | border-right-color: #e1e1e1; 232 | background-color: #fff; 233 | position: relative; 234 | &:before{ 235 | content: ''; 236 | position: absolute; 237 | bottom: -1px; 238 | left: 0; 239 | width: 100%; 240 | height: 1px; 241 | border-bottom: 1px solid #fff; 242 | } 243 | } 244 | } 245 | } 246 | 247 | .tab2{ 248 | margin-top: 22px; 249 | overflow: visible; 250 | border-bottom: 1px solid #ddd; 251 | & .tab-item{ 252 | color: #333; 253 | cursor: pointer; 254 | display: inline-block; 255 | vertical-align: top; 256 | padding: 17px 24px; 257 | font-size: 16px; 258 | border: 1px solid transparent; 259 | border-bottom: none; 260 | border-top-width: 3px; 261 | &.active{ 262 | font-weight: bold; 263 | border-top-color: var(--mainColor); 264 | color: var(--mainColor); 265 | border-left-color: #ddd; 266 | border-right-color: #ddd; 267 | background-color: #fff; 268 | position: relative; 269 | &:before{ 270 | content: ''; 271 | position: absolute; 272 | bottom: -1px; 273 | left: 0; 274 | width: 100%; 275 | height: 1px; 276 | border-bottom: 1px solid #fff; 277 | } 278 | } 279 | } 280 | } 281 | 282 | .list-table{ 283 | margin-top: 38px; 284 | margin-bottom: 31px; 285 | width: 100%; 286 | & th{ 287 | color: #999; 288 | font-size: 12px; 289 | text-align: left; 290 | padding-bottom: 15px; 291 | } 292 | & td{ 293 | &:first-child{ 294 | color: var(--mainColor); 295 | } 296 | font-size: 20px; 297 | color: #1d1d1d; 298 | } 299 | } 300 | 301 | .search-table{ 302 | border-bottom: 1px dotted #ddd; 303 | & th>.cell{ 304 | background-color: #f9f9f9; 305 | line-height: 1; 306 | padding: 16px 18px 15px; 307 | border-bottom: 1px solid #ddd; 308 | } 309 | & td{ 310 | border-bottom: 1px dotted #ddd; 311 | } 312 | } 313 | 314 | .filtrate{ 315 | margin-top: 19px; 316 | margin-bottom: 10px; 317 | & .filtrate-item{ 318 | margin-bottom: 15px; 319 | } 320 | & .item{ 321 | display: inline-block; 322 | padding: 6px 14px; 323 | cursor: pointer; 324 | margin-right: 20px; 325 | &.active{ 326 | background-color: var(--mainColor); 327 | border-radius: 2px; 328 | color: #fff; 329 | } 330 | } 331 | } 332 | 333 | .btn{ 334 | background: #20a0ff; 335 | font-size: 14px; 336 | height: 36px; 337 | line-height: 36px; 338 | color: #fff; 339 | cursor: pointer; 340 | border-radius: 4px; 341 | &:hover{ 342 | background-color: #4db3ff; 343 | } 344 | } 345 | 346 | .form-btn-block{ 347 | width: 260px; 348 | } 349 | 350 | .btn-grey{ 351 | background-color: #ddd; 352 | color: #999; 353 | border: none; 354 | &:hover{ 355 | color: #fff; 356 | } 357 | } 358 | 359 | .btn-dark{ 360 | background-color: #4D5C6F; 361 | color: #fff; 362 | border: none; 363 | &:hover{ 364 | color: #fff; 365 | } 366 | } 367 | 368 | .btn-orange{ 369 | background-color: #ff7200; 370 | color: #fff; 371 | border: none; 372 | &:hover{ 373 | color: #fff; 374 | } 375 | } 376 | 377 | .icon-zhongguoyinhang, 378 | .icon-zhongguoyouzhengchuxuyinhang, 379 | .icon-nongyeyinhang, 380 | .icon-jiansheyinhang, 381 | .icon-jiaotongyinhang, 382 | .icon-gongshangyinhang{ 383 | &.iconfont{ 384 | font-size: 22px; 385 | margin-left: 14px; 386 | margin-right: 8px; 387 | vertical-align: middle; 388 | } 389 | } 390 | 391 | .icon-zhongguoyinhang{ 392 | color: rgb(151, 32, 48); 393 | } 394 | 395 | .icon-zhongguoyouzhengchuxuyinhang{ 396 | color: rgb(0, 126, 62); 397 | } 398 | 399 | .icon-nongyeyinhang{ 400 | color: rgb(0, 133, 102); 401 | } 402 | 403 | .icon-jiansheyinhang{ 404 | color: rgb(5, 61, 143); 405 | } 406 | 407 | .icon-jiaotongyinhang{ 408 | color: rgb(29, 32, 135); 409 | } 410 | 411 | .icon-gongshangyinhang{ 412 | color: rgb(210, 0, 0); 413 | } 414 | 415 | .form-box1{ 416 | width: 400px; 417 | margin: 0 auto; 418 | & .head{ 419 | font-size: 20px; 420 | font-weight: bold; 421 | text-align: center; 422 | padding-top: 50px; 423 | margin-bottom: 45px; 424 | } 425 | & .el-form-item{ 426 | position: relative; 427 | } 428 | & .form{ 429 | & .el-form-item__label{ 430 | position: absolute; 431 | z-index: 1; 432 | padding-left: 17px; 433 | height: 47px; 434 | line-height: 47px; 435 | padding-top: 0; 436 | padding-bottom: 0; 437 | } 438 | & .el-input, 439 | & .el-select{ 440 | width: 100%; 441 | &.form-code-input{ 442 | width: 280px; 443 | } 444 | } 445 | & .el-input__inner{ 446 | padding-left: 117px; 447 | height: 47px; 448 | } 449 | & .el-input__inner::placeholder, 450 | & .el-input__inner::-webkit-input-placeholder{ 451 | color: #cfcfcf; 452 | } 453 | } 454 | & .send-code-btn{ 455 | display: inline-block; 456 | height: 47px; 457 | line-height: 47px; 458 | width: 110px; 459 | border-radius: 4px; 460 | color: #666; 461 | background-color: #f8f9fb; 462 | border: 1px solid #ddd; 463 | vertical-align: middle; 464 | text-align: center; 465 | margin-left: 10px; 466 | cursor: pointer; 467 | } 468 | } 469 | 470 | .breadcrumb{ 471 | & a{ 472 | color: #1d1d1d; 473 | font-weight: normal; 474 | } 475 | & .angle{ 476 | display: inline-block; 477 | margin: 0 10px; 478 | font-weight: normal; 479 | } 480 | } -------------------------------------------------------------------------------- /src/assets/css/projectVar.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* css next */ 3 | --mainColor: #29c086; 4 | --redColor: #F52F3E; 5 | --headerTopHeight: 36px; 6 | --headerTopBgColor: #4a4a4a; 7 | --contentWidth: 1180px; 8 | --contentHorizontalPadding: 30px; 9 | --contentVerticalPadding: 39px; 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | abbr, address, cite, code, 4 | del, dfn, em, img, ins, kbd, q, samp, 5 | small, strong, sub, sup, var, 6 | b, i, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, figcaption, figure, 11 | footer, header, hgroup, menu, nav, section, summary, 12 | time, mark, audio, video { 13 | margin:0; 14 | padding:0; 15 | border:0; 16 | outline:0; 17 | font-size:100%; 18 | vertical-align:baseline; 19 | background:transparent; 20 | box-sizing: border-box; 21 | } 22 | 23 | body { 24 | line-height:1; 25 | } 26 | 27 | :focus { 28 | outline: 1; 29 | } 30 | 31 | article,aside,canvas,details,figcaption,figure, 32 | footer,header,hgroup,menu,nav,section,summary { 33 | display:block; 34 | } 35 | 36 | nav ul { 37 | list-style:none; 38 | } 39 | 40 | blockquote, q { 41 | quotes:none; 42 | } 43 | 44 | blockquote:before, blockquote:after, 45 | q:before, q:after { 46 | content:''; 47 | content:none; 48 | } 49 | 50 | a { 51 | margin:0; 52 | padding:0; 53 | border:0; 54 | font-size:100%; 55 | vertical-align:baseline; 56 | background:transparent; 57 | } 58 | 59 | ins { 60 | background-color:#ff9; 61 | color:#000; 62 | text-decoration:none; 63 | } 64 | 65 | mark { 66 | background-color:#ff9; 67 | color:#000; 68 | font-style:italic; 69 | font-weight:bold; 70 | } 71 | 72 | del { 73 | text-decoration: line-through; 74 | } 75 | 76 | abbr[title], dfn[title] { 77 | border-bottom:1px dotted #000; 78 | cursor:help; 79 | } 80 | 81 | table { 82 | border-collapse:collapse; 83 | border-spacing:0; 84 | } 85 | 86 | hr { 87 | display:block; 88 | height:1px; 89 | border:0; 90 | border-top:1px solid #cccccc; 91 | margin:1em 0; 92 | padding:0; 93 | } 94 | 95 | input, select { 96 | vertical-align:middle; 97 | box-sizing: border-box; 98 | outline: none; 99 | } -------------------------------------------------------------------------------- /src/assets/css/var.css: -------------------------------------------------------------------------------- 1 | @import './projectVar.css'; 2 | 3 | :root { 4 | 5 | } -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | 2 | @font-face {font-family: "iconfont"; 3 | src: url('iconfont.eot?t=1508481610089'); /* IE9*/ 4 | src: url('iconfont.eot?t=1508481610089#iefix') format('embedded-opentype'), /* IE6-IE8 */ 5 | url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'), 6 | url('iconfont.ttf?t=1508481610089') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ 7 | url('iconfont.svg?t=1508481610089#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family:"iconfont" !important; 12 | font-size:16px; 13 | font-style:normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-jiahao:before { content: "\ead8"; } 19 | 20 | .icon-zhengquetianchong:before { content: "\e6e3"; } 21 | 22 | .icon-dengyu:before { content: "\e601"; } 23 | 24 | .icon-cheng:before { content: "\e612"; } 25 | 26 | .icon-jiaotongyinhang:before { content: "\e67f"; } 27 | 28 | .icon-gongshangyinhang:before { content: "\e640"; } 29 | 30 | .icon-jiansheyinhang:before { content: "\e602"; } 31 | 32 | .icon-copy_icon:before { content: "\e659"; } 33 | 34 | .icon-message_icon:before { content: "\e65b"; } 35 | 36 | .icon-nongyeyinhang:before { content: "\e603"; } 37 | 38 | .icon-warning:before { content: "\e600"; } 39 | 40 | .icon-business_license_ico:before { content: "\e6a7"; } 41 | 42 | .icon-salary_icon:before { content: "\e6b9"; } 43 | 44 | .icon-zhongguoyouzhengchuxuyinhang:before { content: "\e84e"; } 45 | 46 | .icon-zhongguoyinhang:before { content: "\e62d"; } 47 | 48 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/images/erweima.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/erweima.jpg -------------------------------------------------------------------------------- /src/assets/images/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/google.png -------------------------------------------------------------------------------- /src/assets/images/left_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/left_bg.jpg -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/logo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/logo_icon.png -------------------------------------------------------------------------------- /src/assets/images/logo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/logo_white.png -------------------------------------------------------------------------------- /src/assets/images/safe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/src/assets/images/safe.png -------------------------------------------------------------------------------- /src/components/BankSelect.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 45 | 46 | 76 | -------------------------------------------------------------------------------- /src/components/DatePicker.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 68 | -------------------------------------------------------------------------------- /src/components/Filtrate.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | 33 | -------------------------------------------------------------------------------- /src/components/SearchTable.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 54 | 55 | 58 | -------------------------------------------------------------------------------- /src/components/Tab.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 24 | -------------------------------------------------------------------------------- /src/config/api.js: -------------------------------------------------------------------------------- 1 | export default { 2 | dealList: '/deal/list', 3 | notices: '/notices', 4 | news: '/news', 5 | login: '/login', 6 | sendNote: '/sendNote', 7 | forgetPwd: '/forgetPwd', 8 | changePwd: '/changePwd', 9 | register: '/register', 10 | capital: '/capital', 11 | capitalList: '/capitalList', 12 | carbon: '/carbon', 13 | carbonList: '/carbonList', 14 | capitalRecharge: '/capitalRecharge', 15 | carbonRecharge: '/carbonRecharge', 16 | capitalWithdraw: '/capitalWithdraw', 17 | carbonWithdraw: '/carbonWithdraw', 18 | saleList: '/saleList', 19 | buyList: '/buyList', 20 | bargainList: '/bargainList', 21 | buy: '/buy', 22 | sell: '/sell', 23 | carousel: '/carousel', 24 | carbonAccount: '/carbonAccount', 25 | capitalAccount: '/capitalAccount', 26 | capitalOrder: '/capitalOrder', 27 | bankCard: '/bankCard', 28 | bank: '/bank' 29 | } 30 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | import api from './api' 2 | 3 | export default { 4 | baseURL: '', 5 | saveDataType: 'query', 6 | api 7 | } 8 | -------------------------------------------------------------------------------- /src/entry-client.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import 'es6-promise/auto' 3 | import { createApp } from './app' 4 | import LoadingBar from 'iview/src/components/loading-bar' 5 | 6 | // a global mixin that calls `asyncData` when a route component's params change 7 | Vue.mixin({ 8 | beforeRouteUpdate (to, from, next) { 9 | const { asyncData } = this.$options 10 | if (asyncData) { 11 | asyncData({ 12 | store: this.$store, 13 | route: to 14 | }).then(next).catch(next) 15 | } else { 16 | next() 17 | } 18 | } 19 | }) 20 | 21 | const { app, router, store } = createApp() 22 | 23 | LoadingBar.config({ 24 | color: '#2b85e4', 25 | height: 3 26 | }) 27 | 28 | // prime the store with server-initialized state. 29 | // the state is determined during SSR and inlined in the page markup. 30 | if (window.__INITIAL_STATE__) { 31 | store.replaceState(window.__INITIAL_STATE__) 32 | } 33 | 34 | // wait until router has resolved all async before hooks 35 | // and async components... 36 | router.onReady(() => { 37 | // Add router hook for handling asyncData. 38 | // Doing it after initial route is resolved so that we don't double-fetch 39 | // the data that we already have. Using router.beforeResolve() so that all 40 | // async components are resolved. 41 | router.beforeResolve((to, from, next) => { 42 | const matched = router.getMatchedComponents(to) 43 | const prevMatched = router.getMatchedComponents(from) 44 | let diffed = false 45 | const activated = matched.filter((c, i) => { 46 | return diffed || (diffed = (prevMatched[i] !== c)) 47 | }) 48 | const asyncDataHooks = activated.map(c => c.asyncData).filter(_ => _) 49 | if (!asyncDataHooks.length) { 50 | return next() 51 | } 52 | 53 | LoadingBar.start() 54 | Promise.all(asyncDataHooks.map(hook => hook({ store, route: to }))) 55 | .then(() => { 56 | LoadingBar.finish() 57 | next() 58 | }) 59 | .catch(next) 60 | }) 61 | 62 | // actually mount to DOM 63 | app.$mount('#app') 64 | }) 65 | 66 | // service worker 67 | if (location.protocol === 'https:' && navigator.serviceWorker) { 68 | navigator.serviceWorker.register('/service-worker.js') 69 | } 70 | -------------------------------------------------------------------------------- /src/entry-server.js: -------------------------------------------------------------------------------- 1 | import { createApp } from './app' 2 | 3 | const isDev = process.env.NODE_ENV !== 'production' 4 | 5 | // This exported function will be called by `bundleRenderer`. 6 | // This is where we perform data-prefetching to determine the 7 | // state of our application before actually rendering it. 8 | // Since data fetching is async, this function is expected to 9 | // return a Promise that resolves to the app instance. 10 | export default context => { 11 | return new Promise((resolve, reject) => { 12 | const s = isDev && Date.now() 13 | const { app, router, store } = createApp() 14 | 15 | const { url } = context 16 | const { fullPath } = router.resolve(url).route 17 | 18 | if (fullPath !== url) { 19 | return reject({ url: fullPath }) 20 | } 21 | 22 | // set router's location 23 | router.push(url) 24 | 25 | // wait until router has resolved possible async hooks 26 | router.onReady(() => { 27 | const matchedComponents = router.getMatchedComponents() 28 | // no matched routes 29 | if (!matchedComponents.length) { 30 | return reject({ code: 404 }) 31 | } 32 | // Call fetchData hooks on components matched by the route. 33 | // A preFetch hook dispatches a store action and returns a Promise, 34 | // which is resolved when the action is complete and store state has been 35 | // updated. 36 | Promise.all(matchedComponents.map(({ asyncData }) => asyncData && asyncData({ 37 | store, 38 | route: router.currentRoute 39 | }))).then(() => { 40 | isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`) 41 | // After all preFetch hooks are resolved, our store is now 42 | // filled with the state needed to render the app. 43 | // Expose the state on the render context, and let the request handler 44 | // inline the state in the HTML response. This allows the client-side 45 | // store to pick-up the server-side state without having to duplicate 46 | // the initial data fetching on the client. 47 | context.state = store.state 48 | resolve(app) 49 | }).catch(reject) 50 | }, reject) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import 'element-ui/lib/theme-default/index.css' 7 | import './assets/css/index.css' 8 | import './assets/iconfont/iconfont.css' 9 | import ElementUI from 'element-ui' 10 | import LoadingBar from 'iview/src/components/loading-bar' 11 | import store from './store' 12 | 13 | Vue.directive('number', { 14 | bind (el, binding, vnode) { 15 | let key = binding.expression 16 | let num = parseInt(binding.arg) > 0 ? parseInt(binding.arg) : 0 17 | let value = el.value 18 | let flag = true 19 | el.addEventListener('input', () => { 20 | let nowValue = el.value 21 | if (nowValue !== '') { 22 | if (num === 0) { 23 | if (nowValue.search(/^[0-9]+$/) === -1 || nowValue === '') { 24 | el.value = value 25 | } 26 | } else { 27 | let re = new RegExp('^[0-9]+(.[0-9]{0,' + num + '})?$') 28 | if (nowValue.search(re) === -1) { 29 | el.value = value 30 | } 31 | } 32 | } 33 | value = el.value 34 | flag = false 35 | vnode.context[key] = el.value 36 | }) 37 | 38 | vnode.context.$watch(key, (val, oldVal) => { 39 | if (!flag) { 40 | flag = true 41 | return false 42 | } 43 | if (!Number.isNaN(val)) { 44 | if (num === 0) { 45 | el.value = parseInt(val) 46 | } else { 47 | el.value = parseFloat(val).toFixed(2) 48 | } 49 | } 50 | }) 51 | } 52 | }) 53 | 54 | Vue.use(ElementUI) 55 | 56 | require('./mock') 57 | 58 | Vue.config.productionTip = false 59 | 60 | LoadingBar.config({ 61 | color: '#2b85e4', 62 | height: 3 63 | }) 64 | 65 | router.beforeEach((to, from, next) => { 66 | LoadingBar.start() 67 | next() 68 | }) 69 | 70 | router.afterEach(route => { 71 | LoadingBar.finish() 72 | }) 73 | 74 | /* eslint-disable no-new */ 75 | new Vue({ 76 | el: '#app', 77 | router, 78 | store, 79 | template: '', 80 | components: { App } 81 | }) 82 | -------------------------------------------------------------------------------- /src/mock.js: -------------------------------------------------------------------------------- 1 | import config from './config' 2 | const { api, baseURL } = config 3 | const Mock = require('mockjs') 4 | // Mock.setup({ 5 | // timeout: '200-600' 6 | // }) 7 | Mock.mock(baseURL + api.dealList, (req, res) => { 8 | const arr = ['北京配额', '广州配额', '上海配额', '深圳配额', '天津配额', '湖北配额', '重庆配额', '福建配额'] 9 | return Mock.mock({ 10 | 'data|8': [ 11 | { 12 | 'one|+1': arr, 13 | 'two|1-100': 1, 14 | 'three|10000-100000': 1, 15 | 'four|10000-100000': 1, 16 | 'five|1-100': 1, 17 | 'six|1-100': 1, 18 | 'seven|1-100.2': 1, 19 | 'eight|1-2': true 20 | } 21 | ] 22 | }) 23 | }) 24 | Mock.mock(baseURL + api.notices, (req, res) => { 25 | return Mock.mock({ 26 | 'data|5-10': [ 27 | { 28 | 'id|+1': 1, 29 | 'title': '@cparagraph(1)', 30 | 'content': '@cparagraph()', 31 | 'date': '@datetime("yyyy-MM-dd HH:mm:ss")' 32 | } 33 | ] 34 | }) 35 | }) 36 | Mock.mock(baseURL + api.news, (req, res) => { 37 | return Mock.mock({ 38 | 'data|5-10': [ 39 | { 40 | 'id|+1': 1, 41 | 'title': '@cparagraph(1)', 42 | 'content': '@cparagraph()', 43 | 'date': '@datetime("yyyy-MM-dd HH:mm:ss")', 44 | 'comefrom|1': ['原创内容'] 45 | } 46 | ] 47 | }) 48 | }) 49 | Mock.mock(baseURL + api.carousel, (req, res) => { 50 | return Mock.mock({ 51 | 'data|3-10': [ 52 | '@dataImage("1920x321")' 53 | ] 54 | }) 55 | }) 56 | Mock.mock(baseURL + api.carbonAccount, (req, res) => { 57 | return Mock.mock({ 58 | 'data': { 59 | 'balance|0-10000000.2': 1, 60 | 'withdrawFreeze|0-10000.2': 1, 61 | 'dealFreeze|0-10000.2': 1 62 | } 63 | }) 64 | }) 65 | Mock.mock(baseURL + api.capitalAccount, (req, res) => { 66 | return Mock.mock({ 67 | 'data': { 68 | 'balance|0-10000000.2': 1, 69 | 'withdrawFreeze|0-10000.2': 1, 70 | 'dealFreeze|0-10000.2': 1 71 | } 72 | }) 73 | }) 74 | Mock.mock(baseURL + api.bankCard, (req, res) => { 75 | return Mock.mock({ 76 | 'data|0-6': [ 77 | { 78 | 'id|+1': 1, 79 | 'name|1': ['中国工商银行', '中国建设银行', '中国农业银行', '中国银行', '中国邮政储蓄银行', '交通银行'], 80 | 'account': '6222 **** **** **** 8888' 81 | } 82 | ] 83 | }) 84 | }) 85 | Mock.mock(baseURL + api.bank, (req, res) => { 86 | return Mock.mock({ 87 | 'data|6': [ 88 | { 89 | 'id|+1': 1, 90 | 'name|+1': ['中国工商银行', '中国建设银行', '中国农业银行', '中国银行', '中国邮政储蓄银行', '交通银行'] 91 | } 92 | ] 93 | }) 94 | }) 95 | Mock.mock(baseURL + api.capitalOrder, (req, res) => { 96 | return Mock.mock({ 97 | 'data': { 98 | 'amount': function () { 99 | return this.data.length >= 10 ? parseInt(Math.random() * 100) + 10 : this.data.length 100 | }, 101 | 'data|0-10': [ 102 | { 103 | 'date': '2017-09-26 10:21:48', 104 | 'type|1': ['充值', '提现'], 105 | 'money|1-10000.2': 1, 106 | 'charge|1-100.2': 1, 107 | 'actual|1-10000.2': 1, 108 | 'status|1': ['审核中', '已完成', '失败'] 109 | } 110 | ] 111 | } 112 | }) 113 | }) 114 | -------------------------------------------------------------------------------- /src/pages/404.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /src/pages/Login.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 173 | 174 | 244 | -------------------------------------------------------------------------------- /src/pages/Main.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 81 | 82 | 143 | 144 | 151 | -------------------------------------------------------------------------------- /src/pages/News.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 66 | 67 | 80 | -------------------------------------------------------------------------------- /src/pages/Notices.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 66 | 67 | 80 | -------------------------------------------------------------------------------- /src/pages/Other.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 77 | 78 | 108 | 109 | -------------------------------------------------------------------------------- /src/pages/Register.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 144 | 145 | 172 | -------------------------------------------------------------------------------- /src/pages/Reset.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 75 | 76 | 104 | -------------------------------------------------------------------------------- /src/pages/UserCenter.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 55 | 56 | 92 | 93 | 141 | 142 | -------------------------------------------------------------------------------- /src/pages/deal/Order.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 90 | 91 | 99 | -------------------------------------------------------------------------------- /src/pages/finance/CapitalAccount.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 201 | 202 | 273 | -------------------------------------------------------------------------------- /src/pages/finance/CarbonAccount.vue: -------------------------------------------------------------------------------- 1 | 182 | 183 | 273 | 274 | 303 | -------------------------------------------------------------------------------- /src/pages/finance/Pandect.vue: -------------------------------------------------------------------------------- 1 | 89 | 90 | 146 | 147 | 244 | 245 | 250 | 251 | -------------------------------------------------------------------------------- /src/pages/message/message.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 89 | 90 | 119 | -------------------------------------------------------------------------------- /src/pages/other/AccountGuide.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 25 | 33 | -------------------------------------------------------------------------------- /src/pages/other/Authentication.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/other/DealGuide.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/other/Help.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/other/Illustrate.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/other/Recharge.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/other/lawer.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/pages/security/CapitalPwd.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 43 | -------------------------------------------------------------------------------- /src/pages/security/Center.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 115 | 116 | 193 | -------------------------------------------------------------------------------- /src/pages/security/Company.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 73 | 74 | 93 | -------------------------------------------------------------------------------- /src/pages/security/Email.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 40 | 41 | 51 | -------------------------------------------------------------------------------- /src/pages/security/Google.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 51 | 52 | 105 | -------------------------------------------------------------------------------- /src/pages/security/Identity.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 109 | 110 | 141 | -------------------------------------------------------------------------------- /src/pages/security/LoginPwd.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /src/pages/security/Phone.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 40 | 41 | 51 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | const NotFoundComponent = resolve => require(['@/pages/404'], resolve) 4 | const Main = resolve => require(['@/pages/Main'], resolve) 5 | const Login = resolve => require(['@/pages/Login'], resolve) 6 | const Register = resolve => require(['@/pages/Register'], resolve) 7 | const Reset = resolve => require(['@/pages/Reset'], resolve) 8 | const News = resolve => require(['@/pages/News'], resolve) 9 | const Notices = resolve => require(['@/pages/Notices'], resolve) 10 | const Deal = resolve => require(['@/pages/Deal'], resolve) 11 | const UserCenter = resolve => require(['@/pages/UserCenter'], resolve) 12 | const Pandect = resolve => require(['@/pages/finance/Pandect'], resolve) 13 | const CapitalAccount = resolve => require(['@/pages/finance/CapitalAccount'], resolve) 14 | const CarbonAccount = resolve => require(['@/pages/finance/CarbonAccount'], resolve) 15 | const Order = resolve => require(['@/pages/deal/Order'], resolve) 16 | const Center = resolve => require(['@/pages/security/Center'], resolve) 17 | const Company = resolve => require(['@/pages/security/Company'], resolve) 18 | const Email = resolve => require(['@/pages/security/Email'], resolve) 19 | const Phone = resolve => require(['@/pages/security/Phone'], resolve) 20 | const Identity = resolve => require(['@/pages/security/Identity'], resolve) 21 | const Google = resolve => require(['@/pages/security/Google'], resolve) 22 | const LoginPwd = resolve => require(['@/pages/security/LoginPwd'], resolve) 23 | const CapitalPwd = resolve => require(['@/pages/security/CapitalPwd'], resolve) 24 | const Message = resolve => require(['@/pages/message/message'], resolve) 25 | const Other = resolve => require(['@/pages/Other'], resolve) 26 | const Help = resolve => require(['@/pages/other/Help'], resolve) 27 | const Lawer = resolve => require(['@/pages/other/Lawer'], resolve) 28 | const AccountGuide = resolve => require(['@/pages/other/AccountGuide'], resolve) 29 | const DealGuide = resolve => require(['@/pages/other/DealGuide'], resolve) 30 | const Authentication = resolve => require(['@/pages/other/Authentication'], resolve) 31 | const Recharge = resolve => require(['@/pages/other/Recharge'], resolve) 32 | const Illustrate = resolve => require(['@/pages/other/Illustrate'], resolve) 33 | 34 | Vue.use(Router) 35 | 36 | export function createRouter () { 37 | return new Router({ 38 | mode: 'history', 39 | scrollBehavior (to, from, savedPosition) { 40 | if (to.hash) { 41 | return { 42 | selector: to.hash 43 | } 44 | } 45 | if (savedPosition) { 46 | return savedPosition 47 | } else { 48 | return { x: 0, y: 0 } 49 | } 50 | }, 51 | routes: [ 52 | { path: '*', 53 | component: NotFoundComponent 54 | }, 55 | { 56 | path: '/', 57 | component: Main 58 | }, 59 | { 60 | path: '/login', 61 | component: Login 62 | }, 63 | { 64 | path: '/register', 65 | component: Register 66 | }, 67 | { 68 | path: '/reset', 69 | component: Reset 70 | }, 71 | { 72 | path: '/news', 73 | component: News 74 | }, 75 | { 76 | path: '/news/:id', 77 | component: News 78 | }, 79 | { 80 | path: '/notices', 81 | component: Notices 82 | }, 83 | { 84 | path: '/notices/:id', 85 | component: Notices 86 | }, 87 | { 88 | path: '/deal', 89 | component: Deal 90 | }, 91 | { 92 | path: '/usercenter', 93 | component: UserCenter, 94 | children: [ 95 | { 96 | path: '/finance/pandect', 97 | component: Pandect 98 | }, 99 | { 100 | path: '/finance/capital', 101 | component: CapitalAccount 102 | }, 103 | { 104 | path: '/finance/carbon', 105 | component: CarbonAccount 106 | }, 107 | { 108 | path: '/deal/order', 109 | component: Order 110 | }, 111 | { 112 | path: '/security/center', 113 | component: Center 114 | }, 115 | { 116 | path: '/security/center/company', 117 | component: Company 118 | }, 119 | { 120 | path: '/security/center/email', 121 | component: Email 122 | }, 123 | { 124 | path: '/security/center/phone', 125 | component: Phone 126 | }, 127 | { 128 | path: '/security/identity', 129 | component: Identity 130 | }, 131 | { 132 | path: '/security/google', 133 | component: Google 134 | }, 135 | { 136 | path: '/security/loginpwd', 137 | component: LoginPwd 138 | }, 139 | { 140 | path: '/security/capitalpwd', 141 | component: CapitalPwd 142 | }, 143 | { 144 | path: '/message', 145 | component: Message 146 | } 147 | ] 148 | }, 149 | { 150 | path: '/other', 151 | component: Other, 152 | children: [ 153 | { 154 | path: 'help', 155 | component: Help 156 | }, 157 | { 158 | path: 'lawer', 159 | component: Lawer 160 | }, 161 | { 162 | path: 'accountGuide', 163 | component: AccountGuide 164 | }, 165 | { 166 | path: 'dealGuide', 167 | component: DealGuide 168 | }, 169 | { 170 | path: 'authentication', 171 | component: Authentication 172 | }, 173 | { 174 | path: 'recharge', 175 | component: Recharge 176 | }, 177 | { 178 | path: 'illustrate', 179 | component: Illustrate 180 | } 181 | ] 182 | } 183 | ] 184 | }) 185 | } 186 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import deal from './modules/deal' 4 | import user from './modules/user' 5 | import account from './modules/account' 6 | import assets from './modules/assets' 7 | import getData from '../util/getData' 8 | import config from '../config' 9 | import { empty } from '@/util/common' 10 | const { api: apiUrl } = config 11 | 12 | Vue.use(Vuex) 13 | 14 | const state = { 15 | notices: null, 16 | news: null, 17 | carousel: null 18 | } 19 | 20 | const getters = { 21 | notices: state => state.notices || [], 22 | news: state => state.news || [], 23 | cNew: (state, getters) => (id) => { 24 | return getters.news.filter((value) => { 25 | return parseInt(value.id) === parseInt(id) 26 | })[0] || {'title': '', 'date': ''} 27 | }, 28 | carousel: state => state.carousel || [] 29 | } 30 | 31 | const mutations = { 32 | saveNotices (state, data) { 33 | state.notices = data 34 | }, 35 | saveNews (state, data) { 36 | state.news = data 37 | }, 38 | saveCarousel (state, data) { 39 | state.carousel = data 40 | } 41 | } 42 | 43 | const actions = { 44 | getNotices ({ state, commit }, options = {}) { 45 | getData(apiUrl.notices, options, state.notices).then((data) => { 46 | commit('saveNotices', data) 47 | }, empty) 48 | }, 49 | getNews ({ state, commit }, options = {}) { 50 | return getData(apiUrl.news, options, state.news).then((data) => { 51 | commit('saveNews', data) 52 | }, empty) 53 | }, 54 | getCarousel ({ state, commit }, options = {}) { 55 | return getData(apiUrl.carousel, options, state.carousel).then((data) => { 56 | commit('saveCarousel', data) 57 | }, empty) 58 | } 59 | } 60 | 61 | export function createStore () { 62 | return new Vuex.Store({ 63 | state, 64 | actions, 65 | mutations, 66 | getters, 67 | modules: [ 68 | deal, 69 | account, 70 | assets, 71 | user 72 | ] 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /src/store/modules/account.js: -------------------------------------------------------------------------------- 1 | import getData from '../../util/getData' 2 | import config from '../../config' 3 | // import api from '../../util/api' 4 | import { empty } from '@/util/common' 5 | const { api: apiUrl } = config 6 | 7 | const state = { 8 | carbonAccount: null, 9 | carbonOrder: null, 10 | carbonOrderAmount: null, 11 | capitalAccount: null, 12 | capitalOrder: null, 13 | capitalOrderAmount: null, 14 | bankCard: null, 15 | bank: null 16 | } 17 | 18 | const getters = { 19 | carbonAccount: state => state.carbonAccount || {}, 20 | carbonOrder: state => state.carbonOrder || [], 21 | carbonOrderAmount: state => state.carbonOrderAmount || 0, 22 | capitalAccount: state => state.capitalAccount || {}, 23 | capitalOrder: state => state.capitalOrder || [], 24 | capitalOrderAmount: state => state.capitalOrderAmount || 0, 25 | bankCard: state => state.bankCard || [], 26 | bank: state => state.bank || [] 27 | } 28 | 29 | const mutations = { 30 | saveCarbonAccount (state, data) { 31 | state.carbonAccount = data 32 | }, 33 | saveCapitalAccount (state, data) { 34 | state.capitalAccount = data 35 | }, 36 | saveBankCard (state, data) { 37 | state.bankCard = data 38 | }, 39 | saveBank (state, data) { 40 | state.bank = data 41 | }, 42 | saveCapitalOrder (state, data) { 43 | state.capitalOrder = data 44 | }, 45 | saveCapitalOrderAmount (state, data) { 46 | state.capitalOrderAmount = data 47 | }, 48 | saveCarbonOrder (state, data) { 49 | state.carbonOrder = data 50 | }, 51 | saveCarbonOrderAmount (state, data) { 52 | state.carbonOrderAmount = data 53 | } 54 | } 55 | 56 | const actions = { 57 | getCarbonAccount ({ commit, state }, options = {}) { 58 | return getData(apiUrl.carbonAccount, options, state.carbonAccount).then((data) => { 59 | commit('saveCarbonAccount', data) 60 | }, empty) 61 | }, 62 | getCapitalAccount ({ commit, state }, options = {}) { 63 | return getData(apiUrl.capitalAccount, options, state.capitalAccount).then((data) => { 64 | commit('saveCapitalAccount', data) 65 | }, empty) 66 | }, 67 | getCapitalOrder ({ commit, state }, options = {}) { 68 | return getData(apiUrl.capitalOrder, options, state.capitalOrder, { data: state.capitalOrder, amount: state.capitalOrderAmount }).then((data) => { 69 | commit('saveCapitalOrder', data.data) 70 | commit('saveCapitalOrderAmount', data.amount) 71 | }, empty) 72 | }, 73 | getCarbonOrder ({ commit, state }, options = {}) { 74 | return getData(apiUrl.carbonOrder, options, state.carbonOrder, { data: state.carbonOrder, amount: state.carbonOrderAmount }).then((data) => { 75 | commit('saveCarbonOrder', data.data) 76 | commit('saveCarbonOrderAmount', data.amount) 77 | }, empty) 78 | }, 79 | getBankCard ({ commit, state }, options = {}) { 80 | return getData(apiUrl.bankCard, options, state.bankCard).then((data) => { 81 | commit('saveBankCard', data) 82 | }, empty) 83 | }, 84 | getBank ({ commit, state }, options = {}) { 85 | return getData(apiUrl.bank, options, state.bank).then((data) => { 86 | commit('saveBank', data) 87 | }, empty) 88 | } 89 | } 90 | 91 | export default { 92 | state, 93 | getters, 94 | mutations, 95 | actions 96 | } 97 | -------------------------------------------------------------------------------- /src/store/modules/assets.js: -------------------------------------------------------------------------------- 1 | import getData from '../../util/getData' 2 | import config from '../../config' 3 | import api from '../../util/api' 4 | import { empty } from '@/util/common' 5 | const { api: apiUrl } = config 6 | 7 | const state = { 8 | capital: null, 9 | capitalList: null, 10 | carbon: null, 11 | carbonList: null 12 | } 13 | 14 | const getters = { 15 | capital: state => state.capital || {}, 16 | carbon: state => state.carbon || {}, 17 | capitalList: state => state.capitalList || [], 18 | carbonList: state => state.carbonList || [] 19 | } 20 | 21 | const mutations = { 22 | saveCapital (state, data) { 23 | state.capital = data 24 | }, 25 | saveCarbon (state, data) { 26 | state.carbon = data 27 | }, 28 | saveCapitalList (state, data) { 29 | state.capitalList = data 30 | }, 31 | saveCarbonList (state, data) { 32 | state.carbonList = data 33 | } 34 | } 35 | 36 | const actions = { 37 | getCapital ({ commit, state }, options = {}) { 38 | return getData(apiUrl.capital, options, state.capital).then((data) => { 39 | commit('saveCapital', data) 40 | }, empty) 41 | }, 42 | getCarbon ({ commit, state }, options = {}) { 43 | return getData(apiUrl.carbon, options, state.carbon).then((data) => { 44 | commit('saveCarbon', data) 45 | }, empty) 46 | }, 47 | getCapitalList ({ commit, state }, options = {}) { 48 | return getData(apiUrl.capitalList, options, state.capitalList).then((data) => { 49 | commit('saveCapitalList', data) 50 | }, empty) 51 | }, 52 | getCarbonList ({ commit, state }, options = {}) { 53 | return getData(apiUrl.carbonList, options, state.carbonList).then((data) => { 54 | commit('saveCarbonList', data) 55 | }, empty) 56 | }, 57 | capitalRecharge ({ commit, state }, options = {}) { 58 | return api.post(apiUrl.capitalRecharge, options).then((data) => { 59 | commit('saveCapital', data) 60 | }) 61 | }, 62 | carbonRecharge ({ commit, state }, options = {}) { 63 | return api.post(apiUrl.carbonRecharge, options).then((data) => { 64 | commit('saveCarbon', data) 65 | }) 66 | }, 67 | capitalWithdraw ({ commit, state }, options = {}) { 68 | return api.post(apiUrl.capitalWithdraw, options).then((data) => { 69 | commit('saveCapital', data) 70 | }) 71 | }, 72 | carbonWithdraw ({ commit, state }, options = {}) { 73 | return api.post(apiUrl.carbonWithdraw, options).then((data) => { 74 | commit('saveCarbon', data) 75 | }) 76 | } 77 | } 78 | 79 | export default { 80 | state, 81 | getters, 82 | mutations, 83 | actions 84 | } 85 | -------------------------------------------------------------------------------- /src/store/modules/deal.js: -------------------------------------------------------------------------------- 1 | import getData from '../../util/getData' 2 | import config from '../../config' 3 | import api from '../../util/api' 4 | import { empty } from '@/util/common' 5 | const { api: apiUrl } = config 6 | 7 | const state = { 8 | list: null, 9 | type1: null, 10 | ownBargainList: null 11 | } 12 | 13 | const getters = { 14 | dealList: state => state.list || [], 15 | ownBargainList: state => state.ownBargainList || [], 16 | deal: (state, getters) => (index) => getters.dealList[index] || {} 17 | } 18 | 19 | const mutations = { 20 | saveDealList (state, data) { 21 | state.list = data 22 | }, 23 | saveSaleList (state, data) { 24 | // TODO: 25 | state.type1 = data 26 | }, 27 | saveBuyList (state, data) { 28 | // TODO: 29 | state.type1 = data 30 | }, 31 | saveBargainList (state, data) { 32 | // TODO: 33 | state.type1 = data 34 | }, 35 | saveOwnBargainList (state, data) { 36 | state.ownBargainList = data 37 | } 38 | } 39 | 40 | const actions = { 41 | getDealList ({ commit, state }, options = {}) { 42 | return getData(apiUrl.dealList, options, state.list).then((data) => { 43 | commit('saveDealList', data) 44 | }, empty) 45 | }, 46 | getSaleList ({ commit, state }, options = {}) { 47 | // TODO: 48 | let cache = state.type1 49 | return getData(apiUrl.saleList, options, cache).then((data) => { 50 | commit('saveSaleList', { 51 | type: '', 52 | data: data 53 | }) 54 | }, empty) 55 | }, 56 | getBuyList ({ commit, state }, options = {}) { 57 | // TODO: 58 | let cache = state.type1 59 | return getData(apiUrl.buyList, options, cache).then((data) => { 60 | commit('savebuyList', { 61 | type: '', 62 | data: data 63 | }) 64 | }, empty) 65 | }, 66 | getBargainList ({ commit, state }, options = {}) { 67 | // TODO: 68 | let cache = state.type1 69 | return getData(apiUrl.bargainList, options, cache).then((data) => { 70 | commit('saveBargainList', { 71 | type: '', 72 | data: data 73 | }) 74 | }, empty) 75 | }, 76 | getOwnBargainList ({ commit, state }, options = {}) { 77 | return getData(apiUrl.ownBargainList, options, state.ownBargainList).then((data) => { 78 | commit('saveOwnBargainList', data) 79 | }, empty) 80 | }, 81 | buy ({ commit, state }, options = {}) { 82 | return api.post(apiUrl.buy, options).then((data) => { 83 | commit('saveOwnBargainList', data) 84 | }) 85 | }, 86 | sell ({ commit, state }, options = {}) { 87 | return api.post(apiUrl.sell, options).then((data) => { 88 | commit('saveOwnBargainList', data) 89 | }) 90 | } 91 | } 92 | 93 | export default { 94 | state, 95 | getters, 96 | mutations, 97 | actions 98 | } 99 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | // import getData from '../../util/getData' 2 | import config from '../../config' 3 | import api from '../../util/api' 4 | const { api: apiUrl } = config 5 | 6 | const state = { 7 | user: null, 8 | token: null 9 | } 10 | 11 | const getters = { 12 | user: state => state.user || {} 13 | } 14 | 15 | const mutations = { 16 | saveUser (state, data) { 17 | state.user = data 18 | }, 19 | removeUser (state) { 20 | state.user = null 21 | } 22 | } 23 | 24 | const actions = { 25 | login ({ commit, state }, options) { 26 | return api.post(apiUrl.login, options).then((data) => { 27 | commit('saveUser', data) 28 | }) 29 | }, 30 | logout ({ commit, state }) { 31 | commit('removeUser') 32 | }, 33 | register ({ commit, state }, options) { 34 | return api.post(apiUrl.register, options).then((data) => { 35 | commit('saveUser', data) 36 | }) 37 | } 38 | } 39 | 40 | export default { 41 | state, 42 | getters, 43 | mutations, 44 | actions 45 | } 46 | -------------------------------------------------------------------------------- /src/util/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' // axios 2 | // import qs from 'qs' 3 | import config from '../config' 4 | const { baseURL, api: apiUrl } = config 5 | 6 | // mock 7 | 8 | import Mock from 'mockjs' 9 | import MockAdapter from 'axios-mock-adapter' 10 | 11 | const mock1 = new MockAdapter(axios) 12 | const list = ['北京配额', '广州配额', '上海配额', '深圳配额', '天津配额', '湖北配额', '重庆配额', '福建配额'] 13 | const bank = ['中国工商银行', '中国建设银行', '中国农业银行', '中国银行', '中国邮政储蓄银行', '交通银行'] 14 | let dealList = Mock.mock({ 15 | 'data|8': [ 16 | { 17 | 'one|+1': list, 18 | 'two|1-100': 1, 19 | 'three|10000-100000': 1, 20 | 'four|10000-100000': 1, 21 | 'five|1-100': 1, 22 | 'six|1-100': 1, 23 | 'seven|1-100.2': 1, 24 | 'eight|1-2': true 25 | } 26 | ] 27 | }) 28 | mock1.onGet(baseURL + apiUrl.dealList).reply(200, JSON.stringify(dealList)) 29 | 30 | let notices = Mock.mock({ 31 | 'data|5-10': [ 32 | { 33 | 'id|+1': 1, 34 | 'title': '@cparagraph(1)', 35 | 'content': '@cparagraph()', 36 | 'date': '@datetime("yyyy-MM-dd HH:mm:ss")' 37 | } 38 | ] 39 | }) 40 | mock1.onGet(baseURL + apiUrl.notices).reply(200, JSON.stringify(notices)) 41 | 42 | let news = Mock.mock({ 43 | 'data|5-10': [ 44 | { 45 | 'id|+1': 1, 46 | 'title': '@cparagraph(1)', 47 | 'content': '@cparagraph()', 48 | 'date': '@datetime("yyyy-MM-dd HH:mm:ss")', 49 | 'comefrom|1': ['原创内容'] 50 | } 51 | ] 52 | }) 53 | mock1.onGet(baseURL + apiUrl.news).reply(200, JSON.stringify(news)) 54 | 55 | let carousel = Mock.mock({ 56 | 'data|3-10': [ 57 | Mock.Random.image('1920x320') 58 | ] 59 | }) 60 | mock1.onGet(baseURL + apiUrl.carousel).reply(200, JSON.stringify(carousel)) 61 | 62 | let carbonAccount = Mock.mock({ 63 | 'data': { 64 | 'balance|0-10000000.2': 1, 65 | 'withdrawFreeze|0-10000.2': 1, 66 | 'dealFreeze|0-10000.2': 1 67 | } 68 | }) 69 | mock1.onGet(baseURL + apiUrl.carbonAccount).reply(200, JSON.stringify(carbonAccount)) 70 | 71 | let capitalAccount = Mock.mock({ 72 | 'data': { 73 | 'balance|0-10000000.2': 1, 74 | 'withdrawFreeze|0-10000.2': 1, 75 | 'dealFreeze|0-10000.2': 1 76 | } 77 | }) 78 | mock1.onGet(baseURL + apiUrl.capitalAccount).reply(200, JSON.stringify(capitalAccount)) 79 | 80 | let bankCard = Mock.mock({ 81 | 'data|0-6': [ 82 | { 83 | 'id|+1': 1, 84 | 'name|1': bank, 85 | 'account': '6222 **** **** **** 8888' 86 | } 87 | ] 88 | }) 89 | mock1.onGet(baseURL + apiUrl.bankCard).reply(200, JSON.stringify(bankCard)) 90 | 91 | let bank1 = Mock.mock({ 92 | 'data|6': [ 93 | { 94 | 'id|+1': 1, 95 | 'name|+1': bank 96 | } 97 | ] 98 | }) 99 | mock1.onGet(baseURL + apiUrl.bank).reply(200, JSON.stringify(bank1)) 100 | 101 | let capitalOrder = Mock.mock({ 102 | 'data': { 103 | 'amount': function () { 104 | return this.data.length >= 10 ? parseInt(Math.random() * 100) + 10 : this.data.length 105 | }, 106 | 'data|0-10': [ 107 | { 108 | 'date': '2017-09-26 10:21:48', 109 | 'type|1': ['充值', '提现'], 110 | 'money|1-10000.2': 1, 111 | 'charge|1-100.2': 1, 112 | 'actual|1-10000.2': 1, 113 | 'status|1': ['审核中', '已完成', '失败'] 114 | } 115 | ] 116 | } 117 | }) 118 | mock1.onGet(baseURL + apiUrl.capitalOrder).reply(200, JSON.stringify(capitalOrder)) 119 | // 120 | 121 | let api = axios.create({ 122 | baseURL: baseURL, 123 | headers: {'Content-Type': 'application/x-www-form-urlencoded'}, 124 | responseType: 'json' 125 | }) 126 | 127 | // 修改返回数据格式 128 | api.defaults.transformResponse = (res) => { 129 | return JSON.parse(res).data 130 | } 131 | 132 | api.defaults.validateStatus = (status) => { 133 | return true 134 | // return status >= 200 && status < 300 135 | } 136 | 137 | api.interceptors.request.use(config => { 138 | // let token = localStorage.getItem('token') 139 | // if (token) { 140 | // config.headers.sx = token 141 | // } 142 | // config.data = qs.stringify(config.data) 143 | return config 144 | }, error => { 145 | return Promise.reject(error) 146 | }) 147 | 148 | api.interceptors.response.use(response => { 149 | // console.log('response:' + response) 150 | // TODO:token过期等状态码处理 151 | return response.data 152 | }, error => { 153 | return Promise.reject(error) 154 | }) 155 | 156 | export default api 157 | -------------------------------------------------------------------------------- /src/util/common.js: -------------------------------------------------------------------------------- 1 | export const bankIcon = (name) => { 2 | let eclass = 'icon-gongshangyinhang' 3 | switch (name) { 4 | case '中国银行': 5 | eclass = 'icon-zhongguoyinhang' 6 | break 7 | case '中国建设银行': 8 | eclass = 'icon-jiansheyinhang' 9 | break 10 | case '中国农业银行': 11 | eclass = 'icon-nongyeyinhang' 12 | break 13 | case '中国邮政储蓄银行': 14 | eclass = 'icon-zhongguoyouzhengchuxuyinhang' 15 | break 16 | case '交通银行': 17 | eclass = 'icon-jiaotongyinhang' 18 | break 19 | } 20 | return eclass 21 | } 22 | 23 | export const empty = () => {} 24 | 25 | export const pluralAsync = (funs) => { 26 | return new Promise((resolve) => { 27 | let num = 0 28 | funs.forEach((fun) => { 29 | fun().then(() => { 30 | num++ 31 | if (num === funs.length) { 32 | resolve() 33 | } 34 | }) 35 | }) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/util/getData.js: -------------------------------------------------------------------------------- 1 | import api from './api' 2 | 3 | let cacheConfig = {} 4 | 5 | const getData = (url, options, judge, cache = judge) => { 6 | let { params, getDataType } = options // 解构 7 | if (!params && !getDataType) { 8 | params = options 9 | } else { 10 | let num = parseInt(getDataType) 11 | if (!Number.isNaN(num)) { 12 | if (judge) { 13 | let cacheTime = cacheConfig[url] 14 | let now = new Date().getTime() 15 | if (now - cacheTime > num * 1000) { 16 | cacheConfig[url] = new Date().getTime() 17 | return api.get(url, { 18 | params: params 19 | }) 20 | } else { 21 | return Promise.reject() 22 | } 23 | } else { 24 | cacheConfig[url] = new Date().getTime() 25 | } 26 | } 27 | } 28 | if (getDataType !== 'nocache') { 29 | if (judge) { 30 | return Promise.reject() 31 | } 32 | } 33 | return api.get(url, { 34 | params: params 35 | }) 36 | } 37 | 38 | export default getData 39 | -------------------------------------------------------------------------------- /src/util/inter.js: -------------------------------------------------------------------------------- 1 | import config from '../../config' 2 | import api from '../../util/api' 3 | const { api: apiUrl } = config 4 | 5 | const sendNote = (options, cb) => { 6 | api.post(apiUrl.sendNote, options).then(cb) 7 | } 8 | 9 | const forgetPwd = (options, cb) => { 10 | api.post(apiUrl.forgetPwd, options).then(cb) 11 | } 12 | 13 | const changePwd = (options, cb) => { 14 | api.post(apiUrl.changePwd, options).then(cb) 15 | } 16 | 17 | export { 18 | sendNote, 19 | forgetPwd, 20 | changePwd 21 | } 22 | -------------------------------------------------------------------------------- /src/util/pageData.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | 3 | export default { 4 | save (self, data) { 5 | if (config.saveDataType === 'query') { 6 | self.$router.push({ 7 | query: { 8 | ...self.$route.query, // 扩展运算符 es6 9 | ...data 10 | } 11 | }) 12 | } else { 13 | localStorage.setItem('pageData', JSON.stringify({ 14 | ...JSON.parse(localStorage.getItem('pageData')), 15 | path: self.$route.path, 16 | ...data 17 | })) 18 | } 19 | }, 20 | get (self) { 21 | if (config.saveDataType === 'query') { 22 | return self.$route.query || {} 23 | } else { 24 | return JSON.parse(localStorage.getItem('pageData')) || {} 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/util/title.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | function getTitle (vm) { 3 | const { title } = vm.$options 4 | if (title) { 5 | return typeof title === 'function' 6 | ? title.call(vm) 7 | : title 8 | } 9 | } 10 | 11 | const serverTitleMixin = { 12 | created () { 13 | const title = getTitle(this) 14 | if (title) { 15 | this.$ssrContext.title = `${config.title} | ${title}` 16 | } 17 | } 18 | } 19 | 20 | const clientTitleMixin = { 21 | mounted () { 22 | const title = getTitle(this) 23 | if (title) { 24 | document.title = `${config.title} | ${title}` 25 | } 26 | } 27 | } 28 | 29 | export default process.env.VUE_ENV === 'server' ? serverTitleMixin : clientTitleMixin 30 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liurongliurong/vue/260ea54c82bc2742ea0fc9b9705133ae8db256f5/static/.gitkeep -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 4 | 5 | server.ready.then(() => { 6 | // 2. run the nightwatch test suite against it 7 | // to run in additional browsers: 8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 9 | // 2. add it to the --env flag below 10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 11 | // For more information on Nightwatch's config file, see 12 | // http://nightwatchjs.org/guide#settings-file 13 | var opts = process.argv.slice(2) 14 | if (opts.indexOf('--config') === -1) { 15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 16 | } 17 | if (opts.indexOf('--env') === -1) { 18 | opts = opts.concat(['--env', 'chrome']) 19 | } 20 | 21 | var spawn = require('cross-spawn') 22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 23 | 24 | runner.on('exit', function (code) { 25 | server.close() 26 | process.exit(code) 27 | }) 28 | 29 | runner.on('error', function (err) { 30 | server.close() 31 | throw err 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.conf') 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' } 30 | ] 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Hello from '@/components/Hello' 3 | 4 | describe('Hello.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(Hello) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .to.equal('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------