mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-26 03:44:57 +08:00
Compare commits
16 Commits
v1.1.7_dev
...
old
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d48c8b1aa4 | ||
|
|
9fa5935929 | ||
|
|
ab0fb19585 | ||
|
|
67e69e0513 | ||
|
|
540b0c5855 | ||
|
|
52f15a044f | ||
|
|
b5ec3aaee3 | ||
|
|
91d66801a9 | ||
|
|
73470ef643 | ||
|
|
6563cbf3ab | ||
|
|
b5f193c731 | ||
|
|
f13eaf838f | ||
|
|
7f8dbbaaf8 | ||
|
|
0a6f071bbd | ||
|
|
846691f789 | ||
|
|
17bb9f13b8 |
13
.env
13
.env
@@ -1,10 +1,12 @@
|
||||
# 全局 API 地址
|
||||
## 需部署 API,详见 https://github.com/Binaryify/NeteaseCloudMusicApi
|
||||
VITE_MUSIC_API = "https://api-music.imsyy.top/"
|
||||
# VITE_MUSIC_API = "https://api-music.imsyy.top/"
|
||||
VITE_MUSIC_API = "http://localhost:3000/"
|
||||
|
||||
# 网易云解灰 API 地址(可选功能)
|
||||
## 需部署 API,详见 https://github.com/imsyy/UNM-Server#%E8%BF%90%E8%A1%8C
|
||||
VITE_UNM_API = "https://api-unm.imsyy.top/"
|
||||
# VITE_UNM_API = "http://localhost:5678/"
|
||||
|
||||
# 站点标题
|
||||
VITE_SITE_TITLE = "SPlayer"
|
||||
@@ -15,9 +17,6 @@ VITE_SITE_URL = "imsyy.top"
|
||||
VITE_SITE_LOGO = "/images/logo/favicon.svg"
|
||||
VITE_SITE_APPLE_LOGO = "/images/logo/favicon-apple.png"
|
||||
|
||||
# 百度统计(若不需要,请设为空即可)
|
||||
VITE_SITE_BAIDUTONGJI = "c6579e9a33cbc5260fc90231678556ec"
|
||||
|
||||
# ICP 备案号
|
||||
## 若不需要,请设为空即可
|
||||
VITE_ICP = "豫ICP备2022018134号-1"
|
||||
@@ -25,8 +24,8 @@ VITE_ICP = "豫ICP备2022018134号-1"
|
||||
# 公告配置
|
||||
## 若无需公告,请将任意一项设为空即可
|
||||
## 公告标题
|
||||
VITE_ANN_TITLE = ""
|
||||
VITE_ANN_TITLE = "温馨提醒"
|
||||
## 公告内容
|
||||
VITE_ANN_CONTENT = ""
|
||||
VITE_ANN_CONTENT = "由于演示站访问量随时可能超出,请自行部署后查看效果"
|
||||
## 公告时长(毫秒)不可超过 999999
|
||||
VITE_ANN_DURATION = 3000
|
||||
VITE_ANN_DURATION = 8000
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
## 说明
|
||||
|
||||
> **当前项目正在重构中,当前版本进入维护模式,仅在遇到重大问题时会进行修复**
|
||||
> - 支持客户端与网页端
|
||||
> - 支持现有版本所有功能
|
||||
> - 新增支持播放与管理本地歌曲
|
||||
|
||||
- 本项目采用 [Vue 3](https://cn.vuejs.org/) 全家桶和 [Naïve UI](https://www.naiveui.com/) 组件库及 `SCSS` 开发
|
||||
- 目前主要以 `Web` 端为主,可能暂时不会考虑使用 `Electron` 构建客户端
|
||||
- 仅对移动端做了基础适配,**不保证功能全部可用**
|
||||
@@ -20,7 +25,7 @@
|
||||
## 🎉 功能
|
||||
|
||||
- 支持扫码登录
|
||||
- 支持手机号登录(上游接口暂时无法使用)
|
||||
- 支持手机号登录
|
||||
- 自动进行每日签到及云贝签到
|
||||
- 支持 [UnblockNeteaseMusic](https://github.com/UnblockNeteaseMusic/server),自动替换变灰歌曲
|
||||
- 由于酷我音源不支持 `https`,故网页端替换可能不全面
|
||||
|
||||
137
index.html
137
index.html
@@ -1,79 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="<%- logo %>" />
|
||||
<link rel="apple-touch-icon" href="<%- appleLogo %>" />
|
||||
<link rel="bookmark" href="<%- appleLogo %>" />
|
||||
<link
|
||||
rel="apple-touch-icon-precomposed"
|
||||
sizes="200x200"
|
||||
href="<%- appleLogo %>"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" /> -->
|
||||
<title><%- title %></title>
|
||||
<meta name="apple-mobile-web-app-title" content="<%- title %>" />
|
||||
<meta name="author" content="<%- author %>" />
|
||||
<meta name="keywords" content="<%- keywords %>" />
|
||||
<meta name="description" content="<%- description %>" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<!-- HarmonyOS Sans -->
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css"
|
||||
/>
|
||||
<!-- IE Out -->
|
||||
<script>
|
||||
if (
|
||||
/*@cc_on!@*/ false ||
|
||||
(!!window.MSInputMethodContext && !!document.documentMode)
|
||||
)
|
||||
window.location.href =
|
||||
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" +
|
||||
encodeURIComponent(window.location.href);
|
||||
</script>
|
||||
<% if (tongji) { %>
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?<%- tongji %>";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
<% } %>
|
||||
<style>
|
||||
noscript {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 16px);
|
||||
font-family: "HarmonyOS_Regular", sans-serif;
|
||||
}
|
||||
noscript .title {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-top: 50px;
|
||||
}
|
||||
noscript .tip {
|
||||
opacity: 0.6;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<noscript>
|
||||
<img src="<%- logo %>" alt="logo" />
|
||||
<p class="title"><%- title %></p>
|
||||
<p class="tip">请开启 JavaScript</p>
|
||||
</noscript>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="%VITE_SITE_LOGO%" />
|
||||
<link rel="apple-touch-icon" href="%VITE_SITE_APPLE_LOGO%" />
|
||||
<link rel="bookmark" href="%VITE_SITE_APPLE_LOGO%" />
|
||||
<link rel="apple-touch-icon-precomposed" sizes="200x200" href="%VITE_SITE_APPLE_LOGO%" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>%VITE_SITE_TITLE%</title>
|
||||
<meta name="apple-mobile-web-app-title" content="%VITE_SITE_TITLE%" />
|
||||
<meta name="author" content="%VITE_SITE_ANTHOR%" />
|
||||
<meta name="keywords" content="%VITE_SITE_KEYWORDS%" />
|
||||
<meta name="description" content="%VITE_SITE_DES%" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<!-- HarmonyOS Sans -->
|
||||
<link rel="stylesheet" href="https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css" />
|
||||
<!-- IE Out -->
|
||||
<script>
|
||||
if (
|
||||
/*@cc_on!@*/
|
||||
false ||
|
||||
(!!window.MSInputMethodContext && !!document.documentMode)
|
||||
)
|
||||
window.location.href =
|
||||
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" +
|
||||
encodeURIComponent(window.location.href);
|
||||
</script>
|
||||
<style>
|
||||
noscript {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(100vh - 16px);
|
||||
font-family: "HarmonyOS_Regular", sans-serif;
|
||||
}
|
||||
|
||||
noscript .title {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
noscript .tip {
|
||||
opacity: 0.6;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<noscript>
|
||||
<img src="%VITE_SITE_LOGO%" alt="logo" />
|
||||
<p class="title">%VITE_SITE_TITLE%</p>
|
||||
<p class="tip">请开启 JavaScript</p>
|
||||
</noscript>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "splayer",
|
||||
"version": "1.1.7",
|
||||
"version": "1.1.9",
|
||||
"author": "imsyy",
|
||||
"home": "https://imsyy.top",
|
||||
"github": "https://github.com/imsyy/SPlayer",
|
||||
@@ -24,7 +24,6 @@
|
||||
"screenfull": "^6.0.2",
|
||||
"swiper": "^9.3.2",
|
||||
"throttle-debounce": "^5.0.0",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vue": "^3.2.45",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.1.6",
|
||||
@@ -38,7 +37,8 @@
|
||||
"naive-ui": "^2.34.4",
|
||||
"unplugin-auto-import": "^0.12.0",
|
||||
"unplugin-vue-components": "^0.22.11",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-pwa": "^0.15.0"
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-pwa": "^0.16.4"
|
||||
}
|
||||
}
|
||||
638
pnpm-lock.yaml
generated
638
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -98,6 +98,7 @@ export const getMusicNewLyric = (id) => {
|
||||
export const getMusicDetail = (ids) => {
|
||||
return axios({
|
||||
method: "GET",
|
||||
hiddenBar: true,
|
||||
url: "/song/detail",
|
||||
params: {
|
||||
ids,
|
||||
@@ -142,6 +143,7 @@ export const getSimiSong = (id) => {
|
||||
export const getSongDownload = (id, br = 999000) => {
|
||||
return axios({
|
||||
method: "GET",
|
||||
hiddenBar: true,
|
||||
url: "/song/download/url",
|
||||
params: {
|
||||
id,
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
<Transition mode="out-in">
|
||||
<div class="datalists" id="datalists" v-if="listData[0]">
|
||||
<n-card
|
||||
v-for="item in listData"
|
||||
v-for="(item, index) in listData"
|
||||
:key="item"
|
||||
:id="'song' + index"
|
||||
:class="
|
||||
music.getPlaySongData && music.getPlaySongData.id == item.id
|
||||
music.getPlaySongData && music.getPlaySongData?.id == item?.id
|
||||
? 'songs play'
|
||||
: 'songs'
|
||||
"
|
||||
@@ -30,28 +31,28 @@
|
||||
:src="item.album.picUrl.replace(/^http:/, 'https:') + '?param=60y60'"
|
||||
fallback-src="/images/pic/default.png"
|
||||
/>
|
||||
<div class="num" v-else-if="item.num">
|
||||
<n-text :depth="2" v-html="item.num" />
|
||||
<div class="num" v-else-if="item?.num">
|
||||
<n-text :depth="2" v-html="item?.num" />
|
||||
</div>
|
||||
<div class="name">
|
||||
<div class="title">
|
||||
<n-text
|
||||
class="text-hidden"
|
||||
depth="2"
|
||||
v-html="item.name"
|
||||
@click.stop="jumpLink(item.id, 1)"
|
||||
v-html="item?.name"
|
||||
@click.stop="jumpLink(item?.id, 1)"
|
||||
/>
|
||||
<n-tag
|
||||
v-if="item.fee == 1 || item.fee == 4"
|
||||
v-if="item?.fee == 1 || item?.fee == 4"
|
||||
class="vip"
|
||||
round
|
||||
:bordered="false"
|
||||
size="small"
|
||||
>
|
||||
{{ item.fee == 1 ? "VIP" : "EP" }}
|
||||
{{ item?.fee == 1 ? "VIP" : "EP" }}
|
||||
</n-tag>
|
||||
<n-tag
|
||||
v-if="item.pc"
|
||||
v-if="item?.pc"
|
||||
class="cloud"
|
||||
round
|
||||
type="info"
|
||||
@@ -61,7 +62,7 @@
|
||||
{{ $t("general.name.cloud") }}
|
||||
</n-tag>
|
||||
<n-tag
|
||||
v-if="item.mv"
|
||||
v-if="item?.mv"
|
||||
class="mv"
|
||||
round
|
||||
type="warning"
|
||||
@@ -74,19 +75,19 @@
|
||||
</div>
|
||||
<div class="meta">
|
||||
<AllArtists
|
||||
v-if="item.artist"
|
||||
v-if="item?.artist"
|
||||
class="text-hidden"
|
||||
:artistsData="item.artist"
|
||||
:artistsData="item?.artist"
|
||||
/>
|
||||
<n-text
|
||||
class="alia text-hidden"
|
||||
depth="3"
|
||||
v-if="item.alia && item.alia[0]"
|
||||
v-if="item?.alia[0]"
|
||||
v-html="item.alia[0]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="album" v-if="!hideAlbum && item.album">
|
||||
<div class="album" v-if="!hideAlbum && item?.album">
|
||||
<n-text
|
||||
v-html="item.album.name"
|
||||
@click.stop="jumpLink(item.album.id, 10)"
|
||||
@@ -97,13 +98,13 @@
|
||||
class="like"
|
||||
size="20"
|
||||
@click.stop="
|
||||
music.getSongIsLike(item.id)
|
||||
? music.changeLikeList(item.id, false)
|
||||
: music.changeLikeList(item.id, true)
|
||||
music.getSongIsLike(item?.id)
|
||||
? music.changeLikeList(item?.id, false)
|
||||
: music.changeLikeList(item?.id, true)
|
||||
"
|
||||
>
|
||||
<Like
|
||||
:theme="music.getSongIsLike(item.id) ? 'filled' : 'outline'"
|
||||
:theme="music.getSongIsLike(item?.id) ? 'filled' : 'outline'"
|
||||
/>
|
||||
</n-icon>
|
||||
<n-icon
|
||||
@@ -269,6 +270,27 @@
|
||||
{{ $t("general.name.album") }}: {{ drawerData.album.name }}
|
||||
</n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="router.currentRoute.value.name === 'user-cloud'"
|
||||
class="item"
|
||||
@click="
|
||||
() => {
|
||||
router.push({
|
||||
path: '/search/songs',
|
||||
query: {
|
||||
keywords: drawerData.name,
|
||||
page: 1,
|
||||
},
|
||||
});
|
||||
drawerShow = false;
|
||||
}
|
||||
"
|
||||
>
|
||||
<n-icon size="20">
|
||||
<Search theme="filled" />
|
||||
</n-icon>
|
||||
<n-text>{{ $t("menu.search") }}</n-text>
|
||||
</div>
|
||||
<div
|
||||
v-if="router.currentRoute.value.name === 'user-cloud'"
|
||||
class="item"
|
||||
@@ -596,7 +618,7 @@ const openDrawer = (data) => {
|
||||
// 播放并添加
|
||||
const playSong = (data, song) => {
|
||||
console.log(data, song);
|
||||
if (music.getPersonalFmMode) {
|
||||
if (music.getPersonalFmMode && typeof $player !== "undefined") {
|
||||
soundStop($player);
|
||||
music.setPersonalFmMode(false);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,19 @@
|
||||
:placeholder="$t('other.asIdDes')"
|
||||
:show-button="false"
|
||||
/>
|
||||
<n-popover trigger="hover" :keep-alive-on-hover="false">
|
||||
<template #trigger>
|
||||
<n-button
|
||||
style="margin-left: 12px"
|
||||
@click="toSearch(cloudMatchBeforeData.name)"
|
||||
>
|
||||
<template #icon>
|
||||
<n-icon :component="Search" />
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
{{ $t("menu.search") }}
|
||||
</n-popover>
|
||||
<n-button
|
||||
style="margin-left: 12px"
|
||||
:disabled="!cloudMatchValue.asid"
|
||||
@@ -61,6 +74,7 @@
|
||||
import { setCloudMatch } from "@/api/user";
|
||||
import { userStore } from "@/store";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Search } from "@icon-park/vue-next";
|
||||
import SmallSongData from "@/components/DataList/SmallSongData.vue";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -102,6 +116,11 @@ const setCloudMatchBtn = (data) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 同名搜索
|
||||
const toSearch = (name) => {
|
||||
window.open(`/search/songs?keywords=${name}&page=1`);
|
||||
};
|
||||
|
||||
// 开启歌曲纠正
|
||||
const openCloudMatch = (data) => {
|
||||
cloudMatchValue.value.sid = data.id;
|
||||
|
||||
@@ -114,7 +114,7 @@ const renderIcon = (icon) => {
|
||||
return () => {
|
||||
return h(
|
||||
NIcon,
|
||||
{ style: { transform: "translateX(2px)" } },
|
||||
{ style: { transform: "translateX(2px) translateY(1px)" } },
|
||||
{
|
||||
default: () => icon,
|
||||
}
|
||||
@@ -257,7 +257,7 @@ const changeDropdownOptions = () => {
|
||||
label: () => {
|
||||
return h(
|
||||
NText,
|
||||
{ style: { transform: "translateX(2px)" } },
|
||||
{ style: { transform: "translateX(2px) translateY(1px)" } },
|
||||
{
|
||||
default: () =>
|
||||
setting.getSiteTheme == "light"
|
||||
@@ -270,7 +270,7 @@ const changeDropdownOptions = () => {
|
||||
icon: () => {
|
||||
return h(
|
||||
NIcon,
|
||||
{ style: { transform: "translateX(2px)" } },
|
||||
{ style: { transform: "translateX(2px) translateY(1px)" } },
|
||||
{
|
||||
default: () =>
|
||||
setting.getSiteTheme == "light" ? h(Moon) : h(SunOne),
|
||||
@@ -292,7 +292,7 @@ const changeDropdownOptions = () => {
|
||||
label: () => {
|
||||
return h(
|
||||
NText,
|
||||
{ style: { transform: "translateX(2px)" } },
|
||||
{ style: { transform: "translateX(2px) translateY(1px)" } },
|
||||
{
|
||||
default: () =>
|
||||
user.userLogin ? t("nav.avatar.logout") : t("nav.avatar.login"),
|
||||
@@ -303,7 +303,7 @@ const changeDropdownOptions = () => {
|
||||
icon: () => {
|
||||
return h(
|
||||
NIcon,
|
||||
{ style: { transform: "translateX(2px)" } },
|
||||
{ style: { transform: "translateX(2px) translateY(1px)" } },
|
||||
{
|
||||
default: () => (user.userLogin ? h(Logout) : h(Login)),
|
||||
}
|
||||
|
||||
@@ -1,71 +1,74 @@
|
||||
<template>
|
||||
<div
|
||||
class="papersonalfm"
|
||||
v-if="music.getPersonalFmData?.id"
|
||||
:style="`background-image: url(${music.getPersonalFmData.album.picUrl.replace(
|
||||
/^http:/,
|
||||
'https:'
|
||||
)}?param=300y300)`"
|
||||
>
|
||||
<div class="gray" />
|
||||
<img
|
||||
class="pic"
|
||||
:src="
|
||||
music.getPersonalFmData.album.picUrl.replace(/^http:/, 'https:') +
|
||||
'?param=300y300'
|
||||
"
|
||||
alt="pic"
|
||||
/>
|
||||
<div class="data">
|
||||
<div class="info">
|
||||
<span
|
||||
class="name text-hidden"
|
||||
@click="router.push(`/song?id=${music.getPersonalFmData.id}`)"
|
||||
>
|
||||
{{ music.getPersonalFmData.name }}
|
||||
</span>
|
||||
<AllArtists
|
||||
class="text-hidden"
|
||||
:artistsData="music.getPersonalFmData.artist"
|
||||
/>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<n-icon
|
||||
class="state"
|
||||
size="46"
|
||||
:component="
|
||||
music.getPersonalFmMode
|
||||
? music.getPlayState
|
||||
? PauseCircleFilled
|
||||
: PlayCircleFilled
|
||||
: PlayCircleFilled
|
||||
"
|
||||
@click="fmPlayOrPause"
|
||||
/>
|
||||
<n-icon
|
||||
class="next"
|
||||
size="30"
|
||||
:component="SkipNextRound"
|
||||
@click="fmNext"
|
||||
/>
|
||||
<n-icon
|
||||
class="dislike"
|
||||
size="20"
|
||||
:component="ThumbDownRound"
|
||||
@click="music.setFmDislike(music.getPersonalFmData.id)"
|
||||
/>
|
||||
<div class="radio">
|
||||
<div class="icon">
|
||||
<n-icon size="20" :component="RadioFilled" />
|
||||
<span>{{ $t("home.modules.papersonalfm.title") }}</span>
|
||||
</div>
|
||||
<span class="tip" v-if="!user.userLogin">
|
||||
{{ $t("home.modules.papersonalfm.subtitle") }}
|
||||
<Transition mode="out-in">
|
||||
<div
|
||||
v-if="music.getPersonalFmData?.id"
|
||||
class="papersonalfm"
|
||||
:style="`background-image: url(${music.getPersonalFmData.album.picUrl.replace(
|
||||
/^http:/,
|
||||
'https:'
|
||||
)}?param=300y300)`"
|
||||
>
|
||||
<div class="gray" />
|
||||
<img
|
||||
class="pic"
|
||||
:src="
|
||||
music.getPersonalFmData.album.picUrl.replace(/^http:/, 'https:') +
|
||||
'?param=300y300'
|
||||
"
|
||||
alt="pic"
|
||||
/>
|
||||
<div class="data">
|
||||
<div class="info">
|
||||
<span
|
||||
class="name text-hidden"
|
||||
@click="router.push(`/song?id=${music.getPersonalFmData.id}`)"
|
||||
>
|
||||
{{ music.getPersonalFmData.name }}
|
||||
</span>
|
||||
<AllArtists
|
||||
class="text-hidden"
|
||||
:artistsData="music.getPersonalFmData.artist"
|
||||
/>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<n-icon
|
||||
class="state"
|
||||
size="46"
|
||||
:component="
|
||||
music.getPersonalFmMode
|
||||
? music.getPlayState
|
||||
? PauseCircleFilled
|
||||
: PlayCircleFilled
|
||||
: PlayCircleFilled
|
||||
"
|
||||
@click="fmPlayOrPause"
|
||||
/>
|
||||
<n-icon
|
||||
class="next"
|
||||
size="30"
|
||||
:component="SkipNextRound"
|
||||
@click="fmNext"
|
||||
/>
|
||||
<n-icon
|
||||
class="dislike"
|
||||
size="20"
|
||||
:component="ThumbDownRound"
|
||||
@click="music.setFmDislike(music.getPersonalFmData.id)"
|
||||
/>
|
||||
<div class="radio">
|
||||
<div class="icon">
|
||||
<n-icon size="20" :component="RadioFilled" />
|
||||
<span>{{ $t("home.modules.papersonalfm.title") }}</span>
|
||||
</div>
|
||||
<span class="tip" v-if="!user.userLogin">
|
||||
{{ $t("home.modules.papersonalfm.subtitle") }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<n-skeleton v-else class="papersonalfm" />
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
v-show="music.getPlaylists[0] && music.showPlayBar"
|
||||
class="player"
|
||||
content-style="padding: 0"
|
||||
@click.stop="setting.bottomClick ? music.setBigPlayerState(true) : null"
|
||||
>
|
||||
<div class="slider">
|
||||
<span>{{ music.getPlaySongTime.songTimePlayed }}</span>
|
||||
@@ -64,7 +65,7 @@
|
||||
<Transition name="fade" mode="out-in">
|
||||
<AllArtists
|
||||
v-if="
|
||||
!music.getPlayState || !music.getPlaySongLyric.lrc[0]
|
||||
!music.getPlayState || !music.getPlaySongLyric?.lrc[0]
|
||||
"
|
||||
class="text-hidden"
|
||||
:artistsData="music.getPlaySongData.artist"
|
||||
@@ -90,15 +91,24 @@
|
||||
<n-text
|
||||
v-else-if="
|
||||
music.getPlaySongLyricIndex != -1 &&
|
||||
music.getPlaySongLyric.lrc[0]
|
||||
music.getPlaySongLyric?.lrc[0]
|
||||
"
|
||||
class="lrc text-hidden"
|
||||
:depth="3"
|
||||
v-html="
|
||||
music.getPlaySongLyric.lrc[music.getPlaySongLyricIndex]
|
||||
.content
|
||||
"
|
||||
/>
|
||||
class="lrc"
|
||||
>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<n-text
|
||||
class="text-hidden"
|
||||
:key="music.getPlaySongLyricIndex"
|
||||
:depth="3"
|
||||
>
|
||||
{{
|
||||
music.getPlaySongLyric.lrc[
|
||||
music.getPlaySongLyricIndex
|
||||
]?.content
|
||||
}}
|
||||
</n-text>
|
||||
</Transition>
|
||||
</n-text>
|
||||
<AllArtists
|
||||
v-else
|
||||
class="text-hidden"
|
||||
@@ -148,7 +158,11 @@
|
||||
/>
|
||||
</div>
|
||||
<div :class="music.getPersonalFmMode ? 'menu fm' : 'menu'">
|
||||
<n-popover v-if="music.getPlaySongData" trigger="hover" :keep-alive-on-hover="false">
|
||||
<n-popover
|
||||
v-if="music.getPlaySongData"
|
||||
trigger="hover"
|
||||
:keep-alive-on-hover="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<div class="like">
|
||||
<n-icon
|
||||
@@ -203,7 +217,7 @@
|
||||
? ShuffleOne
|
||||
: PlayOnce
|
||||
"
|
||||
@click="music.setPlaySongMode()"
|
||||
@click.stop="music.setPlaySongMode()"
|
||||
/>
|
||||
</div>
|
||||
</n-dropdown>
|
||||
@@ -220,19 +234,32 @@
|
||||
{{ $t("general.name.playlists") }}
|
||||
</n-popover>
|
||||
<div class="volume">
|
||||
<n-icon
|
||||
size="28"
|
||||
:component="
|
||||
persistData.playVolume == 0
|
||||
? VolumeOffRound
|
||||
: persistData.playVolume < 0.4
|
||||
? VolumeMuteRound
|
||||
: persistData.playVolume < 0.7
|
||||
? VolumeDownRound
|
||||
: VolumeUpRound
|
||||
"
|
||||
@click.stop="volumeMute"
|
||||
/>
|
||||
<n-popover
|
||||
trigger="hover"
|
||||
placement="top-start"
|
||||
:keep-alive-on-hover="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<n-icon
|
||||
size="28"
|
||||
:component="
|
||||
persistData.playVolume == 0
|
||||
? VolumeOffRound
|
||||
: persistData.playVolume < 0.4
|
||||
? VolumeMuteRound
|
||||
: persistData.playVolume < 0.7
|
||||
? VolumeDownRound
|
||||
: VolumeUpRound
|
||||
"
|
||||
@click.stop="volumeMute"
|
||||
/>
|
||||
</template>
|
||||
{{
|
||||
persistData.playVolume > 0
|
||||
? $t("general.name.mute")
|
||||
: $t("general.name.unmute")
|
||||
}}
|
||||
</n-popover>
|
||||
<n-slider
|
||||
class="volmePg"
|
||||
v-model:value="persistData.playVolume"
|
||||
|
||||
@@ -71,12 +71,21 @@ const osThemeChange = (val) => {
|
||||
|
||||
// 配置主题色
|
||||
const changeThemeColor = (val) => {
|
||||
const color = themeColorData[val];
|
||||
console.log("当前主题色:" + val, color);
|
||||
themeOverrides.value = {
|
||||
common: color,
|
||||
};
|
||||
setting.themeData = color;
|
||||
let color = null;
|
||||
if (val !== "custom") {
|
||||
color = themeColorData[val];
|
||||
console.log("当前主题色:" + val, color);
|
||||
themeOverrides.value = {
|
||||
common: color,
|
||||
};
|
||||
setting.themeData = color;
|
||||
} else {
|
||||
color = setting.themeData;
|
||||
console.log("当前主题色为自定义:" + val, color);
|
||||
themeOverrides.value = {
|
||||
common: color,
|
||||
};
|
||||
}
|
||||
setCssVariable("--main-color", color.primaryColor);
|
||||
setCssVariable("--main-second-color", color.primaryColor + "1f");
|
||||
setCssVariable("--main-boxshadow-color", color.primaryColor + "26");
|
||||
@@ -125,6 +134,10 @@ watch(
|
||||
() => setting.themeType,
|
||||
(val) => changeThemeColor(val)
|
||||
);
|
||||
watch(
|
||||
() => setting.themeData,
|
||||
(val) => changeThemeColor(val.label)
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
changeTheme();
|
||||
|
||||
@@ -83,14 +83,18 @@ export default {
|
||||
qr: "QR",
|
||||
phone: "Captcha",
|
||||
email: "Email",
|
||||
canNotUse: "This login method is temporarily unavailable",
|
||||
getCode: "Get the verification code",
|
||||
getCodeAgain: "Try again",
|
||||
codeSuccess: "Verification code sent successfully",
|
||||
codeError: "Failed to send verification code, please try again",
|
||||
canNotUse: "This login mode is not secure and is temporarily disabled",
|
||||
loggedIn: "Already logged in, please don't log in again",
|
||||
qrText1: "Please open APP and scan the code to login",
|
||||
qrText2: "The current QR code is invalid, please scan it again",
|
||||
qrText3: "Scan successfully, please confirm login in the client",
|
||||
qrText4: "Login successfully",
|
||||
qrText5: "Login error, please try again",
|
||||
qrText6: "Login QR code generation failed",
|
||||
loginStatus1: "Please open APP and scan the code to login",
|
||||
loginStatus2: "The current QR code is invalid, please scan it again",
|
||||
loginStatus3: "Scan successfully, please confirm login in the client",
|
||||
loginStatus4: "Login successfully",
|
||||
loginStatus5: "Login error, please try again",
|
||||
loginStatus6: "Login QR code generation failed",
|
||||
},
|
||||
// Menu
|
||||
menu: {
|
||||
@@ -187,6 +191,10 @@ export default {
|
||||
random: "Random play",
|
||||
single: "Single loop",
|
||||
normal: "list loop",
|
||||
mute: "Mute",
|
||||
unmute: "Unmute",
|
||||
customTheme: "Custom theme",
|
||||
primaryColor: "Primary Color",
|
||||
},
|
||||
dialog: {
|
||||
check: "Check",
|
||||
@@ -366,5 +374,7 @@ export default {
|
||||
lyricsBlur: "Lyric Blur",
|
||||
lyricsBlurTip:
|
||||
"Blur lyrics other than the currently playing ones, experimental feature",
|
||||
bottomClick: "Bottomclick to expand Player ",
|
||||
bottomClickTip: "It may cause mistouch, please open with caution ",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -83,14 +83,18 @@ export default {
|
||||
qr: "扫码登录",
|
||||
phone: "验证码登录",
|
||||
email: "邮箱登录",
|
||||
canNotUse: "该登录方式暂时无法使用",
|
||||
getCode: "获取验证码",
|
||||
getCodeAgain: "重新获取",
|
||||
codeSuccess: "验证码发送成功",
|
||||
codeError: "验证码发送失败,请重试",
|
||||
canNotUse: "该登录方式不安全,暂时禁用",
|
||||
loggedIn: "已登录,请勿重复登录",
|
||||
qrText1: "请打开云音乐 APP 扫码登录",
|
||||
qrText2: "当前二维码已失效,请重新扫码",
|
||||
qrText3: "扫描成功,请在客户端确认登录",
|
||||
qrText4: "登录成功",
|
||||
qrText5: "登录出错,请重试",
|
||||
qrText6: "登录二维码生成失败",
|
||||
loginStatus1: "请打开云音乐 APP 扫码登录",
|
||||
loginStatus2: "当前二维码已失效,请重新扫码",
|
||||
loginStatus3: "扫描成功,请在客户端确认登录",
|
||||
loginStatus4: "登录成功",
|
||||
loginStatus5: "登录出错,请重试",
|
||||
loginStatus6: "登录二维码生成失败",
|
||||
},
|
||||
// 菜单
|
||||
menu: {
|
||||
@@ -185,6 +189,10 @@ export default {
|
||||
random: "随机播放",
|
||||
single: "单曲循环",
|
||||
normal: "列表循环",
|
||||
mute: "静音",
|
||||
unmute: "取消静音",
|
||||
customTheme: "自定义主题",
|
||||
primaryColor: "主色",
|
||||
},
|
||||
dialog: {
|
||||
check: "检查",
|
||||
@@ -208,7 +216,7 @@ export default {
|
||||
createFailed: "歌单新建失败,请重试",
|
||||
deleteSuccess: "删除成功",
|
||||
deleteFailure: "删除失败",
|
||||
downloadSuccess: "{name}下载完成",
|
||||
downloadSuccess: "{name} 下载完成",
|
||||
downloadFailure: "下载失败,请尝试其他音质",
|
||||
downloadError: "下载出现错误,请重试",
|
||||
upCloudSuccess: "{name} 上传成功",
|
||||
@@ -349,5 +357,7 @@ export default {
|
||||
positionCenter: "居中",
|
||||
lyricsBlur: "歌词模糊",
|
||||
lyricsBlurTip: "除当前播放歌词外模糊显示,实验性功能",
|
||||
bottomClick: "点击底栏展开播放器",
|
||||
bottomClickTip: "可能会造成误触,请谨慎开启",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import routes from "./routes";
|
||||
import { getLoginState } from "@/api/login";
|
||||
import { userStore } from "@/store";
|
||||
import { userStore, musicStore } from "@/store";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -11,6 +11,10 @@ const router = createRouter({
|
||||
// 路由守卫
|
||||
router.beforeEach((to, from, next) => {
|
||||
const user = userStore();
|
||||
const music = musicStore();
|
||||
// 关闭播放器
|
||||
music.setBigPlayerState(false);
|
||||
// 开始进度条
|
||||
if (typeof $loadingBar !== "undefined") $loadingBar.start();
|
||||
// 判断是否需要登录
|
||||
if (to.meta.needLogin) {
|
||||
|
||||
@@ -311,7 +311,13 @@ const useMusicDataStore = defineStore("musicData", {
|
||||
}
|
||||
} else {
|
||||
console.log("该歌曲暂无歌词");
|
||||
this.playSongLyric = [];
|
||||
this.playSongLyric = {
|
||||
lrc: [],
|
||||
yrc: [],
|
||||
hasTran: false,
|
||||
hasTran: false,
|
||||
hasYrc: false,
|
||||
};
|
||||
}
|
||||
},
|
||||
// 歌曲播放进度
|
||||
@@ -339,7 +345,7 @@ const useMusicDataStore = defineStore("musicData", {
|
||||
const setting = settingStore();
|
||||
const lrcType = !this.playSongLyric.hasYrc || !setting.showYrc;
|
||||
const lyrics = lrcType ? this.playSongLyric.lrc : this.playSongLyric.yrc;
|
||||
const index = lyrics.findIndex((v) => v.time >= value.currentTime);
|
||||
const index = lyrics?.findIndex((v) => v?.time >= value?.currentTime);
|
||||
this.playSongLyricIndex = index === -1 ? lyrics.length - 1 : index - 1;
|
||||
},
|
||||
// 设置当前播放模式
|
||||
@@ -376,49 +382,63 @@ const useMusicDataStore = defineStore("musicData", {
|
||||
},
|
||||
// 上下曲调整
|
||||
setPlaySongIndex(type) {
|
||||
// 如果 $player 未定义,返回 false
|
||||
if (typeof $player === "undefined") return false;
|
||||
// 停止播放当前歌曲
|
||||
soundStop($player);
|
||||
this.isLoadingSong = true;
|
||||
// 根据播放模式设置加载状态
|
||||
if (this.persistData.playSongMode !== "single") {
|
||||
this.isLoadingSong = true;
|
||||
}
|
||||
// 如果是个人 FM 模式,设置个人 FM 数据
|
||||
if (this.persistData.personalFmMode) {
|
||||
this.setPersonalFmData();
|
||||
} else {
|
||||
let listLength = this.persistData.playlists.length;
|
||||
let listMode = this.persistData.playSongMode;
|
||||
// 根据当前播放模式调整
|
||||
const listLength = this.persistData.playlists.length;
|
||||
const listMode = this.persistData.playSongMode;
|
||||
// 根据当前播放模式调整播放索引
|
||||
if (listMode === "normal") {
|
||||
this.persistData.playSongIndex += type === "next" ? 1 : -1;
|
||||
} else if (listMode === "random") {
|
||||
this.persistData.playSongIndex = Math.floor(
|
||||
Math.random() * listLength
|
||||
);
|
||||
} else if (listMode === "single" && typeof $player !== "undefined") {
|
||||
soundStop($player);
|
||||
} else if (listMode === "single") {
|
||||
// 单曲循环模式
|
||||
console.log("单曲循环模式");
|
||||
fadePlayOrPause($player, "play", this.persistData.playVolume);
|
||||
} else {
|
||||
// 未知播放模式,显示错误消息
|
||||
$message.error(getLanguageData("playError"));
|
||||
}
|
||||
// 判断是否处于最后/第一首
|
||||
if (this.persistData.playSongIndex < 0) {
|
||||
this.persistData.playSongIndex = listLength - 1;
|
||||
} else if (this.persistData.playSongIndex >= listLength) {
|
||||
this.persistData.playSongIndex = 0;
|
||||
soundStop($player);
|
||||
fadePlayOrPause($player, "play", this.persistData.playVolume);
|
||||
// 检查播放索引是否越界,并根据情况进行处理
|
||||
if (listMode !== "single") {
|
||||
if (this.persistData.playSongIndex < 0) {
|
||||
this.persistData.playSongIndex = listLength - 1;
|
||||
} else if (this.persistData.playSongIndex >= listLength) {
|
||||
this.persistData.playSongIndex = 0;
|
||||
soundStop($player);
|
||||
fadePlayOrPause($player, "play", this.persistData.playVolume);
|
||||
}
|
||||
// 如果播放列表长度大于 1,则停止播放当前歌曲
|
||||
if (listLength > 1) {
|
||||
soundStop($player);
|
||||
}
|
||||
// 在下一个事件循环中设置播放状态
|
||||
nextTick().then(() => {
|
||||
this.setPlayState(true);
|
||||
});
|
||||
}
|
||||
if (listMode !== "single" && listLength > 1) {
|
||||
soundStop($player);
|
||||
}
|
||||
nextTick().then(() => {
|
||||
this.setPlayState(true);
|
||||
});
|
||||
}
|
||||
},
|
||||
// 添加歌曲至播放列表
|
||||
addSongToPlaylists(value, play = true) {
|
||||
// 停止当前播放
|
||||
if (typeof $player !== "undefined") soundStop($player);
|
||||
// 判断与上一次播放歌曲是否一致
|
||||
const index = this.persistData.playlists.findIndex(
|
||||
(o) => o.id === value.id
|
||||
);
|
||||
// 判断与上一次播放歌曲是否一致
|
||||
try {
|
||||
if (
|
||||
value.id !==
|
||||
|
||||
@@ -63,6 +63,8 @@ const useSettingDataStore = defineStore("settingData", {
|
||||
memoryLastPlaybackPosition: true,
|
||||
// 语言
|
||||
language: "zh-CN",
|
||||
// 底栏点击展开播放器
|
||||
bottomClick: false,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
|
||||
@@ -113,6 +113,22 @@ body,
|
||||
}
|
||||
}
|
||||
|
||||
// NSlider
|
||||
.n-slider {
|
||||
&:hover {
|
||||
.n-slider-rail {
|
||||
.n-slider-rail__fill {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.n-slider-rail {
|
||||
.n-slider-rail__fill {
|
||||
background-color: var(--main-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 文本超出隐藏
|
||||
.text-hidden {
|
||||
display: -webkit-box !important;
|
||||
|
||||
@@ -79,21 +79,23 @@ export const createSound = (src, autoPlay = true) => {
|
||||
}
|
||||
testNumber = 0;
|
||||
music.setPlayState(true);
|
||||
const songName = music.getPlaySongData.name;
|
||||
const songArtist = music.getPlaySongData.artist[0].name;
|
||||
const songName = music.getPlaySongData?.name;
|
||||
const songArtist = music.getPlaySongData.artist[0]?.name;
|
||||
// 播放通知
|
||||
if (typeof $message !== "undefined") {
|
||||
if (typeof $message !== "undefined" && songArtist !== null) {
|
||||
$message.info(songName + " - " + songArtist, {
|
||||
icon: () =>
|
||||
h(NIcon, null, {
|
||||
default: () => h(MusicNoteFilled),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
$message.warning(getLanguageData("songNotDetails"));
|
||||
}
|
||||
console.log("开始播放:" + songName + " - " + songArtist);
|
||||
setMediaSession(music);
|
||||
// 获取播放器信息
|
||||
timeupdateInterval = setInterval(() => checkAudioTime(sound, music), 250);
|
||||
setMediaSession(music);
|
||||
// 写入播放历史
|
||||
music.setPlayHistory(music.getPlaySongData);
|
||||
// 播放时页面标题
|
||||
|
||||
@@ -36,6 +36,7 @@ const languageData = {
|
||||
songLoadError: "音乐数据获取失败",
|
||||
songPlayError: "歌曲播放失败",
|
||||
songLoadTest: "歌曲重试次数过多,请刷新后重试",
|
||||
songNotDetails: "歌曲详细信息获取失败,可尝试歌曲匹配",
|
||||
},
|
||||
en: {
|
||||
million: "M",
|
||||
@@ -66,6 +67,7 @@ const languageData = {
|
||||
playError: "Playback error, please refresh and try again",
|
||||
addSongToNext: "has been added to the next song to play",
|
||||
removeSong: "has been removed from the playlist",
|
||||
songNotDetails: "Song details failed to get, try song match",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -42,16 +42,16 @@ axios.interceptors.response.use(
|
||||
const data = error.response.data;
|
||||
switch (error.response.status) {
|
||||
case 401:
|
||||
console.error("无权限访问");
|
||||
console.error(data.message ? data.message : "无权限访问");
|
||||
break;
|
||||
case 301:
|
||||
console.error("请求发生重定向");
|
||||
console.error(data.message ? data.message : "请求发生重定向");
|
||||
break;
|
||||
case 404:
|
||||
console.error("请求资源不存在");
|
||||
console.error(data.message ? data.message : "请求资源不存在");
|
||||
break;
|
||||
case 500:
|
||||
console.error("内部服务器错误");
|
||||
console.error(data.message ? data.message : "内部服务器错误");
|
||||
break;
|
||||
default:
|
||||
console.error(data.message ? data.message : "请求失败,请稍后重试");
|
||||
|
||||
@@ -35,16 +35,10 @@
|
||||
:foreground="setting.themeData.primaryColor"
|
||||
/>
|
||||
</n-card>
|
||||
<span class="tip">{{ qrText }}</span>
|
||||
<span class="tip">{{ loginStatus }}</span>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="phone" :tab="$t('login.phone')">
|
||||
<n-alert
|
||||
style="width: 100%; margin-top: -20px; margin-bottom: 12px"
|
||||
type="warning"
|
||||
>
|
||||
{{ $t("login.canNotUse") }}
|
||||
</n-alert>
|
||||
<!-- <n-form
|
||||
<n-form
|
||||
class="phone"
|
||||
ref="phoneFormRef"
|
||||
:model="phoneFormData"
|
||||
@@ -83,10 +77,10 @@
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button style="width: 100%" type="primary" @click="phoneLogin">
|
||||
{{$t("login.login")}}
|
||||
{{ $t("login.login") }}
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form> -->
|
||||
</n-form>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="email" :tab="$t('login.email')">
|
||||
<n-alert
|
||||
@@ -126,7 +120,7 @@ const { numberRule, mobileRule } = formRules();
|
||||
|
||||
// 二维码数据
|
||||
const qrImg = ref(null);
|
||||
const qrText = ref(t("login.qrText1"));
|
||||
const loginStatus = ref(t("login.loginStatus1"));
|
||||
|
||||
// 手机号登录数据
|
||||
const phoneFormRef = ref(null);
|
||||
@@ -139,7 +133,7 @@ const phoneFormRules = {
|
||||
captcha: numberRule,
|
||||
};
|
||||
const captchaTimeOut = ref(null);
|
||||
const captchaText = ref("获取验证码");
|
||||
const captchaText = ref(t("login.getCode"));
|
||||
const captchaDisabled = ref(false);
|
||||
|
||||
// 定时器
|
||||
@@ -157,15 +151,15 @@ const saveLoginData = (data) => {
|
||||
if (res.data.profile) {
|
||||
user.setUserData(res.data.profile);
|
||||
user.userLogin = true;
|
||||
qrText.value = t("login.qrText4");
|
||||
$message.success(t("login.qrText4"));
|
||||
loginStatus.value = t("login.loginStatus4");
|
||||
$message.success(t("login.loginStatus4"));
|
||||
// 自动签到
|
||||
if ($signIn) $signIn();
|
||||
clearInterval(qrCheckInterval.value);
|
||||
router.push("/user");
|
||||
} else {
|
||||
user.userLogOut();
|
||||
$message.error(t("login.qrText5"));
|
||||
$message.error(t("login.loginStatus5"));
|
||||
getQrKeyData();
|
||||
}
|
||||
});
|
||||
@@ -188,7 +182,7 @@ const getQrKeyData = () => {
|
||||
qrImg.value = `https://music.163.com/login?codekey=${res.data.unikey}`;
|
||||
checkQrState(res.data.unikey);
|
||||
} else {
|
||||
$message.error(t("login.qrText6"));
|
||||
$message.error(t("login.loginStatus6"));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -204,14 +198,14 @@ const checkQrState = (key) => {
|
||||
if (res.code == 800) {
|
||||
getQrKeyData();
|
||||
loginStateMessage.value = null;
|
||||
qrText.value = t("login.qrText2");
|
||||
loginStatus.value = t("login.loginStatus2");
|
||||
} else if (res.code == 801) {
|
||||
loginStateMessage.value = null;
|
||||
qrText.value = t("login.qrText1");
|
||||
loginStatus.value = t("login.loginStatus1");
|
||||
} else if (res.code == 802) {
|
||||
qrText.value = t("login.qrText3");
|
||||
loginStatus.value = t("login.loginStatus3");
|
||||
if (!loginStateMessage.value) {
|
||||
loginStateMessage.value = $message.loading(t("login.qrText3"), {
|
||||
loginStateMessage.value = $message.loading(t("login.loginStatus3"), {
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
@@ -229,13 +223,13 @@ const getCaptcha = (data) => {
|
||||
phoneFormRef.value?.validate(
|
||||
(errors) => {
|
||||
if (errors) {
|
||||
$message.error("请输入正确的手机号");
|
||||
$message.error(t("general.message.needCheck"));
|
||||
} else {
|
||||
console.log(data + "发送验证码");
|
||||
sentCaptcha(data).then((res) => {
|
||||
console.log(res);
|
||||
if (res.code == 200) {
|
||||
$message.success("验证码发送成功");
|
||||
$message.success(t("login.codeSuccess"));
|
||||
let countDown = 60;
|
||||
captchaDisabled.value = true;
|
||||
captchaTimeOut.value = setInterval(() => {
|
||||
@@ -243,12 +237,12 @@ const getCaptcha = (data) => {
|
||||
captchaText.value = countDown + "s";
|
||||
if (countDown === 0) {
|
||||
clearInterval(captchaTimeOut.value);
|
||||
captchaText.value = "重新获取";
|
||||
captchaText.value = t("login.getCodeAgain");
|
||||
captchaDisabled.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
$message.error("验证码发送失败,请重试");
|
||||
$message.error(t("login.codeError"));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -265,24 +259,39 @@ const phoneLogin = (e) => {
|
||||
phoneFormRef.value?.validate((errors) => {
|
||||
if (!errors) {
|
||||
console.log("通过");
|
||||
verifyCaptcha(
|
||||
phoneFormData._value.phone,
|
||||
phoneFormData._value.captcha
|
||||
).then((res) => {
|
||||
console.log(res);
|
||||
if (res.code == 200) {
|
||||
toLogin(
|
||||
phoneFormData._value.phone,
|
||||
phoneFormData._value.captcha
|
||||
).then((res) => {
|
||||
console.log(res);
|
||||
// 暂时不支持,等支持了再写
|
||||
});
|
||||
}
|
||||
});
|
||||
verifyCaptcha(phoneFormData._value.phone, phoneFormData._value.captcha)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
if (res.code == 200) {
|
||||
toLogin(
|
||||
phoneFormData._value.phone,
|
||||
phoneFormData._value.captcha
|
||||
).then((res) => {
|
||||
console.log(res);
|
||||
if (res.profile) {
|
||||
saveLoginData(res);
|
||||
user.setUserData(res.profile);
|
||||
user.userLogin = true;
|
||||
$message.success(t("login.loginStatus4"));
|
||||
// 自动签到
|
||||
if ($signIn) $signIn();
|
||||
router.push("/user");
|
||||
} else {
|
||||
user.userLogOut();
|
||||
$message.error(t("login.loginStatus5"));
|
||||
phoneFormData.value.captcha = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
$loadingBar.error();
|
||||
$message.error(t("login.loginStatus5"));
|
||||
});
|
||||
} else {
|
||||
$loadingBar.error();
|
||||
$message.error("请检查您的输入");
|
||||
$message.error(t("general.message.needCheck"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -37,6 +37,13 @@
|
||||
>
|
||||
<n-text v-html="language === 'zh-CN' ? item.name : item.label" />
|
||||
</n-grid-item>
|
||||
<n-grid-item
|
||||
:class="themeType === 'custom' ? 'item check' : 'item'"
|
||||
:style="{ '--color': themeData.primaryColor }"
|
||||
@click="openThemeCustom()"
|
||||
>
|
||||
<n-text v-html="$t('general.name.customTheme')" />
|
||||
</n-grid-item>
|
||||
</n-grid>
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
@@ -98,6 +105,13 @@
|
||||
</div>
|
||||
<n-switch v-model:value="bottomLyricShow" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
{{ $t("setting.bottomClick") }}
|
||||
<span class="tip">{{ $t("setting.bottomClickTip") }}</span>
|
||||
</div>
|
||||
<n-switch v-model:value="bottomClick" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
{{ $t("setting.songVolumeFade") }}
|
||||
@@ -149,6 +163,51 @@
|
||||
</div>
|
||||
<n-switch v-model:value="showLyricSetting" :round="false" />
|
||||
</n-card>
|
||||
<!-- 自定义主题 -->
|
||||
<n-modal
|
||||
class="s-modal"
|
||||
v-model:show="showThemeCustom"
|
||||
preset="card"
|
||||
:title="$t('general.name.customTheme')"
|
||||
:bordered="false"
|
||||
>
|
||||
<n-form class="color-custom" :model="customColorData">
|
||||
<n-form-item
|
||||
:label="$t('general.name.primaryColor')"
|
||||
path="primaryColor"
|
||||
>
|
||||
<n-color-picker v-model:value="customColorData.primaryColor" />
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
:label="$t('general.name.primaryColor') + ' Hover'"
|
||||
path="primaryColorHover"
|
||||
>
|
||||
<n-color-picker v-model:value="customColorData.primaryColorHover" />
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
:label="$t('general.name.primaryColor') + ' Suppl'"
|
||||
path="primaryColorSuppl"
|
||||
>
|
||||
<n-color-picker v-model:value="customColorData.primaryColorSuppl" />
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
:label="$t('general.name.primaryColor') + ' Pressed'"
|
||||
path="primaryColorPressed"
|
||||
>
|
||||
<n-color-picker v-model:value="customColorData.primaryColorPressed" />
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
<template #footer>
|
||||
<n-space justify="end">
|
||||
<n-button @click="showThemeCustom = false">
|
||||
{{ $t("general.dialog.cancel") }}
|
||||
</n-button>
|
||||
<n-button type="primary" @click="setThemeCustom">
|
||||
{{ $t("general.name.customTheme") }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</n-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -172,16 +231,49 @@ const {
|
||||
autoSignIn,
|
||||
searchHistory,
|
||||
themeType,
|
||||
themeData,
|
||||
showLyricSetting,
|
||||
songVolumeFade,
|
||||
useUnmServer,
|
||||
memoryLastPlaybackPosition,
|
||||
language,
|
||||
bottomClick,
|
||||
} = storeToRefs(setting);
|
||||
|
||||
// 国际化
|
||||
const { locale, t } = useI18n();
|
||||
|
||||
// 自定义主题
|
||||
const showThemeCustom = ref(false);
|
||||
const customColorData = ref({
|
||||
primaryColor: null,
|
||||
primaryColorHover: null,
|
||||
primaryColorSuppl: null,
|
||||
primaryColorPressed: null,
|
||||
});
|
||||
|
||||
const openThemeCustom = () => {
|
||||
showThemeCustom.value = true;
|
||||
customColorData.value = {
|
||||
primaryColor: themeData.value.primaryColor,
|
||||
primaryColorHover: themeData.value.primaryColorHover,
|
||||
primaryColorSuppl: themeData.value.primaryColorSuppl,
|
||||
primaryColorPressed: themeData.value.primaryColorPressed,
|
||||
};
|
||||
};
|
||||
|
||||
// 确认自定义颜色
|
||||
const setThemeCustom = () => {
|
||||
console.log(customColorData.value);
|
||||
themeType.value = "custom";
|
||||
themeData.value = {
|
||||
label: "custom",
|
||||
name: t("general.name.customTheme"),
|
||||
...customColorData.value,
|
||||
};
|
||||
showThemeCustom.value = false;
|
||||
};
|
||||
|
||||
// UNM 开关显示
|
||||
const useUnmServerShow = import.meta.env.VITE_UNM_API ? true : false;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
{{
|
||||
$t("general.name.cloudUsed", {
|
||||
used: (100 / (cloudSpace[1] / cloudSpace[0])).toFixed(),
|
||||
remaining: cloudSpace[1] - cloudSpace[0],
|
||||
remaining: (cloudSpace[1] - cloudSpace[0]).toFixed(),
|
||||
})
|
||||
}}
|
||||
</n-text>
|
||||
|
||||
3
vercel.json
Normal file
3
vercel.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rewrites": [{ "source": "/:path*", "destination": "/index.html" }]
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig, loadEnv } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
import { NaiveUiResolver } from "unplugin-vue-components/resolvers";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import { createHtmlPlugin } from "vite-plugin-html";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ mode }) =>
|
||||
@@ -28,21 +28,6 @@ export default ({ mode }) =>
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()],
|
||||
}),
|
||||
createHtmlPlugin({
|
||||
minify: true,
|
||||
template: "index.html",
|
||||
inject: {
|
||||
data: {
|
||||
logo: loadEnv(mode, process.cwd()).VITE_SITE_LOGO,
|
||||
appleLogo: loadEnv(mode, process.cwd()).VITE_SITE_APPLE_LOGO,
|
||||
title: loadEnv(mode, process.cwd()).VITE_SITE_TITLE,
|
||||
author: loadEnv(mode, process.cwd()).VITE_SITE_ANTHOR,
|
||||
keywords: loadEnv(mode, process.cwd()).VITE_SITE_KEYWORDS,
|
||||
description: loadEnv(mode, process.cwd()).VITE_SITE_DES,
|
||||
tongji: loadEnv(mode, process.cwd()).VITE_SITE_BAIDUTONGJI,
|
||||
},
|
||||
},
|
||||
}),
|
||||
// PWA
|
||||
VitePWA({
|
||||
registerType: "autoUpdate",
|
||||
@@ -85,9 +70,11 @@ export default ({ mode }) =>
|
||||
],
|
||||
},
|
||||
}),
|
||||
// viteCompression
|
||||
viteCompression(),
|
||||
],
|
||||
server: {
|
||||
port: 2048,
|
||||
port: 25536,
|
||||
open: true,
|
||||
http: true,
|
||||
ssr: false,
|
||||
|
||||
Reference in New Issue
Block a user