mirror of
https://github.com/iLay1678/i-tools.git
synced 2025-07-03 18:52:17 +08:00
优化界面
This commit is contained in:
parent
0ff9a640ac
commit
6bffcde91f
@ -9,7 +9,7 @@
|
||||
|
||||
# Docker部署教程
|
||||
```
|
||||
docker run -d -p 3000:3000 ghcr.io/ilay1678/alipan-tv-token:latest
|
||||
docker run --name=alipan-tv-token -d -p 3000:3000 ghcr.io/ilay1678/alipan-tv-token:latest
|
||||
```
|
||||
|
||||
# vercel部署
|
||||
|
@ -31,3 +31,41 @@ body {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fade-in 0.3s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-in-out {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -20px);
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
90% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, -20px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-out {
|
||||
animation: fade-in-out 3s ease-out forwards;
|
||||
}
|
||||
|
258
src/app/page.js
258
src/app/page.js
@ -1,22 +1,33 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import ClipboardJS from 'clipboard';
|
||||
export default function Home() {
|
||||
// 添加状态变量
|
||||
const [alertMsg, setAlertMsg] = useState('');
|
||||
const [alertType, setAlertType] = useState('success'); // 可选值:'success','error'
|
||||
const hasGenerated = useRef(false); // 新增
|
||||
const [qrCodeSrc, setQrCodeSrc] = useState(''); // 新增,初始为空表示未加载
|
||||
import ClipboardJS from 'clipboard'
|
||||
|
||||
async function generateQR() {
|
||||
const response = await fetch("/generate_qr", {
|
||||
method: "POST",
|
||||
});
|
||||
const data = await response.json();
|
||||
// 更新二维码图片的 src
|
||||
setQrCodeSrc(data.qr_link);
|
||||
checkStatus(data.sid);
|
||||
export default function Home() {
|
||||
const [alertMsg, setAlertMsg] = useState('')
|
||||
const [alertType, setAlertType] = useState('success')
|
||||
const hasGenerated = useRef(false)
|
||||
const [authUrl, setAuthUrl] = useState('') // 新增授权URL状态
|
||||
const [isLoading, setIsLoading] = useState(true) // 新增加载状态
|
||||
const [hasAccessToken, setHasAccessToken] = useState(false)
|
||||
const [hasRefreshToken, setHasRefreshToken] = useState(false)
|
||||
const [authorizing, setAuthorizing] = useState(false) // 新增授权中状态
|
||||
const [showNotice, setShowNotice] = useState(true) // 新增弹窗控制状态
|
||||
|
||||
async function generateAuthUrl() {
|
||||
try {
|
||||
setIsLoading(true)
|
||||
const response = await fetch("/generate_qr", {
|
||||
method: "POST",
|
||||
});
|
||||
const data = await response.json();
|
||||
// 生成授权URL
|
||||
const url = `https://www.alipan.com/o/oauth/authorize?sid=${data.sid}`
|
||||
setAuthUrl(url);
|
||||
checkStatus(data.sid);
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
async function checkStatus(sid) {
|
||||
@ -24,19 +35,17 @@ export default function Home() {
|
||||
const response = await fetch("/check_status/" + sid);
|
||||
const data = await response.json();
|
||||
if (data.status === "LoginSuccess") {
|
||||
document.querySelector("h1").innerText = "获取成功";
|
||||
document.getElementById("accessToken").value = data.access_token;
|
||||
document.getElementById("refreshToken").value = data.refresh_token;
|
||||
document.getElementById("tokens").style.display = "block";
|
||||
document.getElementById("qrCodeContainer").style.display = "none";
|
||||
document.getElementById("authSection").style.visibility = "hidden"; // 改为隐藏而不是display:none
|
||||
setHasAccessToken(!!data.access_token)
|
||||
setHasRefreshToken(!!data.refresh_token)
|
||||
// 初始化复制功能
|
||||
initializeClipboard();
|
||||
} else if (data.status === "ScanSuccess") {
|
||||
document.querySelector("h1").innerText = "扫码成功,等待手机端授权";
|
||||
// 继续轮询
|
||||
setTimeout(() => checkStatus(sid), 2000);
|
||||
} else if (data.status === "LoginFailed") {
|
||||
document.querySelector("h1").innerText = "登录失败,请刷新页面重试";
|
||||
setAlertMsg("登录失败,请刷新页面重试");
|
||||
setAlertType('error');
|
||||
location.reload();
|
||||
@ -75,61 +84,184 @@ export default function Home() {
|
||||
});
|
||||
}
|
||||
|
||||
// 新增处理点击授权的函数
|
||||
const handleAuth = (url) => {
|
||||
setAuthorizing(true)
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasGenerated.current) {
|
||||
generateQR();
|
||||
generateAuthUrl();
|
||||
hasGenerated.current = true;
|
||||
}
|
||||
}, []); // 依赖项为空数组
|
||||
}, []);
|
||||
|
||||
// 添加消息自动清除的 Effect
|
||||
useEffect(() => {
|
||||
if (alertMsg) {
|
||||
const timer = setTimeout(() => {
|
||||
setAlertMsg('');
|
||||
}, 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [alertMsg]);
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-100">
|
||||
{/* 添加卡片容器 */}
|
||||
<div className="bg-white shadow-lg rounded-lg p-8">
|
||||
{/* 提示组件 */}
|
||||
{alertMsg && (
|
||||
<div className={`mb-4 p-4 text-white rounded ${alertType === 'success' ? 'bg-green-500' : 'bg-red-500'}`}>
|
||||
<>
|
||||
{showNotice && (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
|
||||
<div className="bg-white rounded-lg max-w-md w-full p-6 space-y-4">
|
||||
<h2 className="text-xl font-bold text-gray-800">使用须知</h2>
|
||||
<p className="text-gray-600 text-sm leading-relaxed">
|
||||
本工具能帮助你一键获取「阿里云盘TV版」的刷新令牌,完全免费。TV接口能绕过三方应用权益包的速率限制,但前提你得是SVIP。
|
||||
</p>
|
||||
<div className="flex justify-center pt-2">
|
||||
<button
|
||||
onClick={() => setShowNotice(false)}
|
||||
className="min-w-[120px] bg-blue-500 text-white py-2 px-6 rounded hover:bg-blue-600 transition-colors"
|
||||
>
|
||||
知道了
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{alertMsg && (
|
||||
<div className="fixed top-4 left-1/2 transform -translate-x-1/2 z-50 w-auto min-w-[200px] max-w-[90%] animate-fade-in-out">
|
||||
<div className={`px-6 py-3 rounded-lg shadow-lg text-white text-center ${
|
||||
alertType === 'success' ? 'bg-green-500' : 'bg-red-500'
|
||||
}`}>
|
||||
{alertMsg}
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-2xl font-bold text-gray-800 mb-6 text-center">扫描二维码登录</h1>
|
||||
<div id="qrCodeContainer" className="flex justify-center mb-6">
|
||||
{qrCodeSrc ? (
|
||||
<img
|
||||
id="qrCode"
|
||||
src={qrCodeSrc}
|
||||
alt="二维码"
|
||||
className="w-64 h-64" // 设置固定宽高,与占位符一致
|
||||
/>
|
||||
) : (
|
||||
<div className="w-64 h-64 flex items-center justify-center bg-gray-200">
|
||||
<span className="text-gray-500">二维码加载中...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div id="tokens" className="hidden">
|
||||
<div className="mb-4">
|
||||
<label htmlFor="accessToken" className="block text-gray-700 font-medium mb-2">访问令牌:</label>
|
||||
<div className="flex">
|
||||
<input type="text" id="accessToken" className="flex-1 p-2 border border-gray-300 rounded-l" readOnly />
|
||||
<button data-clipboard-target="#accessToken"
|
||||
className="px-4 bg-blue-500 text-white rounded-r hover:bg-blue-600">
|
||||
复制
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<main className="flex min-h-screen flex-col items-center justify-center bg-gray-100 p-4">
|
||||
<div className="bg-white shadow-lg rounded-lg p-8 w-full max-w-3xl">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-bold text-gray-800">与「阿里云盘」连接</h1>
|
||||
{/* 将 Docker 标移到这里 */}
|
||||
<a
|
||||
href="https://ghcr.io/ilay1678/alipan-tv-token"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-gray-600 hover:text-blue-500 transition-colors"
|
||||
title="Docker Image"
|
||||
>
|
||||
<svg className="h-6 w-6" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M13.983 11.078h2.119a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.119a.185.185 0 00-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 00.186-.186V3.574a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 00.186-.186V6.29a.186.186 0 00-.186-.185h-2.118a.185.185 0 00-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 00.184-.186V6.29a.185.185 0 00-.185-.185H8.1a.185.185 0 00-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 00.185-.186V6.29a.185.185 0 00-.185-.185H5.136a.186.186 0 00-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 00.186-.185V9.006a.186.186 0 00-.186-.186h-2.118a.185.185 0 00-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 00.185-.185V9.006a.185.185 0 00-.184-.186h-2.12a.186.186 0 00-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 00.184-.185V9.006a.185.185 0 00-.184-.186h-2.12a.185.185 0 00-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 00-.75.748 11.376 11.376 0 00.692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137.983.003 1.963-.086 2.93-.266a12.248 12.248 0 003.823-1.389c.98-.567 1.86-1.288 2.61-2.136 1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="refreshToken" className="block text-gray-700 font-medium mb-2">刷新令牌:</label>
|
||||
<div className="flex">
|
||||
<input type="text" id="refreshToken" className="flex-1 p-2 border border-gray-300 rounded-l" readOnly />
|
||||
<button data-clipboard-target="#refreshToken"
|
||||
className="px-4 bg-blue-500 text-white rounded-r hover:bg-blue-600">
|
||||
复制
|
||||
</button>
|
||||
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-2">
|
||||
<div className="relative">
|
||||
<textarea
|
||||
id="accessToken"
|
||||
className="w-full rounded font-mono text-sm leading-normal
|
||||
border-2 border-dashed border-gray-300
|
||||
p-3 pr-10
|
||||
bg-white
|
||||
resize-none
|
||||
focus:outline-none focus:border-blue-500
|
||||
transition-colors
|
||||
min-h-[120px]
|
||||
whitespace-pre-wrap
|
||||
overflow-auto
|
||||
placeholder:text-gray-400"
|
||||
readOnly
|
||||
spellCheck="false"
|
||||
placeholder="访问令牌"
|
||||
/>
|
||||
<button
|
||||
data-clipboard-target="#accessToken"
|
||||
className={`absolute top-2 right-2 p-1 rounded transition-colors ${
|
||||
hasAccessToken
|
||||
? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500'
|
||||
: 'text-gray-300 cursor-not-allowed'
|
||||
}`}
|
||||
title="复制访问令牌"
|
||||
disabled={!hasAccessToken}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="relative">
|
||||
<textarea
|
||||
id="refreshToken"
|
||||
className="w-full rounded font-mono text-sm leading-normal
|
||||
border-2 border-dashed border-gray-300
|
||||
p-3 pr-10
|
||||
bg-white
|
||||
resize-none
|
||||
focus:outline-none focus:border-blue-500
|
||||
transition-colors
|
||||
min-h-[120px]
|
||||
whitespace-pre-wrap
|
||||
overflow-auto
|
||||
placeholder:text-gray-400"
|
||||
readOnly
|
||||
spellCheck="false"
|
||||
placeholder="刷新令牌"
|
||||
/>
|
||||
<button
|
||||
data-clipboard-target="#refreshToken"
|
||||
className={`absolute top-2 right-2 p-1 rounded transition-colors ${
|
||||
hasRefreshToken
|
||||
? 'hover:bg-gray-100 text-gray-500 hover:text-blue-500'
|
||||
: 'text-gray-300 cursor-not-allowed'
|
||||
}`}
|
||||
title="复制刷新令牌"
|
||||
disabled={!hasRefreshToken}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="authSection" className="h-[52px]"> {/* 固定高度 */}
|
||||
{isLoading ? (
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
<span className="ml-3 text-gray-600">正在获取授权链接...</span>
|
||||
</div>
|
||||
) : authUrl && (
|
||||
<button
|
||||
onClick={() => handleAuth(authUrl)}
|
||||
disabled={authorizing}
|
||||
className={`block w-full bg-blue-500 text-white text-center py-3 px-4 rounded
|
||||
transition-colors relative
|
||||
${authorizing ? 'bg-blue-400 cursor-not-allowed' : 'hover:bg-blue-600'}`}
|
||||
>
|
||||
{authorizing ? (
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-2 border-white border-t-transparent mr-2"></div>
|
||||
授权中...
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
授权登录
|
||||
</div>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user