├── .browserslistrc ├── docs ├── mvs_footer.jpg ├── mvs_page.jpg └── pagination.jpg ├── public ├── favicon.png └── index.html ├── src ├── assets │ ├── test.jpg │ ├── coverall.png │ ├── img │ │ ├── disc.png │ │ ├── icon.png │ │ ├── needle.png │ │ ├── al_cover_mask.png │ │ ├── default_avatar.png │ │ ├── icon-next.svg │ │ ├── icon-prev.svg │ │ ├── back.svg │ │ ├── icon-play.svg │ │ ├── order_play.svg │ │ ├── icon-stop.svg │ │ ├── love_liked.svg │ │ ├── 24gf-volumeCross.svg │ │ ├── fanyi_full.svg │ │ ├── love_like.svg │ │ ├── random_play.svg │ │ ├── pull_down.svg │ │ ├── liked.svg │ │ ├── like.svg │ │ ├── fanyi.svg │ │ ├── wan_sex_w.svg │ │ ├── music.svg │ │ ├── 24gf-volumeHigh.svg │ │ ├── single_play.svg │ │ └── collect.svg │ └── css │ │ ├── base.css │ │ ├── effect.css │ │ └── element_ui_style_cover.css ├── utils │ ├── bus │ │ ├── index.js │ │ └── types.js │ ├── audioer.js │ ├── cookie.js │ ├── request.js │ ├── directive.js │ ├── utils.js │ └── filter.js ├── views │ ├── pages │ │ ├── TODO.vue │ │ ├── SongsDetail.vue │ │ ├── home │ │ │ ├── PersonalRec.vue │ │ │ ├── Discover.vue │ │ │ ├── PlaylistCategory.vue │ │ │ ├── NewSongs.vue │ │ │ ├── TopList.vue │ │ │ └── ArtistsCategory.vue │ │ ├── Profile.vue │ │ ├── MyCollects.vue │ │ ├── AlbumDetail.vue │ │ ├── Mv.vue │ │ └── ArtistDetail.vue │ ├── Footer.vue │ ├── Aside.vue │ ├── Header.vue │ ├── Window.vue │ └── PlayPreview.vue ├── components │ ├── layout │ │ ├── List.vue │ │ ├── Grid.vue │ │ └── Rendering.vue │ ├── content │ │ ├── label │ │ │ ├── Album.vue │ │ │ ├── Avatar.vue │ │ │ ├── UserName.vue │ │ │ ├── Artists.vue │ │ │ └── Artist.vue │ │ ├── complound │ │ │ ├── CollectionArtists.vue │ │ │ ├── ArtistAlbums.vue │ │ │ ├── NewSongsGlance.vue │ │ │ ├── ArtistDesc.vue │ │ │ ├── ArtistVideos.vue │ │ │ ├── CollectionAlbums.vue │ │ │ ├── NewAlbumsGlance.vue │ │ │ ├── BetterSongTrack.vue │ │ │ ├── SongsSubscribers.vue │ │ │ ├── CollectionVideos.vue │ │ │ ├── pendant │ │ │ │ └── RelatedVideo.vue │ │ │ ├── UserPlaylist.vue │ │ │ ├── CommentArea.vue │ │ │ └── SongsListDetail.vue │ │ ├── tracks │ │ │ ├── BigVideoTrack.vue │ │ │ ├── SongTracksDecorator.vue │ │ │ ├── UserTrack.vue │ │ │ ├── ArtistTrack.vue │ │ │ ├── SongsTrack.vue │ │ │ ├── AlbumTrack.vue │ │ │ ├── CommentTrack.vue │ │ │ └── DetailSongTrack.vue │ │ ├── matrices │ │ │ ├── ArtistMatrices.vue │ │ │ ├── NormalSongsMatrices.vue │ │ │ ├── CommonVideoMatrices.vue │ │ │ ├── AlbumMatrices.vue │ │ │ └── VideoMatrices.vue │ │ ├── bannar │ │ │ ├── MainBanner.vue │ │ │ └── Banner.vue │ │ ├── covers │ │ │ ├── AlbumCoverStyle.vue │ │ │ ├── ArtistCover.vue │ │ │ ├── NormalSongsCover.vue │ │ │ ├── AlbumCover.vue │ │ │ ├── RelatedSongsCover.vue │ │ │ ├── VideoCover.vue │ │ │ ├── CoverTemplate.vue │ │ │ └── CommonVideoCover.vue │ │ └── clause │ │ │ ├── ArtistCaluse.vue │ │ │ ├── UserCaluse.vue │ │ │ ├── SongsCaluse.vue │ │ │ ├── AlbumCaluse.vue │ │ │ ├── BigVideoCaluse.vue │ │ │ ├── DetailedSongCaluse.vue │ │ │ └── CommentClause.vue │ ├── header │ │ ├── Logo.vue │ │ ├── HistoryMove.vue │ │ ├── SearchSuggestPreview.vue │ │ ├── LoginState.vue │ │ ├── SearchHotPreview.vue │ │ └── Search.vue │ ├── common │ │ ├── video │ │ │ ├── icon │ │ │ │ ├── play.svg │ │ │ │ ├── stop.svg │ │ │ │ ├── replay.svg │ │ │ │ └── full-screen.svg │ │ │ └── Slider.vue │ │ ├── IconSwitch.vue │ │ ├── ProgressBar.vue │ │ ├── LightInput.vue │ │ ├── Icon.vue │ │ ├── BlurBackground.vue │ │ ├── Volume.vue │ │ ├── TextFold.vue │ │ ├── SimpleRadio.vue │ │ ├── LArea.vue │ │ ├── Description.vue │ │ └── Pagination.vue │ ├── aside │ │ ├── OwnSongList.vue │ │ ├── CollectSongList.vue │ │ ├── Recommendation.vue │ │ └── MyMusic.vue │ ├── play │ │ ├── SongBaseInfo.vue │ │ ├── Like.vue │ │ ├── Discplayer.vue │ │ └── Collect.vue │ ├── main │ │ └── Personalized.vue │ └── pages │ │ └── topinfo │ │ ├── ArtistInfoShow.vue │ │ ├── AlbumInfoShow.vue │ │ └── ProfileShow.vue ├── store │ ├── types.js │ └── index.js ├── test.vue ├── network │ ├── request_login.js │ ├── resolved.js │ ├── request_uesr.js │ └── behavior.js ├── App.vue ├── main.js └── router │ └── index.js ├── .gitignore ├── babel.config.js ├── package.json ├── vue.config.js ├── LICENSE └── plugins └── UploadServerPlugin.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead -------------------------------------------------------------------------------- /docs/mvs_footer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/docs/mvs_footer.jpg -------------------------------------------------------------------------------- /docs/mvs_page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/docs/mvs_page.jpg -------------------------------------------------------------------------------- /docs/pagination.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/docs/pagination.jpg -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/assets/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/test.jpg -------------------------------------------------------------------------------- /src/utils/bus/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | const Bus = new Vue() 4 | export default Bus 5 | -------------------------------------------------------------------------------- /src/assets/coverall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/coverall.png -------------------------------------------------------------------------------- /src/assets/img/disc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/img/disc.png -------------------------------------------------------------------------------- /src/assets/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/img/icon.png -------------------------------------------------------------------------------- /src/assets/img/needle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/img/needle.png -------------------------------------------------------------------------------- /src/assets/img/al_cover_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/img/al_cover_mask.png -------------------------------------------------------------------------------- /src/assets/img/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlessWin/cloud_music/HEAD/src/assets/img/default_avatar.png -------------------------------------------------------------------------------- /src/utils/audioer.js: -------------------------------------------------------------------------------- 1 | const Audioer = { 2 | install(Vue) { 3 | Vue.prototype.$audioer = new Audio() 4 | } 5 | } 6 | 7 | export default Audioer 8 | -------------------------------------------------------------------------------- /src/utils/cookie.js: -------------------------------------------------------------------------------- 1 | const cookie = { } 2 | 3 | Object.defineProperty(cookie, 'value', { 4 | get() { 5 | return localStorage.getItem('cookie') 6 | }, 7 | set(v) { 8 | localStorage.setItem('cookie', v) 9 | } 10 | }) 11 | 12 | export default cookie 13 | 14 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/views/pages/TODO.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /src/views/pages/SongsDetail.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/assets/img/icon-next.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/img/icon-prev.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ['@babel/preset-env', { 5 | "useBuiltIns": "entry", 6 | "corejs": "3" 7 | } 8 | ] 9 | ], 10 | "plugins": [ 11 | [ 12 | "component", 13 | { 14 | "libraryName": "element-ui", 15 | "styleLibraryName": "theme-chalk" 16 | } 17 | ] 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/views/Footer.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 22 | -------------------------------------------------------------------------------- /src/utils/bus/types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | USER_LOGIN: 'user_login', 3 | LOGIN_PANEL_CUTOVER : 'login_panel_cutover', 4 | AUDIO_PLAY: 'audio_play', 5 | AUDIO_CHANGE: 'audio_change', 6 | SINGLE_AUDIO_PLAY: 'single_audio_play', 7 | OPEN_PLAYING_PLANE: 'open_playing_plane', 8 | CLOSE_PLAYING_PLANE: 'close_playing_plane', 9 | 10 | PLACE_LOGIN: 'place_login' 11 | } 12 | -------------------------------------------------------------------------------- /src/components/layout/List.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /src/components/content/label/Album.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 20 | -------------------------------------------------------------------------------- /src/assets/css/base.css: -------------------------------------------------------------------------------- 1 | span, div { 2 | font-family: 微软雅黑, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | } 6 | 7 | body, html { 8 | padding: 0; 9 | margin: 0; 10 | } 11 | 12 | .clearfix:before, 13 | .clearfix:after { 14 | display: table; 15 | content: ""; 16 | } 17 | 18 | .clearfix:after { 19 | clear: both; 20 | } 21 | 22 | .clearfix { 23 | *zoom: 1; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/header/Logo.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 21 | -------------------------------------------------------------------------------- /src/store/types.js: -------------------------------------------------------------------------------- 1 | export default { 2 | UPDATE_LOGIN_STATUS: 'update_login_status', 3 | AUDIO_CHANGE: 'audio_change', 4 | AUDIO_PLAY: 'audio_play', 5 | AUDIO_STOP: 'audio_stop', 6 | AUDIO_INSERT: 'audio_insert', 7 | PLAYLIST: 'playlist', 8 | COLL_PLAYLIST: 'coll_playlist', 9 | LIKED_SONG: 'liked_song', 10 | LIKE_LIST_ADD: 'like_list_add', 11 | LIKE_LIST_DEL: 'like_list_del' 12 | } 13 | -------------------------------------------------------------------------------- /src/components/common/video/icon/play.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/content/label/Avatar.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /src/components/common/video/icon/stop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/css/effect.css: -------------------------------------------------------------------------------- 1 | .effect-transition { 2 | transition-property: all; 3 | transition-duration: 0.4s; 4 | } 5 | 6 | .effect-hover-float:focus { 7 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) 8 | } 9 | 10 | 11 | .effect-hover-float:hover { 12 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2) 13 | } 14 | 15 | .eff-shadow { 16 | box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) 17 | } 18 | 19 | .light-scroll::-webkit-scrollbar { 20 | width: 3px; 21 | } 22 | 23 | .light-scroll::-webkit-scrollbar-thumb { 24 | width: 3px; 25 | border-radius: 2px; 26 | background-color: #e0e0e0; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/content/label/UserName.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /src/assets/img/back.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/content/label/Artists.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | 24 | -------------------------------------------------------------------------------- /src/test.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/assets/img/icon-play.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import cookie from "./cookie"; 3 | const HOST = process.env.VUE_APP_HOST || 'http://localhost' 4 | const PORT = process.env.VUE_APP_PORT || 3000 5 | 6 | const instance = axios.create({ 7 | baseURL: `${HOST}:${PORT}`, 8 | timeout: 5000 9 | }) 10 | 11 | instance 12 | .interceptors 13 | .request 14 | .use(config => { 15 | if (!config.params) config.params = {} 16 | if (!config.params.cookie) config.params.cookie = cookie.value 17 | return config 18 | }, error => { 19 | console.log(error) 20 | }) 21 | 22 | instance 23 | .interceptors 24 | .response 25 | .use(result => result.data) 26 | 27 | export default instance 28 | -------------------------------------------------------------------------------- /src/components/aside/OwnSongList.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/components/aside/CollectSongList.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/assets/img/order_play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/layout/Grid.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /src/network/request_login.js: -------------------------------------------------------------------------------- 1 | import axios from '../utils/request' 2 | import md5 from 'js-md5' 3 | const request = axios 4 | 5 | //手机号登录 6 | export function login_cellphone( cellphone, password, hex = true ) { 7 | let params = { phone: cellphone } 8 | if (hex) params['md5_password'] = md5.hex(password) 9 | else params['password'] = password 10 | 11 | return request( { url: '/login/cellphone', params } ) 12 | } 13 | 14 | //刷新登录状态 15 | export function login_refresh() 16 | { return request( { url: 'login/refresh' } ) } 17 | 18 | 19 | //获取登录状态 20 | export function login_status() 21 | { return request( { url: '/login/status' } ) } 22 | 23 | 24 | //退出登录 25 | export function logout() 26 | { return request( { url: '/logout' } ) } 27 | -------------------------------------------------------------------------------- /src/components/content/complound/CollectionArtists.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/components/content/tracks/BigVideoTrack.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 25 | -------------------------------------------------------------------------------- /src/utils/directive.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | install: function(Vue) { 4 | 5 | let text_to_html = function (el, binding) { 6 | for (let i = el.childNodes.length - 1; i >= 0; i--) { 7 | el.removeChild(el.childNodes[i]) 8 | } 9 | if (!binding.value) return 10 | 11 | let texts = binding.value.split('\n') 12 | for (let i = 0; i < texts.length; i++) { 13 | let line = document.createElement('div') 14 | line.appendChild(document.createTextNode(texts[i])) 15 | if (!texts[i]) line.style.height = '1.1em' 16 | el.appendChild(line) 17 | } 18 | } 19 | 20 | Vue.directive('mline-text', { 21 | bind: text_to_html, 22 | update: text_to_html 23 | }) 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/components/content/matrices/ArtistMatrices.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloud_music", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.19.2", 11 | "core-js": "^3.6.5", 12 | "element-ui": "^2.13.2", 13 | "js-md5": "^0.7.3", 14 | "vue": "^2.6.11", 15 | "vue-router": "^3.2.0", 16 | "vuex": "^3.4.0" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "~4.4.0", 20 | "@vue/cli-plugin-router": "~4.4.0", 21 | "@vue/cli-plugin-vuex": "~4.4.0", 22 | "@vue/cli-service": "~4.4.0", 23 | "babel-plugin-component": "^1.1.1", 24 | "vue-template-compiler": "^2.6.11", 25 | "webpack-cli": "^3.3.12" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 网易云音乐(仿) 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/assets/img/icon-stop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/content/bannar/MainBanner.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /src/components/content/covers/AlbumCoverStyle.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 35 | -------------------------------------------------------------------------------- /src/views/pages/home/PersonalRec.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/components/header/HistoryMove.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 29 | -------------------------------------------------------------------------------- /src/components/content/complound/ArtistAlbums.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | 31 | 34 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const UploadServerPlugin = require('./plugins/UploadServerPlugin') 2 | 3 | module.exports = { 4 | publicPath: process.env.NODE_ENV === 'production'? './' : '/', 5 | productionSourceMap: false, 6 | 7 | configureWebpack: { 8 | 9 | plugins: [ 10 | /** 11 | * 项目build后自动将 dist 文件夹下的文件全部上传到服务器 12 | * option{address} 服务器地址 13 | * option{port} 端口 14 | * option{password} 密码, 服务器需要验证密码才能执行更新 15 | * */ 16 | new UploadServerPlugin({ 17 | address: process.env.ADDRESS || 'localhost', 18 | port: process.env.HOT_UPDATE_PORT || '80', 19 | password: process.env.MY_KEY || '' 20 | }) 21 | ], 22 | 23 | resolve: { 24 | alias: { 25 | 'assets': '@/assets', 26 | 'components': '@/components', 27 | 'views': '@/views', 28 | } 29 | }, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/common/IconSwitch.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 30 | 31 | 36 | -------------------------------------------------------------------------------- /src/components/content/complound/NewSongsGlance.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /src/assets/img/love_liked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/complound/ArtistDesc.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/components/content/complound/ArtistVideos.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 31 | 32 | 35 | -------------------------------------------------------------------------------- /src/components/aside/Recommendation.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | 37 | 42 | -------------------------------------------------------------------------------- /src/assets/img/24gf-volumeCross.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/label/Artist.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /src/components/aside/MyMusic.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/components/common/ProgressBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | 19 | 40 | -------------------------------------------------------------------------------- /src/components/common/LightInput.vue: -------------------------------------------------------------------------------- 1 | 9 | : 10 | 20 | 21 | 37 | -------------------------------------------------------------------------------- /src/components/common/video/icon/replay.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/content/complound/CollectionAlbums.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /src/views/Aside.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 33 | 34 | 43 | -------------------------------------------------------------------------------- /src/network/resolved.js: -------------------------------------------------------------------------------- 1 | import { 2 | song_detail, 3 | song_list_detail, 4 | comment_music, 5 | comment_playlist, 6 | comment_video, comment_album, comment_mv 7 | } from "@/network/request_show"; 8 | 9 | export async function song_tracks(sid, offset, limit) { 10 | let result = await song_list_detail(sid) 11 | let songs = result['playlist']['trackIds'].slice(offset, offset + limit).map(value => value['id']) 12 | songs = await song_detail(songs.join(',')) 13 | return { 14 | metadata: { id: sid, total: result['playlist']['trackCount']}, 15 | data: songs 16 | } 17 | } 18 | 19 | export function get_comment_request(type) { 20 | let request = null 21 | if (type === 'playlist') request = comment_playlist 22 | if (type === 'music') request = comment_music 23 | if (type === 'video') request = comment_video 24 | if (type === 'album') request = comment_album 25 | if (type === 'mv') request = comment_mv 26 | if (!request) console.error('no type request') 27 | return request 28 | } 29 | -------------------------------------------------------------------------------- /src/network/request_uesr.js: -------------------------------------------------------------------------------- 1 | import axios from '../utils/request' 2 | const request = axios 3 | 4 | //获取用户信息 , 歌单,收藏,mv, dj 数量 5 | export function user_subcount() { 6 | return request( { url: '/user/subcount' } ) 7 | } 8 | 9 | //获取已收藏的歌曲列表 10 | export function artist_sublist() { 11 | return request( { url: '/artist/sublist' } ) 12 | } 13 | 14 | //获取已收藏的专辑列表 15 | export function album_sublist(offset, limit) { 16 | return request( { url: '/album/sublist', params: { offset, limit } } ) 17 | } 18 | 19 | //获取已收藏的MV列表 20 | export function mv_sublist() { 21 | return request( { url: '/mv/sublist' } ) 22 | } 23 | 24 | //获取用户详情 25 | export function user_detail( uid ) { 26 | return request( { url: '/user/detail', params: { uid } } ) 27 | } 28 | 29 | 30 | //获取用户歌单 31 | export function playlist( uid ) { 32 | return request( { url: '/user/playlist', params: { uid } } ) 33 | } 34 | 35 | 36 | //获取用户喜欢的歌曲列表 37 | export function likelist(uid) { 38 | return request( { url: '/likelist', params: { uid } } ) 39 | } 40 | -------------------------------------------------------------------------------- /src/components/common/Icon.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | 34 | 43 | -------------------------------------------------------------------------------- /src/components/content/tracks/SongTracksDecorator.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 35 | 36 | 39 | -------------------------------------------------------------------------------- /src/components/header/SearchSuggestPreview.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 45 | -------------------------------------------------------------------------------- /src/views/pages/Profile.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /src/components/content/complound/NewAlbumsGlance.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ColorlessWin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/views/pages/MyCollects.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/content/complound/BetterSongTrack.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/components/layout/Rendering.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 45 | 46 | 51 | -------------------------------------------------------------------------------- /src/views/Header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 28 | 29 | 56 | -------------------------------------------------------------------------------- /src/components/header/LoginState.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 29 | 30 | 50 | -------------------------------------------------------------------------------- /src/assets/img/fanyi_full.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/love_like.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/random_play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/covers/ArtistCover.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | 39 | -------------------------------------------------------------------------------- /src/components/play/SongBaseInfo.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 33 | 34 | 51 | -------------------------------------------------------------------------------- /src/assets/img/pull_down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/main/Personalized.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 35 | 36 | 48 | -------------------------------------------------------------------------------- /src/assets/img/liked.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/common/video/icon/full-screen.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/img/like.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/content/clause/ArtistCaluse.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 53 | -------------------------------------------------------------------------------- /src/components/content/complound/SongsSubscribers.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 41 | 42 | 53 | -------------------------------------------------------------------------------- /src/components/content/tracks/UserTrack.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 35 | 36 | 45 | -------------------------------------------------------------------------------- /src/components/content/covers/NormalSongsCover.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/components/content/matrices/NormalSongsMatrices.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /src/components/content/tracks/ArtistTrack.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 39 | 40 | 49 | -------------------------------------------------------------------------------- /src/components/content/matrices/CommonVideoMatrices.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /src/components/common/BlurBackground.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | 32 | 64 | -------------------------------------------------------------------------------- /src/network/behavior.js: -------------------------------------------------------------------------------- 1 | import axios from '../utils/request' 2 | const request = axios 3 | 4 | /** 5 | * 传入 type, 资源 id, 和评论 id cid 和 是否点赞参数 t 即可给对 应评论点赞 ( 需要登录 ) 6 | * 7 | * */ 8 | export function comment_like( id, cid, type, t ) { 9 | let type_map = { 10 | music: 0, 11 | mv : 1, 12 | playlist: 2, 13 | album: 3, 14 | video: 5 15 | } 16 | return request({ 17 | url: 'comment/like', 18 | params: { 19 | id, cid, 20 | type: type_map[type], 21 | t: (t + 0) 22 | } 23 | }) 24 | } 25 | 26 | /** 27 | * 传入类型和歌单 id 可收藏歌单或者取消收藏歌单 28 | * @param {String | Number} id 歌单id 29 | * @param { Boolean } t true 收藏歌单 false取消收藏 30 | * */ 31 | export function playlist_sub(id, t) { 32 | return request( { url: '/playlist/subscribe', params: { id, t: (t + 0) } } ) 33 | } 34 | 35 | /** 36 | * 传入音乐 id, 可喜欢该音乐 37 | * @method like 38 | * @param {String | Number} id 歌曲id 39 | * @param {Boolean} like 默认为 true 即喜欢 , 若传 false, 则取消喜欢 40 | * */ 41 | export function like(id, like = true) { 42 | return request( { url: '/like', params: { id, like } } ) 43 | } 44 | 45 | /** 46 | * 可以添加歌曲到歌单或者从歌单删除某首歌曲 ( 需要登录 ) 47 | * @method playlist_option 48 | * @param {String} op [ 'add' | 'del' ] 添加或删除 49 | * @param {String | Number} pid 歌单id 50 | * @param {String | Number} tracks 歌曲id 51 | * */ 52 | export function playlist_option(op, pid, tracks) { 53 | return request( { url: '/playlist/tracks', params: { op, pid, tracks } } ) 54 | } 55 | -------------------------------------------------------------------------------- /src/components/content/clause/UserCaluse.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | 61 | -------------------------------------------------------------------------------- /src/components/content/matrices/AlbumMatrices.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 46 | 47 | 50 | -------------------------------------------------------------------------------- /src/components/pages/topinfo/ArtistInfoShow.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 34 | 35 | 65 | -------------------------------------------------------------------------------- /src/assets/img/fanyi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/tracks/SongsTrack.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 40 | 41 | 50 | -------------------------------------------------------------------------------- /src/components/content/tracks/AlbumTrack.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 42 | 43 | 52 | -------------------------------------------------------------------------------- /src/components/content/covers/AlbumCover.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | 30 | 54 | -------------------------------------------------------------------------------- /src/components/content/matrices/VideoMatrices.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 43 | 44 | 50 | -------------------------------------------------------------------------------- /src/components/content/complound/CollectionVideos.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 49 | 50 | 53 | -------------------------------------------------------------------------------- /src/components/pages/topinfo/AlbumInfoShow.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 30 | 31 | 58 | -------------------------------------------------------------------------------- /src/assets/img/wan_sex_w.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/music.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/common/Volume.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 37 | 38 | 66 | -------------------------------------------------------------------------------- /src/components/play/Like.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 55 | 56 | 59 | -------------------------------------------------------------------------------- /src/components/content/complound/pendant/RelatedVideo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 51 | 52 | 55 | -------------------------------------------------------------------------------- /src/components/content/clause/SongsCaluse.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 26 | 27 | 68 | -------------------------------------------------------------------------------- /src/components/common/TextFold.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 47 | 48 | 79 | -------------------------------------------------------------------------------- /src/components/content/covers/RelatedSongsCover.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | 22 | 76 | -------------------------------------------------------------------------------- /src/components/content/tracks/CommentTrack.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 44 | 45 | 50 | -------------------------------------------------------------------------------- /src/assets/img/24gf-volumeHigh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/common/SimpleRadio.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 40 | 41 | 64 | -------------------------------------------------------------------------------- /src/components/content/complound/UserPlaylist.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 58 | 59 | 64 | -------------------------------------------------------------------------------- /src/components/common/LArea.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 47 | 48 | 81 | -------------------------------------------------------------------------------- /src/components/content/covers/VideoCover.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 41 | 42 | 51 | -------------------------------------------------------------------------------- /src/views/pages/home/Discover.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 59 | 60 | 66 | -------------------------------------------------------------------------------- /src/components/content/clause/AlbumCaluse.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 31 | 32 | 70 | -------------------------------------------------------------------------------- /src/components/common/Description.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 61 | 62 | 68 | -------------------------------------------------------------------------------- /src/components/header/SearchHotPreview.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 24 | 25 | 77 | -------------------------------------------------------------------------------- /src/assets/img/single_play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/pages/home/PlaylistCategory.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 59 | 60 | 76 | -------------------------------------------------------------------------------- /src/components/play/Discplayer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | 20 | 84 | -------------------------------------------------------------------------------- /src/assets/img/collect.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/bannar/Banner.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 30 | 31 | 76 | -------------------------------------------------------------------------------- /src/components/play/Collect.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 55 | 56 | 83 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /src/views/Window.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 58 | 59 | 95 | -------------------------------------------------------------------------------- /src/components/content/covers/CoverTemplate.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 38 | 39 | 78 | -------------------------------------------------------------------------------- /src/utils/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 看起来很多余的一个方法 但其实是为了方便通过直接调用一个箭头函数来实现一些优雅方便的写法 4 | * 示例: 5 | * let arr = [ 6 | * { n: '$', v: '-1' }, 7 | * ...call(()=> { 8 | * let map = [] 9 | * let str_code = String.fromCharCode 10 | * for (let i = 0; i < 26; i++) { 11 | * map.push({ 'n': str_code(65 + i), 'v': str_code(97 + i)}) 12 | * } return map 13 | * }), { n: '#', v: '0' } 14 | * ] 15 | * 在数组初始化时就优雅的构造了一个复杂的数据: 16 | * arr: [ { n: '$', v: '-1' }, { n: A, v: a}, { n: B, v: b}, ... { n: Z, v: 'z }, { n: '#', v: '0' } ] 17 | * */ 18 | export function call(func) { return func() } 19 | 20 | 21 | export function future(func, delay, _this) { 22 | setTimeout( () => { 23 | if (_this) func.call(_this) 24 | else func() 25 | }, delay) 26 | } 27 | 28 | 29 | 30 | export function str_empty(str) { 31 | if (!str) return true 32 | return str.replace(/(^s*)|(s*$)/g, "").length === 0 33 | } 34 | 35 | 36 | export function debounce(func, delay, _this) { 37 | let timer = null; 38 | return function (...args) { 39 | if (timer) clearTimeout(timer) 40 | timer = setTimeout(() =>{ 41 | if (_this) func.apply(_this, args) 42 | else func(args) 43 | }, delay) 44 | } 45 | } 46 | 47 | 48 | //解析lyric格式的歌词 49 | export function lyricParse(lyric) { 50 | if (!lyric) return {} 51 | let lyrics_arr = lyric.split('\n') 52 | let lyric_map = {} 53 | for (let i = 0; i < lyrics_arr.length - 1; i++) { 54 | let str_time = /\d{1,2}[.,:]\d{1,2}[.,:]\d{2,3}/.exec(lyrics_arr[i]) 55 | if (str_time) { 56 | str_time = str_time[0] 57 | // IE 中的一个大坑 58 | // IE 浏览器不支持正则中的后置断言和前置断言 59 | // let m = /(?<=\[)\d{1,2}/.exec(str_time)[0] 60 | // let s = /(?<=\[\d{1,2}[.,:])\d{1,2}/.exec(str_time)[0] 61 | let times = str_time.split(/[.,:]/) 62 | let m = times[0] 63 | let s = times[1] 64 | let time = (parseInt(m) * 60) + parseInt(s) 65 | let result = lyrics_arr[i].split(/\[\d{1,2}[.,:]\d{1,2}[.,:]\d{2,3}]/)[1] 66 | lyric_map[time] = result ? result : '...' 67 | } 68 | } 69 | return lyric_map 70 | } 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/views/pages/home/NewSongs.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 59 | 60 | 67 | -------------------------------------------------------------------------------- /src/components/content/clause/BigVideoCaluse.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 34 | 35 | 86 | -------------------------------------------------------------------------------- /src/views/PlayPreview.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 49 | 50 | 92 | -------------------------------------------------------------------------------- /src/views/pages/home/TopList.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 46 | 47 | 86 | -------------------------------------------------------------------------------- /src/views/pages/AlbumDetail.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 70 | 71 | 80 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import 'core-js/stable'; 2 | import 'regenerator-runtime/runtime' 3 | 4 | import Vue from 'vue' 5 | import App from './App.vue' 6 | import router from './router' 7 | import store from './store' 8 | import cookie from "./utils/cookie"; 9 | import Bus from "./utils/bus"; 10 | import Filter from "@/utils/filter"; 11 | import Audioer from "@/utils/audioer"; 12 | import Directive from "@/utils/directive" 13 | 14 | 15 | import { 16 | Button, 17 | Select, 18 | Input, 19 | Option, 20 | Container, 21 | Header, 22 | Aside, 23 | Main, 24 | Footer, 25 | Card, 26 | Menu, 27 | MenuItem, 28 | Submenu, 29 | Tabs, 30 | TabPane, 31 | Carousel, 32 | CarouselItem, 33 | Row, 34 | Col, 35 | Pagination, 36 | Slider, 37 | RadioButton, 38 | RadioGroup, 39 | Popover, 40 | 41 | Loading, 42 | Notification, 43 | } from 'element-ui'; 44 | 45 | import 'element-ui/lib/theme-chalk/index.css'; 46 | import '@/assets/css/effect.css' 47 | import '@/assets/css/element_ui_style_cover.css' 48 | 49 | Vue.use(Button) 50 | Vue.use(Select) 51 | Vue.use(Container) 52 | Vue.use(Header) 53 | Vue.use(Aside) 54 | Vue.use(Main) 55 | Vue.use(Footer) 56 | Vue.use(Card) 57 | Vue.use(Input) 58 | Vue.use(Option) 59 | Vue.use(MenuItem) 60 | Vue.use(Menu) 61 | Vue.use(Submenu) 62 | Vue.use(Tabs) 63 | Vue.use(TabPane) 64 | Vue.use(Carousel) 65 | Vue.use(CarouselItem) 66 | Vue.use(Row) 67 | Vue.use(Col) 68 | Vue.use(Pagination) 69 | Vue.use(Slider) 70 | Vue.use(RadioButton) 71 | Vue.use(RadioGroup) 72 | Vue.use(Popover) 73 | Vue.use(Loading.directive) 74 | 75 | //安装全局filter 76 | Vue.use(Filter) 77 | 78 | //安装全局audio 79 | Vue.use(Audioer) 80 | 81 | //自定义v-指令 82 | Vue.use(Directive) 83 | 84 | const Check = function(obj) { 85 | if (!obj) return false 86 | if (typeof obj === 'object' && Object.keys(obj).length === 0) return false 87 | if (Array.isArray(obj) && obj.length === 0) return false 88 | return true 89 | } 90 | 91 | Vue.config.productionTip = false 92 | 93 | Vue.prototype.$cookie = cookie 94 | Vue.prototype.$notify = Notification 95 | Vue.prototype.$bus = Bus 96 | Vue.prototype.$Check = Check 97 | 98 | 99 | new Vue({ 100 | router, 101 | store, 102 | render: h => h(App) 103 | }).$mount('#app') 104 | -------------------------------------------------------------------------------- /plugins/UploadServerPlugin.js: -------------------------------------------------------------------------------- 1 | const axios = require('axios') 2 | const path = require('path') 3 | const fs = require('fs') 4 | 5 | const PLUGIN_NAME = 'UploadServerPlugin' 6 | 7 | class UploadServerPlugin { 8 | 9 | constructor(config) { this.config = config } 10 | 11 | apply(compiler) { 12 | 13 | compiler.hooks.afterEmit.tap(PLUGIN_NAME, compilation => { 14 | 15 | if (process.env.NODE_ENV !== 'production') 16 | return 17 | 18 | let assets = compilation.assets 19 | this.upload(assets) 20 | .then(() => 21 | console.log('\x1B[32m%s\x1B[39m', 'Upload to complete')) 22 | }) 23 | } 24 | 25 | upload(assets) { 26 | return new Promise((resolve, reject) => { 27 | this.beforeUpdate() 28 | .then(() => this.updateAll(assets)) 29 | .then(() => resolve()) 30 | .catch(err => { 31 | let meg = err.response? { 32 | 401: '密码错误!', 33 | 403: '请求暂时被拒接!', 34 | 500: '服务器更新文件失败!' 35 | }[err.response.status]: '' 36 | console.log('\x1B[31m%s\x1B[39m', `上传服务器失败!! \n ${meg} \n ${err}`) 37 | }) 38 | }) 39 | } 40 | 41 | beforeUpdate() { 42 | let config = this.config 43 | return axios 44 | .post(`${config.address}:${config.port}/api/before-update`,{ 45 | key: config.password 46 | }) 47 | } 48 | 49 | updateAll(assets) { 50 | let promises = [] 51 | 52 | Object.keys(assets) 53 | .forEach((filename) => 54 | promises.push(this.update(filename, assets[filename].existsAt))) 55 | 56 | return Promise.all(promises) 57 | } 58 | 59 | update(filename, fullPath) { 60 | let config = this.config 61 | 62 | return new Promise((resolve, reject) => { 63 | fs.readFile(fullPath, (err, data) => { 64 | if (err) { 65 | reject(`fs 读取文件错误:${fullPath} \n ${err}`) 66 | return 67 | } 68 | 69 | axios 70 | .post(`${config.address}:${config.port}/api/hot-update`, { 71 | key: config.password, 72 | filename: filename, 73 | file: data, 74 | }).then(res => { 75 | console.log('\x1B[32m%s\x1B[39m', `上传文件成功: dist/${filename}`) 76 | resolve(res) 77 | }).catch(err => reject(err)) 78 | }) 79 | }) 80 | } 81 | } 82 | 83 | module.exports = UploadServerPlugin 84 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | Vue.use(VueRouter) 5 | 6 | const TODO = ()=> import('@/views/pages/TODO') 7 | 8 | const Discover = () => import('@/views/pages/home/Discover') 9 | const PersonalRec = () => import('@/views/pages/home/PersonalRec') 10 | const PlaylistCategory = () => import('@/views/pages/home/PlaylistCategory') 11 | const TopList = () => import('@/views/pages/home/TopList') 12 | const ArtistCategory = ()=> import('@/views/pages/home/ArtistsCategory') 13 | const NewSongs = () => import('@/views/pages/home/NewSongs') 14 | const Collects = () => import('@/views/pages/MyCollects') 15 | const Mv = () => import('@/views/pages/Mv') 16 | 17 | const SongsDetail = () => import('@/views/pages/SongsDetail') 18 | const SearchDetail = () => import('@/views/pages/SearchDetail') 19 | const VideoPlay = () => import('@/views/pages/VideoPlay') 20 | const AlbumDetail = ()=> import('@/views/pages/AlbumDetail') 21 | const ArtistDetail = ()=> import('@/views/pages/ArtistDetail') 22 | const Profile = ()=> import('@/views/pages/Profile') 23 | 24 | 25 | const routes = [ 26 | { path: '/', redirect: '/discover' }, 27 | 28 | { 29 | path: '/test', 30 | component: () => import('@/test') 31 | }, 32 | 33 | { 34 | path: '/discover', component: Discover, 35 | children: [ 36 | { path: '/', redirect: '/discover/recommend' }, 37 | { path: '/discover/recommend', component: PersonalRec }, 38 | { path: '/discover/playlists', component: PlaylistCategory }, 39 | { path: '/discover/toplist', component: TopList }, 40 | { path: '/discover/artists', component: ArtistCategory }, 41 | { path: '/discover/newsongs', component: NewSongs } 42 | ] 43 | }, 44 | 45 | { path: '/todo', component: TODO }, 46 | 47 | { path: '/playlist/:id', component: SongsDetail }, 48 | 49 | { path: '/search' , component: SearchDetail }, 50 | 51 | { path: '/video' , component: VideoPlay }, 52 | 53 | { path: '/album/:id' , component: AlbumDetail }, 54 | 55 | { path: '/artist/:id' , component: ArtistDetail }, 56 | 57 | { path: '/profile/:id' , component: Profile }, 58 | 59 | { path: '/collects' , component: Collects }, 60 | 61 | { path: '/mv' , component: Mv }, 62 | ] 63 | 64 | const router = new VueRouter({ 65 | routes, 66 | mode: 'hash' 67 | }) 68 | 69 | export default router 70 | -------------------------------------------------------------------------------- /src/components/content/covers/CommonVideoCover.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 45 | 46 | 81 | -------------------------------------------------------------------------------- /src/views/pages/Mv.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 75 | 76 | 85 | -------------------------------------------------------------------------------- /src/assets/css/element_ui_style_cover.css: -------------------------------------------------------------------------------- 1 | /* 用于覆盖Element UI组件的基本样式 */ 2 | 3 | 4 | /*NavMenu Style*/ 5 | .el-menu { 6 | border: none; 7 | } 8 | 9 | .el-submenu .el-submenu__title { 10 | height: 40px; 11 | color: #37474f; 12 | line-height: 40px; 13 | font-size: 12px; 14 | font-weight: bold; 15 | 16 | background-color: #fafafa; 17 | } 18 | 19 | .el-submenu.is-disabled { 20 | background-color: #fafafa; 21 | } 22 | 23 | .el-submenu .el-menu-item { 24 | height: 35px; 25 | line-height: 35px; 26 | font-size: 11px; 27 | } 28 | 29 | .el-submenu .el-menu-item[style] { 30 | padding: 0 10px !important; 31 | text-indent: 10px; 32 | } 33 | 34 | .el-submenu .el-menu-item.is-disabled, 35 | .el-submenu.is-disabled .el-submenu__title { 36 | cursor: default; 37 | } 38 | 39 | 40 | /*Tabs Style*/ 41 | 42 | .el-tabs .el-tabs__item { 43 | font-size: 12px; 44 | } 45 | 46 | .el-tabs .el-tabs__nav-scroll, 47 | .el-tabs.align-left .el-tabs__nav-scroll{ 48 | display: -webkit-box; 49 | display: -ms-flexbox; 50 | display: -webkit-flex; 51 | display: flex; 52 | } 53 | 54 | .el-tabs .el-tabs__nav-scroll { 55 | -webkit-box-pack: center; 56 | -ms-flex-pack: center; 57 | -webkit-justify-content: center; 58 | justify-content: center; 59 | } 60 | 61 | .el-tabs.align-left .el-tabs__nav-scroll { 62 | -webkit-box-pack: start; 63 | -ms-flex-pack: start; 64 | -webkit-justify-content: flex-start; 65 | justify-content: left; 66 | } 67 | 68 | .el-tabs.align-left .el-tabs__nav-wrap { 69 | padding-left: 40px ; 70 | } 71 | 72 | 73 | .el-tabs.small .el-tabs__item { 74 | padding: 0 10px; 75 | font-size: 10px; 76 | height: 35px; 77 | } 78 | 79 | .el-tabs.small .el-tabs__nav-wrap { 80 | padding-left: 20px ; 81 | } 82 | 83 | .el-tabs.small .el-tabs__nav-wrap::after { 84 | height: 1px; 85 | } 86 | 87 | /* Card Style */ 88 | 89 | .el-card { 90 | border: none; 91 | } 92 | 93 | 94 | /* Slider */ 95 | 96 | .el-slider .el-slider__bar, .el-slider .el-slider__runway { 97 | height: 4px; 98 | } 99 | 100 | .el-slider .el-slider__runway { 101 | background-clip: content-box; 102 | padding: 4px 0; 103 | } 104 | 105 | .el-slider .el-slider__button { 106 | height: 5px; 107 | width: 5px; 108 | } 109 | 110 | .el-slider .el-slider__button-wrapper { 111 | outline: none; 112 | transform: translate(-15px, 3px); 113 | } 114 | 115 | .el-slider .el-slider__runway { 116 | margin: 8px 0; 117 | } 118 | -------------------------------------------------------------------------------- /src/components/content/complound/CommentArea.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 90 | 91 | 102 | -------------------------------------------------------------------------------- /src/utils/filter.js: -------------------------------------------------------------------------------- 1 | 2 | const Filter = { 3 | 4 | install: function(Vue) { 5 | 6 | //日期格式化(用法同C# "yyyy-MM-dd HH:mm:ss") 7 | Vue.filter("dateTimeFormat", function (date, fmt = 'yyyy-MM-dd HH:mm:ss') { //日期时间格式化 8 | if (!date) { 9 | return '' 10 | } 11 | if (typeof date === 'string') { 12 | date = date.replace('T', ' ').replace('Z', ''); 13 | date = new Date(date.replace(/-/g, '/')) 14 | } 15 | if (typeof date === 'number') { 16 | date = new Date(date) 17 | } 18 | var o = { 19 | 'M+': date.getMonth() + 1, 20 | 'd+': date.getDate(), 21 | 'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, 22 | 'H+': date.getHours(), 23 | 'm+': date.getMinutes(), 24 | 's+': date.getSeconds(), 25 | 'q+': Math.floor((date.getMonth() + 3) / 3), 26 | 'S': date.getMilliseconds() 27 | } 28 | var week = { 29 | '0': '\u65e5', 30 | '1': '\u4e00', 31 | '2': '\u4e8c', 32 | '3': '\u4e09', 33 | '4': '\u56db', 34 | '5': '\u4e94', 35 | '6': '\u516d' 36 | } 37 | if (/(y+)/.test(fmt)) { 38 | fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) 39 | } 40 | if (/(E+)/.test(fmt)) { 41 | fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') + week[date.getDay() + '']) 42 | } 43 | for (var k in o) { 44 | if (new RegExp('(' + k + ')').test(fmt)) { 45 | fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) 46 | } 47 | } 48 | return fmt 49 | }); 50 | 51 | //秒、毫秒(时长)格式化为时分秒(例:65000ms => 00:01:05) 52 | Vue.filter('timeLongFormat', function (value, isMs=false,dft = '00:00') { 53 | let total = parseInt(value); 54 | if (!isNaN(total)) { 55 | if (isMs) { 56 | total = total/1000; 57 | } 58 | let hours = parseInt(total / 3600); 59 | let minutes = parseInt((total % 3600) / 60); 60 | let seconds = parseInt((total % 3600) % 60); 61 | let h = hours > 9 ? hours : '0' + hours; 62 | let m = minutes > 9 ? minutes : '0' + minutes; 63 | let s = seconds > 9 ? seconds : '0' + seconds; 64 | if (hours === 0) return m + ':' + s; 65 | return h + ':' + m + ':' + s; 66 | } 67 | else { 68 | return dft; 69 | } 70 | }); 71 | 72 | Vue.filter('logogram', function (value) { 73 | if (value >= 10000) { 74 | return value = (value / 10000).toFixed(1) + '万' 75 | } 76 | return value 77 | }) 78 | } 79 | } 80 | 81 | export default Filter 82 | -------------------------------------------------------------------------------- /src/views/pages/ArtistDetail.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 88 | 89 | 94 | -------------------------------------------------------------------------------- /src/components/content/clause/DetailedSongCaluse.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 40 | 41 | 107 | -------------------------------------------------------------------------------- /src/components/content/tracks/DetailSongTrack.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 76 | 77 | 86 | -------------------------------------------------------------------------------- /src/components/content/complound/SongsListDetail.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 94 | 95 | 101 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import types from "./types"; 5 | import { song_list_detail } from "@/network/request_show"; 6 | 7 | Vue.use(Vuex) 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | isLogin: false, 12 | profile: { 13 | name: null, 14 | UID: 0, 15 | avatarUrl: null, 16 | playlist: [], 17 | coll_playlist: [], 18 | liked_song: [], 19 | }, 20 | 21 | player: { 22 | songsId: 0, 23 | current: 0, 24 | playing: false, 25 | playTracks: [], 26 | } 27 | }, 28 | 29 | getters: { 30 | TrackLength: state => { 31 | return state.player.playTracks.length 32 | } 33 | }, 34 | 35 | mutations: { 36 | [types.UPDATE_LOGIN_STATUS] ( state, { isLogin, profile } ) { 37 | state.isLogin = isLogin 38 | state.profile.name = profile['nickname'] 39 | state.profile.UID = profile['userId'] 40 | state.profile.avatarUrl = profile['avatarUrl'] 41 | }, 42 | 43 | [types.AUDIO_CHANGE] (state, { songsId, songsTracks, index } ) { 44 | state.player.playTracks = songsTracks 45 | state.player.current = index 46 | state.player.songsId = songsId 47 | }, 48 | 49 | [types.AUDIO_INSERT] (state, { id }) { 50 | let current = state.player.current 51 | let index = state.player.playTracks.length !== 0? current + 1: 0 52 | state.player.playTracks.splice(index, 0, { id }) 53 | state.player.current = index 54 | }, 55 | 56 | [types.LIKE_LIST_ADD] (state, { id } ) { 57 | let likelist = state.profile.liked_song 58 | if (!likelist.includes(id)) 59 | state.profile.liked_song.push(id) 60 | }, 61 | 62 | [types.LIKE_LIST_DEL] (state, { id } ) { 63 | let likelist = state.profile.liked_song 64 | let index = likelist.indexOf(id) 65 | if (index > -1) { 66 | likelist.splice(index, 1) 67 | } 68 | }, 69 | 70 | [types.AUDIO_PLAY] (state) { state.player.playing = true }, 71 | [types.AUDIO_STOP] (state) { state.player.playing = false }, 72 | [types.PLAYLIST] (state, { track }) { state.profile.playlist = track }, 73 | [types.COLL_PLAYLIST] (state, { track }) { state.profile.coll_playlist = track }, 74 | [types.LIKED_SONG] (state, { track }) { state.profile.liked_song = track } 75 | }, 76 | actions: { 77 | [types.AUDIO_CHANGE] (content, { songsId, index }) { 78 | if (content.state.player.songsId !== songsId) { 79 | song_list_detail(songsId).then(result => { 80 | content.commit( 81 | types.AUDIO_CHANGE, 82 | { songsId, songsTracks: result['playlist']['trackIds'], index } ) 83 | }) 84 | } else { 85 | content.commit( 86 | types.AUDIO_CHANGE, 87 | { songsId, songsTracks: content.state.player.playTracks, index } ) 88 | } 89 | } 90 | }, 91 | modules: { 92 | 93 | } 94 | }) 95 | -------------------------------------------------------------------------------- /src/components/common/video/Slider.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 68 | 69 | 118 | -------------------------------------------------------------------------------- /src/components/pages/topinfo/ProfileShow.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 49 | 50 | 129 | -------------------------------------------------------------------------------- /src/views/pages/home/ArtistsCategory.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 99 | 100 | 105 | -------------------------------------------------------------------------------- /src/components/content/clause/CommentClause.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 70 | 71 | 120 | -------------------------------------------------------------------------------- /src/components/common/Pagination.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 126 | 127 | 132 | -------------------------------------------------------------------------------- /src/components/header/Search.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 80 | 81 | 149 | --------------------------------------------------------------------------------