├── .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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
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 |
--------------------------------------------------------------------------------
/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 |
2 |
11 |
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 |
2 |
3 |
4 |
5 |
6 |
{{text}}
7 |
8 |
{{cancelBtnText}}
9 |
{{confirmBtnText}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
56 |
57 |
--------------------------------------------------------------------------------
/src/components/base/Disc.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
![]()
7 |
8 |
12 |
13 |
14 |
15 |
16 |
17 |
41 |
42 |
72 |
--------------------------------------------------------------------------------
/src/components/base/Load.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
{{title}}
5 |
6 |
7 |
17 |
29 |
--------------------------------------------------------------------------------
/src/components/base/Scroll.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
90 |
91 |
93 |
--------------------------------------------------------------------------------
/src/components/base/Tab.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | {{item.name}}
7 |
8 |
9 |
10 |
11 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/base/Toast.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{title}}
4 |
5 |
6 |
16 |
--------------------------------------------------------------------------------
/src/components/comment/Comment.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
129 |
--------------------------------------------------------------------------------
/src/components/detail/AlbumDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
64 |
--------------------------------------------------------------------------------
/src/components/detail/RankDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
64 |
--------------------------------------------------------------------------------
/src/components/detail/SongListDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
78 |
--------------------------------------------------------------------------------
/src/components/detail/singerDetail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
59 |
--------------------------------------------------------------------------------
/src/components/list/MusicItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 | {{item.name}}
8 |
9 |
10 |
11 | {{list.name}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
53 |
--------------------------------------------------------------------------------
/src/components/list/musicList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
12 |
13 |
14 |
15 | {{title}}
16 |
17 |
18 |
19 |
20 |
随机播放全部
21 |
22 |
23 |
24 |
25 | {{this.$store.state.value}}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
110 |
--------------------------------------------------------------------------------
/src/components/mine/MineLove.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 |
9 |
12 |
13 |
14 |
15 | 清空列表
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
91 |
--------------------------------------------------------------------------------
/src/components/mine/MineRank.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
39 |
--------------------------------------------------------------------------------
/src/components/mine/MineSinger.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
51 |
--------------------------------------------------------------------------------
/src/components/mine/MineSongList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
54 |
--------------------------------------------------------------------------------
/src/components/operatorSong/OperatorSong.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
15 |
16 | -
17 |
18 | 下一首播放
19 |
20 | -
21 |
22 | 添加到我喜欢
23 |
24 | -
25 |
26 | MV
27 |
28 | -
29 |
30 | 获取评论
31 |
32 |
33 |
34 |
35 |
36 | 关闭
37 |
38 |
39 |
40 |
41 |
92 |
156 |
--------------------------------------------------------------------------------
/src/components/play/Player.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
![]()
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{item.name}}
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
![]()
30 |
31 |
32 |
33 |
34 |
{{playingLyric}}
35 |
36 |
37 |
38 |
39 |
40 |
41 |
47 | {{line.txt}}
48 |
49 |
50 | 该歌曲暂无歌词
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
{{forMat(currentTime)}}
65 |
68 |
{{forMat(currentSong.interval)}}
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
![]()
98 |
99 |
100 |
101 |
102 | {{item.name}}
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
119 |
120 |
121 |
122 |
418 |
--------------------------------------------------------------------------------
/src/components/playList/PlayList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | {{modeText}}
12 |
13 |
14 |
15 |
16 |
17 |
33 |
34 |
35 | 关闭
36 |
37 |
40 |
41 |
42 |
43 |
134 |
215 |
--------------------------------------------------------------------------------
/src/components/progress-bar/progressBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
91 |
92 |
--------------------------------------------------------------------------------
/src/components/quickSearch/QuickSearch.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 详细搜索
6 | {{query}}
7 |
8 |
9 |
10 |
{{item.name}}
11 |
12 |
![]()
13 |
{{list.name}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
125 |
--------------------------------------------------------------------------------
/src/components/rank/RankList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
![]()
7 |
8 | -
9 | {{ ++index }}. {{list.title}}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
58 |
59 |
99 |
--------------------------------------------------------------------------------
/src/components/recommend/Banner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
51 |
52 |
--------------------------------------------------------------------------------
/src/components/result/NoResult.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
16 |
--------------------------------------------------------------------------------
/src/components/result/Result.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
70 |
71 |
72 |
73 |
74 |
75 |
216 |
--------------------------------------------------------------------------------
/src/components/singer/SingerCategory.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | -
12 | {{value.name}}
13 |
14 |
15 |
16 |
17 |
18 |
47 |
--------------------------------------------------------------------------------
/src/components/singer/SingerList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
8 |
9 |
10 |
11 | {{item.singer_name}}
12 | {{getFavoriteText(item)}}
13 |
14 |
15 | 单曲:{{item.songNum}}
16 | 专辑:{{item.albumNum}}
17 | MV:{{item.mvNum}}
18 |
19 |
20 |
21 |
22 |
23 |
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 |
2 |
3 |
4 |
5 |
6 |
7 |
个人中心
8 |
9 |
10 | {{item.name}}
11 |
12 |
13 |
14 |
soupJian Music
15 |
学校:江西理工大学
16 |
微信电话:13479291739 QQ: 2242476448
17 |
18 |
19 |
20 |
21 |
51 |
--------------------------------------------------------------------------------
/src/views/rank/Rank.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
40 |
41 |
54 |
--------------------------------------------------------------------------------
/src/views/recommend/Recommend.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
热门歌单推荐
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
69 |
--------------------------------------------------------------------------------
/src/views/search/Search.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 搜索历史
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
热门搜索
36 |
37 | -
39 | {{index+1}}
40 | {{ item.k }}
41 | {{item.n}}
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
115 |
116 |
--------------------------------------------------------------------------------
/src/views/singer/Singer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
71 |
72 |
87 |
--------------------------------------------------------------------------------
/src/views/videoPlay/VideoPlay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 |
7 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
![]()
25 |
26 |
{{item.name}}
27 |
播放次数:{{item.playcnt}}
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
38 |
39 |
{{mvName}}({{total}})
40 |
47 |
48 |
49 |
50 |
51 |

52 |
53 |
54 |
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 | }
--------------------------------------------------------------------------------
14 |-
15 |
16 |
17 |
20 |
21 |
22 |
23 |{{item.nick}}
18 | 19 |