From daa8e7514d287e85d3bbe6db8cb57628ecd31428 Mon Sep 17 00:00:00 2001 From: alger Date: Thu, 7 Aug 2025 22:57:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=20=E6=B7=BB=E5=8A=A0UID=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.mjs | 36 +- src/i18n/lang/en-US/login.ts | 38 ++- src/i18n/lang/ja-JP/login.ts | 22 +- src/i18n/lang/ko-KR/login.ts | 22 +- src/i18n/lang/zh-CN/login.ts | 38 ++- src/i18n/lang/zh-Hant/login.ts | 22 +- src/renderer/api/login.ts | 8 + src/renderer/components/login/CookieLogin.vue | 52 +-- src/renderer/components/login/QrLogin.vue | 53 +-- src/renderer/components/login/UidLogin.vue | 150 +++++++++ src/renderer/layout/components/SearchBar.vue | 5 +- src/renderer/store/modules/user.ts | 22 +- src/renderer/types/music.ts | 261 +++++++++++++++ src/renderer/utils/auth.ts | 125 +++++++ src/renderer/views/login/index.vue | 316 +++++++++++++----- src/renderer/views/user/followers.vue | 10 +- src/renderer/views/user/follows.vue | 10 +- src/renderer/views/user/index.vue | 35 +- 18 files changed, 1030 insertions(+), 195 deletions(-) create mode 100644 src/renderer/components/login/UidLogin.vue create mode 100644 src/renderer/utils/auth.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 4a363ce..52d7192 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -66,14 +66,10 @@ export default [ '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/no-unused-vars': [ - 'warn', - { - argsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^value$|^loading$|^width$|^height$|^showPlaylist$|^id$|^enabledSources$|^progress$|^status$|^success$|^filePath$|^locale$|^channel$|^listener$|^url$|^songId$|^delta$|^item$|^err$|^gradient$|^theme$', - varsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^NONE$|^TIME$|^SONGS$|^PLAYLIST_END$|^c$|^l$|^Window$|^key$|^color$', - ignoreRestSiblings: true - } + 'error', + // we are only using this rule to check for unused arguments since TS + // catches unused variables but not args. + { varsIgnorePattern: '.*', args: 'none' } ], '@typescript-eslint/no-use-before-define': 'off', '@typescript-eslint/ban-ts-comment': 'off', @@ -83,14 +79,10 @@ export default [ 'simple-import-sort/exports': 'error', 'no-console': 'off', 'no-unused-vars': [ - 'warn', - { - argsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^value$|^loading$|^width$|^height$|^showPlaylist$|^id$|^enabledSources$|^progress$|^status$|^success$|^filePath$|^locale$|^channel$|^listener$|^url$|^songId$|^delta$|^item$|^err$|^gradient$|^theme$', - varsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^NONE$|^TIME$|^SONGS$|^PLAYLIST_END$|^c$|^l$|^Window$|^key$|^color$', - ignoreRestSiblings: true - } + 'error', + // we are only using this rule to check for unused arguments since TS + // catches unused variables but not args. + { varsIgnorePattern: '.*', args: 'none' } ], 'no-use-before-define': 'off', 'max-classes-per-file': 'off', @@ -235,14 +227,10 @@ export default [ 'class-methods-use-this': 'off', 'no-case-declarations': 'off', 'no-unused-vars': [ - 'warn', - { - argsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^value$|^loading$|^width$|^height$|^showPlaylist$|^id$|^enabledSources$|^progress$|^status$|^success$|^filePath$|^locale$|^channel$|^listener$|^url$|^songId$|^delta$|^item$|^err$|^gradient$|^theme$', - varsIgnorePattern: - '^_|^e$|^event$|^error$|^data$|^callback$|^args$|^NONE$|^TIME$|^SONGS$|^PLAYLIST_END$|^c$|^l$|^Window$|^key$|^color$', - ignoreRestSiblings: true - } + 'error', + // we are only using this rule to check for unused arguments since TS + // catches unused variables but not args. + { varsIgnorePattern: '.*', args: 'none' } ] } } diff --git a/src/i18n/lang/en-US/login.ts b/src/i18n/lang/en-US/login.ts index 71de02f..0a20664 100644 --- a/src/i18n/lang/en-US/login.ts +++ b/src/i18n/lang/en-US/login.ts @@ -2,35 +2,61 @@ export default { title: { qr: 'QR Code Login', phone: 'Phone Login', - token: 'Cookie Login' + cookie: 'Cookie Login', + uid: 'UID Login' }, qrTip: 'Scan with NetEase Cloud Music APP', phoneTip: 'Login with NetEase Cloud account', tokenTip: 'Enter a valid NetEase Cloud Music Cookie to login', + uidTip: 'Enter User ID for quick login', placeholder: { phone: 'Phone Number', password: 'Password', - token: 'Please enter NetEase Cloud Music Cookie (token)' + cookie: 'Please enter NetEase Cloud Music Cookie (token)', + uid: 'Please enter User ID (UID)' }, button: { login: 'Login', switchToQr: 'QR Code Login', switchToPhone: 'Phone Login', switchToToken: 'Use Cookie Login', + switchToUid: 'UID Login', backToQr: 'Back to QR Code Login', - tokenLogin: 'Cookie Login', - autoGetCookie: 'Auto Get Cookie' + cookieLogin: 'Cookie Login', + autoGetCookie: 'Auto Get Cookie', + refresh: 'Click to Refresh', + refreshing: 'Refreshing...', + refreshQr: 'Refresh QR Code' }, message: { loginSuccess: 'Login successful', + loginFailed: 'Login failed', tokenLoginSuccess: 'Cookie login successful', + uidLoginSuccess: 'UID login successful', loadError: 'Error loading login information', qrCheckError: 'Error checking QR code status', tokenRequired: 'Please enter Cookie', tokenInvalid: 'Invalid Cookie, please check and try again', + uidRequired: 'Please enter User ID', + uidInvalid: 'Invalid User ID or user does not exist', + uidLoginFailed: 'UID login failed, please check if User ID is correct', + phoneRequired: 'Please enter phone number', + passwordRequired: 'Please enter password', + phoneLoginFailed: 'Phone login failed, please check if phone number and password are correct', autoGetCookieSuccess: 'Auto get Cookie successful', autoGetCookieFailed: 'Auto get Cookie failed', - autoGetCookieTip: 'Will open NetEase Cloud Music login page, please complete login and close the window' + autoGetCookieTip: 'Will open NetEase Cloud Music login page, please complete login and close the window', + qrCheckFailed: 'Failed to check QR code status, please refresh and try again', + qrLoading: 'Loading QR code...', + qrExpired: 'QR code has expired, please click to refresh', + qrExpiredShort: 'QR code expired', + qrExpiredWarning: 'QR code has expired, please click to refresh for a new one', + qrScanned: 'QR code scanned, please confirm login on your phone', + qrScannedShort: 'Scanned', + qrScannedInfo: 'QR code scanned, please confirm login on your phone', + qrConfirmed: 'Login successful, redirecting...', + qrGenerating: 'Generating QR code...' }, - qrTitle: 'NetEase Cloud Music QR Code Login' + qrTitle: 'NetEase Cloud Music QR Code Login', + uidWarning: 'Note: UID login is only for viewing user public information and cannot access features that require login permissions.' }; diff --git a/src/i18n/lang/ja-JP/login.ts b/src/i18n/lang/ja-JP/login.ts index e08c044..fe42d7c 100644 --- a/src/i18n/lang/ja-JP/login.ts +++ b/src/i18n/lang/ja-JP/login.ts @@ -2,35 +2,47 @@ export default { title: { qr: 'QRコードログイン', phone: '電話番号ログイン', - token: 'Cookieログイン' + cookie: 'Cookieログイン', + uid: 'UIDログイン' }, qrTip: 'NetEase Cloudアプリでログイン', phoneTip: 'NetEase Cloudアカウントでログイン', tokenTip: '有効なNetEase Cloud MusicのCookieを入力してログイン', + uidTip: 'ユーザーIDを入力してクイックログイン', placeholder: { phone: '電話番号', password: 'パスワード', - token: 'NetEase Cloud MusicのCookie(token)を入力してください' + cookie: 'NetEase Cloud MusicのCookie(token)を入力してください', + uid: 'ユーザーID(UID)を入力してください' }, button: { login: 'ログイン', switchToQr: 'QRコードログイン', switchToPhone: '電話番号ログイン', switchToToken: 'Cookieログインを使用', + switchToUid: 'UIDログイン', backToQr: 'QRコードログインに戻る', - tokenLogin: 'Cookieログイン', - autoGetCookie: 'Cookie自動取得' + cookieLogin: 'Cookieログイン', + autoGetCookie: 'Cookie自動取得', + refresh: 'クリックしてリフレッシュ', + refreshing: 'リフレッシュ中...', + refreshQr: 'QRコードをリフレッシュ' }, message: { loginSuccess: 'ログイン成功', tokenLoginSuccess: 'Cookieログイン成功', + uidLoginSuccess: 'UIDログイン成功', loadError: 'ログイン情報の読み込み中にエラーが発生しました', qrCheckError: 'QRコードの状態確認中にエラーが発生しました', tokenRequired: 'Cookieを入力してください', tokenInvalid: 'Cookieが無効です。確認して再試行してください', + uidRequired: 'ユーザーIDを入力してください', + uidInvalid: 'ユーザーIDが無効またはユーザーが存在しません', + uidLoginFailed: 'UIDログインに失敗しました。ユーザーIDが正しいか確認してください', autoGetCookieSuccess: 'Cookie自動取得成功', autoGetCookieFailed: 'Cookie自動取得失敗', autoGetCookieTip: 'NetEase Cloud Musicのログインページを開きます。ログイン完了後、ウィンドウを閉じてください' }, - qrTitle: 'NetEase Cloud Music QRコードログイン' + qrTitle: 'NetEase Cloud Music QRコードログイン', + uidWarning: '注意:UIDログインはユーザーの公開情報を表示するためのみ使用でき、ログイン権限が必要な機能にはアクセスできません。' }; \ No newline at end of file diff --git a/src/i18n/lang/ko-KR/login.ts b/src/i18n/lang/ko-KR/login.ts index 1d44c72..7fb150e 100644 --- a/src/i18n/lang/ko-KR/login.ts +++ b/src/i18n/lang/ko-KR/login.ts @@ -2,35 +2,47 @@ export default { title: { qr: 'QR코드 로그인', phone: '휴대폰 번호 로그인', - token: 'Cookie 로그인' + cookie: 'Cookie 로그인', + uid: 'UID 로그인' }, qrTip: '넷이즈 클라우드 뮤직 앱으로 QR코드를 스캔하여 로그인', phoneTip: '넷이즈 클라우드 계정으로 로그인', tokenTip: '유효한 넷이즈 클라우드 뮤직 Cookie을 입력하여 로그인', + uidTip: '사용자 ID를 입력하여 빠른 로그인', placeholder: { phone: '휴대폰 번호', password: '비밀번호', - token: '넷이즈 클라우드 뮤직 Cookie(token)을 입력하세요' + cookie: '넷이즈 클라우드 뮤직 Cookie(token)을 입력하세요', + uid: '사용자 ID(UID)를 입력하세요' }, button: { login: '로그인', switchToQr: 'QR코드 로그인', switchToPhone: '휴대폰 번호 로그인', switchToToken: 'Cookie 로그인 사용', + switchToUid: 'UID 로그인', backToQr: 'QR코드 로그인으로 돌아가기', - tokenLogin: 'Cookie 로그인', - autoGetCookie: 'Cookie 자동 가져오기' + cookieLogin: 'Cookie 로그인', + autoGetCookie: 'Cookie 자동 가져오기', + refresh: '새로고침', + refreshing: '새로고침 중...', + refreshQr: 'QR코드 새로고침' }, message: { loginSuccess: '로그인 성공', tokenLoginSuccess: 'Cookie 로그인 성공', + uidLoginSuccess: 'UID 로그인 성공', loadError: '로그인 정보 로드 중 오류 발생', qrCheckError: 'QR코드 상태 확인 중 오류 발생', tokenRequired: 'Cookie을 입력하세요', tokenInvalid: 'Cookie이 유효하지 않습니다. 확인 후 다시 시도하세요', + uidRequired: '사용자 ID를 입력하세요', + uidInvalid: '사용자 ID가 유효하지 않거나 사용자가 존재하지 않습니다', + uidLoginFailed: 'UID 로그인에 실패했습니다. 사용자 ID가 올바른지 확인하세요', autoGetCookieSuccess: 'Cookie 자동 가져오기 성공', autoGetCookieFailed: 'Cookie 자동 가져오기 실패', autoGetCookieTip: '넷이즈 클라우드 뮤직 로그인 페이지를 열겠습니다. 로그인 완료 후 창을 닫아주세요' }, - qrTitle: '넷이즈 클라우드 뮤직 QR코드 로그인' + qrTitle: '넷이즈 클라우드 뮤직 QR코드 로그인', + uidWarning: '주의: UID 로그인은 사용자 공개 정보를 확인하는 데만 사용할 수 있으며, 로그인 권한이 필요한 기능에 액세스할 수 없습니다.' }; \ No newline at end of file diff --git a/src/i18n/lang/zh-CN/login.ts b/src/i18n/lang/zh-CN/login.ts index 8741f38..69f5c1d 100644 --- a/src/i18n/lang/zh-CN/login.ts +++ b/src/i18n/lang/zh-CN/login.ts @@ -2,35 +2,61 @@ export default { title: { qr: '扫码登录', phone: '手机号登录', - token: 'Cookie登录' + cookie: 'Cookie登录', + uid: 'UID登录' }, qrTip: '使用网易云APP扫码登录', phoneTip: '使用网易云账号登录', tokenTip: '输入有效的网易云音乐Cookie即可登录', + uidTip: '输入用户ID快速登录', placeholder: { phone: '手机号', password: '密码', - token: '请输入网易云音乐Cookie(token)' + cookie: '请输入网易云音乐Cookie(token)', + uid: '请输入用户ID(UID)' }, button: { login: '登录', switchToQr: '扫码登录', switchToPhone: '手机号登录', switchToToken: '使用Cookie登录', + switchToUid: 'UID登录', backToQr: '返回二维码登录', - tokenLogin: 'Cookie登录', - autoGetCookie: '自动获取Cookie' + cookieLogin: 'Cookie登录', + autoGetCookie: '自动获取Cookie', + refresh: '点击刷新', + refreshing: '刷新中...', + refreshQr: '刷新二维码' }, message: { loginSuccess: '登录成功', + loginFailed: '登录失败', tokenLoginSuccess: 'Cookie登录成功', + uidLoginSuccess: 'UID登录成功', loadError: '加载登录信息时出错', qrCheckError: '检查二维码状态时出错', tokenRequired: '请输入Cookie', tokenInvalid: 'Cookie无效,请检查后重试', + uidRequired: '请输入用户ID', + uidInvalid: '用户ID无效或用户不存在', + uidLoginFailed: 'UID登录失败,请检查用户ID是否正确', + phoneRequired: '请输入手机号', + passwordRequired: '请输入密码', + phoneLoginFailed: '手机号登录失败,请检查手机号和密码是否正确', autoGetCookieSuccess: '自动获取Cookie成功', autoGetCookieFailed: '自动获取Cookie失败', - autoGetCookieTip: '将打开网易云音乐登录页面,请完成登录后关闭窗口' + autoGetCookieTip: '将打开网易云音乐登录页面,请完成登录后关闭窗口', + qrCheckFailed: '检查二维码状态失败,请刷新重试', + qrLoading: '正在加载二维码...', + qrExpired: '二维码已过期,请点击刷新', + qrExpiredShort: '二维码已过期', + qrExpiredWarning: '二维码已过期,请点击刷新获取新的二维码', + qrScanned: '已扫码,请在手机上确认登录', + qrScannedShort: '已扫码', + qrScannedInfo: '已扫码,请在手机上确认登录', + qrConfirmed: '登录成功,正在跳转...', + qrGenerating: '正在生成二维码...' }, - qrTitle: '扫码登录网易云音乐' + qrTitle: '扫码登录网易云音乐', + uidWarning: '注意:UID登录仅用于查看用户公开信息,无法访问需要登录权限的功能' }; diff --git a/src/i18n/lang/zh-Hant/login.ts b/src/i18n/lang/zh-Hant/login.ts index 64756e8..18a79dc 100644 --- a/src/i18n/lang/zh-Hant/login.ts +++ b/src/i18n/lang/zh-Hant/login.ts @@ -2,35 +2,47 @@ export default { title: { qr: '掃碼登入', phone: '手機號登入', - token: 'Cookie登入' + cookie: 'Cookie登入', + uid: 'UID登入' }, qrTip: '使用網易雲APP掃碼登入', phoneTip: '使用網易雲帳號登入', tokenTip: '輸入有效的網易雲音樂Cookie即可登入', + uidTip: '輸入使用者ID快速登入', placeholder: { phone: '手機號', password: '密碼', - token: '請輸入網易雲音樂Cookie(token)' + cookie: '請輸入網易雲音樂Cookie(token)', + uid: '請輸入使用者ID(UID)' }, button: { login: '登入', switchToQr: '掃碼登入', switchToPhone: '手機號登入', switchToToken: '使用Cookie登入', + switchToUid: 'UID登入', backToQr: '返回二維碼登入', - tokenLogin: 'Cookie登入', - autoGetCookie: '自動取得Cookie' + cookieLogin: 'Cookie登入', + autoGetCookie: '自動取得Cookie', + refresh: '點擊刷新', + refreshing: '刷新中...', + refreshQr: '刷新二維碼' }, message: { loginSuccess: '登入成功', tokenLoginSuccess: 'Cookie登入成功', + uidLoginSuccess: 'UID登入成功', loadError: '載入登入資訊時出錯', qrCheckError: '檢查二維碼狀態時出錯', tokenRequired: '請輸入Cookie', tokenInvalid: 'Cookie無效,請檢查後重試', + uidRequired: '請輸入使用者ID', + uidInvalid: '使用者ID無效或使用者不存在', + uidLoginFailed: 'UID登入失敗,請檢查使用者ID是否正確', autoGetCookieSuccess: '自動取得Cookie成功', autoGetCookieFailed: '自動取得Cookie失敗', autoGetCookieTip: '將開啟網易雲音樂登入頁面,請完成登入後關閉視窗' }, - qrTitle: '掃碼登入網易雲音樂' + qrTitle: '掃碼登入網易雲音樂', + uidWarning: '注意:UID登入僅用於查看使用者公開資訊,無法訪問需要登入權限的功能' }; diff --git a/src/renderer/api/login.ts b/src/renderer/api/login.ts index da01280..0371d9d 100644 --- a/src/renderer/api/login.ts +++ b/src/renderer/api/login.ts @@ -44,3 +44,11 @@ export function loginByCellphone(phone: string, password: string) { password }); } + +// UID登录 - 通过用户ID获取用户信息 +// /user/detail +export function loginByUid(uid: string | number) { + return request.get('/user/detail', { + params: { uid } + }); +} diff --git a/src/renderer/components/login/CookieLogin.vue b/src/renderer/components/login/CookieLogin.vue index 58b29f3..da8142c 100644 --- a/src/renderer/components/login/CookieLogin.vue +++ b/src/renderer/components/login/CookieLogin.vue @@ -2,27 +2,33 @@ import { useMessage } from 'naive-ui'; import { onBeforeUnmount, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRouter } from 'vue-router'; import { getUserDetail } from '@/api/login'; -import { useUserStore } from '@/store/modules/user'; import { isElectron, setAnimationClass } from '@/utils'; defineOptions({ name: 'CookieLogin' }); +// Emits +interface Emits { + (e: 'loginSuccess', userProfile: any, loginType: string): void; + (e: 'loginError', error: string): void; +} + +const emit = defineEmits(); + const { t } = useI18n(); const message = useMessage(); -const router = useRouter(); -const userStore = useUserStore(); const token = ref(''); // Token登录 const loginByToken = async () => { if (!token.value.trim()) { - message.error(t('login.message.tokenRequired')); + const errorMsg = t('login.message.tokenRequired'); + message.error(errorMsg); + emit('loginError', errorMsg); return; } @@ -33,19 +39,22 @@ const loginByToken = async () => { // 获取用户信息验证token有效性 const user = await getUserDetail(); if (user.data && user.data.profile) { - userStore.user = user.data.profile; - localStorage.setItem('user', JSON.stringify(user.data.profile)); - message.success(t('login.message.tokenLoginSuccess')); - router.push('/user'); + const successMsg = t('login.message.tokenLoginSuccess'); + message.success(successMsg); + emit('loginSuccess', user.data.profile, 'token'); } else { // token无效,清除localStorage localStorage.removeItem('token'); - message.error(t('login.message.tokenInvalid')); + const errorMsg = t('login.message.tokenInvalid'); + message.error(errorMsg); + emit('loginError', errorMsg); } } catch (error) { // token无效,清除localStorage localStorage.removeItem('token'); - message.error(t('login.message.tokenInvalid')); + const errorMsg = t('login.message.tokenInvalid'); + message.error(errorMsg); + emit('loginError', errorMsg); console.error('Token登录失败:', error); } }; @@ -70,17 +79,20 @@ const handleCookieReceived = async (_event: any, cookieValue: string) => { // 验证Cookie有效性 const user = await getUserDetail(); if (user.data && user.data.profile) { - userStore.user = user.data.profile; - localStorage.setItem('user', JSON.stringify(user.data.profile)); - message.success(t('login.message.autoGetCookieSuccess')); - router.push('/user'); + const successMsg = t('login.message.autoGetCookieSuccess'); + message.success(successMsg); + emit('loginSuccess', user.data.profile, 'cookie'); } else { localStorage.removeItem('token'); - message.error(t('login.message.autoGetCookieFailed')); + const errorMsg = t('login.message.autoGetCookieFailed'); + message.error(errorMsg); + emit('loginError', errorMsg); } } catch (error) { localStorage.removeItem('token'); - message.error(t('login.message.autoGetCookieFailed')); + const errorMsg = t('login.message.autoGetCookieFailed'); + message.error(errorMsg); + emit('loginError', errorMsg); console.error('自动获取Cookie失败:', error); } }; @@ -102,18 +114,18 @@ onBeforeUnmount(() => {