├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .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 ├── dist ├── index.html └── static │ ├── css │ └── app.2f974656881cbd78de13ece3be738ab5.css │ └── js │ ├── app.b19d8dea837170fa5a87.js │ ├── app.b19d8dea837170fa5a87.js.map │ ├── manifest.340ea3a56f4eadc47981.js │ ├── manifest.340ea3a56f4eadc47981.js.map │ ├── vendor.18774f7b91dbf5afcc17.js │ └── vendor.18774f7b91dbf5afcc17.js.map ├── index.html ├── package-lock.json ├── package.json ├── preview ├── musicHall (2).png ├── musicHall.png ├── player.png ├── playlist.png ├── rank.png ├── rank2.png ├── search.png ├── search2.png └── singer.png ├── src ├── App.vue ├── assets │ ├── Screenshot_20171111-200253.png │ ├── Screenshot_20171125-113153.png │ ├── logo.png │ └── qqMusicLogo.png ├── components │ ├── AlbumOrMVListItem.vue │ ├── HeadNav.vue │ ├── MenuList.vue │ ├── MenusItem.vue │ ├── MvRecom.vue │ ├── PageTitle.vue │ ├── PartTitle.vue │ ├── Player.vue │ ├── PlayerBar.vue │ ├── Playlist.vue │ ├── RecommendSwiper.vue │ ├── SongListItem.vue │ └── SongListRecom.vue ├── config │ └── api.js ├── main.js ├── pages │ ├── AllRank.vue │ ├── AllSingers.vue │ ├── Discover.vue │ ├── MusicHall.vue │ ├── Rank.vue │ ├── Search.vue │ ├── Singer.vue │ └── User.vue ├── router │ └── index.js ├── store │ ├── actions.js │ ├── index.js │ └── mutations.js └── utils │ └── base64.js ├── static └── .gitkeep └── test ├── e2e ├── custom-assertions │ └── elementCount.js ├── nightwatch.conf.js ├── runner.js └── specs │ └── test.js └── unit ├── .eslintrc ├── index.js ├── karma.conf.js └── specs └── Hello.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | build/*.js 2 | config/*.js 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | test/unit/coverage 7 | test/e2e/reports 8 | selenium-debug.log 9 | 10 | # Editor directories and files 11 | .idea 12 | .vscode 13 | *.suo 14 | *.ntvs* 15 | *.njsproj 16 | *.sln 17 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue2-music-app 2 | 3 | > vue + vuex + vue-resource + vue-router + vue-awesome-swiper 模仿qq音乐APP 4 | 5 | > 本人也是初学vue,不足之处还希望大家多多指教,共同学习进步 6 | 7 | ### 已实现功能 8 | 1. 首页 9 | 2. 排行榜 10 | 3. 搜索(热门搜索、历史搜索记录) 11 | 4. 歌手主页 12 | 5. 播放器(播放、暂停、切换上一首、切换下一首、切换播放模式、歌词) 13 | 6. 播放列表 14 | 7. 收藏歌曲 15 | 8. 关注歌手 16 | 17 | ### 效果演示 18 | 19 | [dome](https://luanmingyang.github.io/vue2-music-app/dist/index.html) (请用chrome手机模式查看) 20 | 21 | ### 项目运行 22 | 23 | ``` bash 24 | # 克隆到本地 25 | git clone https://github.com/LuanMingyang/vue2-music-app.git 26 | 27 | # 安装依赖 28 | npm install 29 | 30 | # 开发环境 31 | npm run dev 32 | 33 | # 打包发布 34 | npm run build 35 | ``` 36 | 37 | ### 效果图 38 | 39 | #### 首页 40 | 41 | 首页 42 | 43 | #### 搜索 44 | 45 | 搜索 搜索结果 46 | 47 | #### 歌手首页 48 | 49 | 歌手首页 50 | 51 | #### 排行榜 52 | 53 | 全部排行榜 排行榜详情 54 | 55 | #### 播放器 56 | 57 | 播放器 播放列表 -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, function (err, stats) { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | function exec (cmd) { 7 | return require('child_process').execSync(cmd).toString().trim() 8 | } 9 | 10 | const versionRequirements = [ 11 | { 12 | name: 'node', 13 | currentVersion: semver.clean(process.version), 14 | versionRequirement: packageConfig.engines.node 15 | } 16 | ] 17 | 18 | if (shell.which('npm')) { 19 | versionRequirements.push({ 20 | name: 'npm', 21 | currentVersion: exec('npm --version'), 22 | versionRequirement: packageConfig.engines.npm 23 | }) 24 | } 25 | 26 | module.exports = function () { 27 | const warnings = [] 28 | for (let i = 0; i < versionRequirements.length; i++) { 29 | const mod = versionRequirements[i] 30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 31 | warnings.push(mod.name + ': ' + 32 | chalk.red(mod.currentVersion) + ' should be ' + 33 | chalk.green(mod.versionRequirement) 34 | ) 35 | } 36 | } 37 | 38 | if (warnings.length) { 39 | console.log('') 40 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 41 | console.log() 42 | for (let i = 0; i < warnings.length; i++) { 43 | const warning = warnings[i] 44 | console.log(' ' + warning) 45 | } 46 | console.log() 47 | process.exit(1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /build/dev-client.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict' 3 | require('eventsource-polyfill') 4 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') 5 | 6 | hotClient.subscribe(function (event) { 7 | if (event.action === 'reload') { 8 | window.location.reload() 9 | } 10 | }) 11 | -------------------------------------------------------------------------------- /build/dev-server.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | const config = require('../config') 5 | if (!process.env.NODE_ENV) { 6 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) 7 | } 8 | 9 | const opn = require('opn') 10 | const path = require('path') 11 | const express = require('express') 12 | const webpack = require('webpack') 13 | const proxyMiddleware = require('http-proxy-middleware') 14 | const webpackConfig = (process.env.NODE_ENV === 'testing' || process.env.NODE_ENV === 'production') 15 | ? require('./webpack.prod.conf') 16 | : require('./webpack.dev.conf') 17 | 18 | // default port where dev server listens for incoming traffic 19 | const port = process.env.PORT || config.dev.port 20 | // automatically open browser, if not set will be false 21 | const autoOpenBrowser = !!config.dev.autoOpenBrowser 22 | // Define HTTP proxies to your custom API backend 23 | // https://github.com/chimurai/http-proxy-middleware 24 | const proxyTable = config.dev.proxyTable 25 | 26 | const app = express() 27 | const compiler = webpack(webpackConfig) 28 | 29 | const devMiddleware = require('webpack-dev-middleware')(compiler, { 30 | publicPath: webpackConfig.output.publicPath, 31 | quiet: true 32 | }) 33 | 34 | const hotMiddleware = require('webpack-hot-middleware')(compiler, { 35 | log: false, 36 | heartbeat: 2000 37 | }) 38 | // force page reload when html-webpack-plugin template changes 39 | // currently disabled until this is resolved: 40 | // https://github.com/jantimon/html-webpack-plugin/issues/680 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 | // enable hot-reload and state-preserving 49 | // compilation error display 50 | app.use(hotMiddleware) 51 | 52 | // proxy api requests 53 | Object.keys(proxyTable).forEach(function (context) { 54 | let options = proxyTable[context] 55 | if (typeof options === 'string') { 56 | options = { target: options } 57 | } 58 | app.use(proxyMiddleware(options.filter || context, options)) 59 | }) 60 | 61 | // handle fallback for HTML5 history API 62 | app.use(require('connect-history-api-fallback')()) 63 | 64 | // serve webpack bundle output 65 | app.use(devMiddleware) 66 | 67 | // serve pure static assets 68 | const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 69 | app.use(staticPath, express.static('./static')) 70 | 71 | const uri = 'http://localhost:' + port 72 | 73 | var _resolve 74 | var _reject 75 | var readyPromise = new Promise((resolve, reject) => { 76 | _resolve = resolve 77 | _reject = reject 78 | }) 79 | 80 | var server 81 | var portfinder = require('portfinder') 82 | portfinder.basePort = port 83 | 84 | console.log('> Starting dev server...') 85 | devMiddleware.waitUntilValid(() => { 86 | portfinder.getPort((err, port) => { 87 | if (err) { 88 | _reject(err) 89 | } 90 | process.env.PORT = port 91 | var uri = 'http://localhost:' + port 92 | console.log('> Listening at ' + uri + '\n') 93 | // when env is testing, don't need open it 94 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 95 | opn(uri) 96 | } 97 | server = app.listen(port) 98 | _resolve() 99 | }) 100 | }) 101 | 102 | module.exports = { 103 | ready: readyPromise, 104 | close: () => { 105 | server.close() 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | 6 | exports.assetsPath = function (_path) { 7 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 8 | ? config.build.assetsSubDirectory 9 | : config.dev.assetsSubDirectory 10 | return path.posix.join(assetsSubDirectory, _path) 11 | } 12 | 13 | exports.cssLoaders = function (options) { 14 | options = options || {} 15 | 16 | const cssLoader = { 17 | loader: 'css-loader', 18 | options: { 19 | minimize: process.env.NODE_ENV === 'production', 20 | sourceMap: options.sourceMap 21 | } 22 | } 23 | 24 | // generate loader string to be used with extract text plugin 25 | function generateLoaders (loader, loaderOptions) { 26 | const loaders = [cssLoader] 27 | if (loader) { 28 | loaders.push({ 29 | loader: loader + '-loader', 30 | options: Object.assign({}, loaderOptions, { 31 | sourceMap: options.sourceMap 32 | }) 33 | }) 34 | } 35 | 36 | // Extract CSS when that option is specified 37 | // (which is the case during production build) 38 | if (options.extract) { 39 | return ExtractTextPlugin.extract({ 40 | use: loaders, 41 | fallback: 'vue-style-loader' 42 | }) 43 | } else { 44 | return ['vue-style-loader'].concat(loaders) 45 | } 46 | } 47 | 48 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 49 | return { 50 | css: generateLoaders(), 51 | postcss: generateLoaders(), 52 | less: generateLoaders('less'), 53 | sass: generateLoaders('sass', { indentedSyntax: true }), 54 | scss: generateLoaders('sass'), 55 | stylus: generateLoaders('stylus'), 56 | styl: generateLoaders('stylus') 57 | } 58 | } 59 | 60 | // Generate loaders for standalone style files (outside of .vue) 61 | exports.styleLoaders = function (options) { 62 | const output = [] 63 | const loaders = exports.cssLoaders(options) 64 | for (const extension in loaders) { 65 | const loader = loaders[extension] 66 | output.push({ 67 | test: new RegExp('\\.' + extension + '$'), 68 | use: loader 69 | }) 70 | } 71 | return output 72 | } 73 | -------------------------------------------------------------------------------- /build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | 6 | module.exports = { 7 | loaders: utils.cssLoaders({ 8 | sourceMap: isProduction 9 | ? config.build.productionSourceMap 10 | : config.dev.cssSourceMap, 11 | extract: isProduction 12 | }), 13 | transformToRequire: { 14 | video: 'src', 15 | source: 'src', 16 | img: 'src', 17 | image: 'xlink:href' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | module.exports = { 12 | entry: { 13 | app: './src/main.js' 14 | }, 15 | output: { 16 | path: config.build.assetsRoot, 17 | filename: '[name].js', 18 | publicPath: process.env.NODE_ENV === 'production' 19 | ? config.build.assetsPublicPath 20 | : config.dev.assetsPublicPath 21 | }, 22 | resolve: { 23 | extensions: ['.js', '.vue', '.json'], 24 | alias: { 25 | 'vue$': 'vue/dist/vue.esm.js', 26 | '@': resolve('src'), 27 | } 28 | }, 29 | module: { 30 | rules: [ 31 | // { 32 | // test: /\.(js|vue)$/, 33 | // loader: 'eslint-loader', 34 | // enforce: 'pre', 35 | // include: [resolve('src'), resolve('test')], 36 | // options: { 37 | // formatter: require('eslint-friendly-formatter') 38 | // } 39 | // }, 40 | { 41 | test: /\.vue$/, 42 | loader: 'vue-loader', 43 | options: vueLoaderConfig 44 | }, 45 | { 46 | test: /\.js$/, 47 | loader: 'babel-loader', 48 | include: [resolve('src'), resolve('test')] 49 | }, 50 | { 51 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 52 | loader: 'url-loader', 53 | options: { 54 | limit: 10000, 55 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 56 | } 57 | }, 58 | { 59 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 60 | loader: 'url-loader', 61 | options: { 62 | limit: 10000, 63 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 64 | } 65 | }, 66 | { 67 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 68 | loader: 'url-loader', 69 | options: { 70 | limit: 10000, 71 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 72 | } 73 | } 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | 10 | // add hot-reload related code to entry chunks 11 | Object.keys(baseWebpackConfig.entry).forEach(function (name) { 12 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) 13 | }) 14 | 15 | module.exports = merge(baseWebpackConfig, { 16 | module: { 17 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) 18 | }, 19 | // cheap-module-eval-source-map is faster for development 20 | devtool: '#cheap-module-eval-source-map', 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': config.dev.env 24 | }), 25 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 26 | new webpack.HotModuleReplacementPlugin(), 27 | new webpack.NoEmitOnErrorsPlugin(), 28 | // https://github.com/ampedandwired/html-webpack-plugin 29 | new HtmlWebpackPlugin({ 30 | filename: 'index.html', 31 | template: 'index.html', 32 | inject: true 33 | }), 34 | new FriendlyErrorsPlugin() 35 | ] 36 | }) 37 | -------------------------------------------------------------------------------- /build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | 13 | const env = process.env.NODE_ENV === 'testing' 14 | ? require('../config/test.env') 15 | : config.build.env 16 | 17 | const webpackConfig = merge(baseWebpackConfig, { 18 | module: { 19 | rules: utils.styleLoaders({ 20 | sourceMap: config.build.productionSourceMap, 21 | extract: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? '#source-map' : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify 36 | new webpack.optimize.UglifyJsPlugin({ 37 | compress: { 38 | warnings: false 39 | }, 40 | sourceMap: true 41 | }), 42 | // extract css into its own file 43 | new ExtractTextPlugin({ 44 | filename: utils.assetsPath('css/[name].[contenthash].css') 45 | }), 46 | // Compress extracted CSS. We are using this plugin so that possible 47 | // duplicated CSS from different components can be deduped. 48 | new OptimizeCSSPlugin({ 49 | cssProcessorOptions: { 50 | safe: true 51 | } 52 | }), 53 | // generate dist index.html with correct asset hash for caching. 54 | // you can customize output by editing /index.html 55 | // see https://github.com/ampedandwired/html-webpack-plugin 56 | new HtmlWebpackPlugin({ 57 | filename: process.env.NODE_ENV === 'testing' 58 | ? 'index.html' 59 | : config.build.index, 60 | template: 'index.html', 61 | inject: true, 62 | minify: { 63 | removeComments: true, 64 | collapseWhitespace: true, 65 | removeAttributeQuotes: true 66 | // more options: 67 | // https://github.com/kangax/html-minifier#options-quick-reference 68 | }, 69 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 70 | chunksSortMode: 'dependency' 71 | }), 72 | // keep module.id stable when vender modules does not change 73 | new webpack.HashedModuleIdsPlugin(), 74 | // split vendor js into its own file 75 | new webpack.optimize.CommonsChunkPlugin({ 76 | name: 'vendor', 77 | minChunks: function (module) { 78 | // any required modules inside node_modules are extracted to vendor 79 | return ( 80 | module.resource && 81 | /\.js$/.test(module.resource) && 82 | module.resource.indexOf( 83 | path.join(__dirname, '../node_modules') 84 | ) === 0 85 | ) 86 | } 87 | }), 88 | // extract webpack runtime and module manifest to its own file in order to 89 | // prevent vendor hash from being updated whenever app bundle is updated 90 | new webpack.optimize.CommonsChunkPlugin({ 91 | name: 'manifest', 92 | chunks: ['vendor'] 93 | }), 94 | // copy custom static assets 95 | new CopyWebpackPlugin([ 96 | { 97 | from: path.resolve(__dirname, '../static'), 98 | to: config.build.assetsSubDirectory, 99 | ignore: ['.*'] 100 | } 101 | ]) 102 | ] 103 | }) 104 | 105 | if (config.build.productionGzip) { 106 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 107 | 108 | webpackConfig.plugins.push( 109 | new CompressionWebpackPlugin({ 110 | asset: '[path].gz[query]', 111 | algorithm: 'gzip', 112 | test: new RegExp( 113 | '\\.(' + 114 | config.build.productionGzipExtensions.join('|') + 115 | ')$' 116 | ), 117 | threshold: 10240, 118 | minRatio: 0.8 119 | }) 120 | ) 121 | } 122 | 123 | if (config.build.bundleAnalyzerReport) { 124 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 125 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 126 | } 127 | 128 | module.exports = webpackConfig 129 | -------------------------------------------------------------------------------- /build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // This is the webpack config used for unit tests. 3 | 4 | const utils = require('./utils') 5 | const webpack = require('webpack') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | 9 | const webpackConfig = merge(baseWebpackConfig, { 10 | // use inline sourcemap for karma-sourcemap-loader 11 | module: { 12 | rules: utils.styleLoaders() 13 | }, 14 | devtool: '#inline-source-map', 15 | resolveLoader: { 16 | alias: { 17 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 18 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 19 | 'scss-loader': 'sass-loader' 20 | } 21 | }, 22 | plugins: [ 23 | new webpack.DefinePlugin({ 24 | 'process.env': require('../config/test.env') 25 | }) 26 | ] 27 | }) 28 | 29 | // no need for app entry during tests 30 | delete webpackConfig.entry 31 | 32 | module.exports = webpackConfig 33 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | // Template version: 1.1.3 4 | // see http://vuejs-templates.github.io/webpack for documentation. 5 | 6 | const path = require('path') 7 | 8 | module.exports = { 9 | build: { 10 | env: require('./prod.env'), 11 | index: path.resolve(__dirname, '../dist/index.html'), 12 | assetsRoot: path.resolve(__dirname, '../dist'), 13 | assetsSubDirectory: 'static', 14 | assetsPublicPath: './', 15 | productionSourceMap: true, 16 | // Gzip off by default as many popular static hosts such as 17 | // Surge or Netlify already gzip all static assets for you. 18 | // Before setting to `true`, make sure to: 19 | // npm install --save-dev compression-webpack-plugin 20 | productionGzip: false, 21 | productionGzipExtensions: ['js', 'css'], 22 | // Run the build command with an extra argument to 23 | // View the bundle analyzer report after build finishes: 24 | // `npm run build --report` 25 | // Set to `true` or `false` to always turn it on or off 26 | bundleAnalyzerReport: process.env.npm_config_report 27 | }, 28 | dev: { 29 | env: require('./dev.env'), 30 | port: process.env.PORT || 3000, 31 | autoOpenBrowser: true, 32 | assetsSubDirectory: 'static', 33 | assetsPublicPath: '/', 34 | proxyTable: {}, 35 | // CSS Sourcemaps off by default because relative paths are "buggy" 36 | // with this option, according to the CSS-Loader README 37 | // (https://github.com/webpack/css-loader#sourcemaps) 38 | // In our experience, they generally work as expected, 39 | // just be aware of this issue when enabling this option. 40 | cssSourceMap: false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const devEnv = require('./dev.env') 4 | 5 | module.exports = merge(devEnv, { 6 | NODE_ENV: '"testing"' 7 | }) 8 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | QQ音乐 - 中国最新最全免费正版高品质音乐平台!
-------------------------------------------------------------------------------- /dist/static/js/app.b19d8dea837170fa5a87.js: -------------------------------------------------------------------------------- 1 | webpackJsonp([0],{"+Wu4":function(t,n,e){"use strict";function s(t){e("TWb3")}var a=e("uxdc"),i=e("kkV7"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-1ab57786",null);n.a=c.exports},0:function(t,n){},"006C":function(t,n,e){"use strict";function s(t){e("Dwk5")}var a=e("FbLt"),i=e("jS74"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-ba732a54",null);n.a=c.exports},"0IPU":function(t,n,e){"use strict";function s(t){e("RDfA")}var a=e("AWCa"),i=e("HMBl"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-eee871f4",null);n.a=c.exports},"2GIK":function(t,n){},"2LC8":function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("aJfl"),o=e("rwDq"),r=e("NYxO");n.a={data:function(){return{show:!0}},components:{Playlist:i.a,Player:o.a},computed:a()({},Object(r.d)(["song","playerShow","playlist","playlistShow","playState","playingTime","currentLyricIndex"]),Object(r.c)(["getAlbumCover"]),{getCurrentLyric:function(){var t=1e3*this.playingTime;if(this.song.lyric&&this.song.lyric.lyricArr){var n=this.song.lyric.lyricArr,e=this.song.lyric.timeArr;if(n.length>0&&this.currentLyricIndex<=n.length)return t25?t.scrolled=!0:s<-10&&(t.scrolled=!1),n=e},!1)},search:function(){s.a.push("search")}},mounted:function(){this.handleScroll()}}},BuIE:function(t,n,e){"use strict";function s(t){t=t.replace(/\r\n/g,"\n");for(var n="",e=0;e127&&s<2048?(n+=String.fromCharCode(s>>6|192),n+=String.fromCharCode(63&s|128)):(n+=String.fromCharCode(s>>12|224),n+=String.fromCharCode(s>>6&63|128),n+=String.fromCharCode(63&s|128))}return n}function a(t){for(var n="",e=0,s=0,a=0,i=0;e191&&s<224?(i=t.charCodeAt(e+1),n+=String.fromCharCode((31&s)<<6|63&i),e+=2):(i=t.charCodeAt(e+1),a=t.charCodeAt(e+2),n+=String.fromCharCode((15&s)<<12|(63&i)<<6|63&a),e+=3);return n}var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";n.a={encode:function(t){var n,e,a,o,r,c,l,u="",d=0;for(t=s(t);d>2,r=(3&n)<<4|e>>4,c=(15&e)<<2|a>>6,l=63&a,isNaN(e)?c=l=64:isNaN(a)&&(l=64),u=u+i.charAt(o)+i.charAt(r)+i.charAt(c)+i.charAt(l);return u},decode:function(t){var n,e,s,o,r,c,l,u="",d=0;for(t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");d>4,e=(15&r)<<4|c>>2,s=(3&c)<<6|l,u+=String.fromCharCode(n),64!=c&&(u+=String.fromCharCode(e)),64!=l&&(u+=String.fromCharCode(s));return u=a(u)}}},"BxA+":function(t,n,e){"use strict";var s=e("DapC"),a=e("aYh0"),i=e("YaEn"),o=e("2hMI");n.a={components:{PageTitle:s.a,PlayerBar:a.a},data:function(){return{rankList:[]}},methods:{intoRank:function(t){i.a.push({name:"rank",params:{topid:t}})}},filters:{playAmount:function(t){return parseInt(t/1e4)+"万"}},mounted:function(){var t=this;this.$http.jsonp(o.a.URL_TOPLIST_OPT,{params:{_:(new Date).getTime()},jsonp:"jsonpCallback"}).then(function(n){console.log(n.data),t.rankList=n.data.data.topList}).catch(function(t){console.log(t)})}}},CPTB:function(t,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var s=e("wU6a"),a=e("3vMA"),i=e("VU/8"),o=i(s.a,a.a,!1,null,null,null);n.default=o.exports},DapC:function(t,n,e){"use strict";function s(t){e("zfys")}var a=e("YeRF"),i=e("Hheg"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-b39a1c2e",null);n.a=c.exports},Dwk5:function(t,n){},EFKk:function(t,n){},FbLt:function(t,n,e){"use strict";n.a={props:["type","id","name","pic","singer","publicTime","showInto"]}},GLmR:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"recom-list-wrap"},[e("part-title",{attrs:{partTitle:"M V 推 荐"}}),t._v(" "),e("ul",{staticClass:"recom-list"},t._l(t.recomMVist,function(n){return e("li",{key:n.mv_id},[e("img",{attrs:{src:n.picurl}}),t._v(" "),e("span",{staticClass:"play-amount"},[e("i",{staticClass:"iconfont"},[t._v("")]),t._v(" "+t._s(t._f("playAmount")(n.listennum)))]),t._v(" "),t._m(0,!0),t._v(" "),e("div",{staticClass:"desc-wrap"},[e("p",{staticClass:"mvtitle"},[t._v(t._s(n.mvtitle))]),t._v(" "),e("p",{staticClass:"singer"},[t._v(t._s(n.singer_name))])])])}))],1)},a=[function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("span",{staticClass:"play-btn"},[e("i",{staticClass:"iconfont"},[t._v("")])])}],i={render:s,staticRenderFns:a};n.a=i},GYbY:function(t,n,e){"use strict";var s=e("vhfR");n.a={props:["recomSongList"],components:{PartTitle:s.a},filters:{playAmount:function(t){return parseInt(t/1e4)+"万"}}}},GjOa:function(t,n,e){"use strict";var s=e("xB85"),a=e("PXsT"),i=e("VU/8"),o=i(s.a,a.a,!1,null,null,null);n.a=o.exports},HMBl:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("header",{attrs:{id:"header"}},[e("div",{staticClass:"main-nav-wrap"},[e("i",{staticClass:"btn-left-menu iconfont"},[t._v("")]),t._v(" "),e("ul",{staticClass:"main-nav clearfix"},[e("li",{class:[0===t.activeIndex?"active":""]},[e("router-link",{attrs:{to:"/user"}},[t._v("我的")])],1),t._v(" "),e("li",{class:[1===t.activeIndex?"active":""]},[e("router-link",{attrs:{to:"/"}},[t._v("音乐馆")])],1),t._v(" "),e("li",{class:[2===t.activeIndex?"active":""]},[e("router-link",{attrs:{to:"/discover"}},[t._v("发现")])],1)]),t._v(" "),e("i",{staticClass:"btn-right-menu iconfont"},[t._v("")])]),t._v(" "),e("transition",{attrs:{"enter-active-class":"animated fadeInDown","leave-active-class":"animated fadeOutUp"}},[e("div",{directives:[{name:"show",rawName:"v-show",value:!t.scrolled,expression:"!scrolled"}],staticClass:"search-wrap"},[e("input",{staticClass:"search-input",attrs:{type:"text",placeholder:"搜索"},on:{click:t.search}})])])],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},Hheg:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"page-title-wrap"},[e("p",[e("i",{staticClass:"iconfont btn-back",on:{click:t.goBack}},[t._v("")]),t._v("\n "+t._s(t.title)+"\n "),t._t("default")],2)])},a=[],i={render:s,staticRenderFns:a};n.a=i},IcnI:function(t,n,e){"use strict";var s=e("7+uW"),a=e("NYxO"),i=e("ukYY"),o=e("mUbh");s.a.use(a.a),n.a=new a.a.Store({state:function(){return{playMethod:0,playerShow:!1,playlist:[],playlistShow:!1,song:{id:null,name:"",album:{mid:""},lyric:{lyric:"",lyricArr:[],timeArr:[]}},songIndex:null,duration:0,playingTime:0,currentLyricIndex:0,playState:!1,likedSongs:[]}},getters:{getAlbumCover:function(t){if(t.song.id){return"https://y.gtimg.cn/music/photo_new/T002R500x500M000"+(t.song.album?t.song.album.mid:t.song.albummid)+".jpg"}},getPlayMethod:function(t){var n="",e="";return 0===t.playMethod?(n="",e="顺序播放"):1===t.playMethod?(n="",e="单曲循环"):2===t.playMethod&&(n="",e="随机播放"),{icon:n,text:e}}},mutations:i.a,actions:o.a})},IvzP:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("li",{staticClass:"menu"},[e("router-link",{attrs:{to:t.to}},[e("i",{staticClass:"iconfont",domProps:{innerHTML:t._s(t.iconfont)}}),t._v(" "),e("span",[t._v(t._s(t.title))])])],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},Jnor:function(t,n,e){"use strict";function s(t){e("zbDe")}var a=e("ibHn"),i=e("ZvKi"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-a5144fd8",null);n.a=c.exports},KQbs:function(t,n,e){"use strict";function s(t){e("odRw")}var a=e("pqHc"),i=e("IvzP"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-6b14f114",null);n.a=c.exports},KRkG:function(t,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var s=e("jLJc"),a=e("etAj"),i=e("VU/8"),o=i(s.a,a.a,!1,null,null,null);n.default=o.exports},L878:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"recom-list-wrap"},[e("part-title",{attrs:{partTitle:"歌 单 推 荐"}}),t._v(" "),e("ul",{staticClass:"recom-list"},t._l(t.recomSongList,function(n){return e("li",{key:n.dissid},[e("img",{attrs:{src:n.imgurl}}),t._v(" "),e("span",{staticClass:"play-amount"},[e("i",{staticClass:"iconfont"},[t._v("")]),t._v(" "+t._s(t._f("playAmount")(n.listennum)))]),t._v(" "),t._m(0,!0),t._v(" "),e("div",{staticClass:"desc-wrap"},[e("p",{staticClass:"desc"},[t._v(t._s(n.dissname))])])])}))],1)},a=[function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("span",{staticClass:"play-btn"},[e("i",{staticClass:"iconfont"},[t._v("")])])}],i={render:s,staticRenderFns:a};n.a=i},LKHv:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"playlist-wrap"},[e("div",{staticClass:"mask",on:{click:function(n){n.stopPropagation(),t.setPlaylistShow(!1)}}}),t._v(" "),e("div",{staticClass:"playlist"},[e("header",{staticClass:"clearfix"},[e("div",{staticClass:"play-method"},[e("i",{staticClass:"iconfont btn-play-method",domProps:{innerHTML:t._s(t.getPlayMethod.icon)},on:{click:function(n){n.stopPropagation(),t.changePlayMethod(t.playMethod)}}}),t._v(" "),e("span",{staticClass:"play-method-text"},[t._v(t._s(t.getPlayMethod.text))]),e("span",{directives:[{name:"show",rawName:"v-show",value:1!==t.playMethod,expression:"playMethod !== 1"}],staticClass:"playlist-length"},[t._v("("+t._s(t.playlist.length)+"首)")])]),t._v(" "),e("div",{staticClass:"right-btns"},[e("i",{staticClass:"iconfont btn-download"},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-add"},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-clear",on:{click:function(n){n.stopPropagation(),t.clearPlaylist(n)}}},[t._v("")])])]),t._v(" "),e("ul",{staticClass:"song-list"},t._l(t.playlist,function(n,s){return e("li",{key:s,staticClass:"song",class:t.judgePlaying(n)?"active":"",on:{click:function(e){e.stopPropagation(),t.clickSong(n,s)}}},[e("p",[t._v(t._s(n.name||n.songname)),e("span",[t._v(" - "+t._s(n.singer[0].name))])]),t._v(" "),e("i",{staticClass:"iconfont btn-like",class:t.judgeLiked(n)?"liked":"",domProps:{innerHTML:t._s(t.judgeLiked(n)?"":"")},on:{click:function(e){e.stopPropagation(),t.setSongLiked(n)}}}),t._v(" "),e("i",{staticClass:"iconfont btn-delete",on:{click:function(n){n.stopPropagation(),t.deleteSong(s)}}},[t._v("")])])})),t._v(" "),e("button",{staticClass:"btn-close",on:{click:function(n){n.stopPropagation(),t.setPlaylistShow(!1)}}},[t._v("关闭")])])])},a=[],i={render:s,staticRenderFns:a};n.a=i},M93x:function(t,n,e){"use strict";function s(t){e("mPTa")}var a=e("xJD8"),i=e("7oN3"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,null,null);n.a=c.exports},MWK0:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("ul",{staticClass:"sub-nav clearfix"},t._l(t.menuList,function(t,n){return e("menus-item",{key:n,attrs:{to:t.href,iconfont:t.iconfont,title:t.title}})}))},a=[],i={render:s,staticRenderFns:a};n.a=i},NHnr:function(t,n,e){"use strict";Object.defineProperty(n,"__esModule",{value:!0});var s=e("7+uW"),a=e("M93x"),i=e("YaEn"),o=e("IcnI"),r=e("ORbq"),c=e("oPmM"),l=e.n(c),u=e("F3EI"),d=e.n(u);s.a.use(r.a),s.a.use(l.a),s.a.use(d.a),s.a.config.productionTip=!1,new s.a({el:"#app",router:i.a,store:o.a,template:"",components:{App:a.a}})},Nq9A:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("head-nav",{attrs:{activeIndex:t.activeIndex}}),t._v(" "),e("div",{staticClass:"content"},[e("recommend-swiper",{attrs:{recommends:t.recommends}}),t._v(" "),e("menu-list"),t._v(" "),e("div",{staticClass:"part"},[e("song-list-recom",{attrs:{recomSongList:t.recomSongList}})],1),t._v(" "),e("div",{staticClass:"part"},[e("mv-recom",{attrs:{recomMVist:t.recomMVist}})],1)],1),t._v(" "),e("footer",[e("player-bar")],1)],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},Ocez:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("0IPU"),o=e("mGHl"),r=e("Y4AI"),c=e("k65I"),l=e("zSG5"),u=e("aYh0"),d=e("2hMI"),p=e("NYxO");n.a={data:function(){return{activeIndex:1,recommends:[],recomSongList:[],recomMVist:[]}},components:{HeadNav:i.a,RecommendSwiper:o.a,MenuList:r.a,SongListRecom:c.a,MvRecom:l.a,PlayerBar:u.a},methods:a()({},Object(p.b)(["stateInit"])),mounted:function(){var t=this;this.stateInit(),this.$http.jsonp(d.a.URL_RRCOM,{params:{tpl:"v12",rnd:0},jsonp:"jsonpCallback"}).then(function(n){t.recommends=n.data.data.focus,t.recomSongList=n.data.data.hotdiss.list.slice(0,6),t.recomMVist=n.data.data.shoubomv.all.slice(0,4)}).catch(function(t){console.log(t)})}}},PXsT:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("head-nav",{attrs:{activeIndex:t.activeIndex}}),t._v(" "),e("footer",[e("player-bar")],1)],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},PbYd:function(t,n){},"Q+Dc":function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("li",{staticClass:"song",class:t.songPlaying.id===t.songid||t.songPlaying.songid===t.songid&&t.songPlaying.id?"active":"",on:{click:function(n){t.clickSong(t.song,t.sindex)}}},[e("p",{staticClass:"song-name"},[t._v(t._s(t.songname)+" "),1===t.isOnly?e("i",{staticClass:"iconfont icon icon-only"},[t._v("")]):t._e()]),t._v(" "),e("p",{staticClass:"song-info"},[e("span",{staticClass:"song-singer"},[t._v(t._s(t.singer))]),t._v("·"+t._s(t.album))]),t._v(" "),t.haveMV?e("i",{staticClass:"iconfont btn-right btn-mv"},[t._v("")]):t._e(),t._v(" "),e("i",{staticClass:"iconfont btn-right btn-show-menu"},[t._v("")])])},a=[],i={render:s,staticRenderFns:a};n.a=i},Qlw0:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("header",[e("page-title",{attrs:{title:"排行"}})],1),t._v(" "),e("div",{staticClass:"content"},[e("p",{staticClass:"part-title"},[t._v("Q Q 音 乐 巅 峰 榜")]),t._v(" "),e("ul",{staticClass:"rank-list"},t._l(t.rankList,function(n){return e("li",{key:n.id,staticClass:"rank clearfix",on:{click:function(e){t.intoRank(n.id)}}},[e("div",{staticClass:"rank-img-wrap"},[e("img",{staticClass:"rank-img",attrs:{src:n.picUrl}}),t._v(" "),e("span",{staticClass:"play-amount"},[e("i",{staticClass:"iconfont"},[t._v("")]),t._v(" "+t._s(t._f("playAmount")(n.listenCount)))]),t._v(" "),t._m(0,!0)]),t._v(" "),e("div",{staticClass:"rank-top-three-wrap"},[e("ul",{staticClass:"rank-top-three"},t._l(n.songList,function(n,s){return e("li",{key:s},[e("p",{staticClass:"song"},[t._v(t._s(parseInt(s)+1)+" "+t._s(n.songname)+" - "),e("span",{staticClass:"singer"},[t._v(t._s(n.singername))])])])}))]),t._v(" "),t._m(1,!0)])}))]),t._v(" "),e("footer",[e("player-bar")],1)])},a=[function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("span",{staticClass:"play-btn"},[e("i",{staticClass:"iconfont"},[t._v("")])])},function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"btn-open-wrap"},[e("i",{staticClass:"iconfont btn-open"},[t._v("")])])}],i={render:s,staticRenderFns:a};n.a=i},RDfA:function(t,n){},RlpI:function(t,n){},RtTY:function(t,n,e){"use strict";var s=e("KQbs"),a=[{iconfont:"",title:"歌手",href:"/singer"},{iconfont:"",title:"排行",href:"/rank"},{iconfont:"",title:"电台",href:"/radio"},{iconfont:"",title:"分类歌单",href:"/songList"},{iconfont:"",title:"视频MV",href:"/mv"},{iconfont:"",title:"数字专辑",href:"/album"}];n.a={data:function(){return{menuList:a}},components:{MenusItem:s.a}}},TWb3:function(t,n){},Ur6G:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("head-nav",{attrs:{activeIndex:t.activeIndex}}),t._v(" "),e("footer",[e("player-bar")],1)],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},WzMn:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"container"},[e("div",{staticClass:"search-input-wrap"},[e("i",{staticClass:"iconfont btn-cancel",on:{click:t.back}},[t._v("")]),t._v(" "),e("input",{directives:[{name:"model",rawName:"v-model",value:t.searchKey,expression:"searchKey"}],staticClass:"search-input",attrs:{type:"text",id:"search-input",placeholder:"搜索音乐、歌词、歌单"},domProps:{value:t.searchKey},on:{keyup:[function(n){t.search(void 0,!0)},function(n){if(!("button"in n)&&t._k(n.keyCode,"enter",13,n.key))return null;t.search(void 0,!1)}],input:function(n){n.target.composing||(t.searchKey=n.target.value)}}}),t._v(" "),e("i",{staticClass:"iconfont btn-clear",on:{click:t.clearInput}},[t._v("")])]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:!t.searched,expression:"!searched"}],staticClass:"hot-search part clearfix"},[e("p",{staticClass:"title"},[t._v("热门搜索")]),t._v(" "),e("ul",{staticClass:"hot-key-list"},t._l(t.hotKeys,function(n){return e("li",{key:n.n,on:{click:function(e){t.search(n.k.trim())}}},[t._v(t._s(n.k.trim()))])}))]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:!t.searched&&t.searchHistory.length>0,expression:"!searched && searchHistory.length > 0"}],staticClass:"search-history part"},[e("p",{staticClass:"title"},[t._v("搜索历史 "),e("span",{staticClass:"btn-clear-history",on:{click:t.clearHistory}},[t._v("清空历史")])]),t._v(" "),e("ul",{staticClass:"search-history-list"},t._l(t.searchHistory,function(n,s){return e("li",{key:s,on:{click:function(e){t.search(n)}}},[t._v("\n "+t._s(n)+"\n "),e("i",{staticClass:"iconfont btn-delete",on:{click:function(n){n.stopPropagation(),t.deleteSearchHistory(s)}}},[t._v("")])])}))]),t._v(" "),t.searched&&null!==t.searchResult?e("div",{staticClass:"search-result-wrap"},[e("ul",{staticClass:"result-nav"},[e("li",{class:[0===t.activeIndex?"active":""],on:{click:function(n){t.changeSearch(0)}}},[e("span",[t._v("单曲")])]),t._v(" "),e("li",{class:[1===t.activeIndex?"active":""],on:{click:function(n){t.changeSearch(1)}}},[e("span",[t._v("MV")])]),t._v(" "),e("li",{class:[2===t.activeIndex?"active":""],on:{click:function(n){t.changeSearch(2)}}},[e("span",[t._v("专辑")])]),t._v(" "),e("li",{class:[3===t.activeIndex?"active":""],on:{click:function(n){t.changeSearch(3)}}},[e("span",[t._v("歌单")])]),t._v(" "),e("li",{class:[4===t.activeIndex?"active":""],on:{click:function(n){t.changeSearch(4)}}},[e("span",[t._v("歌词")])])]),t._v(" "),0===t.activeIndex?e("div",{staticClass:"search-result search-song"},[void 0!==t.singer&&null!==t.singer?e("router-link",{attrs:{to:"/singer/"+t.singer.mid}},[e("div",{staticClass:"best-match-wrap clearfix"},[e("p",{staticClass:"best-match-title"},[t._v("最佳匹配")]),t._v(" "),e("div",{staticClass:"best-match"},[e("img",{staticClass:"singer-img",attrs:{src:t.singer.pic}}),t._v(" "),e("div",{staticClass:"singer-text"},[e("p",{staticClass:"singer-name"},[t._v("歌手:"+t._s(t.singer.name))]),t._v(" "),e("p",{staticClass:"singer-data"},[t._v("单曲:"+t._s(t.searchResult.zhida.zhida_singer.songNum)+" 专辑:"+t._s(t.searchResult.zhida.zhida_singer.albumNum))])]),t._v(" "),e("i",{staticClass:"iconfont btn-enter"},[t._v("")])])])]):t._e(),t._v(" "),e("ul",{staticClass:"song-list"},t._l(t.searchResult.song.list,function(n,s){return e("song-list-item",{key:n.id,attrs:{songList:t.searchResult.song.list,song:n,songid:n.id,sindex:s,songname:n.name,isOnly:n.isonly,haveMV:0!==n.mv.id,singer:n.singer[0].name,album:n.album.name}})}))],1):t._e(),t._v(" "),1===t.activeIndex?e("div",{staticClass:"search-result search-mv"},[e("ul",{staticClass:"mv-list"},t._l(t.searchResult.mv.list,function(n){return e("li",{key:n.mv_id,staticClass:"mv clearfix"},[e("div",{staticClass:"mv-img-wrap"},[e("img",{staticClass:"mv-img",attrs:{src:n.mv_pic_url}}),t._v(" "),e("span",{staticClass:"mv-play-amount"},[e("i",{staticClass:"iconfont"},[t._v("")]),t._v(" "+t._s(t._f("playAmountFormat")(n.play_count)))])]),t._v(" "),e("div",{staticClass:"mv-info-wrap"},[e("div",{staticClass:"mv-info"},[e("p",{staticClass:"mv-name"},[t._v(t._s(n.mv_name))]),t._v(" "),e("p",[e("span",{staticClass:"mv-singer"},[t._v(t._s(n.singer_name))]),t._v(" "),e("span",{staticClass:"mv-duration"},[t._v(t._s(t._f("durationFormat")(n.duration)))])])])])])}))]):t._e(),t._v(" "),2===t.activeIndex?e("div",{staticClass:"search-result search-album"},[e("ul",{staticClass:"album-list"},t._l(t.searchResult.album.list,function(t){return e("album-or-m-v-list-item",{key:t.albumID,attrs:{type:"album",name:t.albumName,pic:t.albumPic,singer:t.singerName,publicTime:t.publicTime,showInto:!1}})}))]):t._e(),t._v(" "),3===t.activeIndex?e("div",{staticClass:"search-result search-songList"},[e("p",[t._v("暂无数据")])]):t._e(),t._v(" "),4===t.activeIndex?e("div",{staticClass:"search-result search-lyric"},[e("p",[t._v("暂无数据")])]):t._e()]):t._e(),t._v(" "),e("footer",[e("player-bar")],1)])},a=[],i={render:s,staticRenderFns:a};n.a=i},Xc3J:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return t.song.id?e("div",{staticClass:"player-box-wrap",attrs:{id:"player-box-wrap"}},[e("div",{staticClass:"background",attrs:{id:"background-img"}}),t._v(" "),e("div",{staticClass:"player-box"},[e("i",{staticClass:"iconfont btn btn-hide",on:{click:function(n){n.stopPropagation(),t.setPlayerShow(!1)}}},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn btn-menu"},[t._v("")]),t._v(" "),e("h1",{staticClass:"song-name"},[t._v(t._s(t.getSong.name))]),t._v(" "),e("div",{staticClass:"song-info-top"},[e("p",{staticClass:"song-singer"},[t._v("—  "),e("span",[t._v(t._s(t.song.singer[0].name))]),t._v("  —")]),t._v(" "),t._m(0),t._v(" "),e("div",{staticClass:"song-album-cover"},[e("img",{attrs:{src:t.getAlbumCover}})]),t._v(" "),e("p",{staticClass:"lyric"},[t._v(t._s(t.getCurrentLyric))])]),t._v(" "),e("div",{staticClass:"bottom"},[e("div",{staticClass:"range-wrap"},[e("span",{staticClass:"currentTime"},[t._v(t._s(t._f("playingTimeFormat")(t.playingTime)))]),t._v(" "),e("input",{staticClass:"range",attrs:{type:"range",id:"range",min:"0",max:t.duration},domProps:{value:t.playingTime}}),t._v(" "),e("span",{staticClass:"duration"},[t._v(t._s(t._f("durationFormat")(t.duration)))])]),t._v(" "),e("div",{staticClass:"play-operations"},[e("i",{staticClass:"iconfont btn-play-method",domProps:{innerHTML:t._s(t.getPlayMethod.icon)},on:{click:function(n){n.stopPropagation(),t.changePlayMethod(t.playMethod)}}}),t._v(" "),e("i",{staticClass:"iconfont btn-prev",on:{click:function(n){n.stopPropagation(),t.prevSong(n)}}},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-play",domProps:{innerHTML:t._s(t.playState?"":"")},on:{click:function(n){n.stopPropagation(),t.play(n)}}}),t._v(" "),e("i",{staticClass:"iconfont btn-next",on:{click:function(n){n.stopPropagation(),t.nextSong(n)}}},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-play-list",on:{click:function(n){n.stopPropagation(),t.setPlaylistShow(!0)}}},[t._v("")])]),t._v(" "),e("div",{staticClass:"other-operations"},[e("i",{staticClass:"iconfont btn-like",class:t.liked?"liked":"",domProps:{innerHTML:t._s(t.liked?"":"")},on:{click:function(n){n.stopPropagation(),t.setSongLiked(t.song)}}}),t._v(" "),e("i",{staticClass:"iconfont btn-download"},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-share"},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont btn-comment"},[t._v("")])])])])]):t._e()},a=[function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("p",{staticClass:"tags"},[e("i",{staticClass:"iconfont icon-sq"},[t._v("")]),t._v(" "),e("i",{staticClass:"iconfont icon-only"},[t._v("")])])}],i={render:s,staticRenderFns:a};n.a=i},Y4AI:function(t,n,e){"use strict";function s(t){e("2GIK")}var a=e("RtTY"),i=e("MWK0"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-11071c0b",null);n.a=c.exports},YTHh:function(t,n,e){"use strict";var s=e("F3EI");e.n(s);n.a={props:["recommends"],data:function(){return{notNextTick:!0,swiperOption:{autoplay:3e3,loop:!0,grabCursor:!0,setWrapperSize:!0,pagination:".swiper-pagination",observeParents:!0}}},computed:{swiper:function(){return this.$refs.mySwiper.swiper}},components:{swiper:s.swiper,swiperSlide:s.swiperSlide}}},YaEn:function(t,n,e){"use strict";var s=e("7+uW"),a=e("/ocq"),i=e("wdnp"),o=e("tnDH"),r=e("35em"),c=e("GjOa"),l=e("ZkPE"),u=e("Jnor"),d=e("+Wu4");s.a.use(a.a),n.a=new a.a({routes:[{path:"/",name:"musicHall",component:i.a},{path:"/search",name:"search",component:o.a},{path:"/user",name:"user",component:r.a},{path:"/discover",name:"discover",component:c.a},{path:"/rank",name:"allrank",component:l.a},{path:"/rank/:topid ",name:"rank",component:u.a,props:!0},{path:"/singer/:singermid",name:"singer",component:d.a,props:!0}]})},YeRF:function(t,n,e){"use strict";n.a={props:["title"],methods:{goBack:function(){window.history.back()}}}},ZkPE:function(t,n,e){"use strict";function s(t){e("EFKk")}var a=e("BxA+"),i=e("Qlw0"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-4ccffc8b",null);n.a=c.exports},ZvKi:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return t.rank?e("div",{staticClass:"container"},[e("div",{staticClass:"fixedHeader",attrs:{id:"fixedHeader"}},[e("i",{staticClass:"iconfont btn-back",on:{click:t.goBack}},[t._v("")]),t._v(" "),e("h1",{directives:[{name:"show",rawName:"v-show",value:t.showFixedTitle,expression:"showFixedTitle"}]},[t._v(t._s(t.rank.topinfo.ListName))]),t._v(" "),e("i",{staticClass:"iconfont btn btn-menu"},[t._v("")])]),t._v(" "),e("div",{staticClass:"header"},[e("div",{staticClass:"background",style:{backgroundImage:"url("+t.rank.topinfo.pic_album+")"},attrs:{id:"background"}},[e("div",{staticClass:"mask"})]),t._v(" "),e("div",{staticClass:"rank-info"},[e("p",{staticClass:"rank-name"},[t._v(t._s(t.rank.topinfo.ListName)+" 第"+t._s(t.rank.day_of_year)+"天")]),t._v(" "),e("p",{staticClass:"rank-time"},[t._v(t._s(t.rank.date)+" 更新")])])]),t._v(" "),e("div",{staticClass:"main-content",class:t.showFixedTitle?"fixed":"",attrs:{id:"content"},on:{scroll:t.contentScroll}},[e("div",{staticClass:"nav-wrap",class:t.showFixedTitle?"fixed":""},[e("ul",{staticClass:"nav clearfix"},[e("li",{class:[0===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(0)}}},[t._v("歌曲")]),t._v(" "),e("li",{class:[3===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(3)}}},[t._v("详情")])])]),t._v(" "),0===t.activeIndex?e("ul",{staticClass:"song-list"},t._l(t.rank.songlist,function(n,s){return e("song-list-item",{key:n.data.songid,attrs:{songList:t.rank.songlist,song:n.data,songid:n.data.songid,sindex:s,songname:n.data.songname,isOnly:n.data.isonly,haveMV:!1,singer:n.data.singer[0].name,album:n.data.albumname}})})):t._e()]),t._v(" "),e("footer",[e("player-bar")],1)]):t._e()},a=[],i={render:s,staticRenderFns:a};n.a=i},aJfl:function(t,n,e){"use strict";function s(t){e("5grB")}var a=e("qxMB"),i=e("LKHv"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-0941c6c3",null);n.a=c.exports},aYh0:function(t,n,e){"use strict";function s(t){e("RlpI")}var a=e("2LC8"),i=e("kfaM"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-55b951d4",null);n.a=c.exports},asP6:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("p",{staticClass:"part-title"},[t._v(t._s(t.partTitle)+" "),e("i",{staticClass:"iconfont icon-more"},[t._v("")])])},a=[],i={render:s,staticRenderFns:a};n.a=i},coI1:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("0IPU"),o=e("aYh0"),r=e("NYxO");n.a={data:function(){return{activeIndex:0}},components:{HeadNav:i.a,PlayerBar:o.a},methods:a()({},Object(r.b)(["stateInit"]))}},etAj:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{staticClass:"swiper-container"},[t._t("parallax-bg"),t._v(" "),e("div",{class:t.defaultSwiperClasses.wrapperClass},[t._t("default")],2),t._v(" "),t._t("pagination"),t._v(" "),t._t("button-prev"),t._v(" "),t._t("button-next"),t._v(" "),t._t("scrollbar")],2)},a=[],i={render:s,staticRenderFns:a};n.a=i},huZs:function(t,n,e){"use strict";function s(t){e("nh/f")}var a=e("ptQc"),i=e("Q+Dc"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-e69cc476",null);n.a=c.exports},iPPZ:function(t,n){},iPeh:function(t,n){},ibHn:function(t,n,e){"use strict";var s=e("aYh0"),a=e("huZs"),i=e("2hMI");e("NYxO");n.a={props:["topid"],data:function(){return{scroll:null,showFixedTitle:!1,rank:null,activeIndex:0}},components:{PlayerBar:s.a,SongListItem:a.a},methods:{goBack:function(){window.history.back()},changeNav:function(t){this.activeIndex=t},contentScroll:function(){var t=this.scroll;this.scroll=document.getElementById("content").scrollTop,0===this.scroll&&t>this.scroll?(document.getElementById("fixedHeader").style.backgroundColor="transparent",document.getElementById("background").style.filter="none",this.showFixedTitle=!1):t>this.scroll||this.scroll}},created:function(){var t=this;this.$http.jsonp(i.a.URL_TOPLIST_LIST,{params:{topid:this.topid},jsonp:"jsonpCallback"}).then(function(n){t.rank=n.data}).catch(function(t){console.log(t)})},mounted:function(){var t=this;window.addEventListener("scroll",function(){var n=window.scrollY;if(n>0&&n<215){var e=n/215;document.getElementById("fixedHeader").style.backgroundColor="rgba(0, 0, 0, "+.3*e+")",document.getElementById("background").style.filter="blur("+8*e+"px)",document.getElementById("background").style.transform="scale("+(1+.05*e)+")",t.showFixedTitle=!1}else n>215?(document.getElementById("fixedHeader").style.backgroundColor="rgba(0, 0, 0, .3)",document.getElementById("background").style.filter="blur(8px)",document.getElementById("background").style.transform="scale(1.05)",t.showFixedTitle=!0):t.showFixedTitle&&0===n&&(t.showFixedTitle=!0)},!1)}}},jLJc:function(t,n,e){"use strict";var s="undefined"!=typeof window;s&&(window.Swiper=e("gsqX"),e("v2ns")),n.a={name:"swiper",props:{options:{type:Object,default:function(){return{autoplay:3500}}}},data:function(){return{defaultSwiperClasses:{wrapperClass:"swiper-wrapper"}}},ready:function(){!this.swiper&&s&&(this.swiper=new Swiper(this.$el,this.options))},mounted:function(){var t=this,n=function(){if(!t.swiper&&s){delete t.options.notNextTick;var n=!1;for(var e in t.defaultSwiperClasses)t.defaultSwiperClasses.hasOwnProperty(e)&&t.options[e]&&(n=!0,t.defaultSwiperClasses[e]=t.options[e]);var a=function(){t.swiper=new Swiper(t.$el,t.options)};n?t.$nextTick(a):a()}};this.options.notNextTick?n():this.$nextTick(n)},updated:function(){this.swiper&&this.swiper.update()},beforeDestroy:function(){this.swiper&&(this.swiper.destroy(),delete this.swiper)}}},jS74:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("li",{staticClass:"item clearfix",class:t.type},[e("img",{staticClass:"item-img",attrs:{src:t.pic}}),t._v(" "),e("div",{staticClass:"item-info-wrap"},[e("div",{staticClass:"item-info"},[e("p",{staticClass:"item-name"},[t._v(t._s(t.name))]),t._v(" "),e("p",[t.singer?e("span",{staticClass:"item-singer"},[t._v(t._s(t.singer)+" ")]):t._e(),e("span",{staticClass:"item-public-time"},[t._v(t._s(t.publicTime))])])]),t._v(" "),t.showInto?e("i",{staticClass:"iconfont btn-into"},[t._v("")]):t._e()])])},a=[],i={render:s,staticRenderFns:a};n.a=i},k65I:function(t,n,e){"use strict";function s(t){e("qwEj")}var a=e("GYbY"),i=e("L878"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-12751ac9",null);n.a=c.exports},kfaM:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("div",{directives:[{name:"show",rawName:"v-show",value:t.show,expression:"show"}],staticClass:"player-wrap",on:{click:function(n){t.setPlayerShow(!0)}}},[e("div",{directives:[{name:"show",rawName:"v-show",value:t.playlist.length>0,expression:"playlist.length > 0"}],staticClass:"album-img-wrap"},[e("img",{staticClass:"album-img",attrs:{id:"album-cover",src:t.getAlbumCover}})]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:t.playlist.length>0,expression:"playlist.length > 0"}],staticClass:"music-desc"},[e("p",{staticClass:"music-name"},[t._v(t._s(t.song.name||t.song.songname))]),t._v(" "),e("p",{staticClass:"lyric"},[t._v(t._s(t.getCurrentLyric))])]),t._v(" "),e("div",{directives:[{name:"show",rawName:"v-show",value:0===t.playlist.length,expression:"playlist.length === 0"}],staticClass:"no-song"},[t._v("\n QQ音乐 听我想听的音乐\n ")]),t._v(" "),e("div",{staticClass:"btn-wrap"},[e("i",{staticClass:"iconfont btn btn-play",domProps:{innerHTML:t._s(t.playState?"":"")},on:{click:function(n){n.stopPropagation(),t.play(n)}}}),t._v(" "),e("i",{staticClass:"iconfont btn btn-list",on:{click:function(n){n.stopPropagation(),t.setPlaylistShow(!0)}}},[t._v("")])]),t._v(" "),e("playlist",{directives:[{name:"show",rawName:"v-show",value:t.playlistShow,expression:"playlistShow"}]}),t._v(" "),e("player",{directives:[{name:"show",rawName:"v-show",value:t.playerShow,expression:"playerShow"}]})],1)},a=[],i={render:s,staticRenderFns:a};n.a=i},kkV7:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return t.singer?e("div",{staticClass:"container"},[e("div",{staticClass:"fixedHeader",attrs:{id:"fixedHeader"}},[e("i",{staticClass:"iconfont btn-back",on:{click:t.goBack}},[t._v("")]),t._v(" "),e("h1",{directives:[{name:"show",rawName:"v-show",value:t.showFixedTitle,expression:"showFixedTitle"}]},[t._v(t._s(t.singer.singer_name))]),t._v(" "),e("i",{staticClass:"iconfont btn btn-menu"},[t._v("")])]),t._v(" "),e("div",{staticClass:"header"},[e("div",{staticClass:"background",style:{backgroundImage:"url("+t.getSingerPic+")"},attrs:{id:"background"}},[e("div",{staticClass:"mask"})]),t._v(" "),e("div",{staticClass:"singer-info",attrs:{id:"singer-info"}},[e("p",{staticClass:"singer-name"},[t._v(t._s(t.singer.singer_name))]),t._v(" "),e("p",{staticClass:"singer-fans"},[t._v("————— "),e("span",[t._v(t._s(t._f("fansNumberFormat")(t.singer.fans))+"粉丝")]),t._v(" —————")]),t._v(" "),e("span",{staticClass:"follow",on:{click:t.follow}},[e("i",{directives:[{name:"show",rawName:"v-show",value:t.followed,expression:"followed"}],staticClass:"iconfont icon-get"},[t._v("")]),e("span",{domProps:{innerHTML:t._s(t.followed?"已关注":"关注")}})]),t._v(" "),e("span",{staticClass:"medal"},[t._v("勋章")])])]),t._v(" "),t.data?e("div",{staticClass:"main-content",class:t.showFixedTitle?"fixed":"",attrs:{id:"content"},on:{scroll:t.contentScroll}},[e("div",{staticClass:"nav-wrap",class:t.showFixedTitle?"fixed":"",attrs:{id:"nav-wrap"}},[e("ul",{staticClass:"nav clearfix"},[e("li",{class:[0===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(0)}}},[t._v("歌曲 "+t._s(t.singer.total))]),t._v(" "),e("li",{class:[1===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(1)}}},[t._v("专辑 "+t._s(t.singer.albumTotal))]),t._v(" "),e("li",{class:[2===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(2)}}},[t._v("MV "+t._s(t.singer.mvTotal))]),t._v(" "),e("li",{class:[3===t.activeIndex?"active":""],on:{click:function(n){t.changeNav(3)}}},[t._v("详情")])])]),t._v(" "),0===t.activeIndex?e("ul",{staticClass:"song-list"},t._l(t.data.list,function(n,s){return e("song-list-item",{key:n.musicData.songid,attrs:{songList:t.data.list,song:n.musicData,songid:n.musicData.songid,sindex:s,songname:n.musicData.songname,isOnly:n.musicData.isonly,haveMV:!1,singer:n.musicData.singer[0].name,album:n.musicData.albumname}})})):t._e(),t._v(" "),1===t.activeIndex?e("ul",{staticClass:"album-list"},t._l(t.data.list,function(t){return e("album-or-m-v-list-item",{key:t.albumID,attrs:{type:"album",name:t.albumName,pic:t.albumPic,publicTime:t.pubTime,showInto:!0}})})):t._e(),t._v(" "),2===t.activeIndex?e("ul",{staticClass:"mv-list"},t._l(t.data.list,function(t){return e("album-or-m-v-list-item",{key:t.id,attrs:{type:"mv",name:t.title,pic:t.pic,publicTime:t.date,showInto:!1}})})):t._e()]):t._e(),t._v(" "),e("footer",[e("player-bar")],1)]):t._e()},a=[],i={render:s,staticRenderFns:a};n.a=i},kn7S:function(t,n,e){"use strict";var s=function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("swiper",{ref:"mySwiper",attrs:{options:t.swiperOption,"not-next-tick":t.notNextTick}},[t._l(t.recommends,function(t){return e("swiper-slide",{key:t.id},[e("a",{attrs:{href:t.jumpurl}},[e("img",{attrs:{src:t.pic}})])])}),t._v(" "),e("div",{staticClass:"swiper-pagination",attrs:{slot:"pagination"},slot:"pagination"})],2)},a=[],i={render:s,staticRenderFns:a};n.a=i},mGHl:function(t,n,e){"use strict";var s=e("YTHh"),a=e("kn7S"),i=e("VU/8"),o=i(s.a,a.a,!1,null,null,null);n.a=o.exports},mPTa:function(t,n){},mUbh:function(t,n,e){"use strict";n.a={stateInit:function(t){t.commit("STATE_INIT")},playerInit:function(t){(0,t.commit)("PLAYER_INIT")},updatePlaylist:function(t,n){(0,t.commit)("UPDATE_PLAYLIST",n)},changeSongPlay:function(t,n){(0,t.commit)("CHANGE_AND_PLAY",{song:n.song,index:n.index})},play:function(t){(0,t.commit)("PLAY")},updatePlayingTime:function(t,n){(0,t.commit)("UPDATE_PLAYINGTIME",n)},updateLyricIndex:function(t){(0,t.commit)("UPDATE_LYRICINDEX")},setPlayerShow:function(t,n){(0,t.commit)("SET_PLAYERSHOW",n)},setDuration:function(t,n){(0,t.commit)("SET_DURATION",n)},changePlayMethod:function(t,n){(0,t.commit)("CHANGE_PLAY_METHOD",n)},setPlaylistShow:function(t,n){(0,t.commit)("SET_PLAYLISTSHOW",n)},setSongLiked:function(t,n){(0,t.commit)("SET_SONGLIKED",n)}}},mYI6:function(t,n){},"nh/f":function(t,n){},oPmM:function(t,n){},odRw:function(t,n){},ojbt:function(t,n,e){"use strict";var s=e("mvHQ"),a=e.n(s),i=e("aYh0"),o=e("huZs"),r=e("006C"),c=e("2hMI");e("NYxO");n.a={components:{PlayerBar:i.a,SongListItem:o.a,AlbumOrMVListItem:r.a},data:function(){return{hotKeys:[],searchHistory:[],searchKey:"",searched:!1,activeIndex:0,singer:null,searchResult:null}},methods:{back:function(){window.history.back()},clearInput:function(){this.searchKey="",this.searched=!1,document.getElementById("search-input").focus()},search:function(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.searchKey,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.singer=null,this.searchResult=null;var s="";switch(this.activeIndex){case 0:s=c.a.URL_SEARCH_CLIENT_SONG;break;case 1:s=c.a.URL_SEARCH_CLIENT_MV;break;case 2:s=c.a.URL_SEARCH_CLIENT_ALBUM;break;case 3:s=c.a.URL_SEARCH_CLIENT_DISS;break;case 4:s=c.a.URL_SEARCH_SMARTBOX}""!==n?(this.searched=!0,this.searchKey=n,0===this.activeIndex&&this.$http.jsonp(c.a.URL_SEARCH_SMARTBOX,{params:{key:n},jsonp:"jsonpCallback"}).then(function(n){t.singer=n.data.data.singer.itemlist[0]}).catch(function(t){console.log(t)}),this.$http.jsonp(s,{params:{w:n,query:n},jsonp:"jsonpCallback"}).then(function(s){if(t.searchResult=s.data.data,!e){var i=t.searchHistory.indexOf(n);-1!==i&&t.searchHistory.splice(i,1),t.searchHistory.unshift(n),t.searchHistory=t.searchHistory.slice(0,10),localStorage.searchHistory=a()(t.searchHistory)}}).catch(function(t){console.log(t)})):this.searched=!1},deleteSearchHistory:function(t){this.searchHistory.splice(t,1),localStorage.searchHistory=a()(this.searchHistory)},clearHistory:function(){this.searchHistory=[],localStorage.searchHistory=a()(this.searchHistory)},changeSearch:function(t){this.activeIndex=t,this.search()}},filters:{durationFormat:function(t){var n=parseInt(t/60),e=t%60;return(Array(2).join(0)+n).slice(-2)+":"+(Array(2).join(0)+e).slice(-2)},playAmountFormat:function(t){return(t/1e4).toFixed(1)+"万"}},created:function(){var t=this;document.body.style.backgroundColor="#f5f5f5",this.$http.jsonp(c.a.URL_HOT_KEY,{jsonp:"jsonpCallback"}).then(function(n){t.hotKeys=n.data.data.hotkey.slice(0,8)}).catch(function(t){console.log(t)}),localStorage.searchHistory&&(this.searchHistory=JSON.parse(localStorage.searchHistory))},mounted:function(){document.getElementById("search-input").focus()}}},ol2V:function(t,n){},pqHc:function(t,n,e){"use strict";n.a={props:["to","iconfont","title"]}},ptQc:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("NYxO");n.a={props:["songList","song","songid","sindex","songname","singer","album","isOnly","haveMV"],computed:a()({},Object(i.d)({songPlaying:"song"})),methods:a()({},Object(i.b)(["updatePlaylist","changeSongPlay"]),{clickSong:function(t,n){this.updatePlaylist(this.songList),this.changeSongPlay({song:t,index:n})}})}},qwEj:function(t,n){},qxMB:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("NYxO");n.a={computed:a()({},Object(i.d)({songPlaying:"song",playMethod:"playMethod",playlist:"playlist",playlistShow:"playlistShow",likedSongs:"likedSongs"}),Object(i.c)(["getPlayMethod"])),methods:a()({},Object(i.b)(["playerInit","changePlayMethod","setPlaylistShow","changeSongPlay","updatePlaylist","setSongLiked"]),{clickSong:function(t,n){this.changeSongPlay({song:t,index:n})},clearPlaylist:function(){this.updatePlaylist([]),this.playerInit()},judgePlaying:function(t){return t.id||(t.id=t.songid),this.songPlaying.id?this.songPlaying.id===t.id:!!this.songPlaying.songid&&this.songPlaying.songid===t.id},judgeLiked:function(t){return t.id||(t.id=t.songid),this.likedSongs.indexOf(t.id)>-1},deleteSong:function(t){var n=this.playlist;n.splice(t,1),this.updatePlaylist(n)}}),watch:{playlistShow:function(t){}}}},rwDq:function(t,n,e){"use strict";function s(t){e("iPeh")}var a=e("yFA6"),i=e("Xc3J"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-023dc3b6",null);n.a=c.exports},tnDH:function(t,n,e){"use strict";function s(t){e("PbYd")}var a=e("ojbt"),i=e("WzMn"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-0228eee1",null);n.a=c.exports},ukYY:function(t,n,e){"use strict";var s,a=e("bOdI"),i=e.n(a),o=e("mvHQ"),r=e.n(o),c=e("7+uW"),l=e("2hMI"),u=e("BuIE");n.a=(s={},i()(s,"STATE_INIT",function(t){t.playerShow=!1,t.playlistShow=!1,localStorage.likedSongs&&(t.likedSongs=JSON.parse(localStorage.likedSongs))}),i()(s,"PLAYER_INIT",function(t){t.song={id:null,name:"",album:{mid:""},lyric:{lyric:"",lyricArr:[],timeArr:[]}},t.songIndex=null,t.duration=0,t.playingTime=0,t.currentLyricIndex=0,t.playState=!1}),i()(s,"UPDATE_PLAYLIST",function(t,n){n=n.map(function(t){return t.musicData?t=t.musicData:t.data&&(t=t.data),t}),t.playlist=n}),i()(s,"CHANGE_AND_PLAY",function(t,n){var e=n.song,s=n.index;e.id=e.id||e.songid,e.lyric&&""!==e.lyric||c.a.http.jsonp(l.a.URL_SONG_LYR+e.id,{async:!1,jsonp:"callback"}).then(function(t){var n=[],s=[],a=/\[([0-9:\.]{8})\]/gm;u.a.decode(t.data.lyric).split(a).slice(1).forEach(function(t,e){e%2==0?s.push(t):n.push(t)});for(var i=0;i-1){var e=t.likedSongs.indexOf(n.id);t.likedSongs.splice(e,1)}else t.likedSongs.push(n.id);localStorage.likedSongs=r()(t.likedSongs)}),s)},uxdc:function(t,n,e){"use strict";var s=e("mvHQ"),a=e.n(s),i=e("Dd8w"),o=e.n(i),r=e("aYh0"),c=e("huZs"),l=e("006C"),u=e("2hMI"),d=e("NYxO");n.a={props:["singermid"],data:function(){return{showFixedTitle:!1,singer:null,followedSinger:[],activeIndex:0,data:null}},components:{PlayerBar:r.a,SongListItem:c.a,AlbumOrMVListItem:l.a},computed:o()({},Object(d.d)(["playlist"]),{followed:function(){return this.followedSinger.indexOf(this.singermid)>-1},getSingerPic:function(){return"http://y.gtimg.cn/music/photo_new/T001R300x300M000"+this.singermid+".jpg?max_age=2592000"}}),methods:{goBack:function(){window.history.back()},follow:function(){if(this.followed){var t=this.followedSinger.indexOf(this.singermid);this.followedSinger.splice(t,1)}else this.followedSinger.push(this.singermid);localStorage.followedSinger=a()(this.followedSinger)},changeNav:function(t){this.activeIndex=t,this.requestData()},requestData:function(){var t=this,n="";switch(this.activeIndex){case 0:n=u.a.URL_SINGER_SONGS;break;case 1:n=u.a.URL_SINGER_ALBUM;break;case 2:n=u.a.URL_SINGER_MVS;break;case 3:n=u.a.URL_SINGER_DESC}this.$http.jsonp(n,{params:{begin:0,num:16,singermid:this.singermid},jsonp:"jsonpCallback"}).then(function(n){t.data=n.data.data}).catch(function(t){console.log(t)})},contentScroll:function(){0===document.getElementById("content").scrollTop&&(document.getElementById("fixedHeader").style.backgroundColor="transparent",document.getElementById("background").style.filter="none",this.showFixedTitle=!1)}},filters:{fansNumberFormat:function(t){return(parseInt(t)/1e4).toFixed(1)+"万"}},created:function(){var t=this;localStorage.followedSinger&&(this.followedSinger=JSON.parse(localStorage.followedSinger)),this.$http.jsonp(u.a.URL_SINGER_TRACK,{params:{begin:0,num:8,_:(new Date).getTime(),singermid:this.singermid},jsonp:"jsonpCallback"}).then(function(n){t.singer=n.data.data}).catch(function(t){console.log(t)}),this.requestData()},mounted:function(){var t=this;window.addEventListener("scroll",function(){var n=window.scrollY;if(n>0&&n<215){var e=n/215;document.getElementById("fixedHeader").style.backgroundColor="rgba(0, 0, 0, "+.3*e+")",document.getElementById("background").style.filter="blur("+8*e+"px)",document.getElementById("background").style.transform="scale("+(1+.05*e)+")",t.showFixedTitle=!1}else n>215?(document.getElementById("fixedHeader").style.backgroundColor="rgba(0, 0, 0, .3)",document.getElementById("background").style.filter="blur(8px)",document.getElementById("background").style.transform="scale(1.05)",t.showFixedTitle=!0):t.showFixedTitle&&0===n&&(t.showFixedTitle=!0)},!1)}}},v2ns:function(t,n){},vhfR:function(t,n,e){"use strict";function s(t){e("mYI6")}var a=e("wf2Q"),i=e("asP6"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-600038aa",null);n.a=c.exports},wU6a:function(t,n,e){"use strict";n.a={name:"swiper-slide",data:function(){return{slideClass:"swiper-slide"}},ready:function(){this.update()},mounted:function(){this.update(),this.$parent.options.slideClass&&(this.slideClass=this.$parent.options.slideClass)},updated:function(){this.update()},attached:function(){this.update()},methods:{update:function(){this.$parent&&this.$parent.swiper&&this.$parent.swiper.update&&(this.$parent.swiper.update(!0),this.$parent.options.loop&&this.$parent.swiper.reLoop())}}}},wdnp:function(t,n,e){"use strict";function s(t){e("iPPZ")}var a=e("Ocez"),i=e("Nq9A"),o=e("VU/8"),r=s,c=o(a.a,i.a,!1,r,"data-v-0f7bf1d6",null);n.a=c.exports},wf2Q:function(t,n,e){"use strict";n.a={props:["partTitle"]}},xB85:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("0IPU"),o=e("aYh0"),r=e("NYxO");n.a={data:function(){return{activeIndex:2}},components:{HeadNav:i.a,PlayerBar:o.a},methods:a()({},Object(r.b)(["stateInit"]))}},xJD8:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("NYxO");n.a={name:"app",computed:a()({},Object(i.d)(["song","songIndex","playlist","playState","playMethod"]),{songURL:function(){if(this.song.id)return"http://ws.stream.qqmusic.qq.com/"+this.song.id+".m4a?fromtag=46"}}),methods:a()({},Object(i.b)(["updatePlayingTime","setDuration","changeSongPlay"]),{updateTime:function(){this.updatePlayingTime(document.getElementById("audio").currentTime),this.setDuration(document.getElementById("audio").duration)},nextSong:function(){var t=this.songIndex;0===this.playMethod?(t+=1)===this.playlist.length&&(t=0):1===this.playMethod||2===this.playMethod&&(t=parseInt((this.playlist.length-1)*Math.random()));var n=this.playlist[t];this.changeSongPlay({song:n,index:t})}}),watch:{playState:function(t){t?document.getElementById("audio").play():document.getElementById("audio").pause()}}}},y8T9:function(t,n,e){"use strict";var s=e("vhfR");n.a={props:["recomMVist"],components:{PartTitle:s.a},filters:{playAmount:function(t){return parseInt(t/1e4)+"万"}}}},yFA6:function(t,n,e){"use strict";var s=e("Dd8w"),a=e.n(s),i=e("NYxO");n.a={computed:a()({},Object(i.d)(["playMethod","playState","song","songIndex","playlist","duration","playingTime","currentLyricIndex","likedSongs"]),Object(i.c)(["getAlbumCover","getPlayMethod"]),{getSong:function(){return this.song.id||(this.song.id=this.song.songid),this.song.name||(this.song.name=this.song.songname),this.song},liked:function(){return this.likedSongs.indexOf(this.getSong.id)>-1},getCurrentLyric:function(){var t=1e3*this.playingTime;if(this.song.lyric&&this.song.lyric.lyricArr){var n=this.song.lyric.lyricArr,e=this.song.lyric.timeArr;if(n.length>0)return t 2 | 3 | 4 | 5 | 6 | QQ音乐 - 中国最新最全免费正版高品质音乐平台! 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2.0-music-app", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "LuanMingyang <982684249@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js", 11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 12 | "e2e": "node test/e2e/runner.js", 13 | "test": "npm run unit && npm run e2e", 14 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" 15 | }, 16 | "dependencies": { 17 | "animate.css": "^3.5.2", 18 | "vue": "^2.5.2", 19 | "vue-awesome-swiper": "^2.5.4", 20 | "vue-resource": "^1.3.4", 21 | "vue-router": "^3.0.1", 22 | "vuex": "^3.0.1" 23 | }, 24 | "devDependencies": { 25 | "autoprefixer": "^7.1.2", 26 | "babel-core": "^6.22.1", 27 | "babel-eslint": "^7.1.1", 28 | "babel-loader": "^7.1.1", 29 | "babel-plugin-istanbul": "^4.1.1", 30 | "babel-plugin-transform-runtime": "^6.22.0", 31 | "babel-preset-env": "^1.3.2", 32 | "babel-preset-stage-2": "^6.22.0", 33 | "babel-register": "^6.22.0", 34 | "chai": "^4.1.2", 35 | "chalk": "^2.0.1", 36 | "chromedriver": "^2.27.2", 37 | "connect-history-api-fallback": "^1.3.0", 38 | "copy-webpack-plugin": "^4.0.1", 39 | "cross-env": "^5.0.1", 40 | "cross-spawn": "^5.0.1", 41 | "css-loader": "^0.28.0", 42 | "eslint": "^3.19.0", 43 | "eslint-config-standard": "^10.2.1", 44 | "eslint-friendly-formatter": "^3.0.0", 45 | "eslint-loader": "^1.7.1", 46 | "eslint-plugin-html": "^3.0.0", 47 | "eslint-plugin-import": "^2.7.0", 48 | "eslint-plugin-node": "^5.2.0", 49 | "eslint-plugin-promise": "^3.4.0", 50 | "eslint-plugin-standard": "^3.0.1", 51 | "eventsource-polyfill": "^0.9.6", 52 | "express": "^4.14.1", 53 | "extract-text-webpack-plugin": "^3.0.0", 54 | "file-loader": "^1.1.4", 55 | "friendly-errors-webpack-plugin": "^1.6.1", 56 | "html-webpack-plugin": "^2.30.1", 57 | "http-proxy-middleware": "^0.17.3", 58 | "inject-loader": "^3.0.0", 59 | "karma": "^1.4.1", 60 | "karma-coverage": "^1.1.1", 61 | "karma-mocha": "^1.3.0", 62 | "karma-phantomjs-launcher": "^1.0.2", 63 | "karma-phantomjs-shim": "^1.4.0", 64 | "karma-sinon-chai": "^1.3.1", 65 | "karma-sourcemap-loader": "^0.3.7", 66 | "karma-spec-reporter": "0.0.31", 67 | "karma-webpack": "^2.0.2", 68 | "mocha": "^3.2.0", 69 | "nightwatch": "^0.9.12", 70 | "node-sass": "^4.6.0", 71 | "opn": "^5.1.0", 72 | "optimize-css-assets-webpack-plugin": "^3.2.0", 73 | "ora": "^1.2.0", 74 | "phantomjs-prebuilt": "^2.1.14", 75 | "portfinder": "^1.0.13", 76 | "rimraf": "^2.6.0", 77 | "sass-loader": "^6.0.6", 78 | "selenium-server": "^3.0.1", 79 | "semver": "^5.3.0", 80 | "shelljs": "^0.7.6", 81 | "sinon": "^4.0.0", 82 | "sinon-chai": "^2.8.0", 83 | "url-loader": "^0.5.8", 84 | "vue-loader": "^13.3.0", 85 | "vue-style-loader": "^3.0.1", 86 | "vue-template-compiler": "^2.5.2", 87 | "webpack": "^3.6.0", 88 | "webpack-bundle-analyzer": "^2.9.0", 89 | "webpack-dev-middleware": "^1.12.0", 90 | "webpack-hot-middleware": "^2.18.2", 91 | "webpack-merge": "^4.1.0" 92 | }, 93 | "engines": { 94 | "node": ">= 4.0.0", 95 | "npm": ">= 3.0.0" 96 | }, 97 | "browserslist": [ 98 | "> 1%", 99 | "last 2 versions", 100 | "not ie <= 8" 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /preview/musicHall (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/musicHall (2).png -------------------------------------------------------------------------------- /preview/musicHall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/musicHall.png -------------------------------------------------------------------------------- /preview/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/player.png -------------------------------------------------------------------------------- /preview/playlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/playlist.png -------------------------------------------------------------------------------- /preview/rank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/rank.png -------------------------------------------------------------------------------- /preview/rank2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/rank2.png -------------------------------------------------------------------------------- /preview/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/search.png -------------------------------------------------------------------------------- /preview/search2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/search2.png -------------------------------------------------------------------------------- /preview/singer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/preview/singer.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 66 | 67 | 154 | -------------------------------------------------------------------------------- /src/assets/Screenshot_20171111-200253.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/src/assets/Screenshot_20171111-200253.png -------------------------------------------------------------------------------- /src/assets/Screenshot_20171125-113153.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/src/assets/Screenshot_20171125-113153.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/qqMusicLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/src/assets/qqMusicLogo.png -------------------------------------------------------------------------------- /src/components/AlbumOrMVListItem.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 19 | 20 | 21 | 90 | -------------------------------------------------------------------------------- /src/components/HeadNav.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 54 | 55 | 123 | -------------------------------------------------------------------------------- /src/components/MenuList.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 54 | 55 | 60 | -------------------------------------------------------------------------------- /src/components/MenusItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 16 | 39 | -------------------------------------------------------------------------------- /src/components/MvRecom.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 33 | 34 | 84 | -------------------------------------------------------------------------------- /src/components/PageTitle.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 42 | -------------------------------------------------------------------------------- /src/components/PartTitle.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /src/components/Player.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 176 | 177 | 178 | 427 | -------------------------------------------------------------------------------- /src/components/PlayerBar.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 107 | 108 | 109 | 196 | -------------------------------------------------------------------------------- /src/components/Playlist.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 102 | 103 | 250 | -------------------------------------------------------------------------------- /src/components/RecommendSwiper.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 42 | 43 | -------------------------------------------------------------------------------- /src/components/SongListItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 34 | 35 | 36 | 117 | -------------------------------------------------------------------------------- /src/components/SongListRecom.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 32 | 33 | 83 | -------------------------------------------------------------------------------- /src/config/api.js: -------------------------------------------------------------------------------- 1 | const COMMON_PARAMS = 'g_tk=5381&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0' 2 | const COMMON_PARAMS_H5 = 'g_tk=5381&uin=0&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=h5&needNewCode=0&format=jsonp' 3 | const C_Y_QQ_COM = 'https://c.y.qq.com' 4 | 5 | /* 搜索 */ 6 | // 热门搜索关键字 7 | const URL_HOT_KEY = `${C_Y_QQ_COM}/splcloud/fcgi-bin/gethotkey.fcg?&format=jsonp${COMMON_PARAMS}` 8 | // 搜索结果-单曲 9 | const URL_SEARCH_CLIENT_SONG = `${C_Y_QQ_COM}/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&format=jsonp&${COMMON_PARAMS}` 10 | // 搜索结果-mv 11 | const URL_SEARCH_CLIENT_MV = `${C_Y_QQ_COM}/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.mv&aggr=0&catZhida=1&lossless=0&sem=1&t=12&format=jsonp&${COMMON_PARAMS}` 12 | // 搜素专辑 13 | const URL_SEARCH_CLIENT_ALBUM = `${C_Y_QQ_COM}/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.album&aggr=0&catZhida=1&lossless=0&sem=10&t=8&format=jsonp&${COMMON_PARAMS}` 14 | // 搜索后的智能搜索 15 | const URL_SEARCH_SMARTBOX = `${C_Y_QQ_COM}/splcloud/fcgi-bin/smartbox_new.fcg?is_xml=0&format=jsonp&${COMMON_PARAMS}` 16 | // 搜索后歌单搜素 17 | const URL_SEARCH_CLIENT_DISS = `${C_Y_QQ_COM}//soso/fcgi-bin/client_music_search_songlist?flag_qc=0&format=jsonp&${COMMON_PARAMS}` 18 | 19 | /* 排行 */ 20 | // 排行榜分类 21 | // const URL_TOPLIST_OPT = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_toplist_opt.fcg?page=index&format=html&tpl=macv4&v8debug=1` 22 | const URL_TOPLIST_OPT = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_myqq_toplist.fcg?${COMMON_PARAMS_H5}` 23 | // 某个排行的列表 24 | const URL_TOPLIST_LIST = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_toplist_cp.fcg?tpl=3&page=detail&type=top&format=jsonp&${COMMON_PARAMS}` 25 | 26 | // 首页推荐 27 | const URL_RRCOM = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_first_yqq.fcg?tpl=v12&page=other&format=jsonp&${COMMON_PARAMS}` 28 | 29 | /* 歌曲 */ 30 | // 歌曲歌词 31 | // const URL_SONG_LYR = `${C_Y_QQ_COM}/lyric/fcgi-bin/fcg_query_lyric_new.fcg?${COMMON_PARAMS}` 32 | const URL_SONG_LYR = 'https://api.darlin.me/music/lyric/' 33 | 34 | /* 歌手 */ 35 | // 搜索歌手 36 | const URL_SEARCH_SINGER = `${C_Y_QQ_COM}/v8/fcg-bin/v8.fcg?channel=singer&page=list&format=jsonp&${COMMON_PARAMS}` 37 | // 相似歌手 38 | const URL_SINGER_SIM = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_simsinger.fcg?utf8=1&format=jsonp&${COMMON_PARAMS}` 39 | // 歌手的歌曲 40 | const URL_SINGER_SONGS = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?order=listen&songstatus=1&format=jsonp&${COMMON_PARAMS}` 41 | // 获得歌手的推荐歌曲和3个专辑 42 | const URL_SINGER_TRACK = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?order=listen&platform=h5page&from=h5&format=jsonp&${COMMON_PARAMS}` 43 | // 歌手的专辑 44 | const URL_SINGER_ALBUM = `${C_Y_QQ_COM}/v8/fcg-bin/fcg_v8_singer_album.fcg?order=time&format=jsonp&${COMMON_PARAMS}` 45 | // 歌手描述 46 | const URL_SINGER_DESC = `${C_Y_QQ_COM}/splcloud/fcgi-bin/fcg_get_singer_desc.fcg?utf8=1&outCharset=utf-8&format=xml` 47 | // 歌手的粉丝 48 | const URL_SINGER_FANS = `${C_Y_QQ_COM}/rsc/fcgi-bin/fcg_order_singer_getnum.fcg?utf8=1&format=jsonp&${COMMON_PARAMS}` 49 | // 歌手的MV 50 | const URL_SINGER_MVS = `${C_Y_QQ_COM}/mv/fcgi-bin/fcg_singer_mv.fcg?cid=205360581&format=jsonp&${COMMON_PARAMS}` 51 | 52 | export default { 53 | URL_HOT_KEY, 54 | URL_SEARCH_CLIENT_SONG, 55 | URL_SEARCH_CLIENT_MV, 56 | URL_SEARCH_CLIENT_ALBUM, 57 | URL_SEARCH_SMARTBOX, 58 | URL_TOPLIST_OPT, 59 | URL_TOPLIST_LIST, 60 | URL_SEARCH_CLIENT_DISS, 61 | URL_RRCOM, 62 | URL_SONG_LYR, 63 | URL_SEARCH_SINGER, 64 | URL_SINGER_SIM, 65 | URL_SINGER_SONGS, 66 | URL_SINGER_TRACK, 67 | URL_SINGER_ALBUM, 68 | URL_SINGER_MVS, 69 | URL_SINGER_DESC, 70 | URL_SINGER_FANS 71 | } 72 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | import store from './store' 7 | import VueResource from 'vue-resource' 8 | import Animate from 'animate.css' 9 | import VueAwesomeSwiper from 'vue-awesome-swiper' 10 | 11 | Vue.use(VueResource) 12 | Vue.use(Animate) 13 | Vue.use(VueAwesomeSwiper) 14 | 15 | Vue.config.productionTip = false 16 | 17 | /* eslint-disable no-new */ 18 | new Vue({ 19 | el: '#app', 20 | router, 21 | store, 22 | template: '', 23 | components: { App } 24 | }) 25 | -------------------------------------------------------------------------------- /src/pages/AllRank.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 79 | 80 | 169 | -------------------------------------------------------------------------------- /src/pages/AllSingers.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/src/pages/AllSingers.vue -------------------------------------------------------------------------------- /src/pages/Discover.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 34 | -------------------------------------------------------------------------------- /src/pages/MusicHall.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 77 | 78 | 83 | -------------------------------------------------------------------------------- /src/pages/Rank.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 124 | 125 | 269 | -------------------------------------------------------------------------------- /src/pages/Search.vue: -------------------------------------------------------------------------------- 1 | 101 | 102 | 269 | 270 | 511 | -------------------------------------------------------------------------------- /src/pages/Singer.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 204 | 205 | 378 | -------------------------------------------------------------------------------- /src/pages/User.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 34 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import MusicHall from '@/pages/MusicHall' 4 | import Search from '@/pages/Search' 5 | import User from '@/pages/User' 6 | import Discover from '@/pages/Discover' 7 | import AllRank from '@/pages/AllRank' 8 | import Rank from '@/pages/Rank' 9 | import Singer from '@/pages/Singer' 10 | 11 | Vue.use(Router) 12 | 13 | export default new Router({ 14 | // mode: 'history', 15 | routes: [ 16 | { 17 | path: '/', 18 | name: 'musicHall', 19 | component: MusicHall 20 | }, 21 | { 22 | path: '/search', 23 | name: 'search', 24 | component: Search 25 | }, 26 | { 27 | path: '/user', 28 | name: 'user', 29 | component: User 30 | }, 31 | { 32 | path: '/discover', 33 | name: 'discover', 34 | component: Discover 35 | }, 36 | { 37 | path: '/rank', 38 | name: 'allrank', 39 | component: AllRank 40 | }, 41 | { 42 | path: '/rank/:topid ', 43 | name: 'rank', 44 | component: Rank, 45 | props: true 46 | }, 47 | { 48 | path: '/singer/:singermid', 49 | name: 'singer', 50 | component: Singer, 51 | props: true 52 | } 53 | ] 54 | }) 55 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 状态初始化 3 | stateInit (context) { 4 | context.commit('STATE_INIT') 5 | }, 6 | // 播放器初始化 7 | playerInit ({ commit }) { 8 | commit('PLAYER_INIT') 9 | }, 10 | // 更新播放列表 11 | updatePlaylist ({ commit }, list) { 12 | commit('UPDATE_PLAYLIST', list) 13 | }, 14 | // 设置歌曲并播放 15 | changeSongPlay ({ commit }, { song, index }) { 16 | commit('CHANGE_AND_PLAY', { song, index }) 17 | }, 18 | // 播放(暂停播放)歌曲 19 | play: function ({ commit }) { 20 | commit('PLAY') 21 | }, 22 | // 更新已播放时长 23 | updatePlayingTime ({ commit }, time) { 24 | commit('UPDATE_PLAYINGTIME', time) 25 | }, 26 | // 更新当前歌词下标 27 | updateLyricIndex ({ commit }) { 28 | commit('UPDATE_LYRICINDEX') 29 | }, 30 | // 设置播放器的隐藏与显示 31 | setPlayerShow ({ commit }, show) { 32 | commit('SET_PLAYERSHOW', show) 33 | }, 34 | // 设置当前歌曲总时长 35 | setDuration ({ commit }, duration) { 36 | commit('SET_DURATION', duration) 37 | }, 38 | // 切换播放方式 39 | changePlayMethod ({ commit }, method) { 40 | commit('CHANGE_PLAY_METHOD', method) 41 | }, 42 | // 设置播放列表的隐藏与显示 43 | setPlaylistShow ({ commit }, show) { 44 | commit('SET_PLAYLISTSHOW', show) 45 | }, 46 | // 标记/取消标记歌曲为“我喜欢” 47 | setSongLiked ({ commit }, song) { 48 | commit('SET_SONGLIKED', song) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import mutations from './mutations' 4 | import actions from './actions' 5 | 6 | Vue.use(Vuex) 7 | 8 | export default new Vuex.Store({ 9 | state: function () { 10 | return { 11 | playMethod: 0, // 播放方式:0列表循环,1单曲循环,2随机播放 12 | playerShow: false, // 是否显示播放器 13 | playlist: [], // 播放列表 14 | playlistShow: false, // 是否显示播放列表 15 | // 当前正在播放的歌曲 16 | song: { 17 | id: null, // 歌曲id 18 | name: '', // 歌曲名 19 | album: { 20 | mid: '' // 专辑id 21 | }, 22 | lyric: { 23 | lyric: '', 24 | lyricArr: [], 25 | timeArr: [] 26 | } 27 | }, 28 | songIndex: null, // 当前歌曲在播放列表中的下标 29 | duration: 0, // 当前歌曲的总时长 30 | playingTime: 0, // 当前歌曲已播放时长 31 | currentLyricIndex: 0, // 当前歌词下标 32 | playState: false, // 播放状态 33 | likedSongs: [] // 标记为“我喜欢”的歌曲 34 | } 35 | }, 36 | getters: { 37 | getAlbumCover: function (state) { 38 | if (state.song.id) { 39 | var albummid = '' 40 | if (state.song.album) { 41 | albummid = state.song.album.mid 42 | } else { 43 | albummid = state.song.albummid 44 | } 45 | return 'https://y.gtimg.cn/music/photo_new/T002R500x500M000' + albummid + '.jpg' 46 | } 47 | }, 48 | getPlayMethod: function (state) { 49 | var icon = '' 50 | var text = '' 51 | if (state.playMethod === 0) { 52 | icon = '' 53 | text = '顺序播放' 54 | } else if (state.playMethod === 1) { 55 | icon = '' 56 | text = '单曲循环' 57 | } else if (state.playMethod === 2) { 58 | icon = '' 59 | text = '随机播放' 60 | } 61 | return { icon, text } 62 | } 63 | }, 64 | mutations, 65 | actions 66 | }) 67 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import API from '../config/api' 3 | import Base64 from '../utils/base64' 4 | 5 | const STATE_INIT = 'STATE_INIT' // 初始化 6 | const PLAYER_INIT = 'PLAYER_INIT' // 播放器初始化 7 | const UPDATE_PLAYLIST = 'UPDATE_PLAYLIST' // 更新播放列表 8 | const CHANGE_AND_PLAY = 'CHANGE_AND_PLAY' // 切换歌曲播放并添加到播放列表 9 | const PLAY = 'PLAY' // 切换播放状态 10 | const UPDATE_PLAYINGTIME = 'UPDATE_PLAYINGTIME' // 更新已播放时长 11 | const UPDATE_LYRICINDEX = 'UPDATE_LYRICINDEX' // 更新当前歌词下标 12 | const SET_PLAYERSHOW = 'SET_PLAYERSHOW' // 设置播放器的隐藏与显示 13 | const SET_DURATION = 'SET_DURATION' // 设置当前歌曲总时长 14 | const CHANGE_PLAY_METHOD = 'CHANGE_PLAY_METHOD' // 切换播放方式 15 | const SET_PLAYLISTSHOW = 'SET_PLAYLISTSHOW' // 设置播放列表的隐藏与显示 16 | const SET_SONGLIKED = 'SET_SONGLIKED' // 标记/取消标记歌曲为“我喜欢” 17 | 18 | export default { 19 | // 初始化 20 | [STATE_INIT]: function (state) { 21 | state.playerShow = false // 不显示播放器 22 | state.playlistShow = false // 不显示播放列表 23 | if (localStorage.likedSongs) { 24 | state.likedSongs = JSON.parse(localStorage.likedSongs) // 获取被标记为“我喜欢”的歌曲 25 | } 26 | }, 27 | [PLAYER_INIT] (state) { 28 | state.song = { 29 | id: null, // 歌曲id 30 | name: '', // 歌曲名 31 | album: { 32 | mid: '' // 专辑id 33 | }, 34 | lyric: { 35 | lyric: '', 36 | lyricArr: [], 37 | timeArr: [] 38 | } 39 | } 40 | state.songIndex = null // 当前歌曲在播放列表中的下标 41 | state.duration = 0 // 当前歌曲的总时长 42 | state.playingTime = 0 // 当前歌曲已播放时长 43 | state.currentLyricIndex = 0 // 当前歌词下标 44 | state.playState = false // 播放状态 45 | }, 46 | // 更新播放列表 47 | [UPDATE_PLAYLIST] (state, list) { 48 | list = list.map(function (song) { 49 | if (song.musicData) { 50 | song = song.musicData 51 | } else if (song.data) { 52 | song = song.data 53 | } 54 | return song 55 | }) 56 | state.playlist = list 57 | }, 58 | // 切换歌曲 59 | [CHANGE_AND_PLAY]: function (state, { song, index }) { 60 | song.id = song.id || song.songid 61 | // 如果没有歌词 62 | if (!song.lyric || song.lyric === '') { 63 | // 搜索歌词 64 | Vue.http.jsonp(API.URL_SONG_LYR + song.id, 65 | { 66 | async: false, 67 | jsonp: 'callback' 68 | } 69 | ).then((response) => { 70 | // console.log(Base64.decode(response.data.lyric)) 71 | 72 | var lyricArr = [] // 用于存储歌词 73 | var timeArr = [] // 用于存储时间 74 | var re = /\[([0-9:\.]{8})\]/gm 75 | Base64.decode(response.data.lyric).split(re).slice(1).forEach((value, index) => { 76 | if (index % 2 === 0) { 77 | timeArr.push(value) 78 | } else { 79 | lyricArr.push(value) 80 | } 81 | }) 82 | 83 | // 将时间转换成毫秒,如:00:00.28 => 280, 00:01:10 => 1100 84 | for (var i = 0; i < timeArr.length; i++) { 85 | var minutes = parseInt(timeArr[i].split(':')[0]) 86 | var seconds = parseInt(timeArr[i].split(':')[1].split('.')[0]) 87 | var millisecond = parseInt(timeArr[i].split(':')[1].split('.')[1]) * 10 88 | timeArr[i] = minutes * 60 * 1000 + seconds * 1000 + millisecond 89 | } 90 | 91 | // timeArr.map(time => { 92 | // var minutes = parseInt(time.split(':')[0]) 93 | // var seconds = parseInt(time.split(':')[1].split('.')[0]) 94 | // var bseconds = parseInt(time.split(':')[1].split('.')[1]) * 10 95 | // return minutes * 60 * 1000 + seconds * 1000 + bseconds 96 | // }) 97 | 98 | song.lyric = { 99 | lyric: Base64.decode(response.data.lyric), 100 | lyricArr, 101 | timeArr 102 | } 103 | }).catch(function (response) { 104 | console.log(response) 105 | }) 106 | } 107 | state.song = song 108 | // state.playlist.push(song) 109 | state.songIndex = index 110 | state.playingTime = 0 111 | state.currentLyricIndex = 0 112 | state.playState = true 113 | }, 114 | // 切换播放状态 115 | [PLAY]: function (state) { 116 | state.playState = !state.playState 117 | }, 118 | // 更新已播放时长 119 | [UPDATE_PLAYINGTIME]: function (state, time) { 120 | state.playingTime = time 121 | }, 122 | // 更新当前歌词下标 123 | [UPDATE_LYRICINDEX]: function (state) { 124 | state.currentLyricIndex++ 125 | }, 126 | // 设置播放器的隐藏与显示 127 | [SET_PLAYERSHOW]: function (state, show) { 128 | state.playerShow = show 129 | }, 130 | // 设置当前歌曲总时长 131 | [SET_DURATION]: function (state, duration) { 132 | state.duration = duration 133 | }, 134 | // 切换播放方式 135 | [CHANGE_PLAY_METHOD]: function (state, method) { 136 | if (method === 2) { 137 | method = 0 138 | } else { 139 | method++ 140 | } 141 | state.playMethod = method 142 | }, 143 | // 设置播放列表的隐藏与显示 144 | [SET_PLAYLISTSHOW]: function (state, show) { 145 | state.playlistShow = show 146 | }, 147 | // 标记/取消标记歌曲为“我喜欢” 148 | [SET_SONGLIKED]: function (state, song) { 149 | if (!song.id) { 150 | song.id = song.songid 151 | } 152 | var liked = state.likedSongs.indexOf(song.id) > -1 // 判断是否已标记为“我喜欢” 153 | if (liked) { // 是则取消标记 154 | var index = state.likedSongs.indexOf(song.id) 155 | state.likedSongs.splice(index, 1) 156 | } else { 157 | state.likedSongs.push(song.id) 158 | } 159 | localStorage.likedSongs = JSON.stringify(state.likedSongs) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/utils/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by sioxa on 2016/12/30 0030. 3 | */ 4 | 5 | // //1.加密 6 | // var str = '124中文内容'; 7 | // var base = new Base64(); 8 | // var result = base.encode(str); 9 | // //document.write(result); 10 | // 11 | // //2.解密 12 | // var result2 = base.decode(result); 13 | // document.write(result2); 14 | const _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 15 | 16 | // private method for UTF-8 encoding 17 | function _utf8_encode(string) { 18 | string = string.replace(/\r\n/g, "\n"); 19 | var utftext = ""; 20 | for (var n = 0; n < string.length; n++) { 21 | var c = string.charCodeAt(n); 22 | if (c < 128) { 23 | utftext += String.fromCharCode(c); 24 | } else if ((c > 127) && (c < 2048)) { 25 | utftext += String.fromCharCode((c >> 6) | 192); 26 | utftext += String.fromCharCode((c & 63) | 128); 27 | } else { 28 | utftext += String.fromCharCode((c >> 12) | 224); 29 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 30 | utftext += String.fromCharCode((c & 63) | 128); 31 | } 32 | 33 | } 34 | return utftext; 35 | } 36 | 37 | function _utf8_decode(utftext) { 38 | var string = ""; 39 | var i = 0; 40 | var c = 0; 41 | var c3 = 0; 42 | var c2 = 0; 43 | while (i < utftext.length) { 44 | c = utftext.charCodeAt(i); 45 | if (c < 128) { 46 | string += String.fromCharCode(c); 47 | i++; 48 | } else if ((c > 191) && (c < 224)) { 49 | c2 = utftext.charCodeAt(i + 1); 50 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 51 | i += 2; 52 | } else { 53 | c2 = utftext.charCodeAt(i + 1); 54 | c3 = utftext.charCodeAt(i + 2); 55 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 56 | i += 3; 57 | } 58 | } 59 | return string; 60 | } 61 | 62 | export default { 63 | // public method for encoding 64 | encode: function (input) { 65 | var output = ""; 66 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 67 | var i = 0; 68 | input = _utf8_encode(input); 69 | while (i < input.length) { 70 | chr1 = input.charCodeAt(i++); 71 | chr2 = input.charCodeAt(i++); 72 | chr3 = input.charCodeAt(i++); 73 | enc1 = chr1 >> 2; 74 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 75 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 76 | enc4 = chr3 & 63; 77 | if (isNaN(chr2)) { 78 | enc3 = enc4 = 64; 79 | } else if (isNaN(chr3)) { 80 | enc4 = 64; 81 | } 82 | output = output + 83 | _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + 84 | _keyStr.charAt(enc3) + _keyStr.charAt(enc4); 85 | } 86 | return output; 87 | }, 88 | 89 | // public method for decoding 90 | decode: function (input) { 91 | var output = ""; 92 | var chr1, chr2, chr3; 93 | var enc1, enc2, enc3, enc4; 94 | var i = 0; 95 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 96 | while (i < input.length) { 97 | enc1 = _keyStr.indexOf(input.charAt(i++)); 98 | enc2 = _keyStr.indexOf(input.charAt(i++)); 99 | enc3 = _keyStr.indexOf(input.charAt(i++)); 100 | enc4 = _keyStr.indexOf(input.charAt(i++)); 101 | chr1 = (enc1 << 2) | (enc2 >> 4); 102 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 103 | chr3 = ((enc3 & 3) << 6) | enc4; 104 | output = output + String.fromCharCode(chr1); 105 | if (enc3 != 64) { 106 | output = output + String.fromCharCode(chr2); 107 | } 108 | if (enc4 != 64) { 109 | output = output + String.fromCharCode(chr3); 110 | } 111 | } 112 | output = _utf8_decode(output); 113 | return output; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuanMingyang/vue2-music-app/8ab3c1fa53c09c46274ec3bd9d4c0d7252b0d815/static/.gitkeep -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 4 | 5 | server.ready.then(() => { 6 | // 2. run the nightwatch test suite against it 7 | // to run in additional browsers: 8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 9 | // 2. add it to the --env flag below 10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 11 | // For more information on Nightwatch's config file, see 12 | // http://nightwatchjs.org/guide#settings-file 13 | var opts = process.argv.slice(2) 14 | if (opts.indexOf('--config') === -1) { 15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 16 | } 17 | if (opts.indexOf('--env') === -1) { 18 | opts = opts.concat(['--env', 'chrome']) 19 | } 20 | 21 | var spawn = require('cross-spawn') 22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 23 | 24 | runner.on('exit', function (code) { 25 | server.close() 26 | process.exit(code) 27 | }) 28 | 29 | runner.on('error', function (err) { 30 | server.close() 31 | throw err 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.conf') 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' } 30 | ] 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import HelloWorld from '@/components/HelloWorld' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(HelloWorld) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .to.equal('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------