Merge pull request #81 from imsyy/dev

feat: 支持手机号登录 #67
This commit is contained in:
底层用户
2023-10-25 16:34:15 +08:00
committed by GitHub
9 changed files with 330 additions and 486 deletions

7
.env
View File

@@ -1,7 +1,7 @@
# 全局 API 地址
## 需部署 API详见 https://github.com/Binaryify/NeteaseCloudMusicApi
VITE_MUSIC_API = "https://api-music.imsyy.top/"
# VITE_MUSIC_API = "http://localhost:3000/"
# 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
@@ -17,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"

View File

@@ -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`,故网页端替换可能不全面

View File

@@ -3,17 +3,16 @@
<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 %>" />
<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" />
<!-- <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 %>" />
<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" />
@@ -28,18 +27,6 @@
"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%;
@@ -67,8 +54,8 @@
<body>
<div id="app"></div>
<noscript>
<img src="<%- logo %>" alt="logo" />
<p class="title"><%- title %></p>
<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>

View File

@@ -1,6 +1,6 @@
{
"name": "splayer",
"version": "1.1.8",
"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,8 +37,8 @@
"naive-ui": "^2.34.4",
"unplugin-auto-import": "^0.12.0",
"unplugin-vue-components": "^0.22.11",
"vite": "^4.3.8",
"vite": "^4.4.9",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.15.0"
"vite-plugin-pwa": "^0.16.4"
}
}

619
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -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: {

View File

@@ -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: {

View File

@@ -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"));
}
});
};

View File

@@ -2,7 +2,6 @@ import { fileURLToPath, URL } from "node:url";
import { defineConfig, loadEnv } from "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";
@@ -29,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",