├── src ├── api │ └── index.js ├── assets │ ├── info.png │ ├── logo.png │ ├── about_01.jpg │ ├── clock_ic.png │ ├── default_pic.jpg │ ├── list_sprite.png │ ├── logo_footer.png │ └── search_sprite.png ├── utils.js ├── main.js ├── components │ ├── header │ │ ├── readme.md │ │ └── index.vue │ ├── lyc-item.vue │ ├── song-list.vue │ ├── search-list.vue │ └── play-progress.vue ├── App.vue ├── vuex │ └── store.js ├── localStorage.js ├── router │ └── index.js ├── base64.js └── views │ ├── rank │ ├── index.vue │ └── detail.vue │ ├── recommend │ └── index.vue │ ├── singer-page.vue │ ├── search │ └── index.vue │ └── playing.vue ├── static └── .gitkeep ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── music.gif ├── .gitignore ├── playing.png ├── .editorconfig ├── .babelrc ├── index.html ├── README.md └── package.json /src/api/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /music.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/music.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | .idea/ 6 | -------------------------------------------------------------------------------- /playing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/playing.png -------------------------------------------------------------------------------- /src/assets/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/info.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/about_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/about_01.jpg -------------------------------------------------------------------------------- /src/assets/clock_ic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/clock_ic.png -------------------------------------------------------------------------------- /src/assets/default_pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/default_pic.jpg -------------------------------------------------------------------------------- /src/assets/list_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/list_sprite.png -------------------------------------------------------------------------------- /src/assets/logo_footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/logo_footer.png -------------------------------------------------------------------------------- /src/assets/search_sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chengjun2014/qq_music/HEAD/src/assets/search_sprite.png -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | 2 | function fillWith(num, symbol) { 3 | var fill = symbol ? symbol : '0'; 4 | return (symbol + num).slice(-2); 5 | } 6 | 7 | export default { 8 | sec2time: function(sec) { 9 | return fillWith(sec/60) + ':' + fillWith(sec%60); 10 | }, 11 | } -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false, 8 | "env": { 9 | "test": { 10 | "plugins": [ "istanbul" ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import router from './router' 4 | import vueResource from 'vue-resource' 5 | import store from './vuex/store' 6 | //import axios from 'axios' 7 | 8 | Vue.use(vueResource); 9 | 10 | new Vue({ 11 | el: '#app', 12 | store, 13 | router, 14 | template: '', 15 | components: { App } 16 | }) 17 | -------------------------------------------------------------------------------- /src/components/header/readme.md: -------------------------------------------------------------------------------- 1 | # 头部组件 2 | 3 | ----------- 4 | 5 | ## 应用示例 6 | 7 | 8 | 9 | ## 参数说明 10 | 11 | > 左侧信息 12 | show-logo: 显示顶部logo,默认值`false`(仅首页需要显示) 13 | show-back: 显示左侧返回箭头,默认值`true` 14 | back-text: 左侧返回箭头后紧跟的文字,默认值`'返回'` 15 | 16 | > 中间信息 17 | title: 顶部居中标题 18 | 19 | > 右侧信息(显示依赖全局的login信息) 20 | show-name: 显示右侧用户名,默认值`false` 21 | show-home-icon: 显示右侧主页icon,默认值`false` 22 | show-img: 显示右侧用户头像,默认值`true` 23 | show-register: 显示注册入口,默认值`false` 24 | show-login: 显示登陆入口,默认值`false` 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 46 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vue-qq-music 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /src/vuex/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | export default new Vuex.Store({ 6 | state: { 7 | curTab: 0, 8 | curSong: '', 9 | songIndex: 0, 10 | songList: [], 11 | }, 12 | mutations: { 13 | changeTab (state, n) { 14 | state.curTab = n; 15 | }, 16 | changeSong (state, song) { 17 | state.curSong = song; 18 | }, 19 | changeSongIndex (state, index) { 20 | state.songIndex = index; 21 | }, 22 | changeSongList (state, songList) { 23 | state.songList = songList; 24 | } 25 | }, 26 | getters: { 27 | getTab (state, getters) { 28 | return state.curTab; 29 | }, 30 | getSong (state, getters) { 31 | return state.curSong; 32 | }, 33 | getSongIndex (state, getters) { 34 | return state.songIndex; 35 | }, 36 | getSongByIndex (state, getters) { 37 | return state.songList[state.songIndex]; 38 | } 39 | } 40 | }) -------------------------------------------------------------------------------- /src/localStorage.js: -------------------------------------------------------------------------------- 1 | export default { 2 | set: function(key, val) { 3 | localStorage.setItem(key, val); 4 | }, 5 | get: function(key) { 6 | return localStorage.getItem(key); 7 | }, 8 | clear: function() { 9 | localStorage.clear(); 10 | }, 11 | pushArray: function(arr, val) { 12 | let curArr = this.getArray(arr); 13 | 14 | if (curArr.length == 0) { 15 | this.set(arr, val); 16 | } else { 17 | let index = curArr.indexOf(val); 18 | if (index >= 0) { 19 | curArr.splice(index, 1); 20 | } 21 | curArr.unshift(val); 22 | this.set(arr, curArr.join(',')) 23 | } 24 | }, 25 | getArray: function (arr, n) { // 获取最后 n 个元素,不足则全部返还,默认 n = 10 26 | var keys = this.get(arr), 27 | boundary = n || 10, 28 | result = keys ? keys.split(',') : []; 29 | 30 | if (result.length > boundary) { 31 | result = result.slice(0, boundary); 32 | this.set(arr, result.join(',')); // 防止占用太多本地存储空间,并且过多数据处理起来性能更低 33 | } 34 | return result; 35 | } 36 | } -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Recommend from 'views/recommend/index' 4 | import Rank from 'views/rank/index' 5 | import ListDetail from 'views/rank/detail' 6 | import Search from 'views/search/index' 7 | import Playing from 'views/playing' 8 | import Singer from 'views/singer-page' 9 | 10 | Vue.use(Router); 11 | 12 | var route = new Router({ 13 | routes: [ 14 | { 15 | path: '/', 16 | name: 'Home', 17 | component: Recommend 18 | }, 19 | { 20 | path: '/recommend', 21 | component: Recommend 22 | }, 23 | { 24 | path: '/rank', 25 | component: Rank 26 | }, 27 | { 28 | path: '/search', 29 | name: 'Search', 30 | component: Search 31 | }, 32 | { 33 | name: 'ListDetail', 34 | path: '/listDetail/:id', 35 | component: ListDetail 36 | }, 37 | { 38 | name: 'Playing', 39 | path: '/playing/:songid', 40 | component: Playing 41 | }, 42 | { 43 | name: 'Singer', 44 | path: '/singer/:singerid', 45 | component: Singer 46 | } 47 | ] 48 | }) 49 | 50 | 51 | export default route; 52 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: './', // 原来是 '/' 为了build后在本地能打开故改成'./' by chengjun 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8080, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: {}, 31 | // CSS Sourcemaps off by default because relative paths are "buggy" 32 | // with this option, according to the CSS-Loader README 33 | // (https://github.com/webpack/css-loader#sourcemaps) 34 | // In our experience, they generally work as expected, 35 | // just be aware of this issue when enabling this option. 36 | cssSourceMap: false 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/header/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 26 | 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一个假的qq音乐Vue SPA qq_music 2 | 3 | > 我可能做了一个假的qq音乐demo, 本demo参考`https://y.qq.com`开发的,难怪跟网上其他人的案例界面不一样。。。Orz 4 | 5 | ## QQ音乐小程序版本 6 | 7 | **项目地址:**[查看](https://github.com/chengjun2014/wx_QQmusic) 8 | 9 | ## Build Setup 10 | 11 | ``` bash 12 | # 安装依赖 13 | npm install 14 | 15 | # serve with hot reload at localhost:8080 启动服务 16 | npm run dev (会自动启动浏览器运行项目) 17 | 18 | # build for production with minification 19 | npm run build (对这里已经优化了配置文件使 build 生成的文件可以在本地查看,或作为静态页面线上预览) 20 | 21 | # build for production and view the bundle analyzer report 22 | npm run build --report 23 | 24 | ``` 25 | ## 功能实现 26 | 27 | 首页:banner滚动效果实现,电台下级页面未接入(配的活动页或列表页,情况太复杂没有固定的借口) 28 | 29 | 排行榜:完成榜单列表和榜单歌曲list,并可播放榜单歌曲,榜单页播放按钮可播放当前专辑第一首歌曲 30 | 31 | 搜索页:搜索功能完成,并完成歌手的个人页面,添加搜索框回车提交;热词点击即触发搜索 32 | 33 | 播放页:歌曲正常播放,歌词同步高亮/滚动;播单显示正常;未做播放控制/收藏功能,播发完自动切换到停止状态 34 | 35 | 搜索结果页:搜索结果正常显示/跳转到对应播放页面 36 | 37 | 搜索历史去重/排序 38 | 39 | 自定义 `localstorage` 方法,添加/删除/清空数据; 40 | 41 | 优化 `localstorage` 可存/取数组类型(通过序列化和反序列化) 42 | 43 | 父子模块间数据通信、交互事件相应(播放页面点击列表切换播放页当前播放歌曲,子组件事件传递给父组件) 44 | 45 | 46 | > 个人闲暇时间里做的————持续更新/优化 47 | 48 | ## 目前存在的问题 49 | 50 | 组件划分还不够精细,这个需要更多经验和对产品的理解 51 | 52 | 代码优化还有待加强 53 | 54 | 数据请求可以用api.js统一管理,组件只关心参数和返回值即可 55 | 56 | action管理可以通过dispatch 或commit触发获取数据 57 | 58 | ## 优点 59 | 60 | 用到了比较新且全面的 `Vue` 相关的技术栈, 61 | 62 | 1. "vue": "^2.1.10", 63 | 2. "vue-awesome-swiper": "^2.3.2", 64 | 3. "vue-resource": "^1.2.0", 65 | 4. "vue-router": "^2.2.0", 66 | 5. "vuex": "^2.1.2" 67 | 68 | 代码格式比较整齐(换过一次IDE,可能个别文件缩紧不统一,见到时候会调整回来) 69 | 70 | 71 | ## 案例截图 72 | 73 | 截图是早期版本,最终完成效果以代码运行为准,建议clone代码到本地运行查看效果或源码 74 | 75 | 76 | ### 切换到手机模式在浏览器里打开[点击预览](http://dabaipm.cn/qq_music/index.html) 77 | 78 | 79 | 80 | 81 | ![image](https://github.com/chengjun2014/qq_music/blob/master/playing.png) 82 | 83 | 84 | 85 | ![image](https://github.com/chengjun2014/qq_music/blob/master/music.gif) 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/components/lyc-item.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 47 | 48 | 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lima_spa", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "程军 ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "build": "node build/build.js" 10 | }, 11 | "dependencies": { 12 | "less": "^2.7.2", 13 | "vue": "^2.1.10", 14 | "vue-awesome-swiper": "^2.3.2", 15 | "vue-resource": "^1.2.0", 16 | "vue-router": "^2.2.0", 17 | "vuex": "^2.1.2" 18 | }, 19 | "devDependencies": { 20 | "ansi-html": "^0.0.7", 21 | "ansi-regex": "^2.1.1", 22 | "autoprefixer": "^6.7.2", 23 | "babel-core": "^6.22.1", 24 | "babel-loader": "^6.2.10", 25 | "babel-plugin-transform-runtime": "^6.22.0", 26 | "babel-preset-es2015": "^6.22.0", 27 | "babel-preset-stage-2": "^6.22.0", 28 | "babel-register": "^6.22.0", 29 | "chalk": "^1.1.3", 30 | "connect-history-api-fallback": "^1.3.0", 31 | "copy-webpack-plugin": "^4.0.1", 32 | "css-loader": "^0.26.1", 33 | "eventsource-polyfill": "^0.9.6", 34 | "express": "^4.14.1", 35 | "extract-text-webpack-plugin": "^2.0.0-rc.3", 36 | "file-loader": "^0.10.0", 37 | "friendly-errors-webpack-plugin": "^1.1.3", 38 | "function-bind": "^1.1.0", 39 | "html-entities": "^1.2.0", 40 | "html-webpack-plugin": "^2.28.0", 41 | "http-proxy-middleware": "^0.17.3", 42 | "less-loader": "^2.2.3", 43 | "opn": "^4.0.2", 44 | "optimize-css-assets-webpack-plugin": "^1.3.0", 45 | "ora": "^1.1.0", 46 | "rimraf": "^2.6.0", 47 | "semver": "^5.3.0", 48 | "strip-ansi": "^3.0.1", 49 | "swiper": "^3.4.1", 50 | "url-loader": "^0.5.7", 51 | "vue-loader": "^11.0.0", 52 | "vue-style-loader": "^2.0.0", 53 | "vue-template-compiler": "^2.1.10", 54 | "webpack": "^2.2.1", 55 | "webpack-bundle-analyzer": "^2.2.1", 56 | "webpack-dev-middleware": "^1.10.0", 57 | "webpack-hot-middleware": "^2.16.1", 58 | "webpack-merge": "^2.6.1" 59 | }, 60 | "engines": { 61 | "node": ">= 4.0.0", 62 | "npm": ">= 3.0.0" 63 | } 64 | } -------------------------------------------------------------------------------- /src/components/song-list.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 46 | 47 | -------------------------------------------------------------------------------- /src/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 | } -------------------------------------------------------------------------------- /src/views/rank/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 64 | 65 | 156 | 157 | -------------------------------------------------------------------------------- /src/components/search-list.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 71 | 72 | 120 | -------------------------------------------------------------------------------- /src/views/rank/detail.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 99 | 100 | 163 | -------------------------------------------------------------------------------- /src/views/recommend/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 95 | 96 | 202 | -------------------------------------------------------------------------------- /src/views/singer-page.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 118 | 119 | 261 | -------------------------------------------------------------------------------- /src/components/play-progress.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 78 | 79 | 245 | -------------------------------------------------------------------------------- /src/views/search/index.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 147 | 148 | 266 | -------------------------------------------------------------------------------- /src/views/playing.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 184 | 185 | 305 | --------------------------------------------------------------------------------