mirror of
https://github.com/atdunbg/Nekosonic-Music.git
synced 2026-06-22 10:48:05 +08:00
feat: 跨平台持久化与版本管理优化
- Cookie 存储从 temp_dir 迁移至 Tauri app_data_dir,兼容 Linux - 简单统一风格,UI优化 - recentLocal 播放历史持久化到 localStorage - 添加设置界面可以修改简单的设置
This commit is contained in:
@ -1,21 +1,19 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center bg-gray-950 text-white">
|
||||
<div class="bg-white/5 backdrop-blur-md border border-white/10 p-8 rounded-2xl w-full max-w-sm text-center">
|
||||
<div class="min-h-screen flex items-center justify-center bg-base text-content">
|
||||
<div class="bg-subtle backdrop-blur-md border border-line p-8 rounded-2xl w-full max-w-sm text-center">
|
||||
<h1 class="text-xl font-bold mb-4">扫码登录</h1>
|
||||
<p class="text-sm text-gray-400 mb-6">请使用网易云音乐 App 扫描二维码</p>
|
||||
|
||||
<!-- 二维码展示区 -->
|
||||
<p class="text-sm text-content-2 mb-6">请使用网易云音乐 App 扫描二维码</p>
|
||||
|
||||
<div v-if="qrimg" class="bg-white p-3 rounded-xl inline-block mb-4">
|
||||
<img :src="qrimg" alt="二维码" class="w-48 h-48" />
|
||||
</div>
|
||||
<div v-else class="w-48 h-48 bg-white/5 rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<span v-if="qrLoading" class="text-gray-400">加载中...</span>
|
||||
<span v-else-if="qrError" class="text-red-400 text-sm">{{ qrError }}</span>
|
||||
<div v-else class="w-48 h-48 bg-subtle rounded-xl flex items-center justify-center mx-auto mb-4">
|
||||
<span v-if="qrLoading" class="text-content-2">加载中...</span>
|
||||
<span v-else-if="qrError" class="text-danger text-sm">{{ qrError }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 状态提示 -->
|
||||
|
||||
<p class="text-sm" :class="statusColor">{{ statusText }}</p>
|
||||
<button @click="refreshQr" class="mt-4 text-xs text-green-400 hover:underline">重新获取二维码</button>
|
||||
<button @click="refreshQr" class="mt-4 text-xs text-accent-text hover:underline">重新获取二维码</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -33,7 +31,7 @@ const qrimg = ref('');
|
||||
const qrLoading = ref(true);
|
||||
const qrError = ref('');
|
||||
const statusText = ref('等待扫码...');
|
||||
const statusColor = ref('text-gray-400');
|
||||
const statusColor = ref('text-content-2');
|
||||
let qrKey = '';
|
||||
let pollTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
@ -54,7 +52,6 @@ async function refreshQr() {
|
||||
qrError.value = '';
|
||||
if (pollTimer) clearInterval(pollTimer);
|
||||
try {
|
||||
// 1. 获取 unikey
|
||||
qrKey = await invoke('get_qr_key');
|
||||
if (!qrKey) {
|
||||
qrError.value = '未获取到登录密钥';
|
||||
@ -62,16 +59,13 @@ async function refreshQr() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 拼接网易云标准扫码链接(无需 create_qr)
|
||||
const qrUrl = `https://music.163.com/login?codekey=${qrKey}&type=1`;
|
||||
|
||||
// 3. 用 qrcode 生成二维码图片
|
||||
const canvas = document.createElement('canvas');
|
||||
await QRCode.toCanvas(canvas, qrUrl, { width: 200, margin: 1 });
|
||||
qrimg.value = canvas.toDataURL('image/png');
|
||||
qrLoading.value = false;
|
||||
|
||||
// 4. 开始轮询状态
|
||||
startPolling();
|
||||
} catch (e: any) {
|
||||
qrError.value = '获取二维码失败';
|
||||
@ -79,20 +73,6 @@ async function refreshQr() {
|
||||
}
|
||||
}
|
||||
|
||||
// 新增函数:用 Canvas 生成二维码并赋值给 qrimg
|
||||
// async function drawQrCode(url: string) {
|
||||
// try {
|
||||
// // 等待 DOM 准备好 canvas 元素
|
||||
// const canvas = document.createElement('canvas');
|
||||
// await QRCode.toCanvas(canvas, url, { width: 201, margin: 1 });
|
||||
// // 转为 data URL 赋值给响应式的图片地址
|
||||
// qrimg.value = canvas.toDataURL('image/png');
|
||||
// } catch (e) {
|
||||
// console.error('生成二维码失败', e);
|
||||
// qrError.value = '生成二维码失败';
|
||||
// }
|
||||
// }
|
||||
|
||||
function startPolling() {
|
||||
pollTimer = setInterval(async () => {
|
||||
try {
|
||||
@ -101,23 +81,18 @@ function startPolling() {
|
||||
const code = data.code;
|
||||
if (code === 800) {
|
||||
statusText.value = '二维码已过期,请刷新';
|
||||
statusColor.value = 'text-red-400';
|
||||
statusColor.value = 'text-danger';
|
||||
clearInterval(pollTimer!);
|
||||
} else if (code === 801) {
|
||||
statusText.value = '等待扫码...';
|
||||
statusColor.value = 'text-gray-400';
|
||||
statusColor.value = 'text-content-2';
|
||||
} else if (code === 802) {
|
||||
statusText.value = '请在手机上确认登录';
|
||||
statusColor.value = 'text-yellow-400';
|
||||
statusColor.value = 'text-warning';
|
||||
} else if (code === 803) {
|
||||
// 登录成功
|
||||
clearInterval(pollTimer!);
|
||||
statusText.value = '登录成功!';
|
||||
statusColor.value = 'text-green-400';
|
||||
// 存储 cookie 到 NcmApi(后台线程中自动保留,后续请求都带登录态)
|
||||
// 获取用户信息(简化,可从 /login/status 获取)
|
||||
// 这里需要额外调用获取用户详情的 API,但因为 NcmApi 已有 cookie,可以直接在后台线程中添加
|
||||
// 暂时用简易方式:调用 /user/account 获取用户简档
|
||||
statusColor.value = 'text-accent-text';
|
||||
await fetchUserProfile();
|
||||
setTimeout(() => router.push('/'), 500);
|
||||
}
|
||||
@ -129,9 +104,6 @@ function startPolling() {
|
||||
|
||||
async function fetchUserProfile() {
|
||||
try {
|
||||
// 添加一个快速获取用户信息的命令(可复用之前的 login 命令中获取 profile 的逻辑)
|
||||
// 这里简化,由于后台 NcmApi 已有 cookie,我们可以直接用 reqwest 调 /user/account
|
||||
// 但最好添加一个新命令,这里直接调用现有的 login 逻辑不适用,因此我们在 Rust 侧添加一个 get_login_status 命令
|
||||
const profileJson: string = await invoke('get_login_status');
|
||||
const profile = JSON.parse(profileJson);
|
||||
if (profile.profile) {
|
||||
@ -145,4 +117,4 @@ async function fetchUserProfile() {
|
||||
console.error('获取用户信息失败', e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user