├── .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
├── config
├── dev.env.js
├── index.js
└── prod.env.js
├── index.html
├── package.json
├── src
├── App.vue
├── assets
│ ├── 2x
│ │ ├── swiper1@2x.jpg
│ │ ├── swiper2@2x.jpg
│ │ ├── swiper3@2x.jpg
│ │ └── vue@2x.jpg
│ ├── 3x
│ │ ├── swiper1@3x.jpg
│ │ ├── swiper2@3x.jpg
│ │ ├── swiper3@3x.jpg
│ │ └── vue@3x.jpg
│ ├── head.jpg
│ ├── loading.gif
│ ├── logo.png
│ ├── select.png
│ ├── transition.gif
│ └── vue.jpg
├── base
│ ├── css
│ │ ├── _base.scss
│ │ └── _normalize.scss
│ └── js
│ │ ├── api.js
│ │ ├── base.js
│ │ └── util.js
├── components
│ ├── foot-nav.vue
│ ├── loading.vue
│ ├── pulling-word.vue
│ ├── scroller.vue
│ └── swiper.vue
├── main.js
├── pages
│ ├── car.vue
│ ├── classify.vue
│ ├── film-detail.vue
│ ├── film.vue
│ ├── index.vue
│ ├── login.vue
│ ├── main.vue
│ ├── search.vue
│ └── vip-message.vue
├── router
│ └── index.js
└── store
│ └── index.js
├── static
└── .gitkeep
└── webpack.mdown
/.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Editor directories and files
9 | .idea
10 | *.suo
11 | *.ntvs*
12 | *.njsproj
13 | *.sln
14 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserlist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # app
2 |
3 | > A Vue.js project
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 | ```
20 |
21 | For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
22 |
--------------------------------------------------------------------------------
/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 app = express()
12 | var webpack = require('webpack')
13 | var proxyMiddleware = require('http-proxy-middleware')
14 | var webpackConfig = require('./webpack.dev.conf')
15 |
16 | // default port where dev server listens for incoming traffic
17 | var port = process.env.PORT || config.dev.port
18 | // automatically open browser, if not set will be false
19 | var autoOpenBrowser = !!config.dev.autoOpenBrowser
20 | // Define HTTP proxies to your custom API backend
21 | // https://github.com/chimurai/http-proxy-middleware
22 | var proxyTable = config.dev.proxyTable
23 |
24 | app.post("/login",function(req,res){
25 | res.send({"code":"s"})
26 | })
27 |
28 |
29 |
30 | var compiler = webpack(webpackConfig)
31 |
32 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
33 | publicPath: webpackConfig.output.publicPath,
34 | quiet: true
35 | })
36 |
37 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
38 | log: () => {}
39 | })
40 | // force page reload when html-webpack-plugin template changes
41 | compiler.plugin('compilation', function (compilation) {
42 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
43 | hotMiddleware.publish({ action: 'reload' })
44 | cb()
45 | })
46 | })
47 |
48 | // proxy api requests
49 | Object.keys(proxyTable).forEach(function (context) {
50 | var options = proxyTable[context]
51 | if (typeof options === 'string') {
52 | options = { target: options }
53 | }
54 | app.use(proxyMiddleware(options.filter || context, options))
55 | })
56 |
57 | // handle fallback for HTML5 history API
58 | app.use(require('connect-history-api-fallback')())
59 |
60 | // serve webpack bundle output
61 | app.use(devMiddleware)
62 |
63 | // enable hot-reload and state-preserving
64 | // compilation error display
65 | app.use(hotMiddleware)
66 |
67 | // serve pure static assets
68 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
69 | app.use(staticPath, express.static('./static'))
70 |
71 | var uri = 'http://localhost:' + port
72 |
73 | var _resolve
74 | var readyPromise = new Promise(resolve => {
75 | _resolve = resolve
76 | })
77 |
78 | console.log('> Starting dev server...')
79 | devMiddleware.waitUntilValid(() => {
80 | console.log('> Listening at ' + uri + '\n')
81 | // when env is testing, don't need open it
82 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
83 | opn(uri)
84 | }
85 | _resolve()
86 | })
87 |
88 | var server = app.listen(port)
89 |
90 | module.exports = {
91 | ready: readyPromise,
92 | close: () => {
93 | server.close()
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/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 | output.push({
66 | test: new RegExp('\\.' + extension + '$'),
67 | use: loader
68 | })
69 | }
70 | return output
71 | }
72 |
--------------------------------------------------------------------------------
/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 | '@': resolve('src'),
25 | 'components': resolve('src/components'),
26 | 'pages': resolve('src/pages'),
27 | }
28 | },
29 | module: {
30 | rules: [
31 | {
32 | test: /\.vue$/,
33 | loader: 'vue-loader',
34 | options: vueLoaderConfig
35 | },
36 | {
37 | test: /\.js$/,
38 | loader: 'babel-loader',
39 | include: [resolve('src'), resolve('test')]
40 | },
41 | {
42 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
43 | loader: 'url-loader',
44 | options: {
45 | limit: 10000,
46 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
47 | }
48 | },
49 | {
50 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
51 | loader: 'url-loader',
52 | options: {
53 | limit: 10000,
54 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
55 | }
56 | }
57 | ]
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/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 | // 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/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 | var AutoDllPlugin = require('autodll-webpack-plugin');
12 |
13 | var env = config.build.env
14 |
15 | var webpackConfig = merge(baseWebpackConfig, {
16 | module: {
17 | rules: utils.styleLoaders({
18 | sourceMap: config.build.productionSourceMap,
19 | extract: true
20 | })
21 | },
22 | devtool: config.build.productionSourceMap ? '#source-map' : false,
23 | output: {
24 | path: config.build.assetsRoot,
25 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
26 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
27 | },
28 | plugins: [
29 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
30 | new webpack.DefinePlugin({
31 | 'process.env': env
32 | }),
33 | new webpack.optimize.UglifyJsPlugin({
34 | compress: {
35 | warnings: false
36 | },
37 | sourceMap: true
38 | }),
39 | // extract css into its own file
40 | new ExtractTextPlugin({
41 | filename: utils.assetsPath('css/[name].[contenthash].css')
42 | }),
43 | // Compress extracted CSS. We are using this plugin so that possible
44 | // duplicated CSS from different components can be deduped.
45 | new OptimizeCSSPlugin({
46 | cssProcessorOptions: {
47 | safe: true
48 | }
49 | }),
50 | // generate dist index.html with correct asset hash for caching.
51 | // you can customize output by editing /index.html
52 | // see https://github.com/ampedandwired/html-webpack-plugin
53 | new HtmlWebpackPlugin({
54 | filename: config.build.index,
55 | template: 'index.html',
56 | inject: true,
57 | minify: {
58 | removeComments: true,
59 | collapseWhitespace: true,
60 | removeAttributeQuotes: true
61 | // more options:
62 | // https://github.com/kangax/html-minifier#options-quick-reference
63 | },
64 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
65 | chunksSortMode: 'dependency'
66 | }),
67 | // split vendor js into its own file
68 | new webpack.optimize.CommonsChunkPlugin({
69 | name: 'vendor',
70 | minChunks: function (module, count) {
71 | // any required modules inside node_modules are extracted to vendor
72 | return (
73 | module.resource &&
74 | /\.js$/.test(module.resource) &&
75 | module.resource.indexOf(
76 | path.join(__dirname, '../node_modules')
77 | ) === 0
78 | )
79 | }
80 | }),
81 | // extract webpack runtime and module manifest to its own file in order to
82 | // prevent vendor hash from being updated whenever app bundle is updated
83 | new webpack.optimize.CommonsChunkPlugin({
84 | name: 'manifest',
85 | chunks: ['vendor']
86 | }),
87 | /* new AutoDllPlugin({
88 | inject: true, // will inject the DLL bundle to index.html
89 | debug: true,
90 | filename: '[name]_[hash].js',
91 | path: './dll',
92 | entry: {
93 | vendor: [
94 | 'vue','vuex','vue-router','axios','vue-lazyload','vue-touch','better-scroll'
95 | ]
96 | }
97 | }) ,*/
98 |
99 | // copy custom static assets
100 | new CopyWebpackPlugin([
101 | {
102 | from: path.resolve(__dirname, '../static'),
103 | to: config.build.assetsSubDirectory,
104 | ignore: ['.*']
105 | }
106 | ])
107 | ]
108 | })
109 |
110 | if (config.build.productionGzip) {
111 | var CompressionWebpackPlugin = require('compression-webpack-plugin')
112 |
113 | webpackConfig.plugins.push(
114 | new CompressionWebpackPlugin({
115 | asset: '[path].gz[query]',
116 | algorithm: 'gzip',
117 | test: new RegExp(
118 | '\\.(' +
119 | config.build.productionGzipExtensions.join('|') +
120 | ')$'
121 | ),
122 | threshold: 10240,
123 | minRatio: 0.8
124 | })
125 | )
126 | }
127 |
128 | if (config.build.bundleAnalyzerReport) {
129 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
130 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
131 | }
132 |
133 | module.exports = webpackConfig
134 |
--------------------------------------------------------------------------------
/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 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: require('./prod.env'),
7 | index: path.resolve(__dirname, '../dist/index.html'),
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'static',
10 | assetsPublicPath: '/',
11 | productionSourceMap: true,
12 | // Gzip off by default as many popular static hosts such as
13 | // Surge or Netlify already gzip all static assets for you.
14 | // Before setting to `true`, make sure to:
15 | // npm install --save-dev compression-webpack-plugin
16 | productionGzip: false,
17 | productionGzipExtensions: ['js', 'css'],
18 | // Run the build command with an extra argument to
19 | // View the bundle analyzer report after build finishes:
20 | // `npm run build --report`
21 | // Set to `true` or `false` to always turn it on or off
22 | bundleAnalyzerReport: process.env.npm_config_report
23 | },
24 | dev: {
25 | env: require('./dev.env'),
26 | port: 8088,
27 | autoOpenBrowser: true,
28 | assetsSubDirectory: 'static',
29 | assetsPublicPath: '/',
30 | proxyTable: {},
31 | // CSS Sourcemaps off by default because relative paths are "buggy"
32 | // with this option, according to the CSS-Loader README
33 | // (https://github.com/webpack/css-loader#sourcemaps)
34 | // In our experience, they generally work as expected,
35 | // just be aware of this issue when enabling this option.
36 | cssSourceMap: false
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | app
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "linrun <411690557@qq.com>",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "start": "node build/dev-server.js",
10 | "build": "node build/build.js"
11 | },
12 | "dependencies": {
13 | "better-scroll": "^1.2.6",
14 | "vue": "^2.3.3",
15 | "vue-awesome-swiper": "^2.5.4",
16 | "vue-lazyload": "^1.0.5",
17 | "vue-router": "^2.3.1",
18 | "vue-touch": "^2.0.0-beta.4",
19 | "vuex": "^2.3.1"
20 | },
21 | "devDependencies": {
22 | "autodll-webpack-plugin": "^0.2.1",
23 | "autoprefixer": "^6.7.2",
24 | "axios": "^0.19.0",
25 | "babel-core": "^6.22.1",
26 | "babel-loader": "^6.2.10",
27 | "babel-plugin-transform-runtime": "^6.22.0",
28 | "babel-preset-env": "^1.3.2",
29 | "babel-preset-stage-2": "^6.22.0",
30 | "babel-register": "^6.22.0",
31 | "chalk": "^1.1.3",
32 | "connect-history-api-fallback": "^1.3.0",
33 | "copy-webpack-plugin": "^4.0.1",
34 | "css-loader": "^0.28.0",
35 | "eventsource-polyfill": "^0.9.6",
36 | "express": "^4.14.1",
37 | "extract-text-webpack-plugin": "^2.0.0",
38 | "file-loader": "^0.11.1",
39 | "friendly-errors-webpack-plugin": "^1.1.3",
40 | "html-webpack-plugin": "^2.28.0",
41 | "http-proxy-middleware": "^0.17.3",
42 | "iscroll": "^5.2.0",
43 | "node-sass": "^4.5.3",
44 | "opn": "^4.0.2",
45 | "optimize-css-assets-webpack-plugin": "^1.3.0",
46 | "ora": "^1.2.0",
47 | "rimraf": "^2.6.0",
48 | "sass-loader": "^6.0.6",
49 | "semver": "^5.3.0",
50 | "shelljs": "^0.7.6",
51 | "url-loader": "^0.5.8",
52 | "vue-loader": "^12.1.0",
53 | "vue-style-loader": "^3.0.1",
54 | "vue-template-compiler": "^2.3.3",
55 | "webpack": "^2.6.1",
56 | "webpack-bundle-analyzer": "^2.2.1",
57 | "webpack-dev-middleware": "^1.10.0",
58 | "webpack-hot-middleware": "^2.18.0",
59 | "webpack-merge": "^4.1.0"
60 | },
61 | "engines": {
62 | "node": ">= 4.0.0",
63 | "npm": ">= 3.0.0"
64 | },
65 | "browserslist": [
66 | "> 1%",
67 | "last 2 versions",
68 | "not ie <= 8"
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
42 |
--------------------------------------------------------------------------------
/src/assets/2x/swiper1@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/2x/swiper1@2x.jpg
--------------------------------------------------------------------------------
/src/assets/2x/swiper2@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/2x/swiper2@2x.jpg
--------------------------------------------------------------------------------
/src/assets/2x/swiper3@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/2x/swiper3@2x.jpg
--------------------------------------------------------------------------------
/src/assets/2x/vue@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/2x/vue@2x.jpg
--------------------------------------------------------------------------------
/src/assets/3x/swiper1@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/3x/swiper1@3x.jpg
--------------------------------------------------------------------------------
/src/assets/3x/swiper2@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/3x/swiper2@3x.jpg
--------------------------------------------------------------------------------
/src/assets/3x/swiper3@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/3x/swiper3@3x.jpg
--------------------------------------------------------------------------------
/src/assets/3x/vue@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/3x/vue@3x.jpg
--------------------------------------------------------------------------------
/src/assets/head.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/head.jpg
--------------------------------------------------------------------------------
/src/assets/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/loading.gif
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/select.png
--------------------------------------------------------------------------------
/src/assets/transition.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/transition.gif
--------------------------------------------------------------------------------
/src/assets/vue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/src/assets/vue.jpg
--------------------------------------------------------------------------------
/src/base/css/_base.scss:
--------------------------------------------------------------------------------
1 | @mixin fz($font-size) {
2 | font-size: $font-size;
3 | [data-dpr="2"] & {
4 | font-size: $font-size * 2;
5 | }
6 | [data-dpr="3"] & {
7 | font-size: $font-size * 3;
8 | }
9 | }
10 | @mixin dpr-img($url,$name,$type:".jpg"){
11 | background-image: url($url+"2x/"+ $name+"@2x"+$type);
12 | [data-dpr="3"] &{
13 | background-image: url($url+"3x/"+ $name+"@3x"+$type);
14 | }
15 | }
16 |
17 |
18 |
19 | @mixin table-center {
20 | display: table-cell;
21 | vertical-align: middle;
22 | text-align: center;
23 | }
24 |
25 | @mixin poa-center($w, $h) {
26 | position: absolute;
27 | width: $w;
28 | height: $h;
29 | left: 50%;
30 | top: 50%;
31 | // margin-left:-($w/2);
32 | // margin-top:-($h/2);
33 | transition: translate(-50%, -50%)
34 | }
35 |
36 | @mixin flex-center {
37 | display: flex;
38 | justify-content: center;
39 | align-items: center;
40 | }
41 |
42 | @mixin t-overflow($line:1) {
43 | @if $line==1 {
44 | overflow: hidden;
45 | text-overflow: ellipsis;
46 | white-space: nowrap;
47 | }
48 | @else {
49 | display: -webkit-box;
50 | -webkit-line-clamp: $line;
51 | -webkit-box-orient: vertical;
52 | overflow: hidden;
53 | text-overflow: ellipsis;
54 | }
55 | }
56 |
57 | @function torem($px) {
58 | @return $px / 75px * 1rem;
59 | }
60 |
61 |
62 | $mainColor:#77b59c;
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/src/base/css/_normalize.scss:
--------------------------------------------------------------------------------
1 |
2 | @charset "utf-8";
3 |
4 | html{
5 |
6 | color: #000;
7 | background: #fff;
8 | overflow-y: scroll;
9 | -webkit-text-size-adjust: 100%;
10 | -ms-text-size-adjust: 100%;
11 |
12 | }
13 | html * {
14 | outline: none;
15 | -webkit-text-size-adjust: none;
16 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
17 | }
18 | html,
19 | body {
20 | font-family: sans-serif;
21 | height: 100%;
22 | width:100%;
23 | // overflow:hidden;
24 | }
25 |
26 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,
27 | th,td,hr,button,article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section {
28 | margin: 0;
29 | padding: 0;
30 | }
31 | input,
32 | select,
33 | textarea {
34 | font-size: 100%;
35 | }
36 |
37 | table {
38 | border-collapse: collapse;
39 | border-spacing: 0;
40 | }
41 |
42 | fieldset,
43 | img {
44 | border: 0;
45 | }
46 |
47 | abbr,
48 | acronym {
49 | border: 0;
50 | font-variant: normal;
51 | }
52 |
53 | del {
54 | text-decoration: line-through;
55 | }
56 | address,
57 | caption,
58 | cite,
59 | code,
60 | dfn,
61 | em,
62 | th,
63 | i,
64 | var {
65 | font-style: normal;
66 | font-weight: 500;
67 | }
68 |
69 | ol,
70 | ul {
71 | list-style: none;
72 | }
73 |
74 | caption,
75 | th {
76 | text-align: left;
77 | }
78 |
79 | h1,h2,h3,h4,h5,h6 {
80 | font-size: 100%;
81 | font-weight: 500;
82 | }
83 | q:before,
84 | q:after {
85 | content: '';
86 | }
87 |
88 | sub,
89 | sup {
90 | font-size: 75%;
91 | line-height: 0;
92 | position: relative;
93 | vertical-align: baseline;
94 | }
95 | sup {
96 | top: -0.5em;
97 | }
98 | sub {
99 | bottom: -0.25em;
100 | }
101 |
102 | a:hover {
103 | text-decoration: underline;
104 | }
105 |
106 | ins,
107 | a,
108 | a:active,
109 | a:visited,
110 | a:link{
111 | text-decoration: none;
112 | }
113 | .clearfix{
114 | &:after{
115 | display: table;
116 | clear: both;
117 | content: "";
118 | visibility: hidden;;
119 | height: 0;
120 | }
121 | }
122 | body{
123 | height:100%;
124 | font-family: "Microsoft YaHei";
125 | }
126 |
127 |
--------------------------------------------------------------------------------
/src/base/js/api.js:
--------------------------------------------------------------------------------
1 | import {getDeviceRatio} from './util.js'
2 | export const api="https://api.douban.com/v2/movie/";
3 | export var DEVICE_RATIO=getDeviceRatio();
4 |
5 | export var DOWN_CONFIG={
6 | threshold:80*DEVICE_RATIO,
7 | stop:40*DEVICE_RATIO
8 | }
9 | /*上拉配置*/
10 | export var UP_CONFIG={
11 | threshold:-80*DEVICE_RATIO,
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/src/base/js/base.js:
--------------------------------------------------------------------------------
1 | ;(function(win, lib) {
2 | var doc = win.document;
3 | var docEl = doc.documentElement;
4 | var metaEl = doc.querySelector('meta[name="viewport"]');
5 | var flexibleEl = doc.querySelector('meta[name="flexible"]');
6 | var dpr = 0;
7 | var scale = 0;
8 | var tid;
9 | var flexible = lib.flexible || (lib.flexible = {});
10 |
11 | if (metaEl) {
12 | console.warn('将根据已有的meta标签来设置缩放比例');
13 | var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
14 | if (match) {
15 | scale = parseFloat(match[1]);
16 | dpr = parseInt(1 / scale);
17 | }
18 | } else if (flexibleEl) {
19 | var content = flexibleEl.getAttribute('content');
20 | if (content) {
21 | var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
22 | var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
23 | if (initialDpr) {
24 | dpr = parseFloat(initialDpr[1]);
25 | scale = parseFloat((1 / dpr).toFixed(2));
26 | }
27 | if (maximumDpr) {
28 | dpr = parseFloat(maximumDpr[1]);
29 | scale = parseFloat((1 / dpr).toFixed(2));
30 | }
31 | }
32 | }
33 |
34 | if (!dpr && !scale) {
35 | var isAndroid = win.navigator.appVersion.match(/android/gi);
36 | var isIPhone = win.navigator.appVersion.match(/iphone/gi);
37 | var devicePixelRatio = win.devicePixelRatio;
38 | if (isIPhone) {
39 | // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
40 | if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
41 | dpr = 3;
42 | } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
43 | dpr = 2;
44 | } else {
45 | dpr = 1;
46 | }
47 | } else {
48 | // 其他设备下,仍旧使用1倍的方案
49 | dpr = 1;
50 | }
51 | scale = 1 / dpr;
52 | }
53 |
54 | docEl.setAttribute('data-dpr', dpr);
55 | if (!metaEl) {
56 | metaEl = doc.createElement('meta');
57 | metaEl.setAttribute('name', 'viewport');
58 | metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
59 | if (docEl.firstElementChild) {
60 | docEl.firstElementChild.appendChild(metaEl);
61 | } else {
62 | var wrap = doc.createElement('div');
63 | wrap.appendChild(metaEl);
64 | doc.write(wrap.innerHTML);
65 | }
66 | }
67 |
68 | function refreshRem(){
69 | var width = docEl.getBoundingClientRect().width;
70 | if (width / dpr > 540) {
71 | width = 540 * dpr;
72 | }
73 | var rem = width / 10;
74 | docEl.style.fontSize = rem + 'px';
75 | flexible.rem = win.rem = rem;
76 | }
77 |
78 | win.addEventListener('resize', function() {
79 | clearTimeout(tid);
80 | tid = setTimeout(refreshRem, 300);
81 | }, false);
82 | win.addEventListener('pageshow', function(e) {
83 | if (e.persisted) {
84 | clearTimeout(tid);
85 | tid = setTimeout(refreshRem, 300);
86 | }
87 | }, false);
88 |
89 | if (doc.readyState === 'complete') {
90 | doc.body.style.fontSize = 12 * dpr + 'px';
91 | } else {
92 | doc.addEventListener('DOMContentLoaded', function(e) {
93 | doc.body.style.fontSize = 12 * dpr + 'px';
94 | }, false);
95 | }
96 |
97 |
98 | refreshRem();
99 |
100 | flexible.dpr = win.dpr = dpr;
101 | flexible.refreshRem = refreshRem;
102 | flexible.rem2px = function(d) {
103 | var val = parseFloat(d) * this.rem;
104 | if (typeof d === 'string' && d.match(/rem$/)) {
105 | val += 'px';
106 | }
107 | return val;
108 | }
109 | flexible.px2rem = function(d) {
110 | var val = parseFloat(d) / this.rem;
111 | if (typeof d === 'string' && d.match(/px$/)) {
112 | val += 'rem';
113 | }
114 | return val;
115 | }
116 |
117 | })(window, window['lib'] || (window['lib'] = {}));
--------------------------------------------------------------------------------
/src/base/js/util.js:
--------------------------------------------------------------------------------
1 | export function getStyle(el,style){
2 | return parseInt(window.getComputedStyle(el, false)[style])
3 | }
4 |
5 | export function getDeviceRatio(){
6 | var isAndroid = window.navigator.appVersion.match(/android/gi);
7 | var isIPhone = window.navigator.appVersion.match(/iphone/gi);
8 | var devicePixelRatio = window.devicePixelRatio;
9 | var dpr;
10 | if (isIPhone) {
11 | // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
12 | if (devicePixelRatio >= 3) {
13 | dpr = 3;
14 | } else if (devicePixelRatio >= 2){
15 | dpr = 2;
16 | } else {
17 | dpr = 1;
18 | }
19 | } else {
20 | // 其他设备下,仍旧使用1倍的方案
21 | dpr = 1;
22 | }
23 | return dpr
24 | }
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/foot-nav.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
27 |
28 |
74 |
--------------------------------------------------------------------------------
/src/components/loading.vue:
--------------------------------------------------------------------------------
1 |
2 |
{{loadingWord}}
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/pulling-word.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{loadingWord}}
4 |
5 |
6 |
7 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/scroller.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
138 |
139 |
--------------------------------------------------------------------------------
/src/components/swiper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
25 |
26 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 | import router from './router'
4 | import './base/js/base.js'
5 | import { store } from './store/index.js'
6 | import 'swiper/dist/css/swiper.css'
7 |
8 | /*滑动事件*/
9 | import VueTouch from 'vue-touch'
10 | Vue.use(VueTouch, { name: 'v-touch' })
11 |
12 | /*ajax请求*/
13 | import axios from 'axios'
14 | axios.defaults.baseURL = 'https://api.douban.com/v2/movie'
15 | Vue.prototype.$ajax = axios
16 |
17 | /*图片懒加载*/
18 | import VueLazyload from 'vue-lazyload'
19 | Vue.use(VueLazyload, {
20 | preLoad: 1.3,
21 | loading: require('@/assets/head.jpg'),
22 | attempt: 1
23 | })
24 |
25 | /* 轮播图 */
26 | import VueAwesomeSwiper from 'vue-awesome-swiper'
27 | Vue.use(VueAwesomeSwiper)
28 |
29 | Vue.config.productionTip = false
30 |
31 | /* eslint-disable no-new */
32 | new Vue({
33 | el: '#app',
34 | router,
35 | store,
36 | render: h => h(App)
37 | })
38 |
--------------------------------------------------------------------------------
/src/pages/car.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
99 |
100 |
219 |
--------------------------------------------------------------------------------
/src/pages/classify.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
31 |
32 |
215 |
216 |
315 |
--------------------------------------------------------------------------------
/src/pages/film-detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
![]()
10 |
11 |
12 |
{{filmDetail.title}}
13 |
14 | {{rating}}
15 |
16 |
17 |
{{filmDetail.ratings_count}}人评价
18 |
19 |
20 |
21 |
22 |
导演:{{filterData(filmDetail.directors)}}
23 |
类型:{{filmDetail.genres&&filmDetail.genres.join(" / ")}}
24 |
年份:{{filmDetail.year}}
25 |
地区:{{filmDetail.countries&&filmDetail.countries.join(" / ")}}
26 |
27 |
28 |
{{filmDetail.title}}的剧情简介
29 |
{{filmDetail.summary}}
30 |
31 |
32 |
33 | +1
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
120 |
121 |
--------------------------------------------------------------------------------
/src/pages/film.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{type}}
5 | 更多
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{v.title}}
14 |
评分:{{v.rating.average}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
78 |
79 |
--------------------------------------------------------------------------------
/src/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
15 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
63 |
64 |
--------------------------------------------------------------------------------
/src/pages/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 账号登录
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
49 |
50 |
101 |
--------------------------------------------------------------------------------
/src/pages/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
20 |
21 |
24 |
--------------------------------------------------------------------------------
/src/pages/search.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 |
![]()
25 |
26 |
27 |
{{v.title}}
28 |
导演:{{filterDirectors(v.directors)}}
29 |
年份:{{v.year}}{{v.stock}}
30 |
类别:{{v.genres.join(" / ")}}
31 |
评分:{{v.rating.average}}分
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
135 |
136 |
--------------------------------------------------------------------------------
/src/pages/vip-message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
7 |
8 |
昵称:linrunzheng
9 |
10 |
地址:https://github.com/linrunzheng
11 |
12 |
13 |
14 |
15 |
16 |
44 |
45 |
46 |
52 |
53 |
59 |
60 |
61 |
67 |
68 |
74 |
75 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | - 其他设置
88 | - 其他设置
89 | - 其他设置
90 | - 其他设置
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
120 |
121 |
292 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | const Index = () => import('pages/index')
5 | const Car = () => import('pages/car')
6 | const Search = () => import('pages/search')
7 | const Login = () => import('pages/login')
8 | const Main = () => import('pages/main')
9 | const vipMessage = () => import('pages/vip-message')
10 | const Classify = () => import('pages/classify')
11 | const FilmDetail = () => import('pages/film-detail')
12 |
13 | Vue.use(Router)
14 |
15 | export default new Router({
16 | routes: [
17 | {
18 | path: '',
19 | redirect: '/home'
20 | },
21 | {
22 | path: '/home',
23 | component: Main,
24 | children: [
25 | {
26 | path: '',
27 | redirect: 'index'
28 | },
29 |
30 | {
31 | name: 'index',
32 | path: 'index',
33 | component: Index
34 | },
35 | {
36 | name: 'search',
37 | path: 'search',
38 | component: Search
39 | },
40 | {
41 | name: 'vip',
42 | path: 'vip',
43 | component: vipMessage
44 | }
45 | ]
46 | },
47 | {
48 | path: '/classify/:type',
49 | component: Classify,
50 | name: 'classify'
51 | },
52 | {
53 | path: '/login',
54 | component: Login
55 | },
56 | {
57 | name: 'car',
58 | path: '/car',
59 | component: Car
60 | },
61 | {
62 | path: '/film-detail/:id',
63 | component: FilmDetail
64 | }
65 | ]
66 | })
67 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | Vue.use(Vuex);
4 |
5 | export const store= new Vuex.Store({
6 | state:{
7 | goodsList:localStorage["goodsList"]?JSON.parse(localStorage["goodsList"]): []
8 | },
9 | getters:{
10 | sum:state=>{
11 | var total=0;
12 | state.goodsList.forEach((item)=>{
13 | if(item.select){
14 | total+=item.price*item.number
15 | }
16 | })
17 | return total
18 | },
19 | goddsNumber:state=>{
20 | return state.goodsList.length
21 | }
22 | },
23 | mutations:{
24 | addGoods:(state,data)=>{
25 | state.goodsList.push(data);
26 | localStorage.setItem("goodsList",JSON.stringify(state.goodsList));
27 | },
28 | deleteGoods(state,index){
29 | state.goodsList.splice(index,1);
30 | localStorage.setItem("goodsList",JSON.stringify(state.goodsList));
31 | },
32 | updateGoods(state,data){
33 | const {index,key,value}=data;
34 | state.goodsList[index][key]=value;
35 | localStorage.setItem("goodsList",JSON.stringify(state.goodsList));
36 | }
37 | }
38 | })
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/linrunzheng/vueApp/f3f4b0623f29cc106366a70b8dc4c797b0812398/static/.gitkeep
--------------------------------------------------------------------------------
/webpack.mdown:
--------------------------------------------------------------------------------
1 | 上拉加载以及下拉刷新都是移动端很常见的功能,在搜索或者一些分类列表页面常常会用到。跟横向滚动一样,我们还是采用better-scroll这个库来实现。由于better已经更新了新的版本,之前是0.几的版本,更新了一下发现,现在已经是1.2.6这个版本了,新版本多了一个比较好用的api,所以我也重写了之前的代码,用新的api来实现上拉加载以及下拉刷新。
2 |
3 | 首先把列表基本的样式写好,这里就略过了,然后引入better-scroll库
4 | import BScroll from 'better-scroll'
5 |
6 | 其次,在mounted生命周期实例化scroll,可以获取完数据后再new,也可以先new后,获取完数据调用refresh()。实例时需要传入一个配置参数,由于参数比较多,具体的请参考文档,这里只讲2个重点的:
7 |
8 |
9 | //是否开启下拉刷新,可传入true或者false,如果需要更多配置可以传入一个对象
10 | /**
11 | *
12 | * @param threshold 触发事件的阀值,即滑动多少距离触发
13 | * @param stop 下拉刷新后回滚距离顶部的距离(为了给loading留出一点空间)
14 | */
15 |
16 | pullDownRefresh:{
17 | threshold:80,
18 | stop:40
19 | }
20 |
21 | //是否开启上拉加载,同上,上拉无stop参数,这里需要注意是负数
22 | pullUpLoad:{
23 | threshold:-80,
24 | }
25 |
26 | 以上的数字个人感觉比较合适,但是这里有一个问题,由于我采用的是淘宝flexible.js来适配,这就导致:在安卓下80这个距离是合适的,但是到了iphone6s下,由于被缩放了3陪,所以现在80在iphone6s下就是27左右了,所以对于不同缩放程度的屏幕,还需要乘以对应的缩放比。
27 |
28 |
29 | 淘宝flexible.js里面其实已经有这个获取屏幕缩放比方法,这里直接从里面拿:
30 |
31 | export function getDeviceRatio(){
32 | var isAndroid = window.navigator.appVersion.match(/android/gi);
33 | var isIPhone = window.navigator.appVersion.match(/iphone/gi);
34 | var devicePixelRatio = window.devicePixelRatio;
35 | var dpr;
36 | if (isIPhone) {
37 | // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
38 | if (devicePixelRatio >= 3) {
39 | dpr = 3;
40 | } else if (devicePixelRatio >= 2){
41 | dpr = 2;
42 | } else {
43 | dpr = 1;
44 | }
45 | } else {
46 | // 其他设备下,仍旧使用1倍的方案
47 | dpr = 1;
48 | }
49 | return dpr
50 | }
51 |
52 | /*获取当前缩放比*/
53 | const DEVICE_RATIO=getDeviceRatio();
54 |
55 |
56 | /*下拉配置*/
57 | const DOWN_CONFIG={
58 | threshold:80*DEVICE_RATIO,
59 | stop:40*DEVICE_RATIO
60 | }
61 | /*上拉配置*/
62 | const UP_CONFIG={
63 | threshold:-80*DEVICE_RATIO,
64 | }
65 |
66 | this.scroller = new BScroll(scrollWrap,{
67 | click:true,
68 | probeType:3,
69 | pullDownRefresh:DOWN_CONFIG,
70 | pullUpLoad:UP_CONFIG
71 | });
72 |
73 | 实例化后,接下来就是监听上拉和下拉事件了。betterScroll新增了一些事件,主要的有:
74 |
75 | /*下拉刷新*/
76 | this.scroller.on('pullingDown',()=> {});
77 |
78 | /*上拉加载更多*/
79 | this.scroller.on('pullingUp',()=>{});
80 |
81 | 触发上拉或者下拉事件后,需要我们调用this.scroller.finishPullDown()或者this.scroller.finishPullUp()来通知better-scroll事件完成。
82 |
83 | this.scroller.on('pullingDown',()=> {
84 |
85 |
86 |
87 | });
88 |
89 | 通常操作完成后都需要我们手动触发refresh方法来重新计算可滚动的距离,因此可以写一个watch监听数据的变化,这样我们只需要改变数据,不用每次操作数据后都调用refresh方法。
90 |
91 |
92 | watch:{
93 | dataList(){
94 | this.$nextTick(()=>{
95 | this.scroller.refresh();
96 | })
97 | }
98 | },
99 |
100 |
101 |
102 | 如果你使用的版本还是旧的,那可以在on( scroll )事件的时候进行判断来实现功能
103 |
104 | this.scroller.on("scroll",(pos)=>{
105 | //获取整个滚动列表的高度
106 | var height=getStyle(scroller,"height");
107 |
108 | //获取滚动外层wrap的高度
109 | var pageHeight=getStyle(scrollWrap,"height");
110 |
111 | //触发事件需要的阀值
112 | var distance=80*DEVICE_RATIO;
113 |
114 | //参数pos为当前位置
115 |
116 | if(pos.y>distance){
117 | if(this.pullUpflag){
118 | //console.log("下拉");
119 | //do something
120 | }
121 |
122 | }else if(pos.y-pageHeight<-height-distance){
123 | //console.log("上拉");
124 | //do something
125 | }
126 |
127 | 为了防止多次触发,需要加2个开关类的东西;
128 | var onPullUp=true;
129 | var onPullDown=true;
130 |
131 | 每次触发事件时,將对应的开关设置为false,等操作完成后,再重新设置为true,否则多次下拉或者上拉就会触发多次事件。通过设置开关可以保证每次只有一个事件在进行。
132 |
133 |
134 |
135 | 最后,来封装成一个组件
136 |
137 |
138 |
143 |
144 |
145 | 由于每个页面需要滚动的具体内容都是不一样的,所以用了一个插槽来分发。
146 |
147 |
148 | 组件需要的参数由父级传入,通过prop来接收并设置默认值
149 |
150 | export default {
151 | props: {
152 | dataList:{
153 | type: Array,
154 | default: []
155 | },
156 | probeType: {
157 | type: Number,
158 | default: 3
159 | },
160 | click: {
161 | type: Boolean,
162 | default: true
163 | },
164 | pullDownRefresh: {
165 | type: null,
166 | default: false
167 | },
168 | pullUpLoad: {
169 | type: null,
170 | default: false
171 | },
172 | }
173 |
174 |
175 | 组件挂载后,在事件触发时并不直接处理事件,而是向父级发送一个事件,父级通过在模板v-on接收事件并处理后续的逻辑
176 |
177 | mounted() {
178 | this.scroll = new BScroll(this.$refs.wrapper, {
179 | probeType: this.probeType,
180 | click: this.click,
181 | pullDownRefresh: this.pullDownRefresh,
182 | pullUpLoad: this.pullUpLoad,
183 | })
184 |
185 | this.scroll.on('pullingUp',()=> {
186 | if(this.continuePullUp){
187 | this.beforePullUp();
188 | this.$emit("onPullUp","当前状态:上拉加载");
189 | }
190 | });
191 |
192 | this.scroll.on('pullingDown',()=> {
193 | this.beforePullDown();
194 | this.$emit("onPullDown","当前状态:下拉加载更多");
195 | });
196 |
197 | }
198 |
199 |
200 | 父组件在使用时,需要传入配置参数Props以及处理子组件发射的事件,并且用具体的内容并替换掉 slot 标签
201 |
210 |
211 |
212 |
213 |
214 |
![]()
215 |
216 |
217 |
{{v.title}}
218 |
导演:{{filterDirectors(v.directors)}}
219 |
年份:{{v.year}}{{v.stock}}
220 |
类别:{{v.genres.join(" / ")}}
221 |
评分:{{v.rating.average}}分
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 | 完整的scroller组件内容如下
231 |
232 |
233 |
234 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
--------------------------------------------------------------------------------