├── .babelrc
├── .editorconfig
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
├── 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
├── config
├── dev.env.js
├── index.js
├── prod.env.js
└── test.env.js
├── gulpfile.js
├── index.html
├── mock
├── db.js
└── server.js
├── package.json
├── src
├── App.vue
├── assets
│ ├── css
│ │ └── transition.css
│ └── img
│ │ ├── 1-1.jpg
│ │ ├── 1-2.jpg
│ │ ├── 1-3.jpg
│ │ ├── 1-4.jpg
│ │ ├── 1-5.jpg
│ │ ├── next.png
│ │ └── pre.png
├── components
│ ├── address.vue
│ ├── chart.vue
│ ├── comment.vue
│ ├── header.vue
│ ├── index.js
│ ├── loadingbar.vue
│ ├── qrcode.vue
│ └── slider.vue
├── filters
│ └── index.js
├── main.js
├── pages
│ ├── home
│ │ └── index.vue
│ └── list
│ │ └── index.vue
├── plugins
│ ├── index.js
│ └── toast
│ │ ├── index.css
│ │ └── index.js
├── router
│ └── index.js
├── store
│ ├── actions.js
│ ├── getters.js
│ ├── index.js
│ ├── mutation-types.js
│ ├── mutations.js
│ └── state.js
└── utils
│ └── index.js
├── static
└── .gitkeep
└── test
├── components
├── comment.spec.js
└── header.spec.js
├── karma.conf.js
├── utils.js
└── vuex
└── mutations.spec.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-2"
5 | ],
6 | "plugins": ["transform-runtime"],
7 | "comments": false,
8 | "env": {
9 | "test": {
10 | "presets": ["env", "stage-2"],
11 | "plugins": [ "istanbul" ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | .idea/
8 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "plugins": {
3 | "autoprefixer": {
4 | browsers: ['iOS >= 7', 'Android >= 4.1']
5 | },
6 | "postcss-px2rem": {
7 | remUnit: 75
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-starter-kit
2 |
3 | > A Vue.js project with mock-server and unit-test
4 |
5 | ## components
6 |
7 | * [header](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/header.vue)
8 | * [comment](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/comment.vue)
9 | * [address-select](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/address.vue)
10 | * [image-slider](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/slider.vue)
11 | * [qrcode](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/qrcode.vue)
12 | * [loading-bar](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/loadingbar.vue)
13 | * [chart](https://github.com/yanm1ng/vue-starter-kit/blob/master/src/components/chart.vue)
14 | * waiting for ...
15 |
16 | ## plugins
17 |
18 | * toast
19 |
20 | ## mock-server
21 |
22 | we use `json-server` to get a full fake REST API, use mock.js to build mock data ( as local database :) ), for example:
23 |
24 | ```javascript
25 | // db.js
26 | var Mock = require('mockjs');
27 |
28 | module.exports = {
29 | project_comments: Mock.mock({
30 | "error": 0,
31 | "message": "success",
32 | "result|40": [{
33 | "author": "@name",
34 | "comment": "@cparagraph",
35 | "date": "@datetime"
36 | }]
37 | }),
38 | push_comment: Mock.mock({
39 | "error": 0,
40 | "message": "success",
41 | "result": []
42 | })
43 | };
44 | ```
45 |
46 | ```json
47 | // routes.json
48 | {
49 | "/project/:page/comments.action": "/project_comments?_page=:page&_limit=5",
50 | "/comment/add.action": "/push_comment"
51 | }
52 | ```
53 |
54 | ## unit test
55 |
56 | test components and vuex( include `mutations` `actions` `getters` ), test result:
57 |
58 | 
59 |
60 | ## build setup
61 |
62 | ``` bash
63 | # install dependencies
64 | npm install
65 |
66 | # serve with hot reload at localhost:8181
67 | npm run dev
68 |
69 | # run mock server at localhost:3000
70 | npm run mock
71 |
72 | # run unit test
73 | npm run test
74 |
75 | # build for production with minification
76 | npm run build
77 | ```
78 |
79 | ## TODO
80 |
81 | - [x] add `vuex` to manage app state
82 | - [x] add unit test
83 | - [x] refresh the mock server after modifying `mock/db.js`
--------------------------------------------------------------------------------
/build/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 | console.log(chalk.cyan(' Build complete.\n'))
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ))
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/build/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/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/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 = require('./webpack.dev.conf')
14 |
15 | // default port where dev server listens for incoming traffic
16 | var port = process.env.PORT || config.dev.port
17 | // automatically open browser, if not set will be false
18 | var autoOpenBrowser = !!config.dev.autoOpenBrowser
19 |
20 | var proxyTable = config.dev.proxyTable
21 |
22 | var app = express()
23 | var compiler = webpack(webpackConfig)
24 |
25 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
26 | publicPath: webpackConfig.output.publicPath,
27 | quiet: true
28 | })
29 |
30 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
31 | log: () => {}
32 | })
33 | // force page reload when html-webpack-plugin template changes
34 | compiler.plugin('compilation', function (compilation) {
35 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
36 | hotMiddleware.publish({ action: 'reload' })
37 | cb()
38 | })
39 | })
40 |
41 | // proxy api requests
42 | Object.keys(proxyTable).forEach(function (context) {
43 | var options = proxyTable[context]
44 | if (typeof options === 'string') {
45 | options = { target: options }
46 | }
47 | app.use(proxyMiddleware(options.filter || context, options))
48 | })
49 |
50 | // handle fallback for HTML5 history API
51 | app.use(require('connect-history-api-fallback')())
52 |
53 | // serve webpack bundle output
54 | app.use(devMiddleware)
55 |
56 | // enable hot-reload and state-preserving
57 | // compilation error display
58 | app.use(hotMiddleware)
59 |
60 | // serve pure static assets
61 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
62 | app.use(staticPath, express.static('./static'))
63 |
64 | var uri = 'http://localhost:' + port
65 |
66 | var _resolve
67 | var readyPromise = new Promise(resolve => {
68 | _resolve = resolve
69 | })
70 |
71 | console.log('> Starting dev server...')
72 | devMiddleware.waitUntilValid(() => {
73 | console.log('> Listening at ' + uri + '\n')
74 | // when env is testing, don't need open it
75 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
76 | opn(uri)
77 | }
78 | _resolve()
79 | })
80 |
81 | var server = app.listen(port)
82 |
83 | module.exports = {
84 | ready: readyPromise,
85 | close: () => {
86 | server.close()
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/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 | function generateLoaders (loader, loaderOptions) {
24 | var loaders = [cssLoader]
25 | if (loader) {
26 | loaders.push({
27 | loader: loader + '-loader',
28 | options: Object.assign({}, loaderOptions, {
29 | sourceMap: options.sourceMap
30 | })
31 | })
32 | }
33 |
34 | if (options.extract) {
35 | return ExtractTextPlugin.extract({
36 | use: loaders,
37 | fallback: 'vue-style-loader'
38 | })
39 | } else {
40 | return ['vue-style-loader'].concat(loaders)
41 | }
42 | }
43 |
44 | return {
45 | css: generateLoaders(),
46 | postcss: generateLoaders(),
47 | less: generateLoaders('less'),
48 | sass: generateLoaders('sass', { indentedSyntax: true }),
49 | scss: generateLoaders('sass'),
50 | stylus: generateLoaders('stylus'),
51 | styl: generateLoaders('stylus')
52 | }
53 | }
54 |
55 | exports.styleLoaders = function (options) {
56 | var output = []
57 | var loaders = exports.cssLoaders(options)
58 | for (var extension in loaders) {
59 | var loader = loaders[extension]
60 | output.push({
61 | test: new RegExp('\\.' + extension + '$'),
62 | use: loader
63 | })
64 | }
65 | return output
66 | }
67 |
--------------------------------------------------------------------------------
/build/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 | }
13 |
--------------------------------------------------------------------------------
/build/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: /\.vue$/,
32 | loader: 'vue-loader',
33 | options: vueLoaderConfig
34 | },
35 | {
36 | test: /\.js$/,
37 | loader: 'babel-loader',
38 | include: [resolve('src'), resolve('test')]
39 | },
40 | {
41 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
42 | loader: 'url-loader',
43 | options: {
44 | limit: 10000,
45 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
46 | }
47 | },
48 | {
49 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
50 | loader: 'url-loader',
51 | options: {
52 | limit: 10000,
53 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
54 | }
55 | }
56 | ]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/build/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 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
10 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
11 | })
12 |
13 | module.exports = merge(baseWebpackConfig, {
14 | module: {
15 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
16 | },
17 | devtool: '#cheap-module-eval-source-map',
18 | plugins: [
19 | new webpack.DefinePlugin({
20 | 'process.env': config.dev.env
21 | }),
22 | new webpack.HotModuleReplacementPlugin(),
23 | new webpack.NoEmitOnErrorsPlugin(),
24 | new HtmlWebpackPlugin({
25 | filename: 'index.html',
26 | template: 'index.html',
27 | inject: true
28 | }),
29 | new FriendlyErrorsPlugin()
30 | ]
31 | })
32 |
--------------------------------------------------------------------------------
/build/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 |
12 | var env = config.build.env
13 |
14 | var webpackConfig = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({
17 | sourceMap: config.build.productionSourceMap,
18 | extract: true
19 | })
20 | },
21 | devtool: config.build.productionSourceMap ? '#source-map' : false,
22 | output: {
23 | path: config.build.assetsRoot,
24 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
25 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
26 | },
27 | plugins: [
28 | new webpack.DefinePlugin({
29 | 'process.env': env
30 | }),
31 | new webpack.optimize.UglifyJsPlugin({
32 | compress: {
33 | warnings: false
34 | },
35 | sourceMap: true
36 | }),
37 | // extract css into its own file
38 | new ExtractTextPlugin({
39 | filename: utils.assetsPath('css/[name].[contenthash].css')
40 | }),
41 | new OptimizeCSSPlugin({
42 | cssProcessorOptions: {
43 | safe: true
44 | }
45 | }),
46 | new HtmlWebpackPlugin({
47 | filename: config.build.index,
48 | template: 'index.html',
49 | inject: true,
50 | minify: {
51 | removeComments: true,
52 | collapseWhitespace: true,
53 | removeAttributeQuotes: true
54 | // more options:
55 | },
56 | chunksSortMode: 'dependency'
57 | }),
58 | new webpack.optimize.CommonsChunkPlugin({
59 | name: 'vendor',
60 | minChunks: function (module, count) {
61 | // any required modules inside node_modules are extracted to vendor
62 | return (
63 | module.resource &&
64 | /\.js$/.test(module.resource) &&
65 | module.resource.indexOf(
66 | path.join(__dirname, '../node_modules')
67 | ) === 0
68 | )
69 | }
70 | }),
71 | new webpack.optimize.CommonsChunkPlugin({
72 | name: 'manifest',
73 | chunks: ['vendor']
74 | }),
75 | // copy custom static assets
76 | new CopyWebpackPlugin([
77 | {
78 | from: path.resolve(__dirname, '../static'),
79 | to: config.build.assetsSubDirectory,
80 | ignore: ['.*']
81 | }
82 | ])
83 | ]
84 | })
85 |
86 | if (config.build.productionGzip) {
87 | var CompressionWebpackPlugin = require('compression-webpack-plugin')
88 |
89 | webpackConfig.plugins.push(
90 | new CompressionWebpackPlugin({
91 | asset: '[path].gz[query]',
92 | algorithm: 'gzip',
93 | test: new RegExp(
94 | '\\.(' +
95 | config.build.productionGzipExtensions.join('|') +
96 | ')$'
97 | ),
98 | threshold: 10240,
99 | minRatio: 0.8
100 | })
101 | )
102 | }
103 |
104 | if (config.build.bundleAnalyzerReport) {
105 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
106 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
107 | }
108 |
109 | module.exports = webpackConfig
110 |
--------------------------------------------------------------------------------
/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var webpack = require('webpack')
3 | var merge = require('webpack-merge')
4 | var baseConfig = require('./webpack.base.conf')
5 |
6 | var webpackConfig = merge(baseConfig, {
7 | // use inline sourcemap for karma-sourcemap-loader
8 | module: {
9 | rules: utils.styleLoaders()
10 | },
11 | devtool: '#inline-source-map',
12 | resolveLoader: {
13 | alias: {
14 | 'scss-loader': 'sass-loader'
15 | }
16 | },
17 | plugins: [
18 | new webpack.DefinePlugin({
19 | 'process.env': require('../config/test.env')
20 | })
21 | ]
22 | })
23 |
24 | // no need for app entry during tests
25 | delete webpackConfig.entry
26 |
27 | module.exports = webpackConfig
--------------------------------------------------------------------------------
/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/index.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 |
3 | module.exports = {
4 | build: {
5 | env: require('./prod.env'),
6 | index: path.resolve(__dirname, '../dist/index.html'),
7 | assetsRoot: path.resolve(__dirname, '../dist'),
8 | assetsSubDirectory: 'static',
9 | assetsPublicPath: '/',
10 | productionSourceMap: true,
11 | productionGzip: false,
12 | productionGzipExtensions: ['js', 'css'],
13 | bundleAnalyzerReport: process.env.npm_config_report
14 | },
15 | dev: {
16 | env: require('./dev.env'),
17 | port: 8181,
18 | assetsSubDirectory: 'static',
19 | assetsPublicPath: '/',
20 | proxyTable: {
21 | '/api/': {
22 | target: 'http://localhost:3000',
23 | changeOrigin: true,
24 | pathRewrite: {
25 | '^/api': ''
26 | }
27 | }
28 | },
29 | cssSourceMap: false
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/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 | })
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var nodemon = require('gulp-nodemon');
3 | var path = require('path');
4 | var browserSync = require('browser-sync').create();
5 | var ROOT = path.resolve(__dirname);
6 | var server = path.resolve(ROOT, 'mock');
7 |
8 | // browser-sync配置,配置里启动nodemon任务
9 | gulp.task('browser-sync', ['nodemon'], function() {
10 | browserSync.init(null, {
11 | proxy: "http://localhost:8181",
12 | port: 8081
13 | });
14 | });
15 |
16 | // browser-sync 监听文件
17 | gulp.task('mock', ['browser-sync'], function() {
18 | gulp.watch(['./mock/db.js', './mock/**'], ['bs-delay']);
19 | });
20 |
21 | // 延时刷新
22 | gulp.task('bs-delay', function() {
23 | setTimeout(function() {
24 | browserSync.reload();
25 | console.log('restart');
26 | }, 1000);
27 | });
28 |
29 | // 服务器重启
30 | gulp.task('nodemon', function(cb) {
31 | // 设个变量来防止重复重启
32 | var started = false;
33 | var stream = nodemon({
34 | script: './mock/server.js',
35 | // 监听文件的后缀
36 | ext: "js",
37 | env: {
38 | 'NODE_ENV': 'development'
39 | },
40 | // 监听的路径
41 | watch: [
42 | server
43 | ]
44 | });
45 | stream.on('start', function() {
46 | if (!started) {
47 | cb();
48 | started = true;
49 | }
50 | }).on('crash', function() {
51 | console.error('Application has crashed!\n')
52 | stream.emit('restart', 10)
53 | })
54 | });
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-project
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/mock/db.js:
--------------------------------------------------------------------------------
1 | var Mock = require('mockjs');
2 |
3 | module.exports = {
4 | project_comments: Mock.mock({
5 | "error": 0,
6 | "message": "success",
7 | "result|40": [{
8 | "author": "@name",
9 | "comment": "@cparagraph",
10 | "date": "@datetime"
11 | }]
12 | }),
13 | push_comment: Mock.mock({
14 | "error": 0,
15 | "message": "success",
16 | "result": []
17 | })
18 | };
--------------------------------------------------------------------------------
/mock/server.js:
--------------------------------------------------------------------------------
1 | const jsonServer = require('json-server')
2 | const mock = require('mockjs')
3 | const db = require('./db.js')
4 |
5 | const server = jsonServer.create()
6 | const router = jsonServer.router(db)
7 | const middlewares = jsonServer.defaults()
8 |
9 | const rewriter = jsonServer.rewriter({
10 | "/project/:page/comments.action": "/project_comments?_page=:page&_limit=5",
11 | "/comment/add.action": "/push_comment"
12 | })
13 |
14 | server.use(middlewares)
15 | server.use((request, res, next) => {
16 | request.method = 'GET';
17 | next();
18 | })
19 |
20 | server.use(rewriter)
21 | server.use(router)
22 |
23 | server.listen(3000, () => {
24 | console.log('mock server is running')
25 | })
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-project",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "yummy1121@foxmail.com ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "mock": "gulp mock",
10 | "build": "node build/build.js",
11 | "test": "karma start test/karma.conf.js --single-run"
12 | },
13 | "dependencies": {
14 | "axios": "^0.16.1",
15 | "chart.js": "^2.5.0",
16 | "gulp-nodemon": "^2.2.1",
17 | "json-server": "^0.10.1",
18 | "nodemon": "^1.11.0",
19 | "qrious": "^2.2.0",
20 | "vue": "^2.2.2",
21 | "vue-router": "^2.3.0",
22 | "vuex": "^2.3.1"
23 | },
24 | "devDependencies": {
25 | "autoprefixer": "^6.7.2",
26 | "babel-core": "^6.22.1",
27 | "babel-eslint": "^7.1.1",
28 | "babel-loader": "^6.2.10",
29 | "babel-plugin-transform-runtime": "^6.22.0",
30 | "babel-preset-env": "^1.2.1",
31 | "babel-preset-stage-2": "^6.22.0",
32 | "babel-register": "^6.22.0",
33 | "browser-sync": "^2.18.12",
34 | "chalk": "^1.1.3",
35 | "connect-history-api-fallback": "^1.3.0",
36 | "copy-webpack-plugin": "^4.0.1",
37 | "css-loader": "^0.26.1",
38 | "eslint": "^3.14.1",
39 | "eslint-config-standard": "^6.2.1",
40 | "eslint-friendly-formatter": "^2.0.7",
41 | "eslint-loader": "^1.6.1",
42 | "eslint-plugin-html": "^2.0.0",
43 | "eslint-plugin-promise": "^3.4.0",
44 | "eslint-plugin-standard": "^2.0.1",
45 | "eventsource-polyfill": "^0.9.6",
46 | "express": "^4.14.1",
47 | "extract-text-webpack-plugin": "^2.0.0",
48 | "file-loader": "^0.10.0",
49 | "friendly-errors-webpack-plugin": "^1.1.3",
50 | "gulp": "^3.9.1",
51 | "html-webpack-plugin": "^2.28.0",
52 | "http-proxy-middleware": "^0.17.3",
53 | "jasmine-core": "^2.6.0",
54 | "karma": "^1.6.0",
55 | "karma-jasmine": "^1.1.0",
56 | "karma-phantomjs-launcher": "^1.0.4",
57 | "karma-webpack": "^2.0.3",
58 | "mockjs": "^1.0.1-beta3",
59 | "opn": "^4.0.2",
60 | "optimize-css-assets-webpack-plugin": "^1.3.0",
61 | "ora": "^1.1.0",
62 | "postcss-px2rem": "^0.3.0",
63 | "rimraf": "^2.6.0",
64 | "semver": "^5.3.0",
65 | "shelljs": "^0.7.6",
66 | "url-loader": "^0.5.8",
67 | "vue-loader": "^11.1.4",
68 | "vue-style-loader": "^2.0.0",
69 | "vue-template-compiler": "^2.2.4",
70 | "webpack": "^2.2.1",
71 | "webpack-bundle-analyzer": "^2.2.1",
72 | "webpack-dev-middleware": "^1.10.0",
73 | "webpack-hot-middleware": "^2.16.1",
74 | "webpack-merge": "^2.6.1"
75 | },
76 | "engines": {
77 | "node": ">= 4.0.0",
78 | "npm": ">= 3.0.0"
79 | },
80 | "browserslist": [
81 | "> 1%",
82 | "last 2 versions",
83 | "not ie <= 8"
84 | ]
85 | }
86 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
28 |
29 |
48 |
--------------------------------------------------------------------------------
/src/assets/css/transition.css:
--------------------------------------------------------------------------------
1 | .vux-pop-in-enter-active,.vux-pop-in-leave-active,.vux-pop-out-enter-active,.vux-pop-out-leave-active {
2 | will-change: transform;
3 | -webkit-transition: all .5s;
4 | transition: all .5s;
5 | height: 100%;
6 | position: absolute;
7 | -webkit-backface-visibility: hidden;
8 | backface-visibility: hidden;
9 | -webkit-perspective: 1000;
10 | perspective: 1000
11 | }
12 |
13 | .vux-pop-out-enter {
14 | opacity: 0;
15 | -webkit-transform: translate3d(-100%, 0, 0);
16 | transform: translate3d(-100%, 0, 0)
17 | }
18 |
19 | .vux-pop-in-enter,.vux-pop-out-leave-active {
20 | opacity: 0;
21 | -webkit-transform: translate3d(100%, 0, 0);
22 | transform: translate3d(100%, 0, 0)
23 | }
24 |
25 | .vux-pop-in-leave-active {
26 | opacity: 0;
27 | -webkit-transform: translate3d(-100%, 0, 0);
28 | transform: translate3d(-100%, 0, 0)
29 | }
30 |
--------------------------------------------------------------------------------
/src/assets/img/1-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/1-1.jpg
--------------------------------------------------------------------------------
/src/assets/img/1-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/1-2.jpg
--------------------------------------------------------------------------------
/src/assets/img/1-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/1-3.jpg
--------------------------------------------------------------------------------
/src/assets/img/1-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/1-4.jpg
--------------------------------------------------------------------------------
/src/assets/img/1-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/1-5.jpg
--------------------------------------------------------------------------------
/src/assets/img/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/next.png
--------------------------------------------------------------------------------
/src/assets/img/pre.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/src/assets/img/pre.png
--------------------------------------------------------------------------------
/src/components/address.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
12 |
13 |
14 |
15 |
16 |
69 |
70 |
--------------------------------------------------------------------------------
/src/components/chart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
75 |
76 |
--------------------------------------------------------------------------------
/src/components/comment.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 | {{ item.author }}
7 | {{ item.date }}
8 |
9 | {{ item.comment | wordSlice }}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
28 |
29 |
76 |
--------------------------------------------------------------------------------
/src/components/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
25 |
26 |
63 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import header from './header'
2 | import comment from './comment'
3 | import address from './address'
4 | import slider from './slider'
5 | import qrcode from './qrcode'
6 | import loadingbar from './loadingbar'
7 | import chart from './chart'
8 |
9 | export default {
10 | header,
11 | comment,
12 | address,
13 | slider,
14 | qrcode,
15 | loadingbar,
16 | chart
17 | }
--------------------------------------------------------------------------------
/src/components/loadingbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
112 |
113 |
--------------------------------------------------------------------------------
/src/components/qrcode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/components/slider.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | {{item.tagName}}
7 |
8 |
9 |
10 |
11 |
12 | -
13 |
14 |
15 | -
16 |
17 |
18 |
19 |
20 |
21 |
103 |
212 |
--------------------------------------------------------------------------------
/src/filters/index.js:
--------------------------------------------------------------------------------
1 | export function wordSlice(str) {
2 | if (str.length > 100) {
3 | str = str.slice(0, 97) + '...';
4 | }
5 | return str;
6 | }
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import './plugins/toast/index.css';
2 |
3 | import Vue from 'vue'
4 | import App from './app'
5 | import router from './router'
6 | import store from './store'
7 | import components from './components/' // 加载公共组件
8 | import * as filters from './filters/'
9 | import plugins from './plugins/'
10 |
11 | Object.keys(components).forEach(key => {
12 | var name = key.replace(/(\w)/, (v) => v.toUpperCase()) // 首字母大写
13 | Vue.component(`v${name}`, components[key])
14 | })
15 |
16 | Object.keys(plugins).forEach(key => {
17 | Vue.use(plugins[key]);
18 | })
19 |
20 | Object.keys(filters).forEach(key => {
21 | Vue.filter(key, filters[key])
22 | })
23 |
24 | Vue.config.productionTip = false
25 |
26 | new Vue({
27 | el: '#app',
28 | router,
29 | store,
30 | template: '',
31 | components: { App }
32 | })
33 |
--------------------------------------------------------------------------------
/src/pages/home/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | components
6 |
7 |
8 |
18 |
19 |
20 |
21 |
46 |
47 |
--------------------------------------------------------------------------------
/src/pages/list/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | back
6 |
7 |
8 |
9 |
Address-Select
10 |
11 |
Image-Slider
12 |
13 |
QRCode
14 |
15 |
Toast
16 |
top
17 |
center
18 |
bottom
19 |
loading
20 |
LoadingBar
21 |
22 |
30%
23 |
100%
24 |
Chart
25 |
26 |
27 |
28 |
29 |
30 |
142 |
143 |
--------------------------------------------------------------------------------
/src/plugins/index.js:
--------------------------------------------------------------------------------
1 | import Toast from './toast/index'
2 |
3 | export default {
4 | Toast
5 | }
--------------------------------------------------------------------------------
/src/plugins/toast/index.css:
--------------------------------------------------------------------------------
1 | .lx-toast{
2 | position: fixed;
3 | bottom:100px;
4 | left:50%;
5 | width:180px;
6 | height:40px;
7 | line-height: 40px;
8 | padding:0 10px;
9 | margin-left: -100px;
10 | text-align: center;
11 | z-index: 9999;
12 | font-size:14px;
13 | color: #fff;
14 | border-radius: 5px;
15 | background: rgba(0,0,0,0.7);
16 | animation: show-toast .5s;
17 | -webkit-animation: show-toast .5s;
18 | overflow: hidden;
19 | text-overflow:ellipsis;
20 | white-space:nowrap;
21 | }
22 | .lx-toast.lx-toast-top{
23 | top:50px;
24 | bottom:inherit;
25 | }
26 | .lx-toast.lx-toast-center{
27 | top:50%;
28 | margin-top: -20px;
29 | bottom:inherit;
30 | }
31 | @keyframes show-toast
32 | {
33 | from {
34 | opacity: 0;
35 | transform: translateY(-10px);
36 | -webkit-transform: translateY(-10px);
37 | }
38 | to {
39 | opacity: 1;
40 | transform: translateY(0);
41 | -webkit-transform: translateY(0);
42 | }
43 | }
44 |
45 | .lx-load-mark{
46 | position: fixed;
47 | left: 0;
48 | top: 0;
49 | width: 100%;
50 | height: 100%;
51 | z-index: 9999;
52 | }
53 | .lx-load-box {
54 | position: fixed;
55 | z-index: 3;
56 | width: 7.6em;
57 | min-height: 7.6em;
58 | top: 180px;
59 | left: 50%;
60 | margin-left: -3.8em;
61 | background: rgba(0,0,0,0.7);
62 | text-align: center;
63 | border-radius: 5px;
64 | color: #FFFFFF;
65 | }
66 | .lx-load-content {
67 | margin-top: 64%;
68 | font-size: 14px;
69 | }
70 | .lx-loading{
71 | position: absolute;
72 | width: 0px;
73 | left: 50%;
74 | top: 38%;
75 | }
76 | .loading_leaf{
77 | position: absolute;
78 | top: -1px;
79 | opacity: 0.25;
80 | }
81 | .loading_leaf:before {
82 | content: " ";
83 | position: absolute;
84 | width: 9.14px;
85 | height: 3.08px;
86 | background: #d1d1d5;
87 | box-shadow: rgba(0, 0, 0, 0.0980392) 0px 0px 1px;
88 | border-radius: 1px;
89 | -webkit-transform-origin: left 50% 0px;
90 | transform-origin: left 50% 0px;
91 | }
92 | .loading_leaf_0 {
93 | -webkit-animation: opacity-0 1.25s linear infinite;
94 | animation: opacity-0 1.25s linear infinite;
95 | }
96 | .loading_leaf_0:before {
97 | -webkit-transform: rotate(0deg) translate(7.92px, 0px);
98 | transform: rotate(0deg) translate(7.92px, 0px);
99 | }
100 | .loading_leaf_1 {
101 | -webkit-animation: opacity-1 1.25s linear infinite;
102 | animation: opacity-1 1.25s linear infinite;
103 | }
104 | .loading_leaf_1:before {
105 | -webkit-transform: rotate(30deg) translate(7.92px, 0px);
106 | transform: rotate(30deg) translate(7.92px, 0px);
107 | }
108 | .loading_leaf_2 {
109 | -webkit-animation: opacity-2 1.25s linear infinite;
110 | animation: opacity-2 1.25s linear infinite;
111 | }
112 | .loading_leaf_2:before {
113 | -webkit-transform: rotate(60deg) translate(7.92px, 0px);
114 | transform: rotate(60deg) translate(7.92px, 0px);
115 | }
116 | .loading_leaf_3 {
117 | -webkit-animation: opacity-3 1.25s linear infinite;
118 | animation: opacity-3 1.25s linear infinite;
119 | }
120 | .loading_leaf_3:before {
121 | -webkit-transform: rotate(90deg) translate(7.92px, 0px);
122 | transform: rotate(90deg) translate(7.92px, 0px);
123 | }
124 | .loading_leaf_4 {
125 | -webkit-animation: opacity-4 1.25s linear infinite;
126 | animation: opacity-4 1.25s linear infinite;
127 | }
128 | .loading_leaf_4:before {
129 | -webkit-transform: rotate(120deg) translate(7.92px, 0px);
130 | transform: rotate(120deg) translate(7.92px, 0px);
131 | }
132 | .loading_leaf_5 {
133 | -webkit-animation: opacity-5 1.25s linear infinite;
134 | animation: opacity-5 1.25s linear infinite;
135 | }
136 | .loading_leaf_5:before {
137 | -webkit-transform: rotate(150deg) translate(7.92px, 0px);
138 | transform: rotate(150deg) translate(7.92px, 0px);
139 | }
140 | .loading_leaf_6 {
141 | -webkit-animation: opacity-6 1.25s linear infinite;
142 | animation: opacity-6 1.25s linear infinite;
143 | }
144 | .loading_leaf_6:before {
145 | -webkit-transform: rotate(180deg) translate(7.92px, 0px);
146 | transform: rotate(180deg) translate(7.92px, 0px);
147 | }
148 | .loading_leaf_7 {
149 | -webkit-animation: opacity-7 1.25s linear infinite;
150 | animation: opacity-7 1.25s linear infinite;
151 | }
152 | .loading_leaf_7:before {
153 | -webkit-transform: rotate(210deg) translate(7.92px, 0px);
154 | transform: rotate(210deg) translate(7.92px, 0px);
155 | }
156 | .loading_leaf_8 {
157 | -webkit-animation: opacity-8 1.25s linear infinite;
158 | animation: opacity-8 1.25s linear infinite;
159 | }
160 | .loading_leaf_8:before {
161 | -webkit-transform: rotate(240deg) translate(7.92px, 0px);
162 | transform: rotate(240deg) translate(7.92px, 0px);
163 | }
164 | .loading_leaf_9 {
165 | -webkit-animation: opacity-9 1.25s linear infinite;
166 | animation: opacity-9 1.25s linear infinite;
167 | }
168 | .loading_leaf_9:before {
169 | -webkit-transform: rotate(270deg) translate(7.92px, 0px);
170 | transform: rotate(270deg) translate(7.92px, 0px);
171 | }
172 | .loading_leaf_10 {
173 | -webkit-animation: opacity-10 1.25s linear infinite;
174 | animation: opacity-10 1.25s linear infinite;
175 | }
176 | .loading_leaf_10:before {
177 | -webkit-transform: rotate(300deg) translate(7.92px, 0px);
178 | transform: rotate(300deg) translate(7.92px, 0px);
179 | }
180 | .loading_leaf_11 {
181 | -webkit-animation: opacity-11 1.25s linear infinite;
182 | animation: opacity-11 1.25s linear infinite;
183 | }
184 | .loading_leaf_11:before {
185 | -webkit-transform: rotate(330deg) translate(7.92px, 0px);
186 | transform: rotate(330deg) translate(7.92px, 0px);
187 | }
188 | @-webkit-keyframes opacity-0 {
189 | 0% { opacity: 0.25; }
190 | 0.01% { opacity: 0.25; }
191 | 0.02% { opacity: 1; }
192 | 60.01% { opacity: 0.25; }
193 | 100% { opacity: 0.25; }
194 | }
195 | @-webkit-keyframes opacity-1 {
196 | 0% { opacity: 0.25; }
197 | 8.34333% { opacity: 0.25; }
198 | 8.35333% { opacity: 1; }
199 | 68.3433% { opacity: 0.25; }
200 | 100% { opacity: 0.25; }
201 | }
202 | @-webkit-keyframes opacity-2 {
203 | 0% { opacity: 0.25; }
204 | 16.6767% { opacity: 0.25; }
205 | 16.6867% { opacity: 1; }
206 | 76.6767% { opacity: 0.25; }
207 | 100% { opacity: 0.25; }
208 | }
209 | @-webkit-keyframes opacity-3 {
210 | 0% { opacity: 0.25; }
211 | 25.01% { opacity: 0.25; }
212 | 25.02% { opacity: 1; }
213 | 85.01% { opacity: 0.25; }
214 | 100% { opacity: 0.25; }
215 | }
216 | @-webkit-keyframes opacity-4 {
217 | 0% { opacity: 0.25; }
218 | 33.3433% { opacity: 0.25; }
219 | 33.3533% { opacity: 1; }
220 | 93.3433% { opacity: 0.25; }
221 | 100% { opacity: 0.25; }
222 | }
223 | @-webkit-keyframes opacity-5 {
224 | 0% { opacity: 0.270958333333333; }
225 | 41.6767% { opacity: 0.25; }
226 | 41.6867% { opacity: 1; }
227 | 1.67667% { opacity: 0.25; }
228 | 100% { opacity: 0.270958333333333; }
229 | }
230 | @-webkit-keyframes opacity-6 {
231 | 0% { opacity: 0.375125; }
232 | 50.01% { opacity: 0.25; }
233 | 50.02% { opacity: 1; }
234 | 10.01% { opacity: 0.25; }
235 | 100% { opacity: 0.375125; }
236 | }
237 | @-webkit-keyframes opacity-7 {
238 | 0% { opacity: 0.479291666666667; }
239 | 58.3433% { opacity: 0.25; }
240 | 58.3533% { opacity: 1; }
241 | 18.3433% { opacity: 0.25; }
242 | 100% { opacity: 0.479291666666667; }
243 | }
244 | @-webkit-keyframes opacity-8 {
245 | 0% { opacity: 0.583458333333333; }
246 | 66.6767% { opacity: 0.25; }
247 | 66.6867% { opacity: 1; }
248 | 26.6767% { opacity: 0.25; }
249 | 100% { opacity: 0.583458333333333; }
250 | }
251 | @-webkit-keyframes opacity-9 {
252 | 0% { opacity: 0.687625; }
253 | 75.01% { opacity: 0.25; }
254 | 75.02% { opacity: 1; }
255 | 35.01% { opacity: 0.25; }
256 | 100% { opacity: 0.687625; }
257 | }
258 | @-webkit-keyframes opacity-10 {
259 | 0% { opacity: 0.791791666666667; }
260 | 83.3433% { opacity: 0.25; }
261 | 83.3533% { opacity: 1; }
262 | 43.3433% { opacity: 0.25; }
263 | 100% { opacity: 0.791791666666667; }
264 | }
265 | @-webkit-keyframes opacity-11 {
266 | 0% { opacity: 0.895958333333333; }
267 | 91.6767% { opacity: 0.25; }
268 | 91.6867% { opacity: 1; }
269 | 51.6767% { opacity: 0.25; }
270 | 100% { opacity: 0.895958333333333; }
271 | }
--------------------------------------------------------------------------------
/src/plugins/toast/index.js:
--------------------------------------------------------------------------------
1 | var Toast = {};
2 |
3 | Toast.install = function (Vue, options) {
4 | var opt = {
5 | defaultType: 'bottom',
6 | duration: '2500'
7 | }
8 | for (var property in options) {
9 | opt[property] = options[property];
10 | }
11 | Vue.prototype.$toast = function (tips, type) {
12 |
13 | var curType = type ? type : opt.defaultType;
14 |
15 | if (document.querySelector('.lx-toast')) {
16 | return;
17 | }
18 | var toastTpl = Vue.extend({
19 | template: '' + tips + '
'
20 | });
21 | var tpl = new toastTpl().$mount().$el;
22 | document.body.appendChild(tpl);
23 | setTimeout(function () {
24 | document.body.removeChild(tpl);
25 | }, opt.duration)
26 | };
27 | ['bottom', 'center', 'top'].forEach(function (type) {
28 | Vue.prototype.$toast[type] = function (tips) {
29 | return Vue.prototype.$toast(tips, type)
30 | }
31 | });
32 |
33 | Vue.prototype.$loading = function (tips, type) {
34 | var load = document.querySelector('.lx-load-mark');
35 |
36 | if (type == 'close') {
37 | load && document.body.removeChild(load);
38 | } else {
39 | if (load) {
40 | // 如果loading还在,则不再执行
41 | return;
42 | }
43 | var loadTpl = Vue.extend({
44 | template: ''
45 | });
46 | var tpl = new loadTpl().$mount().$el;
47 | document.body.appendChild(tpl);
48 | }
49 | };
50 |
51 | ['open', 'close'].forEach(function (type) {
52 | Vue.prototype.$loading[type] = function (tips) {
53 | return Vue.prototype.$loading(tips, type)
54 | }
55 | });
56 | }
57 |
58 | export default Toast;
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 | import Home from '@/pages/home'
4 | import List from '@/pages/list'
5 |
6 | Vue.use(Router)
7 |
8 | export default new Router({
9 | routes: [
10 | {
11 | path: '/',
12 | component: Home,
13 | meta: { auth: false }
14 | },
15 | {
16 | path: '/list',
17 | component: List,
18 | meta: { auth: false }
19 | },
20 | ]
21 | })
22 |
23 |
--------------------------------------------------------------------------------
/src/store/actions.js:
--------------------------------------------------------------------------------
1 | import * as types from './mutation-types'
2 | import { timeFormat } from '@/utils'
3 | import axios from 'axios'
4 |
5 | export default {
6 | getComments({ commit, state }, data) {
7 | if (data > -1) {
8 | axios.get(`api/project/${data}/comments.action`, {})
9 | .then((res) => {
10 | const comments = res.data.result;
11 | commit(types.getComments, comments)
12 | })
13 | }
14 | },
15 | addComment({ commit, state }) {
16 | const comment = state.home.comment.trim();
17 | if (comment.length > 0) {
18 | axios.post('api/comment/add.action', {
19 | comment
20 | }).then((res) => {
21 | if (res.data.error === 0) {
22 | commit(types.addComment, {
23 | author: 'Visitor Rookie',
24 | comment,
25 | date: timeFormat('yyyy-MM-dd hh:mm:ss')
26 | });
27 | }
28 | })
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | }
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import state from './state';
4 | import mutations from './mutations'
5 | import actions from './actions'
6 |
7 | Vue.use(Vuex)
8 |
9 | export default new Vuex.Store({
10 | state,
11 | actions,
12 | mutations,
13 | })
--------------------------------------------------------------------------------
/src/store/mutation-types.js:
--------------------------------------------------------------------------------
1 | export const getComments = 'getComments'
2 | export const addComment = 'addComment'
3 | export const updateComment = 'updateComment'
--------------------------------------------------------------------------------
/src/store/mutations.js:
--------------------------------------------------------------------------------
1 | import * as types from './mutation-types'
2 |
3 | export default {
4 | [types.getComments](state, payload) {
5 | state.home.list = state.home.list.concat(payload);
6 | if (payload.length === 0) {
7 | state.home.page = -1;
8 | } else {
9 | state.home.page++;
10 | }
11 | },
12 | [types.addComment](state, payload) {
13 | state.home.list.unshift(payload)
14 | state.home.comment = ''
15 | },
16 | [types.updateComment](state, payload) {
17 | state.home.comment = payload
18 | }
19 | }
--------------------------------------------------------------------------------
/src/store/state.js:
--------------------------------------------------------------------------------
1 | export default {
2 | home: {
3 | list: [],
4 | comment: '',
5 | page: 1
6 | }
7 | }
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | export function timeFormat(fmt) {
2 | var time = new Date();
3 | var o = {
4 | "M+": time.getMonth() + 1,
5 | "d+": time.getDate(),
6 | "h+": time.getHours(),
7 | "m+": time.getMinutes(),
8 | "s+": time.getSeconds(),
9 | "q+": Math.floor((time.getMonth() + 3) / 3),
10 | "S": time.getMilliseconds()
11 | };
12 | if (/(y+)/.test(fmt))
13 | fmt = fmt.replace(RegExp.$1, (time.getFullYear() + "").substr(4 - RegExp.$1.length));
14 | for (var k in o)
15 | if (new RegExp("(" + k + ")").test(fmt))
16 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
17 | return fmt;
18 | }
19 |
20 | export function compact(array) {
21 | function f(element) {
22 | if (element !== false || element !== null || element !== 0 || element !== "" || element !== undefined || element !== NaN) {
23 | return element;
24 | }
25 | }
26 | var filtered = array.filter(f);
27 | return filtered;
28 | }
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yanm1ng/vue-starter-kit/365c669cad4c408a2542cadbdcf6c32ae141dc44/static/.gitkeep
--------------------------------------------------------------------------------
/test/components/comment.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import comment from '@/components/comment'
3 |
4 | import { getRenderedVm } from '../utils';
5 |
6 | describe('test components/comment.vue', () => {
7 | it('组件加载后,list', () => {
8 | let vm = getRenderedVm(comment, {
9 | list: [
10 | {
11 | author: 'author',
12 | comment: 'comment',
13 | date: '2017-04-25 15:50:11'
14 | }
15 | ]
16 | });
17 |
18 | expect(vm.list).length === 1;
19 | });
20 | })
--------------------------------------------------------------------------------
/test/components/header.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import header from '@/components/header'
3 |
4 | import { getRenderedVm } from '../utils';
5 |
6 | describe('test components/header.vue', () => {
7 | it('组件加载后,title应该是「test」', () => {
8 | let vm = getRenderedVm(header, {
9 | title: 'test'
10 | });
11 |
12 | expect(vm.title).toEqual('test');
13 | });
14 | })
--------------------------------------------------------------------------------
/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | var webpackConfig = require('../build/webpack.test.conf')
2 |
3 | module.exports = function (config) {
4 | config.set({
5 | port: 9876,
6 | browsers: ['PhantomJS'],
7 | frameworks: ['jasmine'],
8 | reporters: ['progress'],
9 | files: [
10 | '**/*.spec.js'
11 | ],
12 | preprocessors: {
13 | '**/*.spec.js': ['webpack']
14 | },
15 |
16 | webpack: webpackConfig,
17 |
18 | webpackMiddleware: {
19 | noInfo: true
20 | }
21 |
22 | })
23 | }
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | export function getRenderedVm(Component, propsData) {
4 | const Ctor = Vue.extend(Component)
5 | const vm = new Ctor({ propsData }).$mount()
6 | return vm;
7 | }
--------------------------------------------------------------------------------
/test/vuex/mutations.spec.js:
--------------------------------------------------------------------------------
1 | import mutations from '@/store/mutations'
2 | import state from '@/store/state'
3 |
4 | describe('test mutations', () => {
5 | it('getComments', () => {
6 | mutations.getComments(state, [
7 | { author: 'yanm1ng', comment: 'test', date: '2017-04-26 11:22:11' },
8 | { author: 'rookie', comment: 'test', date: '2017-04-24 09:12:32' }
9 | ])
10 |
11 | expect(state.home.list).length === 3
12 | });
13 | it('addComment', () => {
14 |
15 | mutations.addComment(state, 5)
16 |
17 | expect(state.home.list).length === 1 && expect(state.home.comment).toEqual('')
18 | });
19 | it('updateComment', () => {
20 |
21 | mutations.updateComment(state, 'new comment')
22 |
23 | expect(state.home.comment).toEqual('new comment')
24 | });
25 | })
--------------------------------------------------------------------------------