├── .gitignore ├── README.md ├── auto-imports.d.ts ├── components.d.ts ├── docs ├── assets │ ├── Album.402a1341.js │ ├── Artist.0d846367.js │ ├── Artist.dfdd0cc6.css │ ├── ArtistDetail.3bc343f2.css │ ├── ArtistDetail.a9400840.js │ ├── Category.0252e828.js │ ├── CoverPlay.33350ebd.css │ ├── CoverPlay.d639e45b.js │ ├── DJ.e0f4dc6a.js │ ├── Discover.38e1e773.js │ ├── Fm.46f05eb0.js │ ├── IconPark.402beefe.js │ ├── Music.70c8366c.js │ ├── Music.e9b17b81.css │ ├── Mv.34913284.js │ ├── Mv.8cac6744.css │ ├── OpticalDisk.4907c891.png │ ├── Picked.0c25147a.js │ ├── PlayList.1f8f2eb4.css │ ├── PlayList.7ad8eb52.js │ ├── PlayOne.e72f474c.js │ ├── PlayTwo.fe71fa6d.js │ ├── Right.083d6647.js │ ├── Root.9a2da039.js │ ├── Root.e6a672cb.css │ ├── SongListItem.6fa5a73e.css │ ├── SongListItem.f4bd4734.js │ ├── TopList.33e44e27.js │ ├── Video.94189c43.js │ ├── el-avatar.00087837.css │ ├── el-image-viewer.a8b12883.js │ ├── el-image-viewer.ac997372.css │ ├── el-popper.24515e58.css │ ├── el-tab-pane.29573fb9.css │ ├── index.373df683.js │ ├── index.eec43999.css │ ├── logo.601c4b06.png │ ├── music.991378cb.js │ ├── mvDetail.aa81123a.js │ ├── plugin-vue_export-helper.21dcd24c.js │ ├── vendor.77ac5f7b.css │ ├── vendor.9760fa8a.js │ └── video.3500c715.js ├── favicon.ico └── index.html ├── env.d.ts ├── index.html ├── package.json ├── postcss.config.js ├── public └── favicon.ico ├── src ├── App.vue ├── Host.vue ├── assets │ ├── base.scss │ ├── img │ │ ├── OpticalDisk.png │ │ ├── index.ts │ │ └── logo.png │ ├── logo.svg │ └── theme.scss ├── components │ ├── common │ │ ├── Banner.vue │ │ ├── CoverPlay.vue │ │ ├── IconPark.vue │ │ ├── MoreText.vue │ │ ├── SongListItem.vue │ │ └── Title.vue │ └── layout │ │ ├── footer │ │ ├── Footer.vue │ │ ├── PlayerAction.vue │ │ ├── PlayerController.vue │ │ ├── PlayerSlider.vue │ │ ├── PlayerSong.vue │ │ └── PlayerVolumeSlider.vue │ │ ├── header │ │ ├── Header.vue │ │ ├── SearchPop.vue │ │ ├── SearchSuggest.vue │ │ └── UserInfo.vue │ │ ├── menu │ │ ├── Menu.vue │ │ ├── MenuList.vue │ │ └── useMenu.ts │ │ └── playList │ │ ├── PlayList.vue │ │ └── PlayListSongItem.vue ├── main.ts ├── models │ ├── album.ts │ ├── artist.ts │ ├── artist_detail.ts │ ├── banner.ts │ ├── dj.ts │ ├── mv.ts │ ├── personalized.ts │ ├── playlist.ts │ ├── playlist_cat.ts │ ├── playlist_hot.ts │ ├── search.ts │ ├── song.ts │ ├── song_url.ts │ ├── toplist_detail.ts │ ├── user.ts │ └── video.ts ├── router │ ├── index.ts │ └── pages.ts ├── stores │ ├── common.ts │ ├── dj.ts │ ├── host.ts │ ├── music.ts │ ├── personalized.ts │ ├── player.ts │ ├── search.ts │ ├── user.ts │ └── video.ts ├── utils │ ├── api.ts │ ├── extend.ts │ ├── http.ts │ └── number.ts └── views │ ├── Root.vue │ ├── album │ ├── Album.vue │ └── Info.vue │ ├── artist │ ├── Album.vue │ ├── ArtistDetail.vue │ ├── Desc.vue │ ├── Info.vue │ ├── Songs.vue │ └── Video.vue │ ├── discover │ ├── Discover.vue │ ├── DjProgram.vue │ ├── Mv.vue │ ├── Personalized.vue │ └── PersonalizedNewSong.vue │ ├── dj │ └── DJ.vue │ ├── music │ ├── Music.vue │ ├── MusicController.ts │ ├── artist │ │ └── Artist.vue │ ├── category │ │ ├── Category.vue │ │ ├── PlaylistHighqualityTags.vue │ │ └── PlaylistHot.vue │ ├── picked │ │ ├── Picked.vue │ │ └── Video.vue │ └── toplist │ │ └── TopList.vue │ ├── mv │ └── mvDetail.vue │ ├── playlist │ ├── Info.vue │ ├── PlayList.vue │ └── SongList.vue │ └── video │ └── Video.vue ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.vite-config.json ├── ui ├── image-20220310123410770.png ├── image-20220310123456071.png ├── image-20220310123530635.png ├── image-20220310123545606.png ├── image-20220310123634367.png ├── image-20220310123650090.png ├── image-20220310123711552.png ├── image-20220310123718015.png ├── image-20220310123722684.png └── image-20220310123738142.png ├── vite.config.ts └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # VUE3-MUSIC 4 | 5 | 基于 VUE3+TS 开发的音乐播放器,界面模仿QQ音乐mac客户端。 6 | 7 | 在线体验:[https://smallruraldog.github.io/vue3-music](https://smallruraldog.github.io/vue3-music) 8 | 9 | 将浏览器大小设置成1050*670,体验效果更好!界面是自适应的,使用[tailwindcss.com](https://www.tailwindcss.com)来实现 10 | 11 | 手机端未适配,后期会使用[Flutter](https://flutter.dev)单独开发手机客户端,使用[Electron](https://www.electronjs.org)打包桌面客户端并内置API服务 12 | 13 | 在线演示为了安全考虑,不提供API接口服务,需要准备好自己的API服务地址,并且是HTTPS的,没有HTTPS的服务,可以本地运行,首次打开时会要求设置API地址 14 | 15 | 16 | ## 本地安装 17 | 18 | ``` 19 | git clone https://github.com/SmallRuralDog/vue3-music.git 20 | cd vue3-music 21 | yarn 22 | yarn run dev 23 | ``` 24 | 25 | ## 网易云音乐API 26 | 27 | 需要运行API服务才能正常体验 28 | 29 | [开发文档](https://binaryify.github.io/NeteaseCloudMusicApi) 30 | 31 | 32 | ## UI 33 | 34 | 35 | 36 | ![image-20220310123410770](ui/image-20220310123410770.png) 37 | 38 | ![image-20220310123530635](ui/image-20220310123530635.png) 39 | 40 | ![image-20220310123634367](ui/image-20220310123634367.png) 41 | 42 | ![image-20220310123722684](ui/image-20220310123722684.png) 43 | 44 | ![image-20220310123456071](ui/image-20220310123456071.png) 45 | 46 | ![image-20220310123545606](ui/image-20220310123545606.png) 47 | 48 | ![image-20220310123650090](ui/image-20220310123650090.png) 49 | 50 | ![image-20220310123738142](ui/image-20220310123738142.png) 51 | 52 | ## PS 53 | 54 | 通过此开源项目学习VUE3的强大..加油! 55 | -------------------------------------------------------------------------------- /auto-imports.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by 'unplugin-auto-import' 2 | // We suggest you to commit this file into source control 3 | declare global { 4 | 5 | } 6 | export {} 7 | -------------------------------------------------------------------------------- /components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/vue-next/pull/3399 4 | 5 | declare module 'vue' { 6 | export interface GlobalComponents { 7 | Banner: typeof import('./src/components/common/Banner.vue')['default'] 8 | CoverPlay: typeof import('./src/components/common/CoverPlay.vue')['default'] 9 | ElAffix: typeof import('element-plus/es')['ElAffix'] 10 | ElAlert: typeof import('element-plus/es')['ElAlert'] 11 | ElAvatar: typeof import('element-plus/es')['ElAvatar'] 12 | ElBadge: typeof import('element-plus/es')['ElBadge'] 13 | ElButton: typeof import('element-plus/es')['ElButton'] 14 | ElDialog: typeof import('element-plus/es')['ElDialog'] 15 | ElDrawer: typeof import('element-plus/es')['ElDrawer'] 16 | ElEmpty: typeof import('element-plus/es')['ElEmpty'] 17 | ElImage: typeof import('element-plus/es')['ElImage'] 18 | ElInput: typeof import('element-plus/es')['ElInput'] 19 | ElPopover: typeof import('element-plus/es')['ElPopover'] 20 | ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] 21 | ElSlider: typeof import('element-plus/es')['ElSlider'] 22 | ElSpace: typeof import('element-plus/es')['ElSpace'] 23 | ElTabPane: typeof import('element-plus/es')['ElTabPane'] 24 | ElTabs: typeof import('element-plus/es')['ElTabs'] 25 | Footer: typeof import('./src/components/layout/footer/Footer.vue')['default'] 26 | Header: typeof import('./src/components/layout/header/Header.vue')['default'] 27 | IconPark: typeof import('./src/components/common/IconPark.vue')['default'] 28 | Loading: typeof import('element-plus/es')['ElLoadingDirective'] 29 | Menu: typeof import('./src/components/layout/menu/Menu.vue')['default'] 30 | MenuList: typeof import('./src/components/layout/menu/MenuList.vue')['default'] 31 | MoreText: typeof import('./src/components/common/MoreText.vue')['default'] 32 | PlayerAction: typeof import('./src/components/layout/footer/PlayerAction.vue')['default'] 33 | PlayerController: typeof import('./src/components/layout/footer/PlayerController.vue')['default'] 34 | PlayerSlider: typeof import('./src/components/layout/footer/PlayerSlider.vue')['default'] 35 | PlayerSong: typeof import('./src/components/layout/footer/PlayerSong.vue')['default'] 36 | PlayerVolumeSlider: typeof import('./src/components/layout/footer/PlayerVolumeSlider.vue')['default'] 37 | PlayList: typeof import('./src/components/layout/playList/PlayList.vue')['default'] 38 | PlayListSongItem: typeof import('./src/components/layout/playList/PlayListSongItem.vue')['default'] 39 | SearchPop: typeof import('./src/components/layout/header/SearchPop.vue')['default'] 40 | SearchSuggest: typeof import('./src/components/layout/header/SearchSuggest.vue')['default'] 41 | SongListItem: typeof import('./src/components/common/SongListItem.vue')['default'] 42 | Title: typeof import('./src/components/common/Title.vue')['default'] 43 | UserInfo: typeof import('./src/components/layout/header/UserInfo.vue')['default'] 44 | } 45 | } 46 | 47 | export { } 48 | -------------------------------------------------------------------------------- /docs/assets/Album.402a1341.js: -------------------------------------------------------------------------------- 1 | import{c as p,g as u,h as f,i as e,H as i,j as s,u as r,D as m,C as g,o as w,$ as B,a0 as C,k as d,l as k,I as E,G as F,q as L}from"./vendor.9760fa8a.js";/* empty css */import{A as $}from"./index.373df683.js";import{_}from"./IconPark.402beefe.js";import{P as V}from"./PlayOne.e72f474c.js";import{L as j}from"./PlayTwo.fe71fa6d.js";import{M as z,S as D}from"./SongListItem.f4bd4734.js";import"./plugin-vue_export-helper.21dcd24c.js";const M={class:"flex items-stretch"},T=["src"],A={class:"pl-5 flex flex-col justify-between py-1 flex-1"},N={class:"flex flex-col justify-between flex-1"},H={class:"text-2xl font-bold"},P={class:"flex items-center text-xs text-gray-600"},S={class:""},q={class:"text-xs text-gray-500 leading-normal"},I={class:"justify-self-stretch mt-5 gap-x-2 flex items-center"},U={class:"w-32 button"},G=e("span",null,"\u64AD\u653E\u5168\u90E8",-1),O={class:"w-32 button-outline"},R=e("span",null,"\u6536\u85CF",-1),J={class:"button-outline w-8"},K=p({props:{album:null},setup(a){return(l,o)=>(u(),f("div",M,[e("img",{src:a.album.picUrl,alt:"",class:"w-44 h-44 object-cover rounded-xl flex-shrink-0"},null,8,T),e("div",A,[e("div",N,[e("div",H,i(a.album.name),1),e("div",P,[e("span",S,i(a.album.artist.name),1)]),e("div",q,i(a.album.publishTime.toDate()),1)]),e("div",I,[e("button",U,[s(_,{icon:r(V),size:"22",class:"mr-1"},null,8,["icon"]),G]),e("button",O,[s(_,{icon:r(j),size:"18",class:"mr-1"},null,8,["icon"]),R]),e("button",J,[s(_,{icon:r(z)},null,8,["icon"])])])])]))}}),Q={key:0,class:"p-5"},W=e("div",{class:"flex text-xs text-gray-400 py-2"},[e("div",{class:"flex-auto"},"\u6B4C\u66F2"),e("div",{class:"w-1/3"},"\u6B4C\u624B"),e("div",{class:"w-20"},"\u65F6\u957F")],-1),X=["innerHTML"],ne=p({setup(a){const l=m(),o=m([]),x=m("songs"),h=g(),v=Number(h.query.id);return w(async()=>{const{album:b,songs:n}=await $(v);l.value=b,o.value=n}),(b,n)=>{const c=B,y=C;return l.value?(u(),f("div",Q,[s(K,{album:l.value},null,8,["album"]),s(y,{class:"mt-3",modelValue:x.value,"onUpdate:modelValue":n[0]||(n[0]=t=>x.value=t)},{default:d(()=>[s(c,{lazy:"",label:`\u6B4C\u66F2 ${o.value.length}`,name:"songs"},{default:d(()=>[W,(u(!0),f(E,null,F(o.value,t=>(u(),L(D,{key:t.id,song:t,"show-ar-name":""},null,8,["song"]))),128))]),_:1},8,["label"]),s(c,{lazy:"",label:"\u8BC4\u8BBA",name:"comments"}),s(c,{lazy:"",label:"\u4E13\u8F91\u8BE6\u60C5",name:"desc"},{default:d(()=>{var t;return[e("div",{class:"text-xs text-slate-500 leading-7",style:{"white-space":"pre-wrap"},innerHTML:(t=l.value)==null?void 0:t.description},null,8,X)]}),_:1})]),_:1},8,["modelValue"])])):k("",!0)}}});export{ne as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Artist.0d846367.js: -------------------------------------------------------------------------------- 1 | import{c as B,V as D,a2 as F,F as w,D as A,r as b,o as S,a3 as j,n as q,g as i,h as l,i as s,I as r,G as m,j as d,u as o,k as _,H as k,J as z,m as I}from"./vendor.9760fa8a.js";import{p as L}from"./index.373df683.js";import{_ as M}from"./plugin-vue_export-helper.21dcd24c.js";const G={class:"pb-5"},H={class:"flex-shrink-0 text-slate-400"},J={class:"ml-3"},R=["onClick"],T={class:"grid grid-flow-row grid-cols-6 xl:grid-cols-8 2xl:grid-cols-10 gap-5"},U=["onClick"],V=["src"],$={class:"mt-2 text-sm"},K={class:"py-10"},N=I("\u52A0\u8F7D\u66F4\u591A"),O=B({setup(P){const p=D(F,{direction:"vertical"}),g=w(),u=A([]),e=b({init:!1,loading:!1,page:1,limit:60,initial:"-1",type:-1,area:-1}),y=async()=>{e.loading=!0;try{const t=await L(e);e.page===1?u.value=t:u.value.push(...t),e.init=!0,e.loading=!1}catch{e.page--}},v=()=>{e.page++,y()};S(y);const f=(t,c)=>{console.log(t,c),e.page=1,t==="type"&&(e.type=c),t==="area"&&(e.area=c),t==="initial"&&(e.initial=c),y()},x=[{name:"\u8BED\u79CD",key:"area",list:[{key:-1,name:"\u5168\u90E8"},{key:7,name:"\u534E\u8BED"},{key:96,name:"\u6B27\u7F8E"},{key:8,name:"\u65E5\u672C"},{key:16,name:"\u97E9\u56FD"},{key:0,name:"\u5176\u4ED6"}]},{name:"\u5206\u7C7B",key:"type",list:[{key:-1,name:"\u5168\u90E8"},{key:1,name:"\u7537\u6B4C\u624B"},{key:2,name:"\u5973\u6B4C\u624B"},{key:3,name:"\u4E50\u961F\u7EC4\u5408"}]},{name:"\u7B5B\u9009",key:"initial",list:[{key:"-1",name:"\u70ED\u95E8"},{key:"a",name:"A"},{key:"b",name:"B"},{key:"c",name:"C"},{key:"d",name:"D"},{key:"e",name:"E"},{key:"f",name:"F"},{key:"g",name:"G"},{key:"h",name:"H"},{key:"i",name:"I"},{key:"j",name:"J"},{key:"k",name:"K"},{key:"l",name:"L"},{key:"m",name:"M"},{key:"n",name:"N"},{key:"o",name:"O"},{key:"p",name:"P"},{key:"q",name:"Q"},{key:"r",name:"R"},{key:"s",name:"S"},{key:"t",name:"T"},{key:"u",name:"U"},{key:"v",name:"V"},{key:"w",name:"W"},{key:"x",name:"X"},{key:"y",name:"Y"},{key:"z",name:"Z"},{key:"0",name:"#"}]}];return(t,c)=>{const h=j,E=q;return i(),l(r,null,[s("div",G,[(i(),l(r,null,m(x,a=>s("div",{key:a.key,class:"flex text-xs mb-5"},[s("div",H,k(a.name)+"\uFF1A",1),s("div",J,[d(h,{wrap:"",size:10,spacer:o(p)},{default:_(()=>[(i(!0),l(r,null,m(a.list,(n,C)=>(i(),l("div",{type:"text",class:z(["hover-text px-1 py-0.5",{active:n.key===o(e).type&&a.key==="type"||n.key===o(e).area&&a.key==="area"||n.key===o(e).initial&&a.key==="initial"}]),key:C,onClick:Q=>f(a.key,n.key)},k(n.name),11,R))),128))]),_:2},1032,["spacer"])])])),64))]),s("div",T,[(i(!0),l(r,null,m(u.value,a=>(i(),l("div",{key:a.id,class:"flex items-center flex-col",onClick:n=>o(g).push({name:"artistDetail",query:{id:a.id}})},[s("img",{src:a.img1v1Url+"?param=120y120",alt:"",class:"rounded-full cursor-pointer w-full aspect-square object-cover bg-dc"},null,8,V),s("div",$,k(a.name),1)],8,U))),128))]),s("div",K,[d(E,{type:"text",class:"text-center w-full",onClick:v,loading:o(e).loading},{default:_(()=>[N]),_:1},8,["loading"])])],64)}}});var Z=M(O,[["__scopeId","data-v-1cc54718"]]);export{Z as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Artist.dfdd0cc6.css: -------------------------------------------------------------------------------- 1 | .el-space{display:inline-flex;vertical-align:top}.el-space__item{display:flex;flex-wrap:wrap}.el-space__item>*{flex:1}.el-space--vertical{flex-direction:column}.active[data-v-1cc54718]{border-radius:.25rem;--tw-bg-opacity: 1;background-color:rgb(52 211 153 / var(--tw-bg-opacity));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))} 2 | -------------------------------------------------------------------------------- /docs/assets/ArtistDetail.3bc343f2.css: -------------------------------------------------------------------------------- 1 | :root{--el-loading-spinner-size:42px;--el-loading-fullscreen-spinner-size:50px}.el-loading-parent--relative{position:relative!important}.el-loading-parent--hidden{overflow:hidden!important}.el-loading-mask{position:absolute;z-index:2000;background-color:#ffffffe6;margin:0;top:0;right:0;bottom:0;left:0;transition:opacity var(--el-transition-duration)}.el-loading-mask.is-fullscreen{position:fixed}.el-loading-mask.is-fullscreen .el-loading-spinner{margin-top:calc((0px - var(--el-loading-fullscreen-spinner-size))/ 2)}.el-loading-mask.is-fullscreen .el-loading-spinner .circular{height:var(--el-loading-fullscreen-spinner-size);width:var(--el-loading-fullscreen-spinner-size)}.el-loading-spinner{top:50%;margin-top:calc((0px - var(--el-loading-spinner-size))/ 2);width:100%;text-align:center;position:absolute}.el-loading-spinner .el-loading-text{color:var(--el-color-primary);margin:3px 0;font-size:14px}.el-loading-spinner .circular{display:inline;height:var(--el-loading-spinner-size);width:var(--el-loading-spinner-size);animation:loading-rotate 2s linear infinite}.el-loading-spinner .path{animation:loading-dash 1.5s ease-in-out infinite;stroke-dasharray:90,150;stroke-dashoffset:0;stroke-width:2;stroke:var(--el-color-primary);stroke-linecap:round}.el-loading-spinner i{color:var(--el-color-primary)}.el-loading-fade-enter-from,.el-loading-fade-leave-to{opacity:0}@keyframes loading-rotate{to{transform:rotate(360deg)}}@keyframes loading-dash{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:90,150;stroke-dashoffset:-40px}to{stroke-dasharray:90,150;stroke-dashoffset:-120px}}.el-empty{--el-empty-padding:40px 0;--el-empty-image-width:160px;--el-empty-description-margin-top:20px;--el-empty-bottom-margin-top:20px;--el-empty-fill-color-0:var(--el-color-white);--el-empty-fill-color-1:#fcfcfd;--el-empty-fill-color-2:#f8f9fb;--el-empty-fill-color-3:#f7f8fc;--el-empty-fill-color-4:#eeeff3;--el-empty-fill-color-5:#edeef2;--el-empty-fill-color-6:#e9ebef;--el-empty-fill-color-7:#e5e7e9;--el-empty-fill-color-8:#e0e3e9;--el-empty-fill-color-9:#d5d7de;display:flex;justify-content:center;align-items:center;flex-direction:column;text-align:center;box-sizing:border-box;padding:var(--el-empty-padding)}.el-empty__image{width:var(--el-empty-image-width)}.el-empty__image img{-webkit-user-select:none;user-select:none;width:100%;height:100%;vertical-align:top;object-fit:contain}.el-empty__image svg{fill:var(--el-svg-monochrome-grey);width:100%;height:100%;vertical-align:top}.el-empty__description{margin-top:var(--el-empty-description-margin-top)}.el-empty__description p{margin:0;font-size:var(--el-font-size-base);color:var(--el-text-color-secondary)}.el-empty__bottom{margin-top:var(--el-empty-bottom-margin-top)}.artist-detail .el-tabs__nav-wrap:after{height:0}.artist-detail .el-tabs__header{margin:0} 2 | -------------------------------------------------------------------------------- /docs/assets/Category.0252e828.js: -------------------------------------------------------------------------------- 1 | import{c as v,F as h,D as x,o as C,g as a,h as s,i as r,I as f,G as k,H as m,r as w,n as $,j as g,u as c,k as B,l as D,J as E,m as P}from"./vendor.9760fa8a.js";import{q,r as F,P as H}from"./index.373df683.js";import{_ as N}from"./CoverPlay.d639e45b.js";import"./el-image-viewer.a8b12883.js";import"./IconPark.402beefe.js";import"./PlayOne.e72f474c.js";const V={class:"grid grid-flow-row grid-cols-8 2xl:grid-cols-12 gap-2.5"},T=["onClick"],A=v({emits:["catChange"],setup(b,{emit:i}){const u=d=>{i("catChange",d)};h();const t=x();return C(async()=>{t.value=await q()}),(d,o)=>(a(),s("div",V,[r("div",{class:"button-dc",onClick:o[0]||(o[0]=n=>u("\u5168\u90E8"))},"\u5168\u90E8"),(a(!0),s(f,null,k(t.value,n=>(a(),s("div",{key:n.id,class:"button-dc",onClick:l=>u(n.name)},m(n.name),9,T))),128))]))}}),I={class:"py-5 text-xl"},M={class:"gap-5 grid grid-flow-row grid-cols-3 lg:grid-cols-5 2xl:grid-cols-7"},j=["onClick"],z={class:"mt-2 text-xs text-main leading-5"},G={class:"mt-2 text-xs text-main truncate text-dc"},J={key:0,class:"py-10"},L=P("\u52A0\u8F7D\u66F4\u591A"),X=v({setup(b){const i=x(),u=h(),t=w({init:!1,loading:!1,limit:35,before:0,more:!1,cat:"\u5168\u90E8"}),d=l=>{t.cat=l,t.before=0,t.more=!1,o()},o=async()=>{var l;t.loading=!0;try{const{playlists:_,lasttime:p,more:e}=await F({limit:t.limit,before:t.before,cat:t.cat});t.before<=0?i.value=_:(l=i.value)==null||l.push(..._),t.init=!0,t.loading=!1,t.before=p,t.more=e}catch{}},n=()=>{o()};return C(o),(l,_)=>{const p=$;return a(),s(f,null,[g(A,{onCatChange:d}),r("div",I,m(c(t).cat)+"\u6B4C\u5355",1),r("div",M,[(a(!0),s(f,null,k(i.value,(e,y)=>(a(),s("div",{key:y,class:E({"item-1":y===0}),onClick:R=>c(u).push({name:c(H).playlist,query:{id:e.id}})},[g(N,{name:e.name,"pic-url":e.coverImgUrl,"play-count":e.playCount,"show-play-count":""},null,8,["name","pic-url","play-count"]),r("div",z,m(e.name),1),r("div",G,m(e.creator.nickname),1)],10,j))),128))]),c(t).more?(a(),s("div",J,[g(p,{type:"text",class:"text-center w-full",onClick:n,loading:c(t).loading},{default:B(()=>[L]),_:1},8,["loading"])])):D("",!0)],64)}}});export{X as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/CoverPlay.33350ebd.css: -------------------------------------------------------------------------------- 1 | .cover-play-image{position:relative;cursor:pointer;overflow:hidden;border-radius:.5rem;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.cover-play-image:hover{--tw-translate-y: -.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cover-play-image .mask{position:absolute;top:0px;right:0px;bottom:0px;left:0px;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-bg-opacity: 0;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.5s}.cover-play-image .play-count{position:absolute;bottom:.25rem;right:.25rem;display:flex;--tw-scale-x: .9;--tw-scale-y: .9;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));align-items:center;border-radius:9999px;background-color:rgb(0 0 0 / var(--tw-bg-opacity));--tw-bg-opacity: .8;padding:.125rem .5rem;font-size:.75rem;line-height:1rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity));transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.cover-play-image:hover .mask{--tw-bg-opacity: .5 }.cover-play-image:hover .mask .play-icon{opacity:1}.cover-play-image:hover .play-count{opacity:0} 2 | -------------------------------------------------------------------------------- /docs/assets/CoverPlay.d639e45b.js: -------------------------------------------------------------------------------- 1 | import{j as t,c,R as r,g as o,h as a,i as n,u as l,H as u,l as d,J as m}from"./vendor.9760fa8a.js";import{P as h}from"./el-image-viewer.a8b12883.js";import{g as k}from"./index.373df683.js";import{I as y,_ as i}from"./IconPark.402beefe.js";import{P as f}from"./PlayOne.e72f474c.js";var C=y("headset",!1,function(e){return t("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[t("path",{d:"M42 30V24.4615C42 14.2655 33.9411 6 24 6C14.0589 6 6 14.2655 6 24.4615V30",stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M34 32C34 29.7909 35.7909 28 38 28H42V42H38C35.7909 42 34 40.2091 34 38V32Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M42 32H44C45.1046 32 46 32.8954 46 34V36C46 37.1046 45.1046 38 44 38H42V32Z",fill:e.colors[0]},null),t("path",{d:"M6 32H4C2.89543 32 2 32.8954 2 34V36C2 37.1046 2.89543 38 4 38H6V32Z",fill:e.colors[0]},null),t("path",{d:"M6 28H10C12.2091 28 14 29.7909 14 32V38C14 40.2091 12.2091 42 10 42H6V28Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null)])});const v={class:"mask flex justify-center items-center"},V={key:0,class:"play-count"},B=c({props:{picUrl:null,playCount:null,name:null,showPlayCount:{type:Boolean},onPlay:null,video:{type:Boolean}},setup(e){return(g,H)=>{const s=r;return o(),a("div",{class:m(["cover-play-image",{"aspect-square":!e.video,"aspect-video":e.video}])},[t(s,{src:e.picUrl,alt:e.name,class:"w-full bg-gray-50 object-cover"},null,8,["src","alt"]),n("div",v,[t(i,{icon:l(f),theme:"filled",class:"text-white play-icon opacity-0 transition-opacity hover:text-teal-400",size:50,onClick:e.onPlay},null,8,["icon","onClick"])]),e.showPlayCount?(o(),a("div",V,[t(i,{icon:e.video?l(h):l(C),class:"mr-1",size:12},null,8,["icon"]),n("text",null,u(l(k)(e.playCount||0)),1)])):d("",!0)],2)}}});export{B as _}; 2 | -------------------------------------------------------------------------------- /docs/assets/DJ.e0f4dc6a.js: -------------------------------------------------------------------------------- 1 | import{_ as r}from"./plugin-vue_export-helper.21dcd24c.js";const e={};function _(c,n){return null}var a=r(e,[["render",_]]);export{a as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Discover.38e1e773.js: -------------------------------------------------------------------------------- 1 | import{c as d,F as f,t as g,o as x,g as o,h as n,j as e,i as t,I as p,G as h,J as k,u as i,H as _}from"./vendor.9760fa8a.js";import{_ as v,B as C,a as B}from"./Mv.34913284.js";import{_ as z}from"./CoverPlay.d639e45b.js";import{u as l}from"./music.991378cb.js";import{c as S}from"./index.373df683.js";import"./plugin-vue_export-helper.21dcd24c.js";import"./IconPark.402beefe.js";import"./Right.083d6647.js";import"./video.3500c715.js";import"./el-image-viewer.a8b12883.js";import"./PlayOne.e72f474c.js";const b={class:"grid grid-flow-row grid-cols-3 lg:grid-cols-5 gap-5 2xl:grid-cols-10"},E=["onClick"],F={class:"mt-2 text-xs text-main truncate"},N=d({setup(m){const r=f(),{personalized:a}=g(l()),{getPersonalized:u}=l();return x(async()=>{u()}),(y,$)=>(o(),n("div",null,[e(v,{title:"\u4F60\u7684\u4E13\u5C5E\u6B4C\u5355"}),t("div",b,[(o(!0),n(p,null,h(i(a).sampleSize(10),(s,c)=>(o(),n("div",{key:c,class:k({"item-1":c===0}),onClick:w=>i(r).push({name:"playlist",query:{id:s.id}})},[e(z,{name:s.name,"pic-url":s.picUrl,"play-count":s.playCount,"show-play-count":""},null,8,["name","pic-url","play-count"]),t("div",F,_(s.name),1)],10,E))),128))])]))}}),P={class:"grid grid-flow-row grid-cols-2 2xl:grid-cols-5 gap-y-2.5 gap-x-5 cursor-pointer"},j=["onClick"],A=["src"],M={class:"px-2 text-xs flex-auto flex flex-col w-1/3"},R={class:"text-xs flex-1 truncate"},U={class:"mt-1.5 text-dc"},V=d({setup(m){const{play:r}=S();f();const{personalizedNewSong:a}=g(l()),{getPersonalizedNewSong:u}=l();return x(async()=>{await u()}),(y,$)=>(o(),n(p,null,[e(v,{title:"\u63A8\u8350\u65B0\u97F3\u4E50"}),t("div",P,[(o(!0),n(p,null,h(i(a),(s,c)=>(o(),n("div",{key:c,class:"hover-bg-view transition-all flex items-center",onClick:w=>i(r)(s.id)},[t("img",{src:s.picUrl,alt:"",class:"w-12 h-12 object-cover rounded flex-shrink-0"},null,8,A),t("div",M,[t("div",R,_(s.name),1),t("div",U,_(s.song.artists[0].name),1)])],8,j))),128))])],64))}}),q={class:"px-5"},D=t("h1",{class:"text-3xl font-bold pt-8 pb-4"},"\u63A8\u8350",-1),Y=d({setup(m){return(r,a)=>(o(),n("div",q,[D,e(C,{"per-page":3}),e(N),e(V),e(B)]))}});export{Y as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Fm.46f05eb0.js: -------------------------------------------------------------------------------- 1 | import{I as i}from"./IconPark.402beefe.js";import{j as t}from"./vendor.9760fa8a.js";var l=i("fm",!1,function(e){return t("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[t("circle",{cx:"24",cy:"22",r:"4",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M21.5238 33.0539C22.7619 31.6487 25.2381 31.6486 26.4762 33.0539C27.7143 34.4592 26.4763 43.3608 25.6508 44.2975C24.8254 45.2342 23.1746 45.2342 22.3492 44.2975C21.5238 43.3608 20.2857 34.4591 21.5238 33.0539Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M30.9668 30.513C33.4289 28.4957 35 25.4313 35 22C35 15.9249 30.0751 11 24 11C17.9249 11 13 15.9249 13 22C13 25.4313 14.5711 28.4957 17.0332 30.513",stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M31.9258 38.1656C37.8928 35.2345 42 29.0969 42 22C42 12.0589 33.9411 4 24 4C14.0589 4 6 12.0589 6 22C6 29.0969 10.1072 35.2345 16.0742 38.1656",stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null)])});export{l as F}; 2 | -------------------------------------------------------------------------------- /docs/assets/IconPark.402beefe.js: -------------------------------------------------------------------------------- 1 | import{T as F,j as L,c as d,g as y,q as j,U as v}from"./vendor.9760fa8a.js";var W={size:"1em",strokeWidth:4,strokeLinecap:"round",strokeLinejoin:"round",rtl:!1,theme:"outline",colors:{outline:{fill:"#333",background:"transparent"},filled:{fill:"#333",background:"#FFF"},twoTone:{fill:"#333",twoTone:"#2F88FF"},multiColor:{outStrokeColor:"#333",outFillColor:"#2F88FF",innerStrokeColor:"#FFF",innerFillColor:"#43CCF8"}},prefix:"i"};function z(){return"icon-"+((1+Math.random())*4294967296|0).toString(16).substring(1)}function g(t,i,r){var e=typeof i.fill=="string"?[i.fill]:i.fill||[],o=[],n=i.theme||r.theme;switch(n){case"outline":o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push("none"),o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push("none");break;case"filled":o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push("#FFF"),o.push("#FFF");break;case"two-tone":o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push(typeof e[1]=="string"?e[1]:r.colors.twoTone.twoTone),o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push(typeof e[1]=="string"?e[1]:r.colors.twoTone.twoTone);break;case"multi-color":o.push(typeof e[0]=="string"?e[0]:"currentColor"),o.push(typeof e[1]=="string"?e[1]:r.colors.multiColor.outFillColor),o.push(typeof e[2]=="string"?e[2]:r.colors.multiColor.innerStrokeColor),o.push(typeof e[3]=="string"?e[3]:r.colors.multiColor.innerFillColor);break}return{size:i.size||r.size,strokeWidth:i.strokeWidth||r.strokeWidth,strokeLinecap:i.strokeLinecap||r.strokeLinecap,strokeLinejoin:i.strokeLinejoin||r.strokeLinejoin,colors:o,id:t}}var x=Symbol("icon-context");function w(t,i,r){var e={name:"icon-"+t,props:["size","strokeWidth","strokeLinecap","strokeLinejoin","theme","fill","spin"],setup:function(n){var u=z(),s=F(x,W);return function(){var a=n.size,c=n.strokeWidth,p=n.strokeLinecap,k=n.strokeLinejoin,h=n.theme,f=n.fill,C=n.spin,m=g(u,{size:a,strokeWidth:c,strokeLinecap:p,strokeLinejoin:k,theme:h,fill:f},s),l=[s.prefix+"-icon"];return l.push(s.prefix+"-icon-"+t),i&&s.rtl&&l.push(s.prefix+"-icon-rtl"),C&&l.push(s.prefix+"-icon-spin"),L("span",{class:l.join(" ")},[r(m)])}}};return e}const I=d({props:{icon:null,theme:null,size:null,spin:{type:Boolean},fill:null,strokeLinecap:null,strokeLinejoin:null,strokeWidth:null},setup(t){return(i,r)=>(y(),j(v(t.icon),{theme:t.theme,size:t.size,spin:t.spin,fill:t.fill,strokeLinecap:t.strokeLinecap,strokeLinejoin:t.strokeLinejoin,strokeWidth:t.strokeWidth},null,8,["theme","size","spin","fill","strokeLinecap","strokeLinejoin","strokeWidth"]))}});export{w as I,I as _}; 2 | -------------------------------------------------------------------------------- /docs/assets/Music.70c8366c.js: -------------------------------------------------------------------------------- 1 | import{F as C,C as k,D as v,w as x,c as B,$ as h,a0 as E,a1 as T,p as V,g as l,h as i,j as o,k as m,i as c,I as g,G as w,q as F,u,M}from"./vendor.9760fa8a.js";/* empty css */function R(){const r=[{label:"\u7CBE\u9009",name:"picked"},{label:"\u6709\u58F0\u7535\u53F0",name:"dt"},{label:"\u6392\u884C",name:"toplist"},{label:"\u6B4C\u624B",name:"artist"},{label:"\u5206\u7C7B\u6B4C\u5355",name:"category"},{label:"\u6570\u5B57\u4E13\u8F91",name:"zj"}],s=C(),e=k(),n=v(e.name);return x(()=>e.name,a=>{n.value=a}),{menus:r,currentMenu:n,onTabClick:({props:a})=>{s.push({name:a.name,replace:!0})}}}const y={class:"pl-5 pr-5 music"},j=c("h1",{class:"text-3xl font-bold pt-8 pb-4"},"\u97F3\u4E50\u9986",-1),A={class:"bg-view"},N={class:"mt-5"},D=B({setup(r){const{menus:s,currentMenu:e,onTabClick:n}=R();return(_,a)=>{const p=h,d=E,b=T,f=V("RouterView");return l(),i("div",y,[j,o(b,{target:".music",offset:56},{default:m(()=>[c("div",A,[o(d,{modelValue:u(e),"onUpdate:modelValue":a[0]||(a[0]=t=>M(e)?e.value=t:null),onTabClick:u(n)},{default:m(()=>[(l(!0),i(g,null,w(u(s),t=>(l(),F(p,{key:t.name,label:t.label,name:t.name,class:"text-main"},null,8,["label","name"]))),128))]),_:1},8,["modelValue","onTabClick"])])]),_:1}),c("div",N,[o(f)])])}}});export{D as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Music.e9b17b81.css: -------------------------------------------------------------------------------- 1 | .el-affix--fixed{position:fixed}.music .el-tabs__nav-wrap:after{height:0}.music .el-tabs__header{margin:0} 2 | -------------------------------------------------------------------------------- /docs/assets/Mv.8cac6744.css: -------------------------------------------------------------------------------- 1 | @font-face{font-family:swiper-icons;src:url(data:application/font-woff;charset=utf-8;base64,\ d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA);font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;box-sizing:content-box}.swiper-android .swiper-slide,.swiper-wrapper{transform:translateZ(0)}.swiper-pointer-events{touch-action:pan-y}.swiper-pointer-events.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d,.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide,.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top,.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-horizontal.swiper-css-mode>.swiper-wrapper{-ms-scroll-snap-type:x mandatory;scroll-snap-type:x mandatory}.swiper-vertical.swiper-css-mode>.swiper-wrapper{-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory}.swiper-centered>.swiper-wrapper:before{content:"";flex-shrink:0;order:9999}.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{-webkit-margin-start:var(--swiper-centered-offset-before);margin-inline-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-horizontal>.swiper-wrapper:before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{-webkit-margin-before:var(--swiper-centered-offset-before);margin-block-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-vertical>.swiper-wrapper:before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center}.swiper[data-v-0afd57ba]{margin-left:-.625rem;margin-right:-.625rem}.swiper .swiper-slide[data-v-0afd57ba]{width:100%;padding-left:.625rem;padding-right:.625rem}@media (min-width: 1024px){.swiper .swiper-slide[data-v-0afd57ba]{width:50%}}@media (min-width: 1280px){.swiper .swiper-slide[data-v-0afd57ba]{width:33.333333%}}@media (min-width: 1536px){.swiper .swiper-slide[data-v-0afd57ba]{width:25%}}.banner-image[data-v-0afd57ba]{cursor:pointer;border-radius:.5rem;-o-object-fit:cover;object-fit:cover;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.banner-image[data-v-0afd57ba]:hover{opacity:.8;--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)} 2 | -------------------------------------------------------------------------------- /docs/assets/OpticalDisk.4907c891.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/docs/assets/OpticalDisk.4907c891.png -------------------------------------------------------------------------------- /docs/assets/Picked.0c25147a.js: -------------------------------------------------------------------------------- 1 | import{_ as g,B as C,a as j}from"./Mv.34913284.js";import{u as m}from"./video.3500c715.js";import{_ as f}from"./CoverPlay.d639e45b.js";import{c as _,t as v,F as P,o as h,g as r,h as s,j as e,i as l,I as u,G as $,u as i,H as y,d as w,D as B}from"./vendor.9760fa8a.js";import{P as D,o as z}from"./index.373df683.js";import"./plugin-vue_export-helper.21dcd24c.js";import"./IconPark.402beefe.js";import"./Right.083d6647.js";import"./el-image-viewer.a8b12883.js";import"./PlayOne.e72f474c.js";const S={class:"grid grid-flow-row grid-cols-2 lg:grid-cols-4 gap-5"},E=["onClick"],F={class:"truncate text-xs mt-2"},V=_({setup(o){const{personalizedPrivateContent:a}=v(m()),{getPersonalizedPrivateContent:c}=m(),d=P();return h(async()=>{await c()}),(k,x)=>(r(),s(u,null,[e(g,{title:"\u72EC\u5BB6\u653E\u9001",class:"mt-5"}),l("div",S,[(r(!0),s(u,null,$(i(a),t=>(r(),s("div",{key:t.id,onClick:n=>i(d).push({name:i(D).mvDetail,query:{id:t.id}})},[e(f,{"pic-url":t.sPicUrl,video:"",name:t.name},null,8,["pic-url","name"]),l("div",F,y(t.name),1)],8,E))),128))])],64))}}),p=w("personalized",()=>{const o=B([]);return{djProgram:o,getDjProgram:async()=>{o.value.length||(o.value=await z())}}}),q={class:"grid grid-flow-row grid-cols-3 lg:grid-cols-6 gap-5"},N=["onClick"],R={class:"truncate text-xs mt-2"},U=_({setup(o){const{djProgram:a}=v(p()),{getDjProgram:c}=p(),d=P();return h(async()=>{await c()}),(k,x)=>{const t=g;return r(),s(u,null,[e(t,{title:"\u63A8\u8350\u7535\u53F0",class:"mt-5"}),l("div",q,[(r(!0),s(u,null,$(i(a),n=>(r(),s("div",{key:n.id,onClick:A=>i(d).push({name:"video",query:{id:n.id}})},[e(f,{"pic-url":n.picUrl,name:n.name,"play-count":0},null,8,["pic-url","name"]),l("div",R,y(n.name),1)],8,N))),128))])],64)}}}),Q=_({setup(o){return(a,c)=>(r(),s("div",null,[e(C),e(V),e(U),e(j)]))}});export{Q as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/PlayList.1f8f2eb4.css: -------------------------------------------------------------------------------- 1 | .playlist .el-tabs__nav-wrap:after{height:0}.playlist .el-tabs__header{margin:0} 2 | -------------------------------------------------------------------------------- /docs/assets/PlayList.7ad8eb52.js: -------------------------------------------------------------------------------- 1 | import{c as y,D as r,g as l,h as n,I as p,H as i,i as e,m as $,N as E,j as c,G as w,u as _,e as F,q as D,k as f,l as B,a4 as L,n as P,C as V,o as N,$ as S,a0 as z}from"./vendor.9760fa8a.js";/* empty css */import{c as j,s as M,t as T}from"./index.373df683.js";/* empty css */import{_ as h}from"./IconPark.402beefe.js";import{M as I,S as U}from"./SongListItem.f4bd4734.js";import{P as q}from"./PlayOne.e72f474c.js";import{L as G}from"./PlayTwo.fe71fa6d.js";import"./plugin-vue_export-helper.21dcd24c.js";const H={class:"leading-5"},O={key:0},R={key:1},J=y({props:{text:null,end:null},setup(t){const u=t,o=r(!1),s=r(!1);return u.text&&u.text.length>u.end&&(o.value=!0),(a,d)=>(l(),n("div",H,[o.value?(l(),n(p,{key:0},[s.value?(l(),n("span",R,i(t.text)+"...",1)):(l(),n("span",O,i(t.text.substring(0,t.end))+"...",1)),e("span",{class:"ml-5 text-gray-700 hover-text",onClick:d[0]||(d[0]=x=>s.value=!s.value)},"["+i(s.value?"\u6536\u8D77":"\u8BE6\u60C5")+"]",1)],64)):(l(),n(p,{key:1},[$(i(t.text),1)],64))]))}}),K={class:"flex items-stretch"},Q=["src"],W={class:"pl-5 flex flex-col justify-between py-1 flex-1"},X={class:"text-2xl font-bold"},Y={class:"flex items-center text-xs text-dc pb-2 pt-3"},Z={class:"ml-2"},tt={class:"ml-5 flex text-dc"},et={class:"text-xs text-gray-500 leading-normal"},st={class:"justify-self-stretch mt-5 gap-x-2 flex items-center"},lt=e("span",null,"\u64AD\u653E\u5168\u90E8",-1),at={class:"w-32 button-outline"},nt=e("span",null,"\u6536\u85CF",-1),ot={class:"button-outline w-8"},ct=y({props:{playlist:null,playAll:null},setup(t){return(u,o)=>{const s=E;return l(),n("div",K,[e("img",{src:t.playlist.coverImgUrl,alt:"",class:"w-44 h-44 object-cover rounded-xl flex-shrink-0"},null,8,Q),e("div",W,[e("div",null,[e("div",X,i(t.playlist.name),1),e("div",Y,[c(s,{src:t.playlist.creator.avatarUrl,size:"small",round:""},null,8,["src"]),e("span",Z,i(t.playlist.creator.nickname),1),e("div",tt,[(l(!0),n(p,null,w(t.playlist.tags,a=>(l(),n("div",{class:"ml-2 hover-text",key:a},"#"+i(a),1))),128))])]),e("div",et,[c(J,{text:t.playlist.description,end:90},null,8,["text"])])]),e("div",st,[e("button",{class:"w-32 button",onClick:o[0]||(o[0]=(...a)=>t.playAll&&t.playAll(...a))},[c(h,{icon:_(q),size:"22",class:"mr-1"},null,8,["icon"]),lt]),e("button",at,[c(h,{icon:_(G),size:"18",class:"mr-1"},null,8,["icon"]),nt]),e("button",ot,[c(h,{icon:_(I)},null,8,["icon"])])])])])}}}),ut={class:"mt-2"},it=L('
\u6B4C\u66F2
\u6B4C\u624B
\u4E13\u8F91
\u65F6\u957F
',1),rt={class:"text-sm"},dt={key:0,class:"flex justify-center py-5"},mt=$("\u52A0\u8F7D\u66F4\u591A"),vt=y({props:{songs:null},setup(t){const u=t,o=r(10),s=r(1),a=F(()=>s.value-u.songs.length/o.value>=0),d=()=>{s.value=s.value+1};return(x,g)=>{const b=P;return l(),n("div",ut,[it,e("div",rt,[(l(!0),n(p,null,w(t.songs.slice(0,o.value*s.value),m=>(l(),D(U,{key:m.id,song:m,"show-ar-name":"","show-al-name":""},null,8,["song"]))),128))]),t.songs.length>o.value&&!_(a)?(l(),n("div",dt,[c(b,{type:"text",class:"text-center w-full",onClick:d},{default:f(()=>[mt]),_:1})])):B("",!0)])}}});const _t={class:"playlist"},pt={key:0,class:"p-5"},Bt=y({setup(t){const u=r("tracks"),o=V(),s=r(),a=r([]),{pushPlayList:d,play:x}=j(),g=()=>{d(!0,...a.value),x(a.value.first().id)};return N(()=>{const m=Number(o.query.id);M(m).then(v=>{s.value=v}),T(m).then(v=>{a.value=v})}),(m,v)=>{const k=S,C=z;return l(),n("div",_t,[s.value?(l(),n("div",pt,[c(ct,{playlist:s.value,"play-all":()=>g()},null,8,["playlist","play-all"]),c(C,{class:"mt-3",modelValue:u.value,"onUpdate:modelValue":v[0]||(v[0]=A=>u.value=A)},{default:f(()=>[c(k,{lazy:"",label:`\u6B4C\u66F2 ${a.value.length}`,name:"tracks"},{default:f(()=>[c(vt,{songs:a.value},null,8,["songs"])]),_:1},8,["label"]),c(k,{lazy:"",label:"\u8BC4\u8BBA",name:"comments"})]),_:1},8,["modelValue"])])):B("",!0)])}}});export{Bt as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/PlayOne.e72f474c.js: -------------------------------------------------------------------------------- 1 | import{I as t}from"./IconPark.402beefe.js";import{j as o}from"./vendor.9760fa8a.js";var n=t("play-one",!0,function(e){return o("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[o("path",{d:"M15 24V11.8756L25.5 17.9378L36 24L25.5 30.0622L15 36.1244V24Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null)])});export{n as P}; 2 | -------------------------------------------------------------------------------- /docs/assets/PlayTwo.fe71fa6d.js: -------------------------------------------------------------------------------- 1 | import{I as t}from"./IconPark.402beefe.js";import{j as i}from"./vendor.9760fa8a.js";var n=t("down-two",!1,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("path",{d:"M5 24L24 42L43 24H31V6H17V24H5Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null)])}),r=t("like",!1,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("path",{d:"M15 8C8.92487 8 4 12.9249 4 19C4 30 17 40 24 42.3262C31 40 44 30 44 19C44 12.9249 39.0751 8 33 8C29.2797 8 25.9907 9.8469 24 12.6738C22.0093 9.8469 18.7203 8 15 8Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null)])}),c=t("more-two",!1,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("path",{d:"M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null),i("circle",{cx:"14",cy:"24",r:"3",fill:e.colors[2]},null),i("circle",{cx:"24",cy:"24",r:"3",fill:e.colors[2]},null),i("circle",{cx:"34",cy:"24",r:"3",fill:e.colors[2]},null)])}),s=t("play-two",!0,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("rect",{x:"6",y:"6",width:"36",height:"36",rx:"3",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),i("path",{d:"M18.5 24V16.2058L25.25 20.1029L32 24L25.25 27.8971L18.5 31.7942V24Z",fill:e.colors[3],stroke:e.colors[2],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null)])});export{n as D,r as L,c as M,s as P}; 2 | -------------------------------------------------------------------------------- /docs/assets/Right.083d6647.js: -------------------------------------------------------------------------------- 1 | import{I as i}from"./IconPark.402beefe.js";import{j as t}from"./vendor.9760fa8a.js";var n=i("right",!0,function(e){return t("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[t("path",{d:"M19 12L31 24L19 36",stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null)])});export{n as R}; 2 | -------------------------------------------------------------------------------- /docs/assets/SongListItem.6fa5a73e.css: -------------------------------------------------------------------------------- 1 | .song-item[data-v-938b689c]{padding-top:.625rem;padding-bottom:.625rem;padding-left:.125rem}.song-item:hover .icon-action[data-v-938b689c]{display:inline-block}.playing[data-v-938b689c]{--tw-bg-opacity: 1;background-color:rgb(236 253 245 / var(--tw-bg-opacity))}@media (prefers-color-scheme: dark){.playing[data-v-938b689c]{--tw-bg-opacity: 1;background-color:rgb(41 37 36 / var(--tw-bg-opacity))}} 2 | -------------------------------------------------------------------------------- /docs/assets/SongListItem.f4bd4734.js: -------------------------------------------------------------------------------- 1 | import{I as v,_ as l}from"./IconPark.402beefe.js";import{j as i,c as x,F as w,s as g,g as a,h as u,i as s,u as t,H as r,q as y,l as d,J as m}from"./vendor.9760fa8a.js";import{c as f,P as A,e as z}from"./index.373df683.js";import{_ as D}from"./plugin-vue_export-helper.21dcd24c.js";import{L as N,P as B,D as L,M as C}from"./PlayTwo.fe71fa6d.js";import{P as j}from"./PlayOne.e72f474c.js";var P=v("add",!1,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("rect",{x:"6",y:"6",width:"36",height:"36",rx:"3",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null),i("path",{d:"M24 16V32",stroke:e.colors[2],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null),i("path",{d:"M16 24L32 24",stroke:e.colors[2],"stroke-width":e.strokeWidth,"stroke-linecap":e.strokeLinecap,"stroke-linejoin":e.strokeLinejoin},null)])}),Q=v("more",!1,function(e){return i("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[i("circle",{cx:"12",cy:"24",r:"3",fill:e.colors[0]},null),i("circle",{cx:"24",cy:"24",r:"3",fill:e.colors[0]},null),i("circle",{cx:"36",cy:"24",r:"3",fill:e.colors[0]},null)])});const F={class:"flex-shrink-0 flex-1 flex items-center justify-between pr-5"},M={class:"items-center flex flex-1 w-10 flex-shrink-0"},$={class:"truncate",style:{"max-width":"75%"}},b={class:"hidden icon-action flex-shrink-0"},I={class:"flex items-center gap-x-1.5 text-gray-400 ml-2"},S={class:"w-9/12 truncate"},q={class:"w-9/12 truncate"},T={class:"w-20 flex-shrink-0"},V={class:"w-20 truncate"},W=x({props:{song:null,showArName:{type:Boolean},showAlName:{type:Boolean}},setup(e){const c=w(),{play:h}=f(),{id:k}=g(f());return(E,n)=>(a(),u("div",{class:m(["flex song-item items-center w-full hover-bg-main",{playing:t(k)===e.song.id}]),onDblclick:n[4]||(n[4]=o=>t(h)(e.song.id))},[s("div",F,[s("div",M,[i(l,{icon:t(N),size:"16",class:"text-gray-500 mr-1 cursor-pointer hover:text-red-400"},null,8,["icon"]),s("div",$,[s("small",null,r(e.song.name),1)]),e.song.mv>0?(a(),y(l,{key:0,class:"ml-2 text-orange-400 cursor-pointer",size:"16",icon:t(B),onClick:n[0]||(n[0]=o=>t(c).push({name:t(A).mvDetail,query:{id:e.song.mv}}))},null,8,["icon"])):d("",!0)]),s("div",b,[s("div",I,[i(l,{title:"\u64AD\u653E",icon:t(j),size:"20",class:"hover-text",onClick:n[1]||(n[1]=o=>t(h)(e.song.id))},null,8,["icon"]),i(l,{title:"\u6DFB\u52A0\u5230",icon:t(P),size:"16",class:"hover-text"},null,8,["icon"]),i(l,{title:"\u4E0B\u8F7D",icon:t(L),size:"16",class:"hover-text"},null,8,["icon"]),i(l,{title:"\u66F4\u591A\u64CD\u4F5C",icon:t(C),size:"16",class:"hover-text"},null,8,["icon"])])])]),e.showArName?(a(),u("div",{key:0,class:m(["flex-shrink-0",{"w-1/4":e.showAlName,"w-1/3":!e.showAlName}])},[s("div",S,[s("small",{class:"truncate max-w-full hover-text",onClick:n[2]||(n[2]=o=>t(c).push({name:"artistDetail",query:{id:e.song.ar.first().id}}))},r(e.song.ar.first().name),1)])],2)):d("",!0),e.showAlName?(a(),u("div",{key:1,class:m(["flex-shrink-0",{"w-1/4":e.showArName,"w-1/3":!e.showArName}])},[s("div",q,[s("small",{class:"truncate hover-text",onClick:n[3]||(n[3]=o=>t(c).push({name:"album",query:{id:e.song.al.id}}))},r(e.song.al.name),1)])],2)):d("",!0),s("div",T,[s("div",V,[s("small",null,r(t(z)(e.song.dt/1e3)),1)])])],34))}});var U=D(W,[["__scopeId","data-v-938b689c"]]);export{Q as M,U as S}; 2 | -------------------------------------------------------------------------------- /docs/assets/TopList.33e44e27.js: -------------------------------------------------------------------------------- 1 | import{c as g,t as v,F as y,o as k,g as o,h as e,i as t,I as c,G as a,u,j as _,H as l}from"./vendor.9760fa8a.js";import{_ as p}from"./CoverPlay.d639e45b.js";import{u as x}from"./music.991378cb.js";import"./el-image-viewer.a8b12883.js";import"./IconPark.402beefe.js";import"./index.373df683.js";import"./PlayOne.e72f474c.js";const C=t("div",{class:"text-xl pb-5 font-bold"},"\u5B98\u65B9\u699C",-1),w={class:"grid grid-flow-row grid-cols-2 2xl:grid-cols-4 gap-5"},b=["onClick"],B={class:"px-5 flex-1 flex-shrink-0 flex flex-col"},D={class:"text-xl font-bold"},I={class:"text-xs text-main mt-2"},L={class:"mt-2"},$={class:"flex"},F={class:"mr-1"},M={class:"flex-auto w-20 truncate"},N=t("div",{class:"text-xl py-5 font-bold"},"\u7279\u8272\u699C",-1),R={class:"grid grid-flow-row grid-cols-5 2xl:grid-cols-10 gap-5"},S=["onClick"],U={class:"text-xs mt-2"},A=g({setup(V){const{topListDetailData:i}=v(x()),{getTopListDetailData:f}=x(),m=y();k(async()=>{await f()});const r=d=>{m.push({name:"playlist",query:{id:d.id}})};return(d,j)=>(o(),e(c,null,[C,t("div",w,[(o(!0),e(c,null,a(u(i).slice(0,4),s=>(o(),e("div",{key:s.id,class:"flex bg-dc rounded-lg items-center cursor-pointer",onClick:n=>r(s)},[_(p,{name:s.name,"pic-url":s.coverImgUrl,"play-count":s.playCount,class:"w-36 flex-shrink-0","show-play-count":""},null,8,["name","pic-url","play-count"]),t("div",B,[t("div",D,l(s.name),1),t("div",I,[(o(!0),e(c,null,a(s.tracks,(n,h)=>(o(),e("div",L,[t("div",$,[t("span",F,l(h+1),1),t("div",M,l(n.first)+" - "+l(n.second),1)])]))),256))])])],8,b))),128))]),N,t("div",R,[(o(!0),e(c,null,a(u(i).slice(4),s=>(o(),e("div",{key:s.id,onClick:n=>r(s)},[_(p,{name:s.name,"pic-url":s.coverImgUrl,"play-count":s.playCount},null,8,["name","pic-url","play-count"]),t("div",U,l(s.name),1)],8,S))),128))])],64))}});export{A as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/Video.94189c43.js: -------------------------------------------------------------------------------- 1 | import{c as y,t as b,D as E,r as V,o as $,K as B,O as D,g as o,h as s,i as e,j as a,k as c,I as d,G as p,u as n,J as f,H as u}from"./vendor.9760fa8a.js";/* empty css */import{u as x}from"./video.3500c715.js";import{_ as G}from"./IconPark.402beefe.js";import{B as S}from"./index.373df683.js";import{_ as R}from"./CoverPlay.d639e45b.js";import{R as j}from"./Right.083d6647.js";import"./el-image-viewer.a8b12883.js";import"./PlayOne.e72f474c.js";const L={class:"p-5 video-page"},N={class:"flex items-center justify-between"},z={class:"button-outline px-5"},F=e("span",{class:"mr-2"},"\u5168\u90E8\u89C6\u9891",-1),H={class:"h-96 py-5 pl-5"},I={class:"text-xs gap-5 grid grid-flow-row grid-cols-5"},J=["onClick"],K={class:"text-xs flex gap-x-4"},M=["onClick"],O={class:"grid grid-flow-row grid-cols-3 gap-5 mt-5"},P={class:"text-xs mt-3 truncate"},et=y({setup(U){const{videoGroup:_}=b(x()),{getVideoGroup:h}=x(),v=E([]),i=V({page:1,id:0}),m=async()=>{v.value=await S(i.id,i.page-1)},r=l=>{console.log(l),i.id=l,i.page=1,m()};return $(()=>{h(),m()}),(l,g)=>{const k=B,C=D;return o(),s("div",L,[e("div",N,[a(C,{width:"60%",placement:"bottom-start","popper-style":"padding:0;"},{reference:c(()=>[e("button",z,[F,a(G,{icon:n(j)},null,8,["icon"])])]),default:c(()=>[e("div",null,[e("div",{class:"text-xl pt-5 pl-5 hover-text",onClick:g[0]||(g[0]=t=>r(0))},"\u5168\u90E8\u89C6\u9891"),e("div",H,[a(k,null,{default:c(()=>[e("div",I,[(o(!0),s(d,null,p(n(_),t=>(o(),s("div",{class:f(["hover-text",{"text-active":n(i).id===t.id}]),key:t.id,onClick:w=>r(t.id)},u(t.name),11,J))),128))])]),_:1})])])]),_:1}),e("div",K,[(o(!0),s(d,null,p(n(_).slice(0,8),t=>(o(),s("div",{class:f(["hover-text",{"text-active":n(i).id===t.id}]),onClick:w=>r(t.id),key:t.id},u(t.name),11,M))),128))])]),e("div",O,[(o(!0),s(d,null,p(v.value,({data:t})=>(o(),s("div",{key:t.vid},[a(R,{"pic-url":t.coverUrl,video:""},null,8,["pic-url"]),e("div",P,u(t.title),1)]))),128))])])}}});export{et as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/el-avatar.00087837.css: -------------------------------------------------------------------------------- 1 | .el-avatar{--el-avatar-text-color:#fff;--el-avatar-bg-color:#c0c4cc;--el-avatar-text-size:14px;--el-avatar-icon-size:18px;--el-avatar-border-radius:var(--el-border-radius-base);--el-avatar-size-large:56px;--el-avatar-size-default:40px;--el-avatar-size-small:24px;display:inline-flex;justify-content:center;align-items:center;box-sizing:border-box;text-align:center;overflow:hidden;color:var(--el-avatar-text-color);background:var(--el-avatar-bg-color);width:var(--el-avatar-size);height:var(--el-avatar-size);font-size:var(--el-avatar-text-size)}.el-avatar>img{display:block;height:100%}.el-avatar--circle{border-radius:50%}.el-avatar--square{border-radius:var(--el-avatar-border-radius)}.el-avatar--icon{font-size:var(--el-avatar-icon-size)}.el-avatar--small{--el-avatar-size:24px}.el-avatar--default{--el-avatar-size:40px}.el-avatar--large{--el-avatar-size:56px} 2 | -------------------------------------------------------------------------------- /docs/assets/el-image-viewer.a8b12883.js: -------------------------------------------------------------------------------- 1 | import{I as i}from"./IconPark.402beefe.js";import{j as t}from"./vendor.9760fa8a.js";var r=i("play",!0,function(e){return t("svg",{width:e.size,height:e.size,viewBox:"0 0 48 48",fill:"none"},[t("path",{d:"M24 44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4C12.9543 4 4 12.9543 4 24C4 35.0457 12.9543 44 24 44Z",fill:e.colors[1],stroke:e.colors[0],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null),t("path",{d:"M20 24V17.0718L26 20.5359L32 24L26 27.4641L20 30.9282V24Z",fill:e.colors[3],stroke:e.colors[2],"stroke-width":e.strokeWidth,"stroke-linejoin":e.strokeLinejoin},null)])});export{r as P}; 2 | -------------------------------------------------------------------------------- /docs/assets/el-image-viewer.ac997372.css: -------------------------------------------------------------------------------- 1 | .el-image__error,.el-image__inner,.el-image__placeholder{width:100%;height:100%}.el-image{position:relative;display:inline-block;overflow:hidden}.el-image__inner{vertical-align:top}.el-image__placeholder{background:var(--el-bg-color)}.el-image__error{display:flex;justify-content:center;align-items:center;font-size:14px;background:var(--el-bg-color);color:var(--el-text-color-placeholder);vertical-align:middle}.el-image__preview{cursor:pointer}.el-image-viewer__wrapper{position:fixed;top:0;right:0;bottom:0;left:0}.el-image-viewer__btn{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;border-radius:50%;opacity:.8;cursor:pointer;box-sizing:border-box;-webkit-user-select:none;user-select:none}.el-image-viewer__btn .el-icon{font-size:inherit;cursor:pointer}.el-image-viewer__close{top:40px;right:40px;width:40px;height:40px;font-size:40px}.el-image-viewer__canvas{width:100%;height:100%;display:flex;justify-content:center;align-items:center;-webkit-user-select:none;user-select:none}.el-image-viewer__actions{left:50%;bottom:30px;transform:translate(-50%);width:282px;height:44px;padding:0 23px;background-color:var(--el-text-color-regular);border-color:#fff;border-radius:22px}.el-image-viewer__actions__inner{width:100%;height:100%;text-align:justify;cursor:default;font-size:23px;color:#fff;display:flex;align-items:center;justify-content:space-around}.el-image-viewer__prev{top:50%;transform:translateY(-50%);left:40px;width:44px;height:44px;font-size:24px;color:#fff;background-color:var(--el-text-color-regular);border-color:#fff}.el-image-viewer__next{top:50%;transform:translateY(-50%);right:40px;text-indent:2px;width:44px;height:44px;font-size:24px;color:#fff;background-color:var(--el-text-color-regular);border-color:#fff}.el-image-viewer__close{width:44px;height:44px;font-size:24px;color:#fff;background-color:var(--el-text-color-regular);border-color:#fff}.el-image-viewer__mask{position:absolute;width:100%;height:100%;top:0;left:0;opacity:.5;background:#000}.viewer-fade-enter-active{animation:viewer-fade-in var(--el-transition-duration)}.viewer-fade-leave-active{animation:viewer-fade-out var(--el-transition-duration)}@keyframes viewer-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@keyframes viewer-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}} 2 | -------------------------------------------------------------------------------- /docs/assets/el-popper.24515e58.css: -------------------------------------------------------------------------------- 1 | .el-scrollbar{--el-scrollbar-opacity:.3;--el-scrollbar-bg-color:var(--el-text-color-secondary);--el-scrollbar-hover-opacity:.5;--el-scrollbar-hover-bg-color:var(--el-text-color-secondary);overflow:hidden;position:relative;height:100%}.el-scrollbar__wrap{overflow:auto;height:100%}.el-scrollbar__wrap--hidden-default{scrollbar-width:none}.el-scrollbar__wrap--hidden-default::-webkit-scrollbar{display:none}.el-scrollbar__thumb{position:relative;display:block;width:0;height:0;cursor:pointer;border-radius:inherit;background-color:var(--el-scrollbar-bg-color,var(--el-text-color-secondary));transition:var(--el-transition-duration) background-color;opacity:var(--el-scrollbar-opacity,.3)}.el-scrollbar__thumb:hover{background-color:var(--el-scrollbar-hover-bg-color,var(--el-text-color-secondary));opacity:var(--el-scrollbar-hover-opacity,.5)}.el-scrollbar__bar{position:absolute;right:2px;bottom:2px;z-index:1;border-radius:4px}.el-scrollbar__bar.is-vertical{width:6px;top:2px}.el-scrollbar__bar.is-vertical>div{width:100%}.el-scrollbar__bar.is-horizontal{height:6px;left:2px}.el-scrollbar__bar.is-horizontal>div{height:100%}.el-scrollbar-fade-enter-active{transition:opacity .34s ease-out}.el-scrollbar-fade-leave-active{transition:opacity .12s ease-out}.el-scrollbar-fade-enter-from,.el-scrollbar-fade-leave-active{opacity:0}.el-popover{--el-popover-bg-color:var(--el-color-white);--el-popover-font-size:var(--el-font-size-base);--el-popover-border-color:var(--el-border-color-lighter);--el-popover-padding:12px;--el-popover-padding-large:18px 20px;--el-popover-title-font-size:16px;--el-popover-title-text-color:var(--el-text-color-primary);--el-popover-border-radius:4px}.el-popover.el-popper{background:var(--el-popover-bg-color);min-width:150px;border-radius:var(--el-popover-border-radius);border:1px solid var(--el-popover-border-color);padding:var(--el-popover-padding);z-index:var(--el-index-popper);color:var(--el-text-color-regular);line-height:1.4;text-align:justify;font-size:var(--el-popover-font-size);box-shadow:var(--el-box-shadow-light);word-break:break-all}.el-popover.el-popper--plain{padding:var(--el-popover-padding-large)}.el-popover__title{color:var(--el-popover-title-text-color);font-size:var(--el-popover-title-font-size);line-height:1;margin-bottom:12px}.el-popover__reference:focus:hover,.el-popover__reference:focus:not(.focusing){outline-width:0}.el-popover.el-popper:focus,.el-popover.el-popper:focus:active{outline-width:0}.el-popper{--el-popper-border-radius:var(--el-popover-border-radius, 4px);position:absolute;border-radius:var(--el-popper-border-radius);padding:5px 11px;z-index:2000;font-size:12px;line-height:20px;min-width:10px;word-wrap:break-word;visibility:visible}.el-popper.is-dark{color:var(--el-color-white);background:var(--el-text-color-primary);border:1px solid var(--el-text-color-primary)}.el-popper.is-dark .el-popper__arrow:before{border:1px solid var(--el-text-color-primary);background:var(--el-text-color-primary);right:0}.el-popper.is-light{background:var(--el-color-white);border:1px solid var(--el-border-color-light)}.el-popper.is-light .el-popper__arrow:before{border:1px solid var(--el-border-color-light);background:var(--el-color-white);right:0}.el-popper.is-pure{padding:0}.el-popper__arrow{position:absolute;width:10px;height:10px;z-index:-1}.el-popper__arrow:before{position:absolute;width:10px;height:10px;z-index:-1;content:" ";transform:rotate(45deg);background:var(--el-text-color-primary);box-sizing:border-box}.el-popper[data-popper-placement^=top]>.el-popper__arrow{bottom:-5px}.el-popper[data-popper-placement^=top]>.el-popper__arrow:before{border-bottom-right-radius:2px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow{top:-5px}.el-popper[data-popper-placement^=bottom]>.el-popper__arrow:before{border-top-left-radius:2px}.el-popper[data-popper-placement^=left]>.el-popper__arrow{right:-5px}.el-popper[data-popper-placement^=left]>.el-popper__arrow:before{border-top-right-radius:2px}.el-popper[data-popper-placement^=right]>.el-popper__arrow{left:-5px}.el-popper[data-popper-placement^=right]>.el-popper__arrow:before{border-bottom-left-radius:2px}.el-popper[data-popper-placement^=top] .el-popper__arrow:before{border-top-color:transparent!important;border-left-color:transparent!important}.el-popper[data-popper-placement^=bottom] .el-popper__arrow:before{border-bottom-color:transparent!important;border-right-color:transparent!important}.el-popper[data-popper-placement^=left] .el-popper__arrow:before{border-left-color:transparent!important;border-bottom-color:transparent!important}.el-popper[data-popper-placement^=right] .el-popper__arrow:before{border-right-color:transparent!important;border-top-color:transparent!important} 2 | -------------------------------------------------------------------------------- /docs/assets/logo.601c4b06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/docs/assets/logo.601c4b06.png -------------------------------------------------------------------------------- /docs/assets/music.991378cb.js: -------------------------------------------------------------------------------- 1 | import{d as i,D as s}from"./vendor.9760fa8a.js";import{h as r,i as l,j as u}from"./index.373df683.js";const p=i("music",()=>{const e=s([]),n=async()=>{e.value.length||(e.value=await r())},a=s([]),o=async()=>{a.value.length||(a.value=await l())},t=s([]);return{topListDetailData:e,getTopListDetailData:n,personalized:a,getPersonalized:o,personalizedNewSong:t,getPersonalizedNewSong:async()=>{t.value.length||(t.value=await u())}}});export{p as u}; 2 | -------------------------------------------------------------------------------- /docs/assets/mvDetail.aa81123a.js: -------------------------------------------------------------------------------- 1 | import{c as n,C as u}from"./index.373df683.js";import{c as r,C as i,D as d,o as _,b as m,g as p,h as v,i as e,l as f}from"./vendor.9760fa8a.js";const h={key:0,class:"p-5"},x={class:"flex gap-x-5"},y={class:"flex-1"},B=["src"],k=e("div",{class:"hidden w-80 flex-shrink-0 xl:block"},[e("div",null,"\u5927\u5BB6\u90FD\u5728\u770B")],-1),U=r({setup(C){const t=i(),a=Number(t.query.id),{setPlay:c,setPause:l}=n(),s=d();return _(async()=>{s.value=await u(a),l()}),m(()=>{setTimeout(()=>{c()},1e3)}),(b,w)=>{var o;return s.value?(p(),v("div",h,[e("div",x,[e("div",y,[e("video",{class:"aspect-video w-full",src:(o=s.value)==null?void 0:o.url,autoplay:"",controls:""},null,8,B)]),k])])):f("",!0)}}});export{U as default}; 2 | -------------------------------------------------------------------------------- /docs/assets/plugin-vue_export-helper.21dcd24c.js: -------------------------------------------------------------------------------- 1 | var a=(t,o)=>{const r=t.__vccOpts||t;for(const[e,_]of o)r[e]=_;return r};export{a as _}; 2 | -------------------------------------------------------------------------------- /docs/assets/video.3500c715.js: -------------------------------------------------------------------------------- 1 | import{d as u,D as e}from"./vendor.9760fa8a.js";import{k as l,l as d,m as v,n as c}from"./index.373df683.js";const f=u("video",()=>{const t=e([]),a=async()=>{t.value.length||(t.value=await l())},n=e([]),s=async()=>{n.value.length||(n.value=await d(4))},o=e([]),r=async()=>{o.value.length||(o.value=await v())},i=e([]);return{videoTimelineRecommend:t,getVideoTimelineRecommend:a,personalizedPrivateContent:n,getPersonalizedPrivateContent:s,personalizedMv:o,getPersonalizedMv:r,videoGroup:i,getVideoGroup:async()=>{i.value.length||(i.value=await c())}}});export{f as u}; 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/docs/favicon.ico -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VUE3-MUSIC 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | VUE3-MUSIC 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-ts-music", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "preview": "vite preview --port 5050", 9 | "typecheck": "vue-tsc --noEmit" 10 | }, 11 | "dependencies": { 12 | "@icon-park/vue-next": "^1.3.6", 13 | "axios": "^0.26.0", 14 | "dayjs": "^1.10.8", 15 | "element-plus": "^2.0.4", 16 | "lodash": "^4.17.21", 17 | "pinia": "^2.0.11", 18 | "swiper": "^8.0.6", 19 | "vue": "^3.2.31", 20 | "vue-router": "^4.0.12" 21 | }, 22 | "devDependencies": { 23 | "@types/lodash": "^4.14.179", 24 | "@types/lodash-es": "*", 25 | "@types/node": "^16.11.25", 26 | "@vitejs/plugin-vue": "^2.2.2", 27 | "@vitejs/plugin-vue-jsx": "^1.3.7", 28 | "@vue/tsconfig": "^0.1.3", 29 | "@vueuse/components": "^7.7.0", 30 | "@vueuse/core": "^7.7.0", 31 | "autoprefixer": "^10.4.2", 32 | "postcss": "^8.4.7", 33 | "sass": "^1.49.9", 34 | "tailwindcss": "^3.0.23", 35 | "typescript": "~4.5.5", 36 | "unplugin-auto-import": "^0.6.1", 37 | "unplugin-vue-components": "^0.17.21", 38 | "vite": "^2.8.4", 39 | "vue-tsc": "^0.31.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | 20 | -------------------------------------------------------------------------------- /src/assets/base.scss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --el-color-primary: #34d399 !important; 7 | --el-color-primary-light-1: #065f46 !important; 8 | --el-color-primary-light-2: #047857 !important; 9 | --el-color-primary-light-3: #059669 !important; 10 | --el-color-primary-light-4: #10b981 !important; 11 | --el-color-primary-light-5: #34d399 !important; 12 | --el-color-primary-light-6: #6ee7b7 !important; 13 | --el-color-primary-light-7: #a7f3d0 !important; 14 | --el-color-primary-light-8: #d1fae5 !important; 15 | --el-color-primary-light-9: #ecfdf5 !important; 16 | --el-color-primary-dark-2: #337ecc !important; 17 | --el-text-color-primary: text-slate-700; 18 | } 19 | 20 | @media (prefers-color-scheme: dark) { 21 | :root { 22 | --el-color-primary: #059669 !important; 23 | 24 | --el-color-primary-light-9: #57534e !important; 25 | 26 | --el-border-color-base: #57534e !important; 27 | --el-text-color-regular: #e2e8f0 !important; 28 | --el-text-color-primary: text-slate-200 !important; 29 | --el-bg-color: #474648 !important; 30 | --el-color-white: #1e1e1f !important; 31 | --el-border-color-lighter: #474648 !important; 32 | --el-border-color-light: #474648 !important; 33 | 34 | --el-popover-bg-color: #292524 !important; 35 | } 36 | } 37 | 38 | html, body { 39 | font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 40 | 'Microsoft YaHei', '微软雅黑', Arial, sans-serif; 41 | @apply text-slate-700 bg-white; 42 | @apply dark:bg-[#1e1e1f] dark:text-slate-100; 43 | } 44 | 45 | .hover-text { 46 | @apply hover:text-emerald-400 cursor-pointer; 47 | } 48 | 49 | .badge { 50 | .el-badge__content.is-fixed { 51 | @apply scale-75 bg-white text-slate-500 text-xs bg-opacity-50 border-0 left-2 -top-2; 52 | } 53 | } 54 | 55 | .el-popover.el-popper { 56 | min-width: auto; 57 | } 58 | 59 | .el-tabs__nav-wrap::after { 60 | height: 0 !important; 61 | } 62 | 63 | .el-tabs__header { 64 | @apply m-0; 65 | } 66 | -------------------------------------------------------------------------------- /src/assets/img/OpticalDisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/src/assets/img/OpticalDisk.png -------------------------------------------------------------------------------- /src/assets/img/index.ts: -------------------------------------------------------------------------------- 1 | import Logo from './logo.png' 2 | import OpticalDisk from './OpticalDisk.png' 3 | 4 | export { 5 | Logo, 6 | OpticalDisk 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmallRuralDog/vue3-music/578f4b34889fff4f6c5a6a24662be6fd24255ac3/src/assets/img/logo.png -------------------------------------------------------------------------------- /src/assets/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/theme.scss: -------------------------------------------------------------------------------- 1 | .bg-main { 2 | @apply bg-gray-50; 3 | @apply dark:bg-[#171718]; 4 | } 5 | 6 | .hover-bg-main { 7 | @apply hover:bg-gray-200; 8 | @apply dark:hover:bg-stone-700; 9 | } 10 | 11 | .bg-dc { 12 | @apply bg-gray-100; 13 | @apply dark:bg-stone-800; 14 | } 15 | 16 | .bg-view { 17 | @apply bg-white; 18 | @apply dark:bg-[#1e1e1f]; 19 | } 20 | 21 | .hover-bg-view { 22 | @apply hover:bg-stone-100; 23 | @apply dark:hover:bg-stone-800; 24 | } 25 | 26 | .bg-active { 27 | @apply bg-emerald-400 text-white; 28 | } 29 | .text-active{ 30 | @apply text-emerald-400; 31 | } 32 | 33 | .text-main { 34 | @apply text-slate-700; 35 | @apply dark:text-slate-200; 36 | } 37 | 38 | .text-dc { 39 | @apply text-slate-400; 40 | @apply dark:text-slate-400; 41 | } 42 | 43 | .button { 44 | @apply flex bg-emerald-600 rounded-full justify-center items-center h-8 text-sm transition-all; 45 | @apply text-white; 46 | @apply hover:bg-emerald-700; 47 | } 48 | 49 | .button-outline { 50 | @apply flex rounded-full justify-center items-center h-8 text-sm transition-all border; 51 | @apply hover:border-emerald-500 hover:text-emerald-500; 52 | } 53 | 54 | .button-sm{ 55 | @apply h-7 text-xs; 56 | } 57 | 58 | .button-dc{ 59 | @apply flex bg-[#e6e6e6] rounded justify-center items-center h-8 text-sm transition-all cursor-pointer; 60 | @apply dark:bg-[#303031] dark:text-white; 61 | @apply hover:text-emerald-500; 62 | } 63 | -------------------------------------------------------------------------------- /src/components/common/Banner.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 31 | 32 | 45 | -------------------------------------------------------------------------------- /src/components/common/CoverPlay.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 34 | 35 | 61 | -------------------------------------------------------------------------------- /src/components/common/IconPark.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 20 | -------------------------------------------------------------------------------- /src/components/common/MoreText.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /src/components/common/SongListItem.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 67 | 68 | 82 | -------------------------------------------------------------------------------- /src/components/common/Title.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /src/components/layout/footer/Footer.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 25 | 28 | -------------------------------------------------------------------------------- /src/components/layout/footer/PlayerAction.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /src/components/layout/footer/PlayerController.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 30 | 31 | 32 | 37 | -------------------------------------------------------------------------------- /src/components/layout/footer/PlayerSlider.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 56 | -------------------------------------------------------------------------------- /src/components/layout/footer/PlayerSong.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 35 | 36 | 46 | -------------------------------------------------------------------------------- /src/components/layout/footer/PlayerVolumeSlider.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 29 | -------------------------------------------------------------------------------- /src/components/layout/header/Header.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 44 | 57 | -------------------------------------------------------------------------------- /src/components/layout/header/SearchPop.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 64 | 67 | -------------------------------------------------------------------------------- /src/components/layout/header/SearchSuggest.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 64 | 67 | -------------------------------------------------------------------------------- /src/components/layout/header/UserInfo.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 43 | 44 | 47 | -------------------------------------------------------------------------------- /src/components/layout/menu/Menu.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/components/layout/menu/MenuList.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 28 | 29 | 30 | 43 | -------------------------------------------------------------------------------- /src/components/layout/menu/useMenu.ts: -------------------------------------------------------------------------------- 1 | import {ref, watch} from "vue"; 2 | import {Planet, Music, VideoOne, Fm, Like, Computer, DownloadThree, PlayTwo} from '@icon-park/vue-next' 3 | import {useRoute, useRouter} from "vue-router"; 4 | 5 | interface IMenu { 6 | name: string; 7 | key: string; 8 | icon: any; 9 | theme: 'outline' | 'filled' | 'two-tone' | 'multi-color', 10 | } 11 | 12 | interface IMenus { 13 | name: string, 14 | menus: IMenu[], 15 | } 16 | 17 | export function useMenu() { 18 | const menus: IMenus[] = [ 19 | { 20 | name: "在线音乐", 21 | menus: [ 22 | { 23 | name: "推荐", 24 | key: "discover", 25 | icon: Planet, 26 | theme: 'outline', 27 | }, 28 | { 29 | name: "音乐馆", 30 | key: "music", 31 | icon: Music, 32 | theme: 'outline', 33 | }, 34 | { 35 | name: "视频", 36 | key: "video", 37 | icon: VideoOne, 38 | theme: 'outline', 39 | }, 40 | { 41 | name: "电台", 42 | key: "dj", 43 | icon: Fm, 44 | theme: 'outline', 45 | }, 46 | ] 47 | }, 48 | { 49 | name: "我的音乐", 50 | menus: [ 51 | { 52 | name: "我喜欢", 53 | key: "love", 54 | icon: Like, 55 | theme: 'outline', 56 | }, 57 | { 58 | name: "本地歌曲", 59 | key: "local", 60 | icon: Computer, 61 | theme: 'outline', 62 | }, 63 | { 64 | name: "下载歌曲", 65 | key: "download", 66 | icon: DownloadThree, 67 | theme: 'outline', 68 | }, 69 | { 70 | name: "最近播放", 71 | key: "recently", 72 | icon: PlayTwo, 73 | theme: 'outline' 74 | }, 75 | ] 76 | } 77 | ]; 78 | 79 | const route = useRoute(); 80 | 81 | const currentKey = ref(route.meta.menu); 82 | 83 | const router = useRouter(); 84 | 85 | watch( 86 | () => route.meta.menu, 87 | (menu) => { 88 | currentKey.value = menu; 89 | } 90 | ); 91 | 92 | const click = async (menu: IMenu) => { 93 | await router.push({name: menu.key, replace: true,}) 94 | }; 95 | 96 | return { 97 | menus, 98 | click, 99 | currentKey, 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /src/components/layout/playList/PlayList.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 35 | 42 | -------------------------------------------------------------------------------- /src/components/layout/playList/PlayListSongItem.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 36 | 41 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | import '@/assets/base.scss' 4 | import '@/assets/theme.scss' 5 | import App from './App.vue' 6 | import router from './router' 7 | import "@/utils/extend" 8 | 9 | const app = createApp(App) 10 | 11 | app.use(createPinia()) 12 | app.use(router) 13 | 14 | app.mount('#app') 15 | -------------------------------------------------------------------------------- /src/models/album.ts: -------------------------------------------------------------------------------- 1 | import type {Artist} from "@/models/artist"; 2 | 3 | export interface Album { 4 | songs: any[]; 5 | paid: boolean; 6 | onSale: boolean; 7 | mark: number; 8 | blurPicUrl: string; 9 | companyId: number; 10 | alias: string[]; 11 | artists: Artist[]; 12 | copyrightId: number; 13 | picId: number; 14 | artist: Artist; 15 | publishTime: number; 16 | company: string; 17 | briefDesc: string; 18 | picUrl: string; 19 | commentThreadId: string; 20 | pic: number; 21 | tags: string; 22 | description: string; 23 | status: number; 24 | subType: string; 25 | name: string; 26 | id: number; 27 | type: string; 28 | size: number; 29 | picId_str: string; 30 | } 31 | -------------------------------------------------------------------------------- /src/models/artist.ts: -------------------------------------------------------------------------------- 1 | export interface Artist { 2 | albumSize: number; 3 | alias: string[]; 4 | briefDesc: string; 5 | fansCount: number; 6 | followed: boolean; 7 | id: number; 8 | img1v1Id: number; 9 | img1v1Id_str: string; 10 | img1v1Url: string; 11 | musicSize: number; 12 | name: string; 13 | picId: number; 14 | picId_str: string; 15 | picUrl: string; 16 | topicPerson: number; 17 | trans: string; 18 | } 19 | 20 | export interface Mv { 21 | id: number; 22 | name: string; 23 | status: number; 24 | artistName: string; 25 | artist: Artist; 26 | imgurl16v9: string; 27 | imgurl: string; 28 | duration: number; 29 | playCount: number; 30 | publishTime: string; 31 | subed: boolean; 32 | } 33 | -------------------------------------------------------------------------------- /src/models/artist_detail.ts: -------------------------------------------------------------------------------- 1 | export interface ArtistDetail { 2 | videoCount: number; 3 | identify: ArtistDetailIdentify; 4 | artist: ArtistDetailArtist; 5 | blacklist: boolean; 6 | preferShow: number; 7 | showPriMsg: boolean; 8 | secondaryExpertIdentiy: ArtistDetailSecondaryExpertIdentiy[]; 9 | } 10 | export interface ArtistDetailIdentify { 11 | imageUrl?: any; 12 | imageDesc: string; 13 | actionUrl: string; 14 | } 15 | export interface ArtistDetailArtistRank { 16 | rank: number; 17 | type: number; 18 | } 19 | export interface ArtistDetailArtist { 20 | id: number; 21 | cover: string; 22 | name: string; 23 | transNames: string[]; 24 | identities: string[]; 25 | identifyTag?: any; 26 | briefDesc: string; 27 | rank: ArtistDetailArtistRank; 28 | albumSize: number; 29 | musicSize: number; 30 | mvSize: number; 31 | } 32 | export interface ArtistDetailSecondaryExpertIdentiy { 33 | expertIdentiyId: number; 34 | expertIdentiyName: string; 35 | expertIdentiyCount: number; 36 | } 37 | 38 | 39 | export interface ArtistDesc { 40 | introduction: ArtistDescIntroduction[]; 41 | briefDesc: string; 42 | count: number; 43 | topicData: ArtistDescTopicData[]; 44 | } 45 | export interface ArtistDescIntroduction { 46 | ti: string; 47 | txt: string; 48 | } 49 | export interface ArtistDescTopicDataTopicContent { 50 | type: number; 51 | id: number; 52 | content: string; 53 | } 54 | export interface ArtistDescTopicDataTopic { 55 | id: number; 56 | addTime: number; 57 | mainTitle: string; 58 | title: string; 59 | content: ArtistDescTopicDataTopicContent[]; 60 | userId: number; 61 | cover: number; 62 | headPic: number; 63 | shareContent: string; 64 | wxTitle: string; 65 | showComment: boolean; 66 | status: number; 67 | seriesId: number; 68 | pubTime: number; 69 | readCount: number; 70 | tags: string[]; 71 | pubImmidiatly: boolean; 72 | auditor: string; 73 | auditTime: number; 74 | auditStatus: number; 75 | startText: string; 76 | delReason: string; 77 | showRelated: boolean; 78 | fromBackend: boolean; 79 | rectanglePic: number; 80 | updateTime: number; 81 | reward: boolean; 82 | summary: string; 83 | memo?: any; 84 | adInfo: string; 85 | categoryId: number; 86 | hotScore: number; 87 | recomdTitle: string; 88 | recomdContent: string; 89 | number: number; 90 | } 91 | export interface ArtistDescTopicDataCreatorExperts { 92 | 1: string; 93 | } 94 | export interface ArtistDescTopicDataCreator { 95 | userId: number; 96 | userType: number; 97 | nickname: string; 98 | avatarImgId: number; 99 | avatarUrl: string; 100 | backgroundImgId: number; 101 | backgroundUrl: string; 102 | signature: string; 103 | createTime: number; 104 | userName: string; 105 | accountType: number; 106 | shortUserName: string; 107 | birthday: number; 108 | authority: number; 109 | gender: number; 110 | accountStatus: number; 111 | province: number; 112 | city: number; 113 | authStatus: number; 114 | description?: any; 115 | detailDescription?: any; 116 | defaultAvatar: boolean; 117 | expertTags?: any; 118 | experts: ArtistDescTopicDataCreatorExperts; 119 | djStatus: number; 120 | locationStatus: number; 121 | vipType: number; 122 | followed: boolean; 123 | mutual: boolean; 124 | authenticated: boolean; 125 | lastLoginTime: number; 126 | lastLoginIP: string; 127 | remarkName?: any; 128 | viptypeVersion: number; 129 | authenticationTypes: number; 130 | avatarDetail?: any; 131 | anchor: boolean; 132 | } 133 | export interface ArtistDescTopicData { 134 | topic: ArtistDescTopicDataTopic; 135 | creator: ArtistDescTopicDataCreator; 136 | shareCount: number; 137 | commentCount: number; 138 | likedCount: number; 139 | liked: boolean; 140 | rewardCount: number; 141 | rewardMoney: number; 142 | relatedResource?: any; 143 | rectanglePicUrl: string; 144 | coverUrl: string; 145 | categoryId: number; 146 | categoryName: string; 147 | reward: boolean; 148 | shareContent: string; 149 | wxTitle: string; 150 | addTime: number; 151 | seriesId: number; 152 | showComment: boolean; 153 | showRelated: boolean; 154 | memo?: any; 155 | summary: string; 156 | recmdTitle: string; 157 | recmdContent: string; 158 | commentThreadId: string; 159 | mainTitle: string; 160 | tags: string[]; 161 | readCount: number; 162 | url: string; 163 | title: string; 164 | id: number; 165 | number: number; 166 | } 167 | -------------------------------------------------------------------------------- /src/models/banner.ts: -------------------------------------------------------------------------------- 1 | export interface Banner { 2 | pic:string 3 | targetId:number 4 | targetType:number 5 | typeTitle:string 6 | bannerId:number 7 | } 8 | -------------------------------------------------------------------------------- /src/models/dj.ts: -------------------------------------------------------------------------------- 1 | export interface DJBanner { 2 | targetId: number; 3 | targetType: number; 4 | pic: string; 5 | url: string; 6 | typeTitle: string; 7 | exclusive: boolean; 8 | } 9 | -------------------------------------------------------------------------------- /src/models/mv.ts: -------------------------------------------------------------------------------- 1 | export interface MvUrl { 2 | id: number; 3 | url: string; 4 | r: number; 5 | size: number; 6 | md5: string; 7 | code: number; 8 | expi: number; 9 | fee: number; 10 | mvFee: number; 11 | st: number; 12 | promotionVo?: any; 13 | msg: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/models/personalized.ts: -------------------------------------------------------------------------------- 1 | export interface Personalized { 2 | id: number; 3 | type: number; 4 | name: string; 5 | copywriter: string; 6 | picUrl: string; 7 | canDislike: boolean; 8 | trackNumberUpdateTime: number; 9 | playCount: number; 10 | trackCount: number; 11 | highQuality: boolean; 12 | alg: string; 13 | } 14 | 15 | 16 | export interface PersonalizedNewSong { 17 | id: number; 18 | type: number; 19 | name: string; 20 | picUrl: string; 21 | canDislike: boolean; 22 | song: PNSSong; 23 | alg: string; 24 | } 25 | 26 | export interface PNSSongArtists { 27 | name: string; 28 | id: number; 29 | picId: number; 30 | img1v1Id: number; 31 | briefDesc: string; 32 | picUrl: string; 33 | img1v1Url: string; 34 | albumSize: number; 35 | alias: any[]; 36 | trans: string; 37 | musicSize: number; 38 | topicPerson: number; 39 | } 40 | 41 | export interface PNSSongAlbumArtist { 42 | name: string; 43 | id: number; 44 | picId: number; 45 | img1v1Id: number; 46 | briefDesc: string; 47 | picUrl: string; 48 | img1v1Url: string; 49 | albumSize: number; 50 | alias: any[]; 51 | trans: string; 52 | musicSize: number; 53 | topicPerson: number; 54 | } 55 | 56 | export interface PNSSongAlbumArtists { 57 | name: string; 58 | id: number; 59 | picId: number; 60 | img1v1Id: number; 61 | briefDesc: string; 62 | picUrl: string; 63 | img1v1Url: string; 64 | albumSize: number; 65 | alias: any[]; 66 | trans: string; 67 | musicSize: number; 68 | topicPerson: number; 69 | } 70 | 71 | export interface PNSSongAlbum { 72 | name: string; 73 | id: number; 74 | type: string; 75 | size: number; 76 | picId: number; 77 | blurPicUrl: string; 78 | companyId: number; 79 | pic: number; 80 | picUrl: string; 81 | publishTime: number; 82 | description: string; 83 | tags: string; 84 | company: string; 85 | briefDesc: string; 86 | artist: PNSSongAlbumArtist; 87 | songs: any[]; 88 | alias: any[]; 89 | status: number; 90 | copyrightId: number; 91 | commentThreadId: string; 92 | artists: PNSSongAlbumArtists[]; 93 | subType: string; 94 | onSale: boolean; 95 | mark: number; 96 | picId_str: string; 97 | } 98 | 99 | export interface PNSSongBMusic { 100 | id: number; 101 | size: number; 102 | extension: string; 103 | sr: number; 104 | dfsId: number; 105 | bitrate: number; 106 | playTime: number; 107 | volumeDelta: number; 108 | } 109 | 110 | export interface PNSSongHMusic { 111 | id: number; 112 | size: number; 113 | extension: string; 114 | sr: number; 115 | dfsId: number; 116 | bitrate: number; 117 | playTime: number; 118 | volumeDelta: number; 119 | } 120 | 121 | export interface PNSSongMMusic { 122 | id: number; 123 | size: number; 124 | extension: string; 125 | sr: number; 126 | dfsId: number; 127 | bitrate: number; 128 | playTime: number; 129 | volumeDelta: number; 130 | } 131 | 132 | export interface PNSSongLMusic { 133 | id: number; 134 | size: number; 135 | extension: string; 136 | sr: number; 137 | dfsId: number; 138 | bitrate: number; 139 | playTime: number; 140 | volumeDelta: number; 141 | } 142 | 143 | export interface PNSSongPrivilegeFreeTrialPrivilege { 144 | resConsumable: boolean; 145 | userConsumable: boolean; 146 | } 147 | 148 | export interface PNSSongPrivilegeChargeInfoList { 149 | rate: number; 150 | chargeType: number; 151 | } 152 | 153 | export interface PNSSongPrivilege { 154 | id: number; 155 | fee: number; 156 | payed: number; 157 | st: number; 158 | pl: number; 159 | dl: number; 160 | sp: number; 161 | cp: number; 162 | subp: number; 163 | cs: boolean; 164 | maxbr: number; 165 | fl: number; 166 | toast: boolean; 167 | flag: number; 168 | preSell: boolean; 169 | playMaxbr: number; 170 | downloadMaxbr: number; 171 | freeTrialPrivilege: PNSSongPrivilegeFreeTrialPrivilege; 172 | chargeInfoList: PNSSongPrivilegeChargeInfoList[]; 173 | } 174 | 175 | export interface PNSSong { 176 | name: string; 177 | id: number; 178 | position: number; 179 | alias: any[]; 180 | status: number; 181 | fee: number; 182 | copyrightId: number; 183 | disc: string; 184 | no: number; 185 | artists: PNSSongArtists[]; 186 | album: PNSSongAlbum; 187 | starred: boolean; 188 | popularity: number; 189 | score: number; 190 | starredNum: number; 191 | duration: number; 192 | playedNum: number; 193 | dayPlays: number; 194 | hearTime: number; 195 | ringtone: string; 196 | copyFrom: string; 197 | commentThreadId: string; 198 | ftype: number; 199 | rtUrls: any[]; 200 | copyright: number; 201 | mark: number; 202 | originCoverType: number; 203 | single: number; 204 | mvid: number; 205 | bMusic: PNSSongBMusic; 206 | rtype: number; 207 | hMusic: PNSSongHMusic; 208 | mMusic: PNSSongMMusic; 209 | lMusic: PNSSongLMusic; 210 | exclusive: boolean; 211 | privilege: PNSSongPrivilege; 212 | } 213 | 214 | export interface PersonalizedMv { 215 | id: number; 216 | type: number; 217 | name: string; 218 | copywriter: string; 219 | picUrl: string; 220 | canDislike: boolean; 221 | trackNumberUpdateTime?: any; 222 | duration: number; 223 | playCount: number; 224 | subed: boolean; 225 | artists: { 226 | id: number; 227 | name: string; 228 | }[]; 229 | artistName: string; 230 | artistId: number; 231 | alg: string; 232 | } 233 | export interface DjProgram { 234 | id: number; 235 | type: number; 236 | name: string; 237 | copywriter: string; 238 | picUrl: string; 239 | canDislike: boolean; 240 | trackNumberUpdateTime?: any; 241 | } 242 | -------------------------------------------------------------------------------- /src/models/playlist.ts: -------------------------------------------------------------------------------- 1 | export interface PlayListDetail { 2 | id: number; 3 | name: string; 4 | coverImgId: number; 5 | coverImgUrl: string; 6 | coverImgId_str: string; 7 | adType: number; 8 | userId: number; 9 | createTime: number; 10 | status: number; 11 | opRecommend: boolean; 12 | highQuality: boolean; 13 | newImported: boolean; 14 | updateTime: number; 15 | trackCount: number; 16 | specialType: number; 17 | privacy: number; 18 | trackUpdateTime: number; 19 | commentThreadId: string; 20 | playCount: number; 21 | trackNumberUpdateTime: number; 22 | subscribedCount: number; 23 | cloudTrackCount: number; 24 | ordered: boolean; 25 | description: string; 26 | tags: string[]; 27 | updateFrequency?: any; 28 | backgroundCoverId: number; 29 | backgroundCoverUrl?: any; 30 | titleImage: number; 31 | titleImageUrl?: any; 32 | englishTitle?: any; 33 | officialPlaylistType?: any; 34 | subscribers: PlayListDetailSubscribers[]; 35 | subscribed: boolean; 36 | creator: PlayListDetailCreator; 37 | tracks: PlayListDetailTracks[]; 38 | videoIds?: any; 39 | videos?: any; 40 | trackIds: PlayListDetailTrackIds[]; 41 | shareCount: number; 42 | commentCount: number; 43 | remixVideo?: any; 44 | sharedUsers?: any; 45 | historySharedUsers?: any; 46 | } 47 | 48 | export interface PlayListDetailSubscribers { 49 | defaultAvatar: boolean; 50 | province: number; 51 | authStatus: number; 52 | followed: boolean; 53 | avatarUrl: string; 54 | accountStatus: number; 55 | gender: number; 56 | city: number; 57 | birthday: number; 58 | userId: number; 59 | userType: number; 60 | nickname: string; 61 | signature: string; 62 | description: string; 63 | detailDescription: string; 64 | avatarImgId: number; 65 | backgroundImgId: number; 66 | backgroundUrl: string; 67 | authority: number; 68 | mutual: boolean; 69 | expertTags?: any; 70 | experts?: any; 71 | djStatus: number; 72 | vipType: number; 73 | remarkName?: any; 74 | authenticationTypes: number; 75 | avatarDetail?: any; 76 | avatarImgIdStr: string; 77 | backgroundImgIdStr: string; 78 | anchor: boolean; 79 | avatarImgId_str: string; 80 | } 81 | 82 | export interface PlayListDetailCreatorAvatarDetail { 83 | userType: number; 84 | identityLevel: number; 85 | identityIconUrl: string; 86 | } 87 | 88 | export interface PlayListDetailCreator { 89 | defaultAvatar: boolean; 90 | province: number; 91 | authStatus: number; 92 | followed: boolean; 93 | avatarUrl: string; 94 | accountStatus: number; 95 | gender: number; 96 | city: number; 97 | birthday: number; 98 | userId: number; 99 | userType: number; 100 | nickname: string; 101 | signature: string; 102 | description: string; 103 | detailDescription: string; 104 | avatarImgId: number; 105 | backgroundImgId: number; 106 | backgroundUrl: string; 107 | authority: number; 108 | mutual: boolean; 109 | expertTags?: any; 110 | experts?: any; 111 | djStatus: number; 112 | vipType: number; 113 | remarkName?: any; 114 | authenticationTypes: number; 115 | avatarDetail: PlayListDetailCreatorAvatarDetail; 116 | avatarImgIdStr: string; 117 | backgroundImgIdStr: string; 118 | anchor: boolean; 119 | avatarImgId_str: string; 120 | } 121 | 122 | export interface PlayListDetailTracksAr { 123 | id: number; 124 | name: string; 125 | tns: any[]; 126 | alias: any[]; 127 | } 128 | 129 | export interface PlayListDetailTracksAl { 130 | id: number; 131 | name: string; 132 | picUrl: string; 133 | tns: any[]; 134 | pic_str: string; 135 | pic: number; 136 | } 137 | 138 | export interface PlayListDetailTracksH { 139 | br: number; 140 | fid: number; 141 | size: number; 142 | vd: number; 143 | } 144 | 145 | export interface PlayListDetailTracksM { 146 | br: number; 147 | fid: number; 148 | size: number; 149 | vd: number; 150 | } 151 | 152 | export interface PlayListDetailTracksL { 153 | br: number; 154 | fid: number; 155 | size: number; 156 | vd: number; 157 | } 158 | 159 | export interface PlayListDetailTracks { 160 | name: string; 161 | id: number; 162 | pst: number; 163 | t: number; 164 | ar: PlayListDetailTracksAr[]; 165 | alia: any[]; 166 | pop: number; 167 | st: number; 168 | rt: string; 169 | fee: number; 170 | v: number; 171 | crbt?: any; 172 | cf: string; 173 | al: PlayListDetailTracksAl; 174 | dt: number; 175 | h: PlayListDetailTracksH; 176 | m: PlayListDetailTracksM; 177 | l: PlayListDetailTracksL; 178 | a?: any; 179 | cd: string; 180 | no: number; 181 | rtUrl?: any; 182 | ftype: number; 183 | rtUrls: any[]; 184 | djId: number; 185 | copyright: number; 186 | s_id: number; 187 | mark: number; 188 | originCoverType: number; 189 | originSongSimpleData?: any; 190 | single: number; 191 | noCopyrightRcmd?: any; 192 | cp: number; 193 | mv: number; 194 | rtype: number; 195 | rurl?: any; 196 | mst: number; 197 | publishTime: number; 198 | } 199 | 200 | export interface PlayListDetailTrackIds { 201 | id: number; 202 | v: number; 203 | t: number; 204 | at: number; 205 | alg?: any; 206 | uid: number; 207 | rcmdReason: string; 208 | sc?: any; 209 | } 210 | 211 | 212 | 213 | export interface PlaylistHighqualityTag { 214 | id: number; 215 | name: string; 216 | type: number; 217 | category: number; 218 | hot: boolean; 219 | } 220 | -------------------------------------------------------------------------------- /src/models/playlist_cat.ts: -------------------------------------------------------------------------------- 1 | export interface PlayListCat { 2 | name: string; 3 | resourceCount: number; 4 | imgId: number; 5 | imgUrl?: any; 6 | type: number; 7 | category: number; 8 | resourceType: number; 9 | hot: boolean; 10 | activity: boolean; 11 | } 12 | -------------------------------------------------------------------------------- /src/models/playlist_hot.ts: -------------------------------------------------------------------------------- 1 | export interface PlayListHot { 2 | playlistTag: PlayListHotPlaylistTag; 3 | activity: boolean; 4 | position: number; 5 | category: number; 6 | hot: boolean; 7 | createTime: number; 8 | usedCount: number; 9 | name: string; 10 | id: number; 11 | type: number; 12 | } 13 | 14 | export interface PlayListHotPlaylistTag { 15 | id: number; 16 | name: string; 17 | category: number; 18 | usedCount: number; 19 | type: number; 20 | position: number; 21 | createTime: number; 22 | highQuality: number; 23 | highQualityPos: number; 24 | officialPos: number; 25 | } 26 | -------------------------------------------------------------------------------- /src/models/search.ts: -------------------------------------------------------------------------------- 1 | export interface SearchHotDetail { 2 | searchWord: string; 3 | score: number; 4 | content: string; 5 | source: number; 6 | iconType: number; 7 | iconUrl?: string; 8 | url: string; 9 | alg: string; 10 | } 11 | 12 | export interface SearchSuggest { 13 | albums: SearchSuggestAlbums[]; 14 | artists: SearchSuggestArtists[]; 15 | songs: SearchSuggestSongs[]; 16 | playlists: SearchSuggestPlaylists[]; 17 | order: string[]; 18 | } 19 | export interface SearchSuggestAlbumsArtist { 20 | id: number; 21 | name: string; 22 | picUrl: string; 23 | alias: string[]; 24 | albumSize: number; 25 | picId: number; 26 | img1v1Url: string; 27 | img1v1: number; 28 | alia: string[]; 29 | trans?: any; 30 | } 31 | export interface SearchSuggestAlbums { 32 | id: number; 33 | name: string; 34 | artist: SearchSuggestAlbumsArtist; 35 | publishTime: number; 36 | size: number; 37 | copyrightId: number; 38 | status: number; 39 | picId: number; 40 | mark: number; 41 | } 42 | export interface SearchSuggestArtists { 43 | id: number; 44 | name: string; 45 | picUrl: string; 46 | alias: string[]; 47 | albumSize: number; 48 | picId: number; 49 | img1v1Url: string; 50 | img1v1: number; 51 | transNames: string[]; 52 | alia: string[]; 53 | trans: string; 54 | } 55 | export interface SearchSuggestSongsArtists { 56 | id: number; 57 | name: string; 58 | picUrl?: any; 59 | alias: any[]; 60 | albumSize: number; 61 | picId: number; 62 | img1v1Url: string; 63 | img1v1: number; 64 | trans?: any; 65 | } 66 | export interface SearchSuggestSongsAlbumArtist { 67 | id: number; 68 | name: string; 69 | picUrl?: any; 70 | alias: any[]; 71 | albumSize: number; 72 | picId: number; 73 | img1v1Url: string; 74 | img1v1: number; 75 | trans?: any; 76 | } 77 | export interface SearchSuggestSongsAlbum { 78 | id: number; 79 | name: string; 80 | artist: SearchSuggestSongsAlbumArtist; 81 | publishTime: number; 82 | size: number; 83 | copyrightId: number; 84 | status: number; 85 | picId: number; 86 | mark: number; 87 | } 88 | export interface SearchSuggestSongs { 89 | id: number; 90 | name: string; 91 | artists: SearchSuggestSongsArtists[]; 92 | album: SearchSuggestSongsAlbum; 93 | duration: number; 94 | copyrightId: number; 95 | status: number; 96 | alias: any[]; 97 | rtype: number; 98 | ftype: number; 99 | mvid: number; 100 | fee: number; 101 | rUrl?: any; 102 | mark: number; 103 | } 104 | export interface SearchSuggestPlaylists { 105 | id: number; 106 | name: string; 107 | coverImgUrl: string; 108 | creator?: any; 109 | subscribed: boolean; 110 | trackCount: number; 111 | userId: number; 112 | playCount: number; 113 | bookCount: number; 114 | specialType: number; 115 | officialTags?: any; 116 | action?: any; 117 | actionType?: any; 118 | description?: any; 119 | highQuality: boolean; 120 | } 121 | -------------------------------------------------------------------------------- /src/models/song.ts: -------------------------------------------------------------------------------- 1 | export interface Song { 2 | name: string; 3 | id: number; 4 | pst: number; 5 | t: number; 6 | ar: SongAr[]; 7 | alia: string[]; 8 | pop: number; 9 | st: number; 10 | rt?: any; 11 | fee: number; 12 | v: number; 13 | crbt?: any; 14 | cf: string; 15 | al: SongAl; 16 | dt: number; 17 | h: SongH; 18 | m: SongM; 19 | l: SongL; 20 | a?: any; 21 | cd: string; 22 | no: number; 23 | rtUrl?: any; 24 | ftype: number; 25 | rtUrls: any[]; 26 | djId: number; 27 | copyright: number; 28 | s_id: number; 29 | mark: number; 30 | originCoverType: number; 31 | originSongSimpleData?: any; 32 | tagPicList?: any; 33 | resourceState: boolean; 34 | version: number; 35 | songJumpInfo?: any; 36 | entertainmentTags?: any; 37 | single: number; 38 | noCopyrightRcmd?: any; 39 | rtype: number; 40 | rurl?: any; 41 | mst: number; 42 | cp: number; 43 | mv: number; 44 | publishTime: number; 45 | } 46 | 47 | export interface SongAr { 48 | id: number; 49 | name: string; 50 | tns: any[]; 51 | alias: any[]; 52 | } 53 | 54 | export interface SongAl { 55 | id: number; 56 | name: string; 57 | picUrl: string; 58 | tns: any[]; 59 | pic_str: string; 60 | pic: number; 61 | } 62 | 63 | export interface SongH { 64 | br: number; 65 | fid: number; 66 | size: number; 67 | vd: number; 68 | } 69 | 70 | export interface SongM { 71 | br: number; 72 | fid: number; 73 | size: number; 74 | vd: number; 75 | } 76 | 77 | export interface SongL { 78 | br: number; 79 | fid: number; 80 | size: number; 81 | vd: number; 82 | } 83 | -------------------------------------------------------------------------------- /src/models/song_url.ts: -------------------------------------------------------------------------------- 1 | export interface SongUrl { 2 | id: number; 3 | url: string; 4 | br: number; 5 | size: number; 6 | md5: string; 7 | code: number; 8 | expi: number; 9 | type: string; 10 | gain: number; 11 | fee: number; 12 | payed: number; 13 | flag: number; 14 | canExtend: boolean; 15 | freeTrialPrivilege: RootObjectFreeTrialPrivilege; 16 | freeTimeTrialPrivilege: RootObjectFreeTimeTrialPrivilege; 17 | urlSource: number; 18 | freeTrialInfo:{ 19 | start:number 20 | end:number 21 | } 22 | } 23 | 24 | export interface RootObjectFreeTrialPrivilege { 25 | resConsumable: boolean; 26 | userConsumable: boolean; 27 | } 28 | 29 | export interface RootObjectFreeTimeTrialPrivilege { 30 | resConsumable: boolean; 31 | userConsumable: boolean; 32 | type: number; 33 | remainTime: number; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/models/toplist_detail.ts: -------------------------------------------------------------------------------- 1 | export interface TopListDetail { 2 | subscribers: any[]; 3 | subscribed?: any; 4 | creator?: any; 5 | artists?: any; 6 | tracks: TopListDetailTracks[]; 7 | updateFrequency: string; 8 | backgroundCoverId: number; 9 | backgroundCoverUrl?: any; 10 | titleImage: number; 11 | titleImageUrl?: any; 12 | englishTitle?: any; 13 | opRecommend: boolean; 14 | recommendInfo?: any; 15 | subscribedCount: number; 16 | cloudTrackCount: number; 17 | userId: number; 18 | highQuality: boolean; 19 | createTime: number; 20 | specialType: number; 21 | coverImgId: number; 22 | newImported: boolean; 23 | anonimous: boolean; 24 | updateTime: number; 25 | trackCount: number; 26 | coverImgUrl: string; 27 | commentThreadId: string; 28 | trackUpdateTime: number; 29 | totalDuration: number; 30 | privacy: number; 31 | playCount: number; 32 | trackNumberUpdateTime: number; 33 | adType: number; 34 | description: string; 35 | ordered: boolean; 36 | tags: any[]; 37 | status: number; 38 | name: string; 39 | id: number; 40 | coverImgId_str: string; 41 | ToplistType: string; 42 | } 43 | export interface TopListDetailTracks { 44 | first: string; 45 | second: string; 46 | } 47 | -------------------------------------------------------------------------------- /src/models/user.ts: -------------------------------------------------------------------------------- 1 | export interface UserProfile { 2 | userId: number; 3 | userType: number; 4 | nickname: string; 5 | avatarImgId: number; 6 | avatarUrl: string; 7 | backgroundImgId: number; 8 | backgroundUrl: string; 9 | signature?: any; 10 | createTime: number; 11 | userName: string; 12 | accountType: number; 13 | shortUserName: string; 14 | birthday: number; 15 | authority: number; 16 | gender: number; 17 | accountStatus: number; 18 | province: number; 19 | city: number; 20 | authStatus: number; 21 | description?: any; 22 | detailDescription?: any; 23 | defaultAvatar: boolean; 24 | expertTags?: any; 25 | experts?: any; 26 | djStatus: number; 27 | locationStatus: number; 28 | vipType: number; 29 | followed: boolean; 30 | mutual: boolean; 31 | authenticated: boolean; 32 | lastLoginTime: number; 33 | lastLoginIP: string; 34 | remarkName?: any; 35 | viptypeVersion: number; 36 | authenticationTypes: number; 37 | avatarDetail?: any; 38 | anchor: boolean; 39 | } 40 | -------------------------------------------------------------------------------- /src/models/video.ts: -------------------------------------------------------------------------------- 1 | export interface Video { 2 | type: number; 3 | displayed: boolean; 4 | alg: string; 5 | extAlg?: any; 6 | data: VideoData; 7 | } 8 | export interface VideoDataResolutions { 9 | resolution: number; 10 | size: number; 11 | } 12 | export interface VideoDataCreator { 13 | defaultAvatar: boolean; 14 | province: number; 15 | authStatus: number; 16 | followed: boolean; 17 | avatarUrl: string; 18 | accountStatus: number; 19 | gender: number; 20 | city: number; 21 | birthday: number; 22 | userId: number; 23 | userType: number; 24 | nickname: string; 25 | signature: string; 26 | description: string; 27 | detailDescription: string; 28 | avatarImgId: number; 29 | backgroundImgId: number; 30 | backgroundUrl: string; 31 | authority: number; 32 | mutual: boolean; 33 | expertTags?: any; 34 | experts?: any; 35 | djStatus: number; 36 | vipType: number; 37 | remarkName?: any; 38 | avatarImgIdStr: string; 39 | backgroundImgIdStr: string; 40 | } 41 | export interface VideoDataUrlInfo { 42 | id: string; 43 | url: string; 44 | size: number; 45 | validityTime: number; 46 | needPay: boolean; 47 | payInfo?: any; 48 | r: number; 49 | } 50 | export interface VideoDataVideoGroup { 51 | id: number; 52 | name: string; 53 | alg?: any; 54 | } 55 | export interface VideoDataRelateSongAr { 56 | id: number; 57 | name: string; 58 | tns: any[]; 59 | alias: any[]; 60 | } 61 | export interface VideoDataRelateSongAl { 62 | id: number; 63 | name: string; 64 | picUrl: string; 65 | tns: any[]; 66 | pic: number; 67 | } 68 | export interface VideoDataRelateSongH { 69 | br: number; 70 | fid: number; 71 | size: number; 72 | vd: number; 73 | } 74 | export interface VideoDataRelateSongM { 75 | br: number; 76 | fid: number; 77 | size: number; 78 | vd: number; 79 | } 80 | export interface VideoDataRelateSongL { 81 | br: number; 82 | fid: number; 83 | size: number; 84 | vd: number; 85 | } 86 | export interface VideoDataRelateSongPrivilege { 87 | id: number; 88 | fee: number; 89 | payed: number; 90 | st: number; 91 | pl: number; 92 | dl: number; 93 | sp: number; 94 | cp: number; 95 | subp: number; 96 | cs: boolean; 97 | maxbr: number; 98 | fl: number; 99 | toast: boolean; 100 | flag: number; 101 | preSell: boolean; 102 | } 103 | export interface VideoDataRelateSong { 104 | name: string; 105 | id: number; 106 | pst: number; 107 | t: number; 108 | ar: VideoDataRelateSongAr[]; 109 | alia: any[]; 110 | pop: number; 111 | st: number; 112 | rt: string; 113 | fee: number; 114 | v: number; 115 | crbt?: any; 116 | cf: string; 117 | al: VideoDataRelateSongAl; 118 | dt: number; 119 | h: VideoDataRelateSongH; 120 | m: VideoDataRelateSongM; 121 | l: VideoDataRelateSongL; 122 | a?: any; 123 | cd: string; 124 | no: number; 125 | rtUrl?: any; 126 | ftype: number; 127 | rtUrls: any[]; 128 | djId: number; 129 | copyright: number; 130 | s_id: number; 131 | rtype: number; 132 | rurl?: any; 133 | mst: number; 134 | cp: number; 135 | mv: number; 136 | publishTime: number; 137 | privilege: VideoDataRelateSongPrivilege; 138 | } 139 | export interface VideoData { 140 | alg: string; 141 | scm: string; 142 | threadId: string; 143 | coverUrl: string; 144 | height: number; 145 | width: number; 146 | title: string; 147 | description?: any; 148 | commentCount: number; 149 | shareCount: number; 150 | resolutions: VideoDataResolutions[]; 151 | creator: VideoDataCreator; 152 | urlInfo: VideoDataUrlInfo; 153 | videoGroup: VideoDataVideoGroup[]; 154 | previewUrl?: any; 155 | previewDurationms: number; 156 | hasRelatedGameAd: boolean; 157 | markTypes: number[]; 158 | relateSong: VideoDataRelateSong[]; 159 | relatedInfo?: any; 160 | videoUserLiveInfo?: any; 161 | vid: string; 162 | durationms: number; 163 | playTime: number; 164 | praisedCount: number; 165 | praised: boolean; 166 | subscribed: boolean; 167 | } 168 | 169 | export interface PersonalizedPrivateContent { 170 | id: number; 171 | url: string; 172 | picUrl: string; 173 | sPicUrl: string; 174 | type: number; 175 | copywriter: string; 176 | name: string; 177 | time: number; 178 | } 179 | 180 | export interface VideoGroup { 181 | id: number; 182 | name: string; 183 | url?: any; 184 | relatedVideoType?: any; 185 | selectTab: boolean; 186 | abExtInfo?: any; 187 | } 188 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router' 2 | import {Pages} from "@/router/pages"; 3 | 4 | const router = createRouter({ 5 | history: createWebHashHistory(import.meta.env.BASE_URL), 6 | routes: [ 7 | { 8 | path: '/', 9 | name: Pages.home, 10 | component: () => import('@/views/Root.vue'), 11 | redirect: {name: Pages.discover}, 12 | children: [ 13 | { 14 | path: 'discover', 15 | name: 'discover', 16 | component: () => import("@/views/discover/Discover.vue"), 17 | meta: { 18 | menu: 'discover', 19 | keepAlive: true, 20 | } 21 | }, 22 | { 23 | path: 'music', 24 | name: 'music', 25 | component: () => import('@/views/music/Music.vue'), 26 | redirect: {name: 'picked'}, 27 | meta: { 28 | menu: 'music' 29 | }, 30 | children: [ 31 | { 32 | path: 'picked', 33 | name: 'picked', 34 | component: () => import("@/views/music/picked/Picked.vue"), 35 | meta: { 36 | menu: 'music', 37 | keepAlive: true, 38 | } 39 | }, 40 | { 41 | path: 'toplist', 42 | name: 'toplist', 43 | component: () => import("@/views/music/toplist/TopList.vue"), 44 | meta: { 45 | menu: 'music', 46 | keepAlive: true, 47 | } 48 | }, 49 | { 50 | path: 'artist', 51 | name: 'artist', 52 | component: () => import('@/views/music/artist/Artist.vue'), 53 | meta: { 54 | menu: 'music', 55 | title: '歌手', 56 | keepAlive: true, 57 | } 58 | }, 59 | { 60 | path: Pages.category, 61 | name: Pages.category, 62 | component: () => import('@/views/music/category/Category.vue'), 63 | meta: { 64 | menu: 'music', 65 | title: '分类歌单', 66 | keepAlive: true, 67 | } 68 | } 69 | ] 70 | }, 71 | { 72 | path: 'playlist', 73 | name: 'playlist', 74 | component: () => import('@/views/playlist/PlayList.vue'), 75 | }, 76 | { 77 | path: 'artistDetail', 78 | name: 'artistDetail', 79 | component: () => import('@/views/artist/ArtistDetail.vue'), 80 | }, 81 | { 82 | path: 'album', 83 | name: 'album', 84 | component: () => import('@/views/album/Album.vue'), 85 | }, 86 | { 87 | path: 'video', 88 | name: 'video', 89 | component: () => import('@/views/video/Video.vue'), 90 | meta: { 91 | menu: 'video', 92 | title: '视频', 93 | keepAlive: true, 94 | } 95 | }, 96 | { 97 | path: 'dj', 98 | name: 'dj', 99 | component: () => import('@/views/dj/DJ.vue'), 100 | meta: { 101 | menu: 'dj', 102 | title: '电台', 103 | keepAlive: true, 104 | } 105 | }, 106 | { 107 | path: Pages.mvDetail, 108 | name: Pages.mvDetail, 109 | component: () => import('@/views/mv/mvDetail.vue'), 110 | } 111 | ], 112 | }, 113 | ] 114 | }) 115 | 116 | export default router 117 | -------------------------------------------------------------------------------- /src/router/pages.ts: -------------------------------------------------------------------------------- 1 | export const Pages = { 2 | home: 'home', 3 | discover: 'discover', 4 | music: 'music', 5 | picked: 'picked', 6 | toplist: 'toplist', 7 | category: 'category', 8 | artist: 'artist', 9 | playlist: 'playlist', 10 | artistDetail: 'artistDetail', 11 | mvDetail: 'mvDetail', 12 | album: 'album', 13 | video: 'video', 14 | dj: 'dj', 15 | } 16 | -------------------------------------------------------------------------------- /src/stores/common.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { ref } from "vue"; 3 | import { useBanner } from "@/utils/api"; 4 | import type { Banner } from "@/models/banner"; 5 | 6 | export const useCommonStore = defineStore("common", () => { 7 | const banners = ref([]); 8 | 9 | const getBanners = async () => { 10 | if (banners.value.length) return; 11 | banners.value = await useBanner(); 12 | }; 13 | 14 | return { 15 | banners, 16 | getBanners, 17 | }; 18 | }); 19 | -------------------------------------------------------------------------------- /src/stores/dj.ts: -------------------------------------------------------------------------------- 1 | import type { DJBanner } from "@/models/dj"; 2 | import { defineStore } from "pinia"; 3 | import { ref } from "vue"; 4 | 5 | export const useDJStore = defineStore("dj", () => { 6 | 7 | const djBanners = ref([]) 8 | 9 | }) -------------------------------------------------------------------------------- /src/stores/host.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | 3 | export const useHostStore = defineStore('host', { 4 | state: () => { 5 | return { 6 | base_url: localStorage.getItem('BASE_URL') || '', 7 | 8 | } 9 | }, 10 | getters: { 11 | isInit: state => { 12 | return state.base_url != '' 13 | } 14 | }, 15 | actions: { 16 | init() { 17 | 18 | }, 19 | setHost(host: string) { 20 | localStorage.setItem('BASE_URL', host) 21 | location.reload() 22 | } 23 | } 24 | }) 25 | -------------------------------------------------------------------------------- /src/stores/music.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | import {ref} from "vue"; 3 | import {usePersonalized, usePersonalizedNewSong, useTopListDetail} from "@/utils/api"; 4 | import type {Personalized, PersonalizedNewSong} from "@/models/personalized"; 5 | import type {TopListDetail} from "@/models/toplist_detail"; 6 | 7 | export const useMusicStore = defineStore('music', () => { 8 | 9 | const topListDetailData = ref([]) 10 | const getTopListDetailData = async () => { 11 | if (topListDetailData.value.length) return; 12 | topListDetailData.value = await useTopListDetail() 13 | } 14 | 15 | 16 | const personalized = ref([]); 17 | const getPersonalized = async () => { 18 | if (personalized.value.length) return; 19 | personalized.value = await usePersonalized() 20 | } 21 | 22 | 23 | const personalizedNewSong = ref([]); 24 | const getPersonalizedNewSong = async () => { 25 | if (personalizedNewSong.value.length) return; 26 | personalizedNewSong.value = await usePersonalizedNewSong() 27 | } 28 | 29 | 30 | return { 31 | topListDetailData, 32 | getTopListDetailData, 33 | 34 | personalized, 35 | getPersonalized, 36 | 37 | personalizedNewSong, 38 | getPersonalizedNewSong 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/stores/personalized.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | import {ref} from "vue"; 3 | import {usePersonalizedDjProgram} from "@/utils/api"; 4 | import type {DjProgram} from "@/models/personalized"; 5 | 6 | export const usePersonalizedStore = defineStore('personalized', () => { 7 | 8 | const djProgram = ref([]) 9 | const getDjProgram = async () => { 10 | if (djProgram.value.length) return; 11 | djProgram.value = await usePersonalizedDjProgram() 12 | } 13 | 14 | 15 | return { 16 | djProgram, getDjProgram, 17 | } 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /src/stores/player.ts: -------------------------------------------------------------------------------- 1 | import {defineStore, storeToRefs} from "pinia"; 2 | import {useDetail, useSongUrl} from "@/utils/api"; 3 | import {onMounted, onUnmounted, toRefs, watch} from "vue"; 4 | import type {Song} from "@/models/song"; 5 | import type {SongUrl} from "@/models/song_url"; 6 | 7 | const KEYS = { 8 | volume: 'PLAYER-VOLUME' 9 | } 10 | 11 | export const usePlayerStore = defineStore({ 12 | id: "player", 13 | state: () => ({ 14 | audio: new Audio(), 15 | loopType: 0,//循环模式 0 单曲循环 1 列表循环 2随机播放 16 | volume: localStorage.getItem(KEYS.volume)?.toInt() || 60,//音量 17 | playList: [] as Song[],//播放列表, 18 | showPlayList: false, 19 | id: 0, 20 | url: '', 21 | songUrl: {} as SongUrl, 22 | song: {} as Song, 23 | isPlaying: false, //是否播放中 24 | isPause: false,//是否暂停 25 | sliderInput: false,//是否正在拖动进度条 26 | ended: false,//是否播放结束 27 | muted: false,//是否静音 28 | currentTime: 0,//当前播放时间 29 | duration: 0,//总播放时长 30 | }), 31 | getters: { 32 | playListCount: state => { 33 | return state.playList.length; 34 | }, 35 | thisIndex: state => { 36 | return state.playList.findIndex(song => song.id === state.id); 37 | }, 38 | nextSong(state): Song { 39 | const {thisIndex, playListCount} = this 40 | if (thisIndex === playListCount - 1) { 41 | return state.playList.first(); 42 | } else { 43 | const nextIndex: number = thisIndex + 1 44 | return state.playList[nextIndex]; 45 | } 46 | }, 47 | prevSong(state): Song { 48 | const {thisIndex} = this 49 | if (thisIndex === 0) { 50 | return state.playList.last(); 51 | } else { 52 | const prevIndex: number = thisIndex - 1 53 | return state.playList[prevIndex]; 54 | } 55 | } 56 | }, 57 | actions: { 58 | init() { 59 | this.audio.volume = this.volume / 100; 60 | }, 61 | //播放列表里面添加音乐 62 | pushPlayList(replace: boolean, ...list: Song[]) { 63 | if (replace) { 64 | this.playList = list; 65 | return; 66 | } 67 | list.forEach(song => { 68 | if (this.playList.filter(s => s.id == song.id).length <= 0) { 69 | this.playList.push(song) 70 | } 71 | }) 72 | }, 73 | clearPlayList() { 74 | this.songUrl = {} as SongUrl 75 | this.url = '' 76 | this.id = 0; 77 | this.song = {} as Song 78 | this.isPlaying = false; 79 | this.isPause = false; 80 | this.sliderInput = false; 81 | this.ended = false; 82 | this.muted = false; 83 | this.currentTime = 0; 84 | this.playList = [] as Song[]; 85 | this.showPlayList = false; 86 | this.audio.load(); 87 | setTimeout(() => { 88 | this.duration = 0; 89 | }, 500) 90 | }, 91 | async play(id: number) { 92 | if (id == this.id) return; 93 | this.isPlaying = false 94 | const data = await useSongUrl(id) 95 | this.audio.src = data.url; 96 | this.audio.play().then(res => { 97 | this.isPlaying = true 98 | this.songUrl = data 99 | this.url = data.url 100 | this.id = id; 101 | this.songDetail() 102 | }).catch(res => { 103 | console.log(res) 104 | }) 105 | }, 106 | //播放结束 107 | playEnd() { 108 | console.log('播放结束') 109 | switch (this.loopType) { 110 | case 0: 111 | this.rePlay() 112 | break; 113 | case 1: 114 | this.next() 115 | break; 116 | case 2: 117 | this.randomPlay() 118 | break; 119 | } 120 | }, 121 | async songDetail() { 122 | this.song = await useDetail(this.id) 123 | 124 | this.pushPlayList(false, this.song) 125 | }, 126 | //重新播放 127 | rePlay() { 128 | setTimeout(() => { 129 | this.currentTime = 0; 130 | this.audio.play() 131 | }, 1500) 132 | }, 133 | //下一曲 134 | next() { 135 | if (this.loopType === 2) { 136 | this.randomPlay() 137 | } else { 138 | this.play(this.nextSong.id) 139 | } 140 | 141 | }, 142 | //上一曲 143 | prev() { 144 | this.play(this.prevSong.id) 145 | }, 146 | //随机播放 147 | randomPlay() { 148 | this.play(this.playList.sample().id) 149 | }, 150 | //播放、暂停 151 | togglePlay() { 152 | if (!this.song.id) return; 153 | this.isPlaying = !this.isPlaying 154 | if (!this.isPlaying) { 155 | this.audio.pause(); 156 | this.isPause = true 157 | } else { 158 | this.audio.play(); 159 | this.isPause = false 160 | } 161 | }, 162 | setPlay() { 163 | if (!this.song.id) return; 164 | this.isPlaying = true 165 | this.audio.play(); 166 | this.isPause = false 167 | 168 | }, 169 | setPause() { 170 | if (!this.song.id) return; 171 | this.isPlaying = false 172 | this.audio.pause(); 173 | this.isPause = true 174 | }, 175 | //切换循环类型 176 | toggleLoop() { 177 | if (this.loopType == 2) { 178 | this.loopType = 0; 179 | } else { 180 | this.loopType++; 181 | } 182 | }, 183 | //静音切换 184 | toggleMuted() { 185 | this.muted = !this.muted 186 | this.audio.muted = this.muted 187 | }, 188 | //音量设置 189 | setVolume(n: number) { 190 | n = n > 100 ? 100 : n 191 | n = n < 0 ? 0 : n 192 | this.volume = n 193 | this.audio.volume = n / 100 194 | localStorage.setItem('PLAYER-VOLUME', n.toString()) 195 | }, 196 | //修改播放时间 197 | onSliderChange(val: number) { 198 | this.currentTime = val 199 | this.sliderInput = false; 200 | this.audio.currentTime = val 201 | }, 202 | //播放时间拖动中 203 | onSliderInput(val: number) { 204 | this.sliderInput = true; 205 | }, 206 | //定时器 207 | interval() { 208 | if (this.isPlaying && !this.sliderInput) { 209 | this.currentTime = parseInt(this.audio.currentTime.toString()); 210 | this.duration = parseInt(this.audio.duration.toString()); 211 | this.ended = this.audio.ended 212 | } 213 | } 214 | } 215 | }) 216 | 217 | 218 | export const userPlayerInit = () => { 219 | let timer: NodeJS.Timer; 220 | const {init, interval, playEnd} = usePlayerStore() 221 | 222 | const {ended} = storeToRefs(usePlayerStore()) 223 | 224 | //监听播放结束 225 | watch(ended, ended => { 226 | if (!ended) return 227 | playEnd() 228 | }) 229 | 230 | //启动定时器 231 | onMounted(() => { 232 | init() 233 | console.log('启动定时器') 234 | timer = setInterval(interval, 1000) 235 | }) 236 | //清除定时器 237 | onUnmounted(() => { 238 | console.log('清除定时器') 239 | clearInterval(timer) 240 | }) 241 | } 242 | -------------------------------------------------------------------------------- /src/stores/search.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | import {useSearchSuggest} from "@/utils/api"; 3 | import type {SearchSuggest} from "@/models/search"; 4 | 5 | export const useSearchStore = defineStore("search", { 6 | state: () => { 7 | return { 8 | showSearchView: false, 9 | searchKeyword: '', 10 | suggestData: {} as SearchSuggest, 11 | } 12 | }, 13 | getters: { 14 | showHot: state => { 15 | return state.searchKeyword == '' 16 | } 17 | }, 18 | actions: { 19 | async suggest() { 20 | this.suggestData = await useSearchSuggest(this.searchKeyword) 21 | } 22 | } 23 | }) 24 | -------------------------------------------------------------------------------- /src/stores/user.ts: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | import {useLogin, useLoginStatus} from "@/utils/api"; 3 | import type {UserProfile} from "@/models/user"; 4 | 5 | export const useUserStore = defineStore("user", { 6 | state: () => { 7 | return { 8 | token: '', 9 | cookie: '', 10 | showLogin: false, 11 | profile: {} as UserProfile, 12 | } 13 | }, 14 | getters: { 15 | isLogin: state => { 16 | return state.profile?.userId > 0 17 | } 18 | }, 19 | actions: { 20 | async login(phone: string, password: string) { 21 | const res = await useLogin(phone, password) 22 | if (res.code == 200) { 23 | this.token = res.token 24 | this.cookie = res.cookie 25 | document.cookie = res.cookie 26 | localStorage.setItem("USER-TOKEN", this.token) 27 | localStorage.setItem("USER-COOKIE", this.cookie) 28 | this.checkLogin() 29 | } 30 | }, 31 | async checkLogin() { 32 | const {data} = await useLoginStatus() 33 | if (data.code === 200) { 34 | this.profile = data.profile 35 | this.showLogin = false 36 | } 37 | 38 | } 39 | } 40 | }) 41 | -------------------------------------------------------------------------------- /src/stores/video.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { ref } from "vue"; 3 | import type { PersonalizedPrivateContent, Video, VideoGroup } from "@/models/video"; 4 | import { usePersonalizedMv, usePersonalizedPrivateContentList, useVideoGroupList, useVideoTimelineRecommend } from "@/utils/api"; 5 | import type { PersonalizedMv } from "@/models/personalized"; 6 | 7 | export const useVideoStore = defineStore('video', () => { 8 | 9 | const videoTimelineRecommend = ref([]) 10 | const getVideoTimelineRecommend = async () => { 11 | if (videoTimelineRecommend.value.length) return; 12 | videoTimelineRecommend.value = await useVideoTimelineRecommend() 13 | } 14 | 15 | 16 | const personalizedPrivateContent = ref([]) 17 | const getPersonalizedPrivateContent = async () => { 18 | if (personalizedPrivateContent.value.length) return; 19 | personalizedPrivateContent.value = await usePersonalizedPrivateContentList(4) 20 | } 21 | 22 | const personalizedMv = ref([]) 23 | const getPersonalizedMv = async () => { 24 | if (personalizedMv.value.length) return; 25 | personalizedMv.value = await usePersonalizedMv() 26 | } 27 | 28 | const videoGroup = ref([]) 29 | const getVideoGroup = async () => { 30 | if (videoGroup.value.length) return; 31 | videoGroup.value = await useVideoGroupList() 32 | } 33 | 34 | return { 35 | videoTimelineRecommend, getVideoTimelineRecommend, 36 | 37 | personalizedPrivateContent, getPersonalizedPrivateContent, 38 | 39 | personalizedMv, getPersonalizedMv, 40 | 41 | videoGroup, getVideoGroup, 42 | } 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /src/utils/api.ts: -------------------------------------------------------------------------------- 1 | import type {Banner} from "@/models/banner"; 2 | import type {DjProgram, Personalized, PersonalizedMv, PersonalizedNewSong} from "@/models/personalized"; 3 | import type {PlayListDetail, PlaylistHighqualityTag} from "@/models/playlist"; 4 | import type {PlayListCat} from "@/models/playlist_cat"; 5 | import type {Song} from "@/models/song"; 6 | import type {SongUrl} from "@/models/song_url"; 7 | import type {TopListDetail} from "@/models/toplist_detail"; 8 | import http from "@/utils/http"; 9 | import type {Artist, Mv} from "@/models/artist"; 10 | import type {ArtistDesc, ArtistDetail} from "@/models/artist_detail"; 11 | import type {Album} from "@/models/album"; 12 | import type {PersonalizedPrivateContent, Video, VideoGroup} from "@/models/video"; 13 | import type {SearchHotDetail, SearchSuggest} from "@/models/search"; 14 | import type {MvUrl} from "@/models/mv"; 15 | import type {PlayListHot} from "@/models/playlist_hot"; 16 | import type {UserProfile} from "@/models/user"; 17 | 18 | export async function useLogin(phone: string, password: string) { 19 | return await http.get<{ 20 | code: number, 21 | cookie: string, 22 | token: string, 23 | }>("login/cellphone", {phone: phone, password: password}) 24 | } 25 | 26 | export async function useLoginStatus() { 27 | return await http.get<{ 28 | data: { 29 | code: number, 30 | profile: UserProfile 31 | }, 32 | }>("login/status") 33 | } 34 | 35 | export async function useSongUrl(id: number) { 36 | const {data} = await http.get<{ data: SongUrl[] }>('/song/url', {id: id}) 37 | return data.first() 38 | } 39 | 40 | export async function useDetail(id: number) { 41 | const {songs} = await http.get<{ songs: Song[] }>('/song/detail', {ids: id}) 42 | return songs.first() 43 | } 44 | 45 | export async function useBanner() { 46 | const {banners} = await http.get<{ banners: Banner[] }>('/banner', {type: 1}) 47 | return banners 48 | } 49 | 50 | export async function usePersonalized() { 51 | const {result} = await http.get<{ result: Personalized[] }>('/personalized') 52 | return result 53 | } 54 | 55 | export async function usePersonalizedNewSong() { 56 | const {result} = await http.get<{ result: PersonalizedNewSong[] }>('/personalized/newsong') 57 | return result 58 | } 59 | 60 | export async function usePlayListDetail(id: number, s: number = 8) { 61 | const {playlist} = await http.get<{ playlist: PlayListDetail }>('/playlist/detail', {id: id, s: s}) 62 | return playlist 63 | } 64 | 65 | export async function usePlayListTrackAll(id: number) { 66 | 67 | const {songs} = await http.get<{ songs: Song[] }>('playlist/track/all', {id: id}) 68 | return songs 69 | } 70 | 71 | export async function useTopListDetail() { 72 | const {list} = await http.get<{ list: TopListDetail[] }>('/toplist/detail') 73 | return list 74 | } 75 | 76 | export async function usePlayListCatList() { 77 | const {sub, categories} = await http.get<{ sub: PlayListCat[], categories: string[] }>('playlist/catlist') 78 | 79 | return {sub, categories} 80 | } 81 | 82 | export async function userArtistList(pageData: { type: number, area: number, initial: string, page: number, limit: number }) { 83 | const res = await http.get<{ artists: Artist[] }>('artist/list', { 84 | type: pageData.type, 85 | area: pageData.area, 86 | initial: pageData.initial, 87 | limit: pageData.limit, 88 | offset: (pageData.page - 1) * pageData.limit 89 | }) 90 | 91 | return res.artists 92 | } 93 | 94 | export async function useArtistDetail(id: number) { 95 | const {data} = await http.get<{ data: ArtistDetail }>('artist/detail', {id: id}) 96 | return data 97 | } 98 | 99 | export async function useArtistDesc(id: number) { 100 | return await http.get('artist/desc', {id: id}) 101 | } 102 | 103 | export async function useArtistSongs(id: number, order: string = 'time', limit: number = 10, offset: number = 0) { 104 | return await http.get<{ songs: Song[] }>('artist/songs', {id: id, order: order, limit: limit, offset: offset}) 105 | } 106 | 107 | export async function useArtistAlbum(id: number, limit: number = 10, offset: number = 0) { 108 | return await http.get<{ hotAlbums: Album[] }>('artist/album', {id: id, limit: limit, offset: offset}) 109 | } 110 | 111 | export async function useArtistMv(id: number, limit: number = 10, offset: number = 0) { 112 | return await http.get<{ mvs: Mv[] }>('artist/mv', {id: id, limit: limit, offset: offset}) 113 | } 114 | 115 | export async function useVideoTimelineRecommend(offset: number = 0) { 116 | const {datas} = await http.get<{ datas: Video[] }>('video/timeline/recommend', {offset: offset}) 117 | return datas 118 | } 119 | 120 | export async function usePersonalizedPrivateContentList(limit: number = 10, offset: number = 0) { 121 | const {result} = await http.get<{ result: PersonalizedPrivateContent[] }>('personalized/privatecontent/list', { 122 | limit: limit, 123 | offset: offset 124 | }) 125 | return result 126 | } 127 | 128 | 129 | export async function usePersonalizedMv() { 130 | const {result} = await http.get<{ result: PersonalizedMv[] }>('personalized/mv') 131 | return result 132 | } 133 | 134 | export async function usePersonalizedDjProgram() { 135 | const {result} = await http.get<{ result: DjProgram[] }>('personalized/djprogram') 136 | return result 137 | } 138 | 139 | 140 | export async function useVideoGroupList() { 141 | const {data} = await http.get<{ data: VideoGroup[] }>('video/group/list') 142 | return data 143 | } 144 | 145 | export async function useVideoGroup(id?: number, offset?: number) { 146 | const {datas} = await http.get<{ datas: Video[] }>(id ? 'video/group' : 'video/timeline/all', { 147 | id: id, 148 | offset: offset 149 | }) 150 | return datas 151 | } 152 | 153 | 154 | export async function useAlbum(id: number) { 155 | const {album, songs} = await http.get<{ album: Album, songs: Song[] }>('album', {id: id}) 156 | 157 | return {album, songs} 158 | } 159 | 160 | export async function useSearchHotDetail() { 161 | const {data} = await http.get<{ data: SearchHotDetail[] }>('search/hot/detail') 162 | return data 163 | } 164 | 165 | export async function useSearchSuggest(keywords: string) { 166 | const {result} = await http.get<{ result: SearchSuggest }>('search/suggest', {keywords: keywords}) 167 | return result 168 | } 169 | 170 | export async function useMvDetail(mvid: number) { 171 | 172 | } 173 | 174 | export async function useMvUrl(id: number) { 175 | const {data} = await http.get<{ data: MvUrl }>("mv/url", {id: id}) 176 | return data 177 | } 178 | 179 | 180 | export async function usePlaylistHighqualityTags() { 181 | const {tags} = await http.get<{ tags: PlaylistHighqualityTag[] }>("playlist/highquality/tags") 182 | 183 | return tags 184 | } 185 | 186 | export async function usePlaylistHot() { 187 | const {tags} = await http.get<{ tags: PlayListHot[] }>("playlist/hot") 188 | 189 | return tags 190 | } 191 | 192 | export async function useTopPlaylistHighquality(params?: { limit?: number, before?: number, cat: string }) { 193 | return await http.get<{ playlists: PlayListDetail[], total: number, more: boolean, lasttime: number }>("top/playlist/highquality", params) 194 | 195 | } 196 | -------------------------------------------------------------------------------- /src/utils/extend.ts: -------------------------------------------------------------------------------- 1 | import {first,last, sampleSize,sample, chunk, trimEnd} from 'lodash' 2 | import dayjs from 'dayjs' 3 | import {useNumberFormat} from "@/utils/number"; 4 | 5 | declare global { 6 | interface Array { 7 | /** 8 | * 获取数组第一个元素 9 | */ 10 | first(this: T[]): T 11 | 12 | last(this: T[]): T 13 | sample(this: T[]): T 14 | 15 | /** 16 | * 获得 n 个随机元素 17 | * @param size 18 | */ 19 | sampleSize(this: T[], size: number): T[] 20 | 21 | /** 22 | * 将数组(array)拆分成多个 size 长度的区块 23 | * @param size 24 | */ 25 | chunk(this: T[], size: number): T[][] 26 | 27 | } 28 | 29 | interface String { 30 | /** 31 | * 转换成int类型 32 | */ 33 | toInt(this: string): number 34 | 35 | trimEnd(this: string, chars?: string): string 36 | } 37 | 38 | interface Number { 39 | toDate(this: number, format?: string): string 40 | 41 | numberFormat(this: number): string | number 42 | } 43 | } 44 | Array.prototype.first = function (this: T[]): T { 45 | return first(this) as T 46 | } 47 | Array.prototype.last = function (this: T[]): T { 48 | return last(this) as T 49 | } 50 | Array.prototype.sample = function (this: T[]): T { 51 | return sample(this) as T 52 | } 53 | Array.prototype.sampleSize = function (this: T[], size: number): T[] { 54 | return sampleSize(this, size) as T[] 55 | } 56 | Array.prototype.chunk = function (this: T[], size: number): T[][] { 57 | return chunk(this, size) as T[][] 58 | } 59 | String.prototype.toInt = function (this: string): number { 60 | return parseInt(this) 61 | } 62 | 63 | String.prototype.trimEnd = function (this: string, chars: string = ' '): string { 64 | return trimEnd(this, chars) 65 | } 66 | 67 | Number.prototype.toDate = function (this: number, format: string = 'YYYY-MM-DD'): string { 68 | return dayjs(this).format(format) 69 | } 70 | 71 | Number.prototype.numberFormat = function (this: number): string | number { 72 | return useNumberFormat(this) 73 | } 74 | -------------------------------------------------------------------------------- /src/utils/http.ts: -------------------------------------------------------------------------------- 1 | import axios, {type AxiosRequestConfig} from "axios"; 2 | 3 | axios.defaults.baseURL = localStorage.getItem('BASE_URL')?.toString(); 4 | axios.defaults.timeout = 20 * 1000; 5 | axios.defaults.maxBodyLength = 5 * 1024 * 1024; 6 | axios.defaults.withCredentials = true 7 | 8 | axios.interceptors.request.use( 9 | (config: AxiosRequestConfig | any) => { 10 | 11 | config.params = { 12 | ...config.params, 13 | t: Date.now(), 14 | } 15 | return config; 16 | }, 17 | function (error) { 18 | return Promise.reject(error); 19 | } 20 | ); 21 | 22 | // 添加响应拦截器 23 | axios.interceptors.response.use( 24 | (response) => { 25 | return response; 26 | }, 27 | function (error) { 28 | return Promise.reject(error); 29 | } 30 | ); 31 | 32 | interface ResType { 33 | code: number; 34 | data?: T; 35 | msg: string; 36 | err?: string; 37 | } 38 | 39 | interface Http { 40 | get(url: string, params?: unknown): Promise; 41 | 42 | post(url: string, params?: unknown): Promise; 43 | 44 | upload(url: string, params: unknown): Promise; 45 | 46 | put(url: string, params: unknown): Promise; 47 | 48 | delete(url: string, params: unknown): Promise; 49 | 50 | download(url: string): void; 51 | } 52 | 53 | const http: Http = { 54 | get(url, params) { 55 | return new Promise((resolve, reject) => { 56 | axios 57 | .get(url, {params}) 58 | .then((res) => { 59 | resolve(res.data); 60 | }) 61 | .catch((err) => { 62 | reject(err.data); 63 | }); 64 | }); 65 | }, 66 | 67 | post(url, params) { 68 | return new Promise((resolve, reject) => { 69 | axios 70 | .post(url, JSON.stringify(params)) 71 | .then((res) => { 72 | resolve(res.data); 73 | }) 74 | .catch((err) => { 75 | reject(err.data); 76 | }); 77 | }); 78 | }, 79 | 80 | put(url, params) { 81 | return new Promise((resolve, reject) => { 82 | axios 83 | .put(url, JSON.stringify(params)) 84 | .then((res) => { 85 | resolve(res.data); 86 | }) 87 | .catch((err) => { 88 | reject(err.data); 89 | }); 90 | }); 91 | }, 92 | 93 | delete(url, params) { 94 | return new Promise((resolve, reject) => { 95 | axios 96 | .delete(url, {params}) 97 | .then((res) => { 98 | resolve(res.data); 99 | }) 100 | .catch((err) => { 101 | reject(err.data); 102 | }); 103 | }); 104 | }, 105 | 106 | upload(url, file) { 107 | return new Promise((resolve, reject) => { 108 | axios 109 | .post(url, file, { 110 | headers: {"Content-Type": "multipart/form-data"}, 111 | }) 112 | .then((res) => { 113 | resolve(res.data); 114 | }) 115 | .catch((err) => { 116 | reject(err.data); 117 | }); 118 | }); 119 | }, 120 | 121 | download(url) { 122 | const iframe = document.createElement("iframe"); 123 | iframe.style.display = "none"; 124 | iframe.src = url; 125 | iframe.onload = function () { 126 | document.body.removeChild(iframe); 127 | }; 128 | 129 | document.body.appendChild(iframe); 130 | }, 131 | }; 132 | 133 | export default http; 134 | -------------------------------------------------------------------------------- /src/utils/number.ts: -------------------------------------------------------------------------------- 1 | export function useNumberFormat(number: number): string | number { 2 | 3 | 4 | if (number > 100000000) { 5 | return Number((number / 100000000).toFixed(1)) + ' 亿'; 6 | } 7 | 8 | if (number > 10000000) { 9 | return Number((number / 10000000).toFixed(1)) + ' 千万'; 10 | } 11 | 12 | if (number > 10000) { 13 | return Number((number / 10000).toFixed(1)) + ' 万'; 14 | } 15 | 16 | return number; 17 | } 18 | 19 | export function useFormatDuring(during: number) { 20 | const s = Math.floor(during) % 60; 21 | during = Math.floor(during / 60); 22 | const i = during % 60; 23 | 24 | const ii = i < 10 ? `0${i}` : i; 25 | const ss = s < 10 ? `0${s}` : s; 26 | 27 | return ii + ':' + ss; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/views/Root.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 33 | 35 | -------------------------------------------------------------------------------- /src/views/album/Album.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 49 | 52 | -------------------------------------------------------------------------------- /src/views/album/Info.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 40 | -------------------------------------------------------------------------------- /src/views/artist/Album.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 71 | 74 | -------------------------------------------------------------------------------- /src/views/artist/ArtistDetail.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 44 | 55 | -------------------------------------------------------------------------------- /src/views/artist/Desc.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 25 | 28 | -------------------------------------------------------------------------------- /src/views/artist/Info.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 44 | -------------------------------------------------------------------------------- /src/views/artist/Songs.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 105 | 108 | -------------------------------------------------------------------------------- /src/views/artist/Video.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 69 | 72 | -------------------------------------------------------------------------------- /src/views/discover/Discover.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/discover/DjProgram.vue: -------------------------------------------------------------------------------- 1 |