├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── loading.gif │ └── logo.png ├── common │ ├── fonts │ │ ├── music-icon.eot │ │ ├── music-icon.svg │ │ ├── music-icon.ttf │ │ └── music-icon.woff │ ├── image │ │ └── default.png │ ├── js │ │ ├── mixin.js │ │ └── request.js │ └── stylus │ │ ├── base.styl │ │ ├── icon.styl │ │ ├── index.styl │ │ ├── mixin.styl │ │ ├── reset.styl │ │ └── variable.styl ├── components │ ├── app │ │ └── m-header │ │ │ ├── Mheader.vue │ │ │ ├── logo@2x.png │ │ │ └── logo@3x.png │ ├── base │ │ ├── Confirm.vue │ │ ├── Disc.vue │ │ ├── Load.vue │ │ ├── Scroll.vue │ │ ├── Tab.vue │ │ └── Toast.vue │ ├── comment │ │ └── Comment.vue │ ├── detail │ │ ├── AlbumDetail.vue │ │ ├── RankDetail.vue │ │ ├── SongListDetail.vue │ │ └── singerDetail.vue │ ├── list │ │ ├── MusicItem.vue │ │ └── musicList.vue │ ├── mine │ │ ├── MineLove.vue │ │ ├── MineRank.vue │ │ ├── MineSinger.vue │ │ └── MineSongList.vue │ ├── operatorSong │ │ └── OperatorSong.vue │ ├── play │ │ └── Player.vue │ ├── playList │ │ └── PlayList.vue │ ├── progress-bar │ │ └── progressBar.vue │ ├── quickSearch │ │ └── QuickSearch.vue │ ├── rank │ │ └── RankList.vue │ ├── recommend │ │ └── Banner.vue │ ├── result │ │ ├── NoResult.vue │ │ └── Result.vue │ └── singer │ │ ├── SingerCategory.vue │ │ └── SingerList.vue ├── main.js ├── router │ └── index.js ├── store │ └── index.js └── views │ ├── mine │ └── Mine.vue │ ├── rank │ └── Rank.vue │ ├── recommend │ └── Recommend.vue │ ├── search │ └── Search.vue │ ├── singer │ └── Singer.vue │ └── videoPlay │ └── VideoPlay.vue └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-qqMusic 2 | ## 利用vue定制一个专属自己的项目音乐项目 3 | ### 项目支持 4 | 1. MVVM框架 Vue.js(2.6) 5 | 2. 工具支持 6 | 1. 脚手架: `vue-cli` 7 | 2. 自动化构建工具: `webpack` 8 | 3. 代码规范:`eslint` 9 | 4. css预处理 `stylus` 10 | 3. 架构支撑 11 | 1. 基础组件库 12 | 2. 状态管理 `vuex` 13 | 3. 路由 `vue-router` 14 | 4. 服务端通讯 `axios` 15 | 5. 第三方插件 16 | 1. 首页轮播图 vue-awesome-swiper 17 | npm install swiper vue-awesome-swiper --save 18 | 2. 滚动组件 better-scroll 19 | npm install better-scroll@next -S 20 | 3. 图片懒加载 Vue-Lazyload 21 | npm i vue-lazyload -S 22 | 4. 歌词处理 lyric-parser 23 | npm install lyric-parser 24 | ### 应用技术 25 | 1. html css js 26 | 2. vue.js 27 | 3. npm 28 | 4. es6 29 | ### 实战收获 30 | 1. 组件化,模块化开发方式 31 | 2. 第三方插件的熟练运用 32 | 3. 原生App交互 33 | ### 项目预览位置 https://www.bilibili.com/video/BV1aa4y1E7te/ 34 | ### 线上访问地址: http://www.tj123.xyz 35 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-music", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.2", 12 | "better-scroll": "^1.15.2", 13 | "core-js": "^3.6.5", 14 | "lyric-parser": "^1.0.1", 15 | "swiper": "^5.4.5", 16 | "vue": "^2.6.11", 17 | "vue-awesome-swiper": "^4.1.1", 18 | "vue-lazyload": "^1.3.3", 19 | "vue-router": "^3.2.0", 20 | "vuex": "^3.4.0" 21 | }, 22 | "devDependencies": { 23 | "@vue/cli-plugin-babel": "~4.4.0", 24 | "@vue/cli-plugin-eslint": "~4.4.0", 25 | "@vue/cli-plugin-router": "~4.4.0", 26 | "@vue/cli-plugin-vuex": "~4.4.0", 27 | "@vue/cli-service": "~4.4.0", 28 | "babel-eslint": "^10.1.0", 29 | "eslint": "^6.7.2", 30 | "eslint-plugin-vue": "^6.2.2", 31 | "stylus": "^0.54.7", 32 | "stylus-loader": "^3.0.2", 33 | "vue-template-compiler": "^2.6.11" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 36 | 39 | -------------------------------------------------------------------------------- /src/assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/assets/loading.gif -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/assets/logo.png -------------------------------------------------------------------------------- /src/common/fonts/music-icon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/common/fonts/music-icon.eot -------------------------------------------------------------------------------- /src/common/fonts/music-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/common/fonts/music-icon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/common/fonts/music-icon.ttf -------------------------------------------------------------------------------- /src/common/fonts/music-icon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/common/fonts/music-icon.woff -------------------------------------------------------------------------------- /src/common/image/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/common/image/default.png -------------------------------------------------------------------------------- /src/common/js/mixin.js: -------------------------------------------------------------------------------- 1 | import { mapState } from 'vuex' 2 | // 播放器 3 | export const playListMixin = { 4 | computed: { 5 | ...mapState({ 6 | playList: 'playList' 7 | }) 8 | }, 9 | mounted() { 10 | this.handlePlaylist(this.playList) 11 | }, 12 | activated() { 13 | this.handlePlaylist(this.playList) 14 | }, 15 | watch: { 16 | playList(newAal) { 17 | this.handlePlaylist(newAal) 18 | } 19 | }, 20 | methods: { 21 | handlePlaylist() { 22 | throw new Error('component must implement handlePlay methods') 23 | }, 24 | } 25 | } 26 | // 收藏歌曲 27 | export const playerMixin = { 28 | computed: { 29 | ...mapState({ 30 | loveMusic: 'loveMusic', 31 | currentSong: 'currentSong' 32 | }) 33 | }, 34 | methods: { 35 | toggleFavorite(song) { 36 | const index = this.isFavorite(song) 37 | if(index > -1) { 38 | // 已有这首收藏歌曲,不再收藏 39 | this.loveMusic.splice(index,1) 40 | } else { // 这首歌没有收藏 41 | this.loveMusic.unshift(song) 42 | } 43 | localStorage.setItem("loveMusic", JSON.stringify(this.loveMusic)) 44 | }, 45 | getFavoriteIcon(song) { 46 | const index = this.isFavorite(song) 47 | if ( index > -1) { 48 | return 'icon-favorite' 49 | } else { 50 | return 'icon-not-favorite' 51 | } 52 | }, 53 | // 判断是否收藏歌曲 54 | isFavorite(song) { 55 | const index = this.loveMusic.findIndex((item) => { 56 | return item.mid === song.mid 57 | }) 58 | // 有收藏歌曲,索引 59 | return index 60 | }, 61 | // 本地存储 62 | local(arr,name) { 63 | // 进行本地存储 64 | arr.forEach((item,index)=> { 65 | if(item.mid === this.currentSong.mid) { 66 | arr.splice(index,1) 67 | } 68 | }) 69 | arr.unshift(this.currentSong) 70 | localStorage.setItem(name, JSON.stringify(arr)) 71 | }, 72 | // 找到当前歌曲在新列表中的索引,防止切换播放顺序时会自动切歌 73 | resetCurrentIndex(list) { 74 | let index = list.findIndex((item) => { 75 | return item.mid === this.currentSong.mid 76 | }) 77 | this.$store.commit('setCurrentIndex',index) 78 | }, 79 | } 80 | } 81 | // 收藏歌手 82 | export const singerMixin = { 83 | computed: { 84 | ...mapState({ 85 | loveSinger: 'loveSinger' 86 | }) 87 | }, 88 | methods: { 89 | toggleFavorite(singer) { 90 | const index = this.isFavorite(singer) 91 | if(index > -1) { 92 | // 已关注歌手 93 | this.loveSinger.splice(index,1) 94 | } else { // 这首歌没有收藏 95 | this.loveSinger.unshift(singer) 96 | } 97 | localStorage.setItem("loveSinger", JSON.stringify(this.loveSinger)) 98 | }, 99 | getFavoriteText(singer) { 100 | const index = this.isFavorite(singer) 101 | if ( index > -1) { 102 | return '已关注' 103 | } else { 104 | return '关注' 105 | } 106 | }, 107 | // 判断是否收藏歌曲 108 | isFavorite(singer) { 109 | const index = this.loveSinger.findIndex((item) => { 110 | return item.singer_id === singer.singer_id 111 | }) 112 | // 有收藏歌曲,索引 113 | return index 114 | } 115 | } 116 | } 117 | export const listMixin = { 118 | computed: { 119 | ...mapState({ 120 | value: 'value', 121 | loveSongList: 'loveSongList', 122 | loveAlbum: 'loveAlbum', 123 | loveRank: 'loveRank', 124 | loveSinger: 'loveSinger', 125 | songList: 'songList', 126 | album: 'album', 127 | rank: 'rank', 128 | singer: 'singer' 129 | }) 130 | }, 131 | methods: { 132 | toggleFavorite() { 133 | const index = this.isFavorite() 134 | let loveList,item,loveLocal 135 | let value = this.value 136 | if(value == "歌单") { 137 | loveList = this.loveSongList 138 | item = this.songList 139 | loveLocal = "loveSongList" 140 | } 141 | if(value == "歌手") { 142 | loveList = this.loveSinger 143 | item = this.singer 144 | loveLocal = "loveSinger" 145 | } 146 | if(value == "排行") { 147 | loveList = this.loveRank 148 | item = this.rank 149 | loveLocal = "loveRank" 150 | } 151 | if(value == "专辑") { 152 | loveList = this.loveAlbum 153 | item = this.album 154 | loveLocal = "loveAlbum" 155 | } 156 | if(index > -1) { 157 | // 已有这首收藏歌单,不再收藏 158 | loveList.splice(index,1) 159 | } else { // 歌单没有收藏 160 | loveList.unshift(item) 161 | } 162 | localStorage.setItem(loveLocal, JSON.stringify(loveList)) 163 | }, 164 | getFavoriteIcon() { 165 | const index = this.isFavorite() 166 | if ( index > -1) { 167 | return 'icon-favorite' 168 | } else { 169 | return 'icon-not-favorite' 170 | } 171 | }, 172 | // 判断是否收藏歌曲 173 | isFavorite() { 174 | let index 175 | if(this.value == "歌单") { 176 | index = this.loveSongList.findIndex((item) => { 177 | return item.content_id === this.songList.content_id 178 | }) 179 | return index 180 | } 181 | if(this.value == "排行") { 182 | index = this.loveRank.findIndex((item) => { 183 | return item.topId === this.rank.topId 184 | }) 185 | return index 186 | } 187 | if(this.value == "专辑") { 188 | index = this.loveAlbum.findIndex((item) => { 189 | return item.albumMID === this.album.albumMID 190 | }) 191 | return index 192 | } 193 | if(this.value == "歌手") { 194 | index = this.loveSinger.findIndex((item) => { 195 | return item.singer_id === this.singer.singer_id 196 | }) 197 | return index 198 | } 199 | } 200 | } 201 | } -------------------------------------------------------------------------------- /src/common/js/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | const request = (params) => { 3 | const baseUrl = '/api' // https://api.qq.jsososo.com 4 | const url = baseUrl + params.url 5 | return axios.get(url,{params: params.data}) 6 | } 7 | export default request -------------------------------------------------------------------------------- /src/common/stylus/base.styl: -------------------------------------------------------------------------------- 1 | @import "variable.styl" 2 | 3 | body, html 4 | line-height: 1 5 | font-family: 'PingFang SC', 'STHeitiSC-Light', 'Helvetica-Light', arial, sans-serif, 'Droid Sans Fallback' 6 | user-select: none 7 | -webkit-tap-highlight-color: transparent 8 | background: $color-background 9 | color: $color-text -------------------------------------------------------------------------------- /src/common/stylus/icon.styl: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: 'music-icon' 3 | src: url('../fonts/music-icon.eot?2qevqt') 4 | src: url('../fonts/music-icon.eot?2qevqt#iefix') format('embedded-opentype'), 5 | url('../fonts/music-icon.ttf?2qevqt') format('truetype'), 6 | url('../fonts/music-icon.woff?2qevqt') format('woff'), 7 | url('../fonts/music-icon.svg?2qevqt#music-icon') format('svg') 8 | font-weight: normal 9 | font-style: normal 10 | 11 | [class^="icon-"], [class*=" icon-"] 12 | /* use !important to prevent issues with browser extensions that change fonts */ 13 | font-family: 'music-icon' !important 14 | speak: none 15 | font-style: normal 16 | font-weight: normal 17 | font-variant: normal 18 | text-transform: none 19 | line-height: 1 20 | 21 | /* Better Font Rendering =========== */ 22 | -webkit-font-smoothing: antialiased 23 | -moz-osx-font-smoothing: grayscale 24 | 25 | .icon-ok:before 26 | content: "\e900" 27 | 28 | .icon-close:before 29 | content: "\e901" 30 | 31 | .icon-add:before 32 | content: "\e902" 33 | 34 | .icon-play-mini:before 35 | content: "\e903" 36 | 37 | .icon-playlist:before 38 | content: "\e904" 39 | 40 | .icon-music:before 41 | content: "\e905" 42 | 43 | .icon-search:before 44 | content: "\e906" 45 | 46 | .icon-clear:before 47 | content: "\e907" 48 | 49 | .icon-delete:before 50 | content: "\e908" 51 | 52 | .icon-favorite:before 53 | content: "\e909" 54 | 55 | .icon-not-favorite:before 56 | content: "\e90a" 57 | 58 | .icon-pause:before 59 | content: "\e90b" 60 | 61 | .icon-play:before 62 | content: "\e90c" 63 | 64 | .icon-prev:before 65 | content: "\e90d" 66 | 67 | .icon-loop:before 68 | content: "\e90e" 69 | 70 | .icon-sequence:before 71 | content: "\e90f" 72 | 73 | .icon-random:before 74 | content: "\e910" 75 | 76 | .icon-back:before 77 | content: "\e911" 78 | 79 | .icon-mine:before 80 | content: "\e912" 81 | 82 | .icon-next:before 83 | content: "\e913" 84 | 85 | .icon-dismiss:before 86 | content: "\e914" 87 | 88 | .icon-pause-mini:before 89 | content: "\e915" 90 | -------------------------------------------------------------------------------- /src/common/stylus/index.styl: -------------------------------------------------------------------------------- 1 | @import "./reset.styl" 2 | @import "./base.styl" 3 | @import "./icon.styl" -------------------------------------------------------------------------------- /src/common/stylus/mixin.styl: -------------------------------------------------------------------------------- 1 | // 背景图片 2 | bg-image($url) 3 | background-image: url($url + "@2x.png") 4 | @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3) 5 | background-image: url($url + "@3x.png") 6 | 7 | // 不换行 8 | no-wrap() 9 | text-overflow: ellipsis 10 | overflow: hidden 11 | white-space: nowrap 12 | 13 | // 扩展点击区域 14 | extend-click() 15 | position: relative 16 | &:before 17 | content: '' 18 | position: absolute 19 | top: -10px 20 | left: -10px 21 | right: -10px 22 | bottom: -10px -------------------------------------------------------------------------------- /src/common/stylus/reset.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) 3 | * http://cssreset.com 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video, input 18 | margin: 0 19 | padding: 0 20 | border: 0 21 | font-size: 100% 22 | font-weight: normal 23 | vertical-align: baseline 24 | 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, menu, nav, section 28 | display: block 29 | 30 | body 31 | line-height: 1 32 | 33 | blockquote, q 34 | quotes: none 35 | 36 | blockquote:before, blockquote:after, 37 | q:before, q:after 38 | content: none 39 | 40 | table 41 | border-collapse: collapse 42 | border-spacing: 0 43 | 44 | /* custom */ 45 | 46 | a 47 | color: #7e8c8d 48 | -webkit-backface-visibility: hidden 49 | text-decoration: none 50 | 51 | li 52 | list-style: none 53 | 54 | body 55 | -webkit-text-size-adjust: none 56 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0) 57 | -------------------------------------------------------------------------------- /src/common/stylus/variable.styl: -------------------------------------------------------------------------------- 1 | // 颜色定义规范 2 | $color-background = #222 3 | $color-background-d = rgba(0, 0, 0, 0.3) 4 | $color-highlight-background = #333 5 | $color-dialog-background = #666 6 | $color-theme = #ffcd32 7 | $color-theme-d = rgba(255, 205, 49, 0.5) 8 | $color-sub-theme = #d93f30 9 | $color-text = #fff 10 | $color-text-d = rgba(255, 255, 255, 0.3) 11 | $color-text-l = rgba(255, 255, 255, 0.5) 12 | $color-text-ll = rgba(255, 255, 255, 0.8) 13 | //字体定义规范 14 | $font-size-small-s = 10px 15 | $font-size-small = 12px 16 | $font-size-medium = 14px 17 | $font-size-medium-x = 16px 18 | $font-size-large = 18px 19 | $font-size-large-x = 22px -------------------------------------------------------------------------------- /src/components/app/m-header/Mheader.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | -------------------------------------------------------------------------------- /src/components/app/m-header/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/components/app/m-header/logo@2x.png -------------------------------------------------------------------------------- /src/components/app/m-header/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soupJian/vue-qqMusic/4c25fe1e84c8bd1540ce39a31955d44f1202ac6f/src/components/app/m-header/logo@3x.png -------------------------------------------------------------------------------- /src/components/base/Confirm.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 56 | 57 | -------------------------------------------------------------------------------- /src/components/base/Disc.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 41 | 42 | 72 | -------------------------------------------------------------------------------- /src/components/base/Load.vue: -------------------------------------------------------------------------------- 1 | 7 | 17 | 29 | -------------------------------------------------------------------------------- /src/components/base/Scroll.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 90 | 91 | 93 | -------------------------------------------------------------------------------- /src/components/base/Tab.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | -------------------------------------------------------------------------------- /src/components/base/Toast.vue: -------------------------------------------------------------------------------- 1 | 6 | 16 | -------------------------------------------------------------------------------- /src/components/comment/Comment.vue: -------------------------------------------------------------------------------- 1 | 32 | 129 | -------------------------------------------------------------------------------- /src/components/detail/AlbumDetail.vue: -------------------------------------------------------------------------------- 1 | 6 | 64 | -------------------------------------------------------------------------------- /src/components/detail/RankDetail.vue: -------------------------------------------------------------------------------- 1 | 6 | 64 | -------------------------------------------------------------------------------- /src/components/detail/SongListDetail.vue: -------------------------------------------------------------------------------- 1 | 6 | 78 | -------------------------------------------------------------------------------- /src/components/detail/singerDetail.vue: -------------------------------------------------------------------------------- 1 | 6 | 59 | -------------------------------------------------------------------------------- /src/components/list/MusicItem.vue: -------------------------------------------------------------------------------- 1 | 18 | 53 | -------------------------------------------------------------------------------- /src/components/list/musicList.vue: -------------------------------------------------------------------------------- 1 | 38 | 110 | -------------------------------------------------------------------------------- /src/components/mine/MineLove.vue: -------------------------------------------------------------------------------- 1 | 27 | 91 | -------------------------------------------------------------------------------- /src/components/mine/MineRank.vue: -------------------------------------------------------------------------------- 1 | 13 | 39 | -------------------------------------------------------------------------------- /src/components/mine/MineSinger.vue: -------------------------------------------------------------------------------- 1 | 15 | 51 | -------------------------------------------------------------------------------- /src/components/mine/MineSongList.vue: -------------------------------------------------------------------------------- 1 | 15 | 54 | -------------------------------------------------------------------------------- /src/components/operatorSong/OperatorSong.vue: -------------------------------------------------------------------------------- 1 | 41 | 92 | 156 | -------------------------------------------------------------------------------- /src/components/play/Player.vue: -------------------------------------------------------------------------------- 1 | 122 | 418 | -------------------------------------------------------------------------------- /src/components/playList/PlayList.vue: -------------------------------------------------------------------------------- 1 | 43 | 134 | 215 | -------------------------------------------------------------------------------- /src/components/progress-bar/progressBar.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 91 | 92 | -------------------------------------------------------------------------------- /src/components/quickSearch/QuickSearch.vue: -------------------------------------------------------------------------------- 1 | 20 | 125 | -------------------------------------------------------------------------------- /src/components/rank/RankList.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 58 | 59 | 99 | -------------------------------------------------------------------------------- /src/components/recommend/Banner.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 51 | 52 | -------------------------------------------------------------------------------- /src/components/result/NoResult.vue: -------------------------------------------------------------------------------- 1 | 6 | 16 | -------------------------------------------------------------------------------- /src/components/result/Result.vue: -------------------------------------------------------------------------------- 1 | 75 | 216 | -------------------------------------------------------------------------------- /src/components/singer/SingerCategory.vue: -------------------------------------------------------------------------------- 1 | 18 | 47 | -------------------------------------------------------------------------------- /src/components/singer/SingerList.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 47 | 48 | 84 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | // 引入全局样式文件 6 | import './common/stylus/index.styl' 7 | // 使用axios 8 | import axios from 'axios' 9 | Vue.prototype.$http = axios 10 | // lazyload 11 | import VueLazyload from 'vue-lazyload' 12 | Vue.use(VueLazyload, { 13 | loading: require('./assets/logo.png') 14 | }) 15 | 16 | Vue.config.productionTip = false 17 | 18 | new Vue({ 19 | router, 20 | store, 21 | render: h => h(App) 22 | }).$mount('#app') 23 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | Vue.use(VueRouter) 4 | 5 | const routes = [ 6 | { 7 | path: '/', 8 | redirect: '/recommend' 9 | }, 10 | { 11 | path: '/recommend', 12 | name: 'recommend', 13 | component: () => import ('../views/recommend/Recommend.vue') 14 | }, 15 | { 16 | path: '/singer', 17 | name: 'singer', 18 | component: () => import ('../views/singer/Singer.vue') 19 | }, 20 | { 21 | path: '/rank', 22 | name: 'rank', 23 | component: () => import ('../views/rank/Rank.vue') 24 | }, 25 | { 26 | path: '/singer/:mid', 27 | name: 'singerDetail', 28 | component: () => import ('../components/detail/singerDetail.vue') 29 | }, 30 | { 31 | path: '/mine', 32 | name: 'mine', 33 | component: () => import ('../views/mine/Mine.vue') 34 | }, 35 | { 36 | path: '/search', 37 | name: 'search', 38 | component: () => import ('../views/search/Search.vue') 39 | }, 40 | { 41 | path: '/songList/:id', 42 | name: 'SongListDetail', 43 | component: () => import ('../components/detail/SongListDetail.vue') 44 | }, 45 | { 46 | path: '/album/:id', 47 | name: 'AlbunDetail', 48 | component: () => import ('../components/detail/AlbumDetail.vue') 49 | }, 50 | { 51 | path: '/rank/:id', 52 | name: 'RankDetail', 53 | component: () => import ('../components/detail/RankDetail.vue') 54 | }, 55 | { 56 | path: '/search/:query', 57 | name: 'searchQuery', 58 | component: () => import ('../components/result/Result.vue') 59 | }, 60 | { 61 | path: '/mine/love', 62 | name: 'mineLove', 63 | component: () => import ('../components/mine/MineLove.vue') 64 | }, 65 | { 66 | path: '/mine/history', 67 | name: 'mineHistory', 68 | component: () => import ('../components/mine/MineLove.vue') 69 | }, 70 | { 71 | path: '/mine/singer', 72 | name: 'mineSinger', 73 | component: () => import ('../components/mine/MineSinger.vue') 74 | }, 75 | { 76 | path: '/mine/songList', 77 | name: 'mineSongList', 78 | component: () => import ('../components/mine/MineSongList.vue') 79 | }, 80 | { 81 | path: '/mine/album', 82 | name: 'mineAlbum', 83 | component: () => import ('../components/mine/MineSongList.vue') 84 | }, 85 | { 86 | path: '/mine/rank', 87 | name: 'mineRank', 88 | component: () => import ('../components/mine/MineRank.vue') 89 | }, 90 | { 91 | path: '/comment/:id', 92 | name: 'comment', 93 | component: () => import ('../components/comment/Comment.vue') 94 | }, 95 | { 96 | path: '/mv/:vid', 97 | name: 'mv', 98 | component: () => import ('../views/videoPlay/VideoPlay.vue') 99 | } 100 | ] 101 | 102 | const router = new VueRouter({ 103 | routes 104 | }) 105 | 106 | export default router 107 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | singer: {}, // 歌手详情 9 | songList: {}, // 歌单详情 10 | album: {}, // 专辑详情 11 | rank: {}, // 排行榜详情 12 | playing: false, // 播放状态 13 | fullScreen: false, // 事都全屏 14 | playList: [], // 播放列表 15 | sequenceList: [], // 顺序播放列表 16 | mode: 0, // 播放模式 17 | currentIndex: -1, //当前播放歌曲索引 18 | currentSong: {}, // 当前播放歌曲 19 | historyPlay: JSON.parse(localStorage.getItem('historyPlay')) || [],// 历史播放 20 | loveSinger: JSON.parse(localStorage.getItem('loveSinger')) || [], // 关注的歌手 21 | loveAlbum: JSON.parse(localStorage.getItem('loveAlbum')) || [], // 收藏的专辑 22 | loveSongList: JSON.parse(localStorage.getItem('loveSongList')) || [], // 收藏的歌单 23 | loveMusic: JSON.parse(localStorage.getItem('loveMusic')) || [], // 喜欢的歌曲 24 | loveRank: JSON.parse(localStorage.getItem('loveRank')) || [], // 收藏的排行榜 25 | value: '' ,// 解决收藏歌单专辑排行调用一个组件,难以识别收藏是哪个问题 26 | songItem: {} // 点击歌曲右边的加号传递歌曲 27 | }, 28 | mutations: { 29 | // 点击歌手进行切换详情页,传递singer 30 | setSinger(state,singer) { 31 | state.singer = singer 32 | }, 33 | // 点击歌单进行切换详情页,传递songList 34 | setSongList(state,songList) { 35 | state.songList = songList 36 | }, 37 | // 点击专辑进行切换详情页,传递album 38 | setAlbum(state,album) { 39 | state.album = album 40 | }, 41 | // 点击排行榜详情 42 | setRank(state, rank) { 43 | state.rank = rank 44 | }, 45 | // 设置是否全屏展示 46 | setFullScreen(state,boolean) { 47 | state.fullScreen = boolean 48 | }, 49 | // 控制播放暂停状态 50 | setPlaying(state, boolean){ 51 | state.playing = boolean 52 | }, 53 | // 控制顺序播放列表 54 | setSequenceList(state, list){ 55 | state.sequenceList = JSON.parse(JSON.stringify(list)) 56 | }, 57 | // 控制当前播放列表 58 | setPlayList(state, list){ 59 | state.playList = JSON.parse(JSON.stringify(list)) 60 | state.currentSong = state.playList[state.currentIndex] 61 | }, 62 | setCurrentSong(state,currentSong) { 63 | state.currentSong = currentSong 64 | }, 65 | // 设置当前播放歌曲索引 66 | setCurrentIndex(state,index){ 67 | state.currentIndex = index 68 | state.currentSong = state.playList[state.currentIndex] 69 | }, 70 | // 控制播放模式 71 | setMode(state,mode) { 72 | state.mode = mode 73 | }, 74 | // 解决收藏歌单专辑排行调用一个组件,难以识别收藏是哪个问题 75 | setValue(state,value) { 76 | state.value = value 77 | }, 78 | // 点击歌曲右边的加号传递歌曲 79 | setSongItem(state,item) { 80 | state.songItem = item 81 | }, 82 | // 清空我的收藏 83 | clearLoveMusic(state){ 84 | state.loveMusic = [] 85 | }, 86 | // 清空历史播放 87 | clearHistoryPlay(state){ 88 | state.historyPlay = [] 89 | } 90 | }, 91 | actions: { 92 | }, 93 | modules: { 94 | } 95 | }) 96 | -------------------------------------------------------------------------------- /src/views/mine/Mine.vue: -------------------------------------------------------------------------------- 1 | 21 | 51 | -------------------------------------------------------------------------------- /src/views/rank/Rank.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 40 | 41 | 54 | -------------------------------------------------------------------------------- /src/views/recommend/Recommend.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 69 | -------------------------------------------------------------------------------- /src/views/search/Search.vue: -------------------------------------------------------------------------------- 1 | 53 | 115 | 116 | -------------------------------------------------------------------------------- /src/views/singer/Singer.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 71 | 72 | 87 | -------------------------------------------------------------------------------- /src/views/videoPlay/VideoPlay.vue: -------------------------------------------------------------------------------- 1 | 55 | 176 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: process.env.NODE_ENV === 'production'? './': '/', 3 | devServer: { 4 | open: true, //配置自动启动浏览器 5 | disableHostCheck: true, // Invalid Host/Origin header 6 | port: 3001, 7 | // host: '172.17.0.13', 8 | hot: true, 9 | // public: '172.17.0.13:3001' , // 本地ip 10 | headers: 11 | { 12 | "Access-Control-Allow-Origin": "*" 13 | }, 14 | proxy: { 15 | '/api': { 16 | target: 'https://api.qq.jsososo.com', // 需要请求的地址 17 | ws: true, 18 | secure: false, 19 | changeOrigin: true, // 是否跨域 20 | pathRewrite: { 21 | '^/api': '' 22 | } 23 | } 24 | } // 配置多个代理 25 | } 26 | } --------------------------------------------------------------------------------