├── .gitignore ├── .vscode └── extensions.json ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public └── vite.svg ├── search ├── src ├── App.vue ├── assets │ ├── area.txt │ ├── img │ │ ├── defaultPic.png │ │ └── playDefaultPic.png │ └── vue.svg ├── axios │ └── http.js ├── components │ ├── avatar │ │ └── avatar.vue │ ├── calendar │ │ └── calendar.vue │ ├── cloudPan.vue │ ├── comment │ │ └── comment.vue │ ├── commune.vue │ ├── cropper │ │ └── cropper.vue │ ├── home.vue │ ├── login │ │ └── login.vue │ ├── lyric │ │ └── lyric.vue │ ├── my.vue │ ├── myInfo │ │ └── myInfo.vue │ ├── playmusic │ │ ├── playmusic.vue │ │ └── url.js │ ├── reviseMyInfo │ │ └── reviseMyInfo.vue │ ├── search │ │ └── search.vue │ ├── songListPage │ │ └── songListPage.vue │ ├── songPage │ │ └── songPage.vue │ ├── songlist │ │ └── songlist.vue │ ├── tababr │ │ └── tabbar.vue │ └── video │ │ └── video.vue ├── main.js ├── mixins │ └── mixin.js ├── router │ └── router.js ├── store │ ├── cloud.js │ ├── home.js │ ├── my-temp.js │ ├── my.js │ ├── play-temp.js │ ├── play.js │ ├── store.js │ ├── video-temp.js │ └── video.js └── style.css └── vite.config.js /.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 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简单的仿网易云项目 2 | 学完vue独立做的第一个项目,缺陷很多,希望得到大家的斧正^_^ 3 | 前端:vue3 + elementPlus + vueX 4 | 后端:感谢[Binaryify大佬](https://github.com/Binaryify)提供的接口 5 | 6 | ##安装 7 | 8 | npm install 9 | 10 | npm run dev 11 | 12 | ## 在线演示 13 | 🛸🛸[仿网易云](http://121.4.123.199:8883/#/home) 14 | 现在已经将后端接口和前端页面都部署在国内。 15 | ps:请打开谷歌浏览器的移动端模式。 16 | **如果首页无数据,请尝试下拉刷新**!! 17 | **目前已经支持二维码登录,但云盘与刷新视频等功能暂时无法使用**!! 18 | ## 功能 19 | 20 | ### 🏚️home页面: 21 | 1.搜索功能:由于后端的数据较少,所以做的比较简单; 22 | 23 | ![image](https://user-images.githubusercontent.com/100813306/199457815-a46117cc-ceaa-4317-8843-23ae82eee185.png) 24 | 25 | 2.每日推荐歌单; 26 | 3.大家都在听; 27 | 4.下拉获取新数据; 28 | 29 | ### 😃my页面: 30 | 1.初始进入时显示需要登录状态: 31 | 32 | ![image](https://user-images.githubusercontent.com/100813306/199456767-f630ae69-08b9-4941-b05e-0eb216146261.png) 33 | ![image](https://user-images.githubusercontent.com/100813306/199457035-0b04d67b-6eff-4c63-9af4-92648fca4d52.png) 34 | 35 | 2.登录后获取个人信息并本地化存储: 36 | 37 | ![image](https://user-images.githubusercontent.com/100813306/199457191-ce549c26-bcea-4669-9b71-bc930c7e0603.png) 38 | 39 | 3.查看个人资料详情功能: 40 | 41 | ![image](https://user-images.githubusercontent.com/100813306/199457389-00d4b839-d90f-49c1-b2f6-4a98db424e99.png) 42 | 43 | 4.修改个人资料: 44 | 包括头像、生日、nickname等: 45 | 46 | ![image](https://user-images.githubusercontent.com/100813306/199457583-ac12cf71-217e-46a7-8c33-951811002f81.png) 47 | ![image](https://user-images.githubusercontent.com/100813306/199457629-bc40085a-f348-418e-9a88-cdffb4ba15ea.png) 48 | 49 | ### ☁️cloud云盘页面: 50 | 51 | 1.获取云端存储的歌曲,并且可对其进行管理: 52 | 53 | ![image](https://user-images.githubusercontent.com/100813306/199458294-99edddef-ae8e-4a1a-b24e-964eac4b5ef7.png) 54 | 55 | ### 🎧歌曲详情页面: 56 | 1.歌曲详情页面可进行封面/歌词切换,顶部可下载和查看评论,顶部进行歌曲切换: 57 | 58 | ![image](https://user-images.githubusercontent.com/100813306/199458768-e409f88f-8a8d-438b-8852-d0f4e287437b.png) 59 | ![image](https://user-images.githubusercontent.com/100813306/199458801-27b3c616-7302-4bca-9ece-0c4a74067ff9.png) 60 | ![image](https://user-images.githubusercontent.com/100813306/199458886-b8fc5347-4b3c-4f75-9080-99b046b4cd4e.png) 61 | 62 | ### 🎬社区功能: 63 | 1.实现听音乐的同时,刷刷视频,感觉某些地方的逻辑怪怪的,后续找到问题会修复的 64 | 65 | ![image](https://user-images.githubusercontent.com/100813306/200152945-ee3c40f6-9399-4f0f-b758-fa14c058ac4d.png) 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 仿网易云音乐 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wy", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@element-plus/icons-vue": "^2.0.9", 13 | "axios": "^0.27.2", 14 | "element-plus": "^2.2.17", 15 | "pinia": "^2.0.30", 16 | "qrcodejs2": "^0.0.2", 17 | "qs": "^6.11.0", 18 | "vue": "^3.2.37", 19 | "vue-qr": "^4.0.9", 20 | "vue-router": "^4.1.5", 21 | "vuejs-loadmore": "^1.0.7", 22 | "vuex": "^4.0.2", 23 | "vuex-persist": "^3.1.3", 24 | "vuex-persistedstate": "^4.1.0" 25 | }, 26 | "devDependencies": { 27 | "@vitejs/plugin-vue": "^3.1.0", 28 | "less": "^4.1.3", 29 | "less-loader": "^11.0.0", 30 | "vite": "^3.1.0", 31 | "vue-cropper": "^1.0.5" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /search: -------------------------------------------------------------------------------- 1 | comment 2 | download 3 | login 4 | main 5 | my 6 | myInfo 7 | potimization 8 | reviseMyInfo 9 | * search 10 | songListPage 11 | songPage 12 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 96 | 101 | 102 | 107 | -------------------------------------------------------------------------------- /src/assets/area.txt: -------------------------------------------------------------------------------- 1 | 1100 北京 北京;1200 天津 天津; 2 | 1301 河北 石家庄; 3 | 1302 河北 唐山; 4 | 1303 河北 秦皇岛; 5 | 1304 河北 邯郸; 6 | 1305 河北 邢台; 7 | 1306 河北 保定; 8 | 1307 河北 张家口; 9 | 1308 河北 承德; 10 | 1309 河北 沧州; 11 | 1310 河北 廊坊; 12 | 1311 河北 衡水; 13 | 1401 山西 太原; 14 | 1402 山西 大同; 15 | 1403 山西 阳泉; 16 | 1404 山西 长治; 17 | 1405 山西 晋城; 18 | 1406 山西 朔州; 19 | 1407 山西 晋中; 20 | 1408 山西 运城; 21 | 1409 山西 忻州; 22 | 1410 山西 临汾; 23 | 1411 山西 吕梁; 24 | 1424 山西 晋中; 25 | 1501 内蒙古 呼和浩特; 26 | 1502 内蒙古 包头; 27 | 1503 内蒙古 乌海; 28 | 1504 内蒙古 赤峰; 29 | 1505 内蒙古 通辽; 30 | 1506 内蒙古 鄂尔多斯 31 | 1507 内蒙古 呼伦贝尔 32 | 1508 内蒙古 巴彦淖尔 33 | 1509 内蒙古 乌兰察布 34 | 1522 内蒙古 兴安盟 35 | 1525 内蒙古 锡林郭勒 36 | 1529 内蒙古 阿拉善盟 37 | 2101 辽宁 沈阳 38 | 2102 辽宁 大连 39 | 2103 辽宁 鞍山 40 | 2104 辽宁 抚顺 41 | 2105 辽宁 本溪 42 | 2106 辽宁 丹东 43 | 2107 辽宁 锦州 44 | 2108 辽宁 营口 45 | 2109 辽宁 阜新 46 | 2110 辽宁 辽阳 47 | 2111 辽宁 盘锦 48 | 2112 辽宁 铁岭 49 | 2113 辽宁 朝阳 50 | 2114 辽宁 葫芦岛 51 | 2200 吉林 吉林 52 | 2201 吉林 长春 53 | 2202 吉林 吉林 54 | 2203 吉林 四平 55 | 2204 吉林 辽源 56 | 2205 吉林 通化 57 | 2206 吉林 白山 58 | 2207 吉林 松原 59 | 2208 吉林 白城 60 | 2224 吉林 延边州 61 | 2301 黑龙江 哈尔滨 62 | 2302 黑龙江 齐齐哈尔 63 | 2303 黑龙江 鸡西 64 | 2304 黑龙江 鹤岗 65 | 2305 黑龙江 双鸭山 66 | 2306 黑龙江 大庆 67 | 2307 黑龙江 伊春 68 | 2308 黑龙江 佳木斯 69 | 2309 黑龙江 七台河 70 | 2310 黑龙江 牡丹江 71 | 2311 黑龙江 黑河 72 | 2312 黑龙江 绥化 73 | 2321 黑龙江 哈尔滨 74 | 2327 黑龙江 大兴安岭 75 | 3100 上海 上海 76 | 3201 江苏 南京 77 | 3202 江苏 无锡 78 | 3203 江苏 徐州 79 | 3204 江苏 常州 80 | 3205 江苏 苏州 81 | 3206 江苏 南通 82 | 3207 江苏 连云港 83 | 3208 江苏 淮安 84 | 3209 江苏 盐城 85 | 3210 江苏 扬州 86 | 3211 江苏 镇江 87 | 3212 江苏 泰州 88 | 3301 浙江省 杭州 89 | 3302 浙江省 宁波 90 | 3303 浙江省 温州 91 | 3304 浙江省 嘉兴 92 | 3305 浙江省 湖州 93 | 3306 浙江省 绍兴 94 | 3307 浙江省 金华 95 | 3308 浙江省 衢州 96 | 3309 浙江省 舟山 97 | 3310 浙江省 台州 98 | 3311 浙江省 丽水 99 | 3401 安徽 合肥 100 | 3402 安徽 芜湖 101 | 3403 安徽 蚌埠 102 | 3404 安徽 淮南 103 | 3405 安徽 马鞍山 104 | 3406 安徽 淮北 105 | 3407 安徽 铜陵 106 | 3408 安徽 安庆 107 | 3410 安徽 黄山 108 | 3411 安徽 滁州 109 | 3412 安徽 阜阳 110 | 3413 安徽 宿州 111 | 3415 安徽 六安 112 | 3416 安徽 亳州 113 | 3417 安徽 池州 114 | 3418 安徽 宣城 115 | 3422 安徽 宿县 116 | 3501 福建 福州 117 | 3502 福建 厦门 118 | 3503 福建 莆田 119 | 3504 福建 三明 120 | 3505 福建 泉州 121 | 3506 福建 漳州 122 | 3507 福建 南平 123 | 3508 福建 龙岩 124 | 3509 福建 宁德 125 | 3601 江西 南昌 126 | 3602 江西 景德镇 127 | 3603 江西 萍乡 128 | 3604 江西 九江 129 | 3605 江西 新余 130 | 3606 江西 鹰潭 131 | 3607 江西 赣州 132 | 3608 江西 吉安 133 | 3609 江西 宜春 134 | 3610 江西 抚州 135 | 3611 江西 上饶 136 | 3701 山东 济南 137 | 3702 山东 青岛 138 | 3703 山东 淄博 139 | 3704 山东 枣庄 140 | 3705 山东 东营 141 | 3706 山东 烟台 142 | 3707 山东 潍坊 143 | 3708 山东 济宁 144 | 3709 山东 泰安 145 | 3710 山东 威海 146 | 3711 山东 日照 147 | 3712 山东 莱芜 148 | 3713 山东 临沂 149 | 3714 山东 德州 150 | 3715 山东 聊城 151 | 3716 山东 滨州 152 | 3717 山东 菏泽 153 | 4101 河南 郑州 154 | 4102 河南 开封 155 | 4103 河南 洛阳 156 | 4104 河南 平顶山 157 | 4105 河南 安阳 158 | 4106 河南 鹤壁 159 | 4107 河南 新乡 160 | 4108 河南 焦作 161 | 4109 河南 濮阳 162 | 4110 河南 许昌 163 | 4111 河南 漯河 164 | 4112 河南 三门峡 165 | 4113 河南 南阳 166 | 4114 河南 商丘 167 | 4115 河南 信阳 168 | 4116 河南 周口 169 | 4117 河南 驻马店 170 | 4201 湖北 武汉 171 | 4202 湖北 黄石 172 | 4203 湖北 十堰 173 | 4205 湖北 宜昌 174 | 4206 湖北 襄阳 175 | 4207 湖北 鄂州 176 | 4208 湖北 荆门 177 | 4209 湖北 孝感 178 | 4210 湖北 荆州 179 | 4211 湖北 黄冈 180 | 4212 湖北 咸宁 181 | 4213 湖北 随州 182 | 4228 湖北 恩施州 183 | 4301 湖南 长沙 184 | 4302 湖南 株洲 185 | 4303 湖南 湘潭 186 | 4304 湖南 衡阳 187 | 4305 湖南 邵阳 188 | 4306 湖南 岳阳 189 | 4307 湖南 常德 190 | 4309 湖南 益阳 191 | 4310 湖南 郴州 192 | 4311 湖南 永州 193 | 4312 湖南 怀化 194 | 4313 湖南 娄底 195 | 4331 湖南 湘西州 196 | 4401 广东 广州 197 | 4402 广东 韶关 198 | 4403 广东 深圳 199 | 4404 广东 珠海 200 | 4405 广东 汕头 201 | 4406 广东 佛山 202 | 4407 广东 江门 203 | 4408 广东 湛江 204 | 4409 广东 茂名 205 | 4412 广东 肇庆 206 | 4413 广东 惠州 207 | 4414 广东 梅州 208 | 4415 广东 汕尾 209 | 4416 广东 河源 210 | 4417 广东 阳江 211 | 4418 广东 清远 212 | 4419 广东 东莞 213 | 4420 广东 中山 214 | 4451 广东 潮州 215 | 4452 广东 揭阳 216 | 4453 广东 云浮 217 | 4501 广西 南宁 218 | 4502 广西 柳州 219 | 4503 广西 桂林 220 | 4504 广西 梧州 221 | 4505 广西 北海 222 | 4506 广西 防城港 223 | 4507 广西 钦州 224 | 4508 广西 贵港 225 | 4509 广西 玉林 226 | 4510 广西 百色 227 | 4511 广西 贺州 228 | 4512 广西 河池 229 | 4513 广西 来宾 230 | 4514 广西 崇左 231 | 4524 广西 贺州 232 | 4601 海南 海口 233 | 4602 海南 三亚 234 | 4690 海南 儋州 235 | 4690 海南 三沙 236 | 5000 重庆 重庆 237 | 5101 四川 成都 238 | 5103 四川 自贡 239 | 5104 四川 攀枝花 240 | 5105 四川 泸州 241 | 5106 四川 德阳 242 | 5107 四川 绵阳 243 | 5108 四川 广元 244 | 5109 四川 遂宁 245 | 5110 四川 内江 246 | 5111 四川 乐山 247 | 5113 四川 南充 248 | 5114 四川 眉山 249 | 5115 四川 宜宾 250 | 5116 四川 广安 251 | 5117 四川 达州 252 | 5119 四川 巴中 253 | 5120 四川 资阳 254 | 5134 四川 凉山州 255 | 5201 贵州 贵阳 256 | 5202 贵州 六盘水 257 | 5203 贵州 遵义 258 | 5204 贵州 安顺 259 | 5205 贵州 毕节 260 | 5206 贵州 铜仁 261 | 5223 贵州 黔西南州 262 | 5226 贵州 黔东南州 263 | 5227 贵州 黔南州 264 | 5301 云南 昆明 265 | 5303 云南 曲靖 266 | 5304 云南 玉溪 267 | 5305 云南 保山 268 | 5306 云南 昭通 269 | 5307 云南 丽江 270 | 5308 云南 普洱 271 | 5309 云南 临沧 272 | 5323 云南 楚雄州 273 | 5325 云南 红河州 274 | 5326 云南 文山州 275 | 5328 云南 西双版纳 276 | 5329 云南 大理州 277 | 5331 云南 德宏州 278 | 5333 云南 怒江州 279 | 5334 云南 迪庆州 280 | 5401 西藏 拉萨 281 | 5421 西藏 昌都 282 | 5422 西藏 山南 283 | 5423 西藏 日喀则 284 | 5426 西藏 林芝 285 | 6101 陕西 西安 286 | 6102 陕西 铜川 287 | 6103 陕西 宝鸡 288 | 6104 陕西 咸阳 289 | 6105 陕西 渭南 290 | 6106 陕西 延安 291 | 6107 陕西 汉中 292 | 6108 陕西 榆林 293 | 6109 陕西 安康 294 | 6110 陕西 商洛 295 | 6201 甘肃省 兰州 296 | 6202 甘肃省 嘉峪关 297 | 6203 甘肃省 金昌 298 | 6204 甘肃省 白银 299 | 6205 甘肃省 天水 300 | 6206 甘肃省 武威 301 | 6207 甘肃省 张掖 302 | 6208 甘肃省 平凉 303 | 6209 甘肃省 酒泉 304 | 6210 甘肃省 庆阳 305 | 6211 甘肃省 定西 306 | 6212 甘肃省 陇南 307 | 6229 甘肃省 临夏州 308 | 6230 甘肃省 甘南州 309 | 6301 青海 西宁 310 | 6321 青海 海东 311 | 6322 青海 海北州 312 | 6323 青海 黄南州 313 | 6325 青海 海南州 314 | 6326 青海 果洛州 315 | 6327 青海 玉树州 316 | 6328 青海 海西州 317 | 6401 宁夏 银川 318 | 6402 宁夏 石嘴山 319 | 6403 宁夏 吴忠 320 | 6404 宁夏 固原 321 | 6405 宁夏 中卫 322 | 6501 新疆 乌鲁木齐 323 | 6502 新疆 克拉玛依 324 | 6521 新疆 吐鲁番 325 | 6522 新疆 哈密 326 | 6523 新疆 昌吉州 327 | 6527 新疆 博尔塔拉州 328 | 6528 新疆 巴音郭楞 329 | 6530 新疆 克孜勒苏州 330 | 6531 新疆 喀什地区 331 | 6532 新疆 和田地区 332 | 6540 新疆 伊犁州 333 | 6542 新疆 塔城地区 334 | 6590 新疆 石河子 335 | -------------------------------------------------------------------------------- /src/assets/img/defaultPic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yeti-xxx/Cloud-Music-move/12a36a10bd7daf3b745eff94718e52c06227078a/src/assets/img/defaultPic.png -------------------------------------------------------------------------------- /src/assets/img/playDefaultPic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yeti-xxx/Cloud-Music-move/12a36a10bd7daf3b745eff94718e52c06227078a/src/assets/img/playDefaultPic.png -------------------------------------------------------------------------------- /src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/axios/http.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import qs from 'qs' 3 | 4 | let api_base_url = '' 5 | if (import.meta.env.MODE === 'development') { 6 | api_base_url = 'http://111.67.195.237:8886/' 7 | } else if (import.meta.env.MODE === 'production') { 8 | api_base_url = 'http://111.67.195.237:8886/' 9 | } 10 | let instance = axios.create({ 11 | timeout: 1000 * 80, 12 | baseURL: api_base_url 13 | }) 14 | instance.defaults.responseType = 'json' 15 | instance.defaults.withCredentials = true 16 | instance.defaults.transformRequest = [ 17 | data => { 18 | return qs.stringify(data) 19 | } 20 | ] 21 | instance.defaults.validateStatus = function () { 22 | // return status >= 200 && status < 400; // 200- 399 resolve 其他状态码 reject 23 | // 如果在响应拦截设置了状态码判断,这里设置返回 true 24 | return true 25 | } 26 | // 请求拦截器 27 | instance.interceptors.request.use( 28 | config => { 29 | 30 | return config 31 | }, 32 | error => { 33 | return Promise.reject(error) 34 | } 35 | ) 36 | // 响应拦截器即异常处理 37 | // 服务器 Response 对象 38 | instance.interceptors.response.use( 39 | response => { 40 | // {data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, request:{…}} 41 | let data = response.data //响应的数据部分 42 | let status = response.status //标准状态码 43 | if (status === 200) { //如果响应正常则放行 数据 44 | return Promise.resolve(data) // *响应拦截器,只取数据部分* 45 | } 46 | 47 | else if (status >= 400 && status <= 499) { 48 | return 49 | } 50 | else { 51 | //其他错误 52 | return Promise.reject(response) 53 | } 54 | }, 55 | error => { 56 | } 57 | ) 58 | let api = {} 59 | api.get = function (url) { 60 | return new Promise((resolve, reject) => { 61 | instance 62 | .get(url) 63 | .then(response => { 64 | resolve(response) 65 | }) 66 | .catch(error => { 67 | reject(error) 68 | }) 69 | }) 70 | } 71 | api.post = function (url, data) { 72 | return new Promise((resolve, reject) => { 73 | instance 74 | .post(url, data) 75 | .then(response => { 76 | resolve(response) 77 | }) 78 | .catch(error => { 79 | reject(error) 80 | }) 81 | }) 82 | } 83 | export default api -------------------------------------------------------------------------------- /src/components/avatar/avatar.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 50 | -------------------------------------------------------------------------------- /src/components/calendar/calendar.vue: -------------------------------------------------------------------------------- 1 | 2 | 58 | 59 | 237 | 238 | 388 | -------------------------------------------------------------------------------- /src/components/cloudPan.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 347 | 348 | 530 | 531 | -------------------------------------------------------------------------------- /src/components/comment/comment.vue: -------------------------------------------------------------------------------- 1 | 50 | 174 | 175 | 324 | -------------------------------------------------------------------------------- /src/components/commune.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 153 | 154 | 238 | -------------------------------------------------------------------------------- /src/components/cropper/cropper.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 106 | 107 | 135 | -------------------------------------------------------------------------------- /src/components/home.vue: -------------------------------------------------------------------------------- 1 | 122 | 123 | 295 | 296 | 506 | -------------------------------------------------------------------------------- /src/components/login/login.vue: -------------------------------------------------------------------------------- 1 | 86 | 251 | -------------------------------------------------------------------------------- /src/components/lyric/lyric.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 45 | 46 | 70 | 71 | -------------------------------------------------------------------------------- /src/components/my.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 138 | 139 | 246 | -------------------------------------------------------------------------------- /src/components/myInfo/myInfo.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 193 | 194 | 381 | -------------------------------------------------------------------------------- /src/components/playmusic/playmusic.vue: -------------------------------------------------------------------------------- 1 | 29 | 332 | -------------------------------------------------------------------------------- /src/components/reviseMyInfo/reviseMyInfo.vue: -------------------------------------------------------------------------------- 1 | 77 | 78 | 252 | 253 | 426 | -------------------------------------------------------------------------------- /src/components/search/search.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | 193 | 194 | 418 | -------------------------------------------------------------------------------- /src/components/songListPage/songListPage.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 187 | 188 | 381 | -------------------------------------------------------------------------------- /src/components/songPage/songPage.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 235 | 236 | 405 | -------------------------------------------------------------------------------- /src/components/songlist/songlist.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | 26 | 58 | -------------------------------------------------------------------------------- /src/components/tababr/tabbar.vue: -------------------------------------------------------------------------------- 1 | 95 | 96 | 125 | 126 | 196 | -------------------------------------------------------------------------------- /src/components/video/video.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 89 | 90 | 130 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import ElementPlus from 'element-plus' 4 | import '../node_modules/element-plus/dist/index.css' 5 | import './style.css' 6 | import * as ElementPlusIconsVue from '@element-plus/icons-vue' 7 | import axios from './axios/http' 8 | import Router from './router/router.js' 9 | import store from './store/store.js' 10 | 11 | const app = createApp(App) 12 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 13 | app.component(key, component) 14 | } 15 | app.config.globalProperties.$h = axios; 16 | app.use(ElementPlus) 17 | app.use(Router) 18 | app.use(store) 19 | app.mount('#app') -------------------------------------------------------------------------------- /src/mixins/mixin.js: -------------------------------------------------------------------------------- 1 | import { ElMessage } from 'element-plus' 2 | export default { 3 | methods: { 4 | async getUserInfo(uid) { 5 | const res = await this.$h.get('/user/detail?uid=' + uid) 6 | return res 7 | 8 | }, 9 | // 向父组件中的音乐播放器组件传入数据 10 | async playMusic(songInfo) { 11 | const res = await this.getMusicUrl(songInfo); 12 | if (res) { 13 | this.playMusictoApp(this.musicUrl, songInfo.picUrl, songInfo.name) 14 | } 15 | }, 16 | // 获取歌曲url 17 | async getMusicUrl(id) { 18 | const res = await this.$h.get('/song/url/v1?id=' + id + '&level=standard') 19 | if (res.data[0].url === null) { 20 | return 'urlNull' 21 | } 22 | return res 23 | }, 24 | // 获取歌曲信息 25 | async getMusicDetail(id) { 26 | const res = await this.$h.get('/song/detail?ids=' + id) 27 | return res 28 | }, 29 | test() { 30 | 31 | }, 32 | // 点击歌单进入歌单详情页面 33 | goToList(ListId) { 34 | this.$router.push({ 35 | path: '/songListPage', 36 | query: { 37 | id: ListId 38 | } 39 | }) 40 | }, 41 | // 时间戳转时间 42 | getDate(timeStr) { 43 | let date = new Date(timeStr) 44 | let Y = date.getFullYear() 45 | let M = parseInt((date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1)) 46 | let D = parseInt((date.getDate() < 10 ? '0' + date.getDate() : date.getDate())) 47 | const time = { 48 | Y, 49 | M, D 50 | } 51 | return time 52 | }, 53 | // 判断年代 54 | getAge(time) { 55 | let Y = time.Y 56 | let res1 = '神秘后' 57 | switch (true) { 58 | case 2030 > Y && Y >= 2020: 59 | res1 = '20后' 60 | break; 61 | case 2020 > Y && Y >= 2010: 62 | res1 = '10后' 63 | break; 64 | case 2010 > Y && Y >= 2000: 65 | res1 = '00后' 66 | break; 67 | case 2000 > Y && Y >= 1990: 68 | res1 = '90后' 69 | break; 70 | case 1990 > Y && Y >= 1980: 71 | res1 = '80后' 72 | break; 73 | case 1980 > Y && Y >= 1970: 74 | res1 = '70后' 75 | break; 76 | case 1970 > Y && Y >= 1960: 77 | res1 = '60后' 78 | break; 79 | case 1960 > Y && Y >= 1950: 80 | res1 = '50后' 81 | break; 82 | case 1950 > Y && Y >= 1940: 83 | res1 = '40后' 84 | break; 85 | case 1940 > Y && Y >= 1930: 86 | res1 = '30后' 87 | break; 88 | case 1930 > Y && Y >= 1920: 89 | res1 = '20后' 90 | break; 91 | 92 | } 93 | return res1 94 | }, 95 | // 判断星座 96 | getZodiac(time) { 97 | const M = parseInt(time.M) 98 | const D = parseInt(time.D) 99 | // 星座判断表 100 | const sdate = [20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22] 101 | // 星座表 102 | const conts = ['摩羯座', '水瓶座', '双鱼座', '白羊座', '金牛座', '双子座', '巨蟹座', '狮子座', '处女座', '天秤座', '天蝎座', '射手座', '摩羯座'] 103 | if (D < sdate[M - 1]) { 104 | return conts[M - 1] 105 | } else { 106 | return conts[M] 107 | } 108 | }, 109 | // 歌词处理 110 | wordHandle() { 111 | //先通过“\n”将每行歌词存入数组之中 112 | let arr = this.word.split('\n') 113 | for (let i = 0; i < arr.length; i++) { 114 | let lyricRow = {} //将每行歌词及其出现的时间视为一个对象 115 | let row = arr[i].split(']') //文本切割 116 | let text = row[1] //拿到当前行的真正的歌词 117 | let time_arr = row[0].substr(1, row[0].length - 1).split(":") //将[01:13]秒转为["01","13"]用于先处理 118 | let s = parseInt(time_arr[0]) * 60 + Math.ceil(time_arr[1]) //将时间统一转为秒 119 | lyricRow.time = s //向对象存入数据 120 | lyricRow.text = text 121 | // push进lyryc数组 122 | this.lyric.push(lyricRow) 123 | } 124 | }, 125 | // 获取歌词 126 | async getWord(id) { 127 | const res = await this.$h.get('/lyric?id=' + id) 128 | return res.lrc 129 | }, 130 | // 文件下载 131 | downloadFile(url, fileName, cb) { 132 | const xhr = new XMLHttpRequest(); 133 | xhr.open('GET', url, true); 134 | // 响应类型设置为blob 135 | xhr.responseType = 'blob'; 136 | // 请求成功 137 | xhr.addEventListener('load', function () { 138 | if (xhr.status === 200) { 139 | const a = document.createElement('a'); 140 | a.href = window.URL.createObjectURL(xhr.response); 141 | a.download = fileName; 142 | // 将a标签添加到body中是为了更好的兼容性,谷歌浏览器可以不用添加 143 | document.body.appendChild(a); 144 | a.click(); 145 | // 移除 146 | a.remove(); 147 | // 释放url 148 | window.URL.revokeObjectURL(a.href); 149 | cb | cb() //完成后判断是否调用回调函数 150 | } 151 | }); 152 | // 监听下载进度 153 | let percent = xhr.addEventListener('progress', function (e) { 154 | return Math.trunc(e.loaded / e.total * 100); 155 | }); 156 | if (percent === 100) { 157 | (47477); 158 | } 159 | // 错误处理 160 | xhr.addEventListener('error', function (e) { 161 | 162 | }); 163 | // 发送请求 164 | xhr.send(); 165 | 166 | } 167 | 168 | }, 169 | } -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHashHistory } from "vue-router"; 2 | import home from '../components/home.vue' 3 | import my from '../components/my.vue' 4 | import cloudPan from '../components/cloudPan.vue' 5 | import commune from '../components/commune.vue' 6 | import songListPage from '../components/songListPage/songListPage.vue' 7 | import songPage from '../components/songPage/songPage.vue' 8 | import comment from '../components/comment/comment.vue' 9 | import myInfo from '../components/myInfo/myInfo.vue' 10 | import reviseMyInfo from '../components/reviseMyInfo/reviseMyInfo.vue' 11 | import search from '../components/search/search.vue' 12 | 13 | const routes = [ 14 | { path: '/', redirect: '/home' }, 15 | { path: '/home', component: home, name: 'home' }, 16 | { path: '/my', component: my, name: 'my' }, 17 | { path: '/cloudPan', component: cloudPan, name: 'cloudPan' }, 18 | { path: '/commune', component: commune, name: 'commune' }, 19 | { path: '/songListPage', component: songListPage, name: 'songListPage' }, 20 | { path: '/songPage', component: songPage, name: 'songPage' }, 21 | { path: '/comment', component: comment, name: 'comment' }, 22 | { path: '/myInfo', component: myInfo, name: 'myInfo' }, 23 | { path: '/reviseMyInfo', component: reviseMyInfo, name: 'reviseMyInfo' }, 24 | { path: '/search', component: search, name: 'search' } 25 | ] 26 | 27 | const Router = createRouter({ 28 | history: createWebHashHistory(), 29 | // 声明路由规则 30 | routes 31 | }) 32 | 33 | export default Router; 34 | -------------------------------------------------------------------------------- /src/store/cloud.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | cloudPanSongArray: 'null', 5 | sizeStore: 'null' 6 | }), 7 | mutations: { 8 | updatedCloudPanSongArray(state, cloudPanSongArray) { 9 | (cloudPanSongArray); 10 | state.cloudPanSongArray = cloudPanSongArray 11 | }, 12 | updatedSize(state, sizeStore) { 13 | state.sizeStore = sizeStore 14 | }, 15 | }, 16 | getters: { 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /src/store/home.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | musicListinStore: '11', 5 | weListeninStore: '22', 6 | refreshTime: '0', 7 | searchHot: '11', 8 | searchHistory: '11', 9 | bannerArrayinStore: '11' 10 | }), 11 | mutations: { 12 | updatedMusicListinStore(state, musicListinStore) { 13 | state.musicListinStore = musicListinStore 14 | }, 15 | updatedWeListeninStore(state, weListeninStore) { 16 | state.weListeninStore = weListeninStore 17 | }, 18 | uodateRefreshTime(state, refreshTime) { 19 | state.refreshTime = refreshTime 20 | }, 21 | updatedSerachHot(state, searchHot) { 22 | (searchHot); 23 | state.searchHot = searchHot 24 | }, 25 | updatedSearchHistory(state, searchHistory) { 26 | state.searchHistory = searchHistory 27 | }, 28 | updatedBannerArrayinStore(state,bannerArrayinStore){ 29 | (bannerArrayinStore); 30 | state.bannerArrayinStore = bannerArrayinStore 31 | } 32 | }, 33 | getters: { 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /src/store/my-temp.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | birthdayTimeInStore: {} 5 | }), 6 | mutations: { 7 | updatedBirthdayTimeInStore(state, birthdayTimeInStore) { 8 | (birthdayTimeInStore); 9 | state.birthdayTimeInStore = birthdayTimeInStore 10 | }, 11 | }, 12 | getters: { 13 | 14 | } 15 | } -------------------------------------------------------------------------------- /src/store/my.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | accountStore: '11', 5 | userInfo: '{}', 6 | songListinStore: '11', 7 | cookie: '' 8 | 9 | }), 10 | mutations: { 11 | updateAccount(state, account) { 12 | state.accountStore = account 13 | }, 14 | updateUserInfo(state, userInfo) { 15 | (userInfo); 16 | state.accountStore = 'true' 17 | state.userInfo = userInfo 18 | 19 | }, 20 | updateSongListinStore(state, songListinStore) { 21 | state.songListinStore = songListinStore 22 | }, 23 | updateCookie(state, cookie) { 24 | (11); 25 | state.cookie = cookie 26 | }, 27 | }, 28 | getters: { 29 | 30 | } 31 | } -------------------------------------------------------------------------------- /src/store/play-temp.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | TsongListStore: [], 5 | TsongBigListStore: ['01'], 6 | TsongPageIdStore: '11', //解决从其他歌曲评论页面返回时,页面导航进入最初的歌曲 7 | TListNowStore: null, 8 | TListLengthStore: null, 9 | TlyricTime:null, 10 | downloadFlag:'wait' 11 | }), 12 | mutations: { 13 | updateTsongBigListStore(state, TsongBigListStore) { 14 | state.TsongBigListStore = TsongBigListStore 15 | }, 16 | updateTsongPageIdStore(state, TsongPageIdStore) { 17 | state.TsongPageIdStore = TsongPageIdStore 18 | }, 19 | updateTListNowStore(state, TListNowStore) { 20 | state.TListNowStore = TListNowStore 21 | 22 | }, 23 | updatedTListLengthStore(state, TListLengthStore) { 24 | state.TListLengthStore = TListLengthStore 25 | }, 26 | updatedTlyricTime(state,TlyricTime){ 27 | state.TlyricTime = TlyricTime 28 | }, 29 | updatedDownloadFlag(state,downloadFlag){ 30 | state.downloadFlag = downloadFlag 31 | } 32 | 33 | }, 34 | getters: { 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /src/store/play.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | showPlay: true, 5 | playIt: true, //若设为false可能导致第一次进入play页面时 唱片不进行旋转并且播放按钮为暂停状态 6 | changeMusic: '', 7 | songStore:{}, 8 | songListStore:[] 9 | }), 10 | mutations: { 11 | updateShowPlay(state) { 12 | state.showPlay = !state.showPlay 13 | }, 14 | updateplayIt(state) { 15 | // (11); 16 | state.playIt = !state.playIt 17 | }, 18 | // 覆盖play为true 19 | playTotrue(state) { 20 | state.playIt = true 21 | }, 22 | updateChangeMusic(state, direction) { 23 | if (direction === 'next') { 24 | state.changeMusic = direction 25 | } else if (direction === 'pre') { 26 | state.changeMusic = 'pre' 27 | }else{ 28 | state.changeMusic = direction 29 | } 30 | }, 31 | updatesongStore(state,songStore){ 32 | state.songStore = songStore 33 | } 34 | }, 35 | getters: { 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import Vuex from 'vuex' 2 | import modulesMy from './my.js' 3 | import modulesPlay from './play.js' 4 | import modulesHome from './home.js' 5 | import playTemp from './play-temp.js' 6 | import cloud from './cloud.js' 7 | import createPersistedState from 'vuex-persistedstate'; 8 | import myTemp from './my-temp' 9 | import myVideo from './video' 10 | import videoTemp from './video-temp' 11 | 12 | 13 | const store = new Vuex.Store({ 14 | 15 | modules: { 16 | m_my: modulesMy, 17 | m_play: modulesPlay, 18 | m_home: modulesHome, 19 | t_play: playTemp, 20 | m_cloud: cloud, 21 | t_my: myTemp, 22 | m_video: myVideo, 23 | t_video: videoTemp 24 | }, 25 | plugins: [createPersistedState({ 26 | paths: ["m_my", "m_play", "m_home", "m_cloud", "m_video"] 27 | })] 28 | }) 29 | 30 | export default store -------------------------------------------------------------------------------- /src/store/video-temp.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | videoScrollTop: 0, 5 | toPlayScrollTop: 0 6 | }), 7 | mutations: { 8 | updatedVideoScrollTop(state, videoScrollTop) { 9 | state.videoScrollTop = videoScrollTop 10 | }, 11 | updatedToPlayScrollTop(state,toPlayScrollTop){ 12 | state.toPlayScrollTop = toPlayScrollTop 13 | } 14 | 15 | }, 16 | getters: { 17 | 18 | } 19 | } -------------------------------------------------------------------------------- /src/store/video.js: -------------------------------------------------------------------------------- 1 | export default { 2 | namespaced: true, 3 | state: () => ({ 4 | videoPageInstore: 1, 5 | videoArrayInstore: '11', 6 | videoScrollTop: 0 7 | }), 8 | mutations: { 9 | updatedVideoPageInstore(state, videoPageInstore) { 10 | state.videoPageInstore = videoPageInstore 11 | }, 12 | updatedVideoArrayInstore(state, videoArrayInstore) { 13 | state.videoArrayInstore = videoArrayInstore 14 | }, 15 | 16 | }, 17 | getters: { 18 | 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | /* :root { 2 | font-family: Inter, Avenir, Helvetica, Arial, sans-serif; 3 | font-size: 16px; 4 | line-height: 24px; 5 | font-weight: 400; 6 | 7 | color-scheme: light dark; 8 | color: rgba(255, 255, 255, 0.87); 9 | background-color: #242424; 10 | 11 | font-synthesis: none; 12 | text-rendering: optimizeLegibility; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | -webkit-text-size-adjust: 100%; 16 | } 17 | 18 | a { 19 | font-weight: 500; 20 | color: #646cff; 21 | text-decoration: inherit; 22 | } 23 | a:hover { 24 | color: #535bf2; 25 | } 26 | 27 | a { 28 | font-weight: 500; 29 | color: #646cff; 30 | text-decoration: inherit; 31 | } 32 | a:hover { 33 | color: #535bf2; 34 | } 35 | 36 | body { 37 | margin: 0; 38 | display: flex; 39 | place-items: center; 40 | min-width: 320px; 41 | min-height: 100vh; 42 | } 43 | 44 | h1 { 45 | font-size: 3.2em; 46 | line-height: 1.1; 47 | } 48 | 49 | button { 50 | border-radius: 8px; 51 | border: 1px solid transparent; 52 | padding: 0.6em 1.2em; 53 | font-size: 1em; 54 | font-weight: 500; 55 | font-family: inherit; 56 | background-color: #1a1a1a; 57 | cursor: pointer; 58 | transition: border-color 0.25s; 59 | } 60 | button:hover { 61 | border-color: #646cff; 62 | } 63 | button:focus, 64 | button:focus-visible { 65 | outline: 4px auto -webkit-focus-ring-color; 66 | } 67 | 68 | .card { 69 | padding: 2em; 70 | } 71 | 72 | #app { 73 | max-width: 1280px; 74 | margin: 0 auto; 75 | padding: 2rem; 76 | text-align: center; 77 | } 78 | 79 | @media (prefers-color-scheme: light) { 80 | :root { 81 | color: #213547; 82 | background-color: #ffffff; 83 | } 84 | a:hover { 85 | color: #747bff; 86 | } 87 | button { 88 | background-color: #f9f9f9; 89 | } 90 | } */ 91 | html,body,#app{ 92 | height: 100%; 93 | width: 100%; 94 | margin: 0; 95 | padding: 0; 96 | /* background: #2b2b2b; */ 97 | background: #151515; 98 | font-family: 'YaHei Consolas Hybrid'; 99 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | export default defineConfig({ 4 | plugins: [vue()], 5 | // 跨域 6 | server: { 7 | cors: true, // 默认启用并允许任何源 8 | proxy: { 9 | '/agent': { 10 | target: 'http://111.67.195.237:8886/', 11 | //原先接口部署在vercel上,虽然免费但是太慢了...... 12 | changeOrigin: true, 13 | }, 14 | } 15 | }, 16 | base:'./' 17 | }) --------------------------------------------------------------------------------