Files
JieXi/templates/profile.html
2025-11-28 21:20:40 +08:00

448 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>个人中心 - 短视频解析平台</title>
<link rel="stylesheet" href="/static/css/ui-components.css?v=3">
<style>
body {
background: linear-gradient(135deg, #f0f9ff 0%, #e0e7ff 100%);
min-height: 100vh;
padding: 2rem 1rem;
}
.container {
max-width: 900px;
margin: 0 auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.page-title {
font-size: 1.75rem;
font-weight: 700;
color: var(--secondary-900);
}
.header-actions {
display: flex;
gap: 0.75rem;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.stat-card {
background: white;
border-radius: var(--radius-lg);
padding: 1.5rem;
box-shadow: var(--shadow-md);
}
.stat-label {
font-size: 0.875rem;
color: var(--text-muted);
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: var(--secondary-900);
}
.stat-value.primary {
color: var(--primary-600);
}
.stat-value.success {
color: var(--success);
}
.stat-value.warning {
color: var(--warning);
}
.info-card {
background: white;
border-radius: var(--radius-lg);
padding: 1.5rem;
box-shadow: var(--shadow-md);
margin-bottom: 2rem;
}
.info-card h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--secondary-900);
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border-color);
}
.info-row {
display: flex;
justify-content: space-between;
padding: 0.75rem 0;
border-bottom: 1px solid var(--secondary-100);
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
color: var(--text-muted);
font-size: 0.875rem;
}
.info-value {
font-weight: 500;
color: var(--secondary-800);
}
.group-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
}
.group-badge.normal {
background: var(--secondary-100);
color: var(--secondary-700);
}
.group-badge.vip {
background: linear-gradient(135deg, #fbbf24, #f59e0b);
color: white;
}
.group-badge.svip {
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
color: white;
}
.logs-card {
background: white;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
overflow: hidden;
}
.logs-header {
padding: 1rem 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.logs-header h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--secondary-900);
margin: 0;
}
.logs-table {
width: 100%;
border-collapse: collapse;
}
.logs-table th,
.logs-table td {
padding: 0.875rem 1rem;
text-align: left;
font-size: 0.875rem;
}
.logs-table th {
background: var(--secondary-50);
color: var(--text-muted);
font-weight: 500;
}
.logs-table tr:not(:last-child) td {
border-bottom: 1px solid var(--secondary-100);
}
.logs-table .platform {
font-weight: 500;
color: var(--secondary-800);
}
.logs-table .url {
color: var(--primary-600);
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
transition: color 0.2s;
}
.logs-table .url:hover {
color: var(--primary-700);
text-decoration: underline;
}
.status-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: var(--radius-sm);
font-size: 0.75rem;
font-weight: 500;
}
.status-badge.success {
background: #dcfce7;
color: #166534;
}
.status-badge.failed {
background: #fee2e2;
color: #991b1b;
}
.empty-state {
text-align: center;
padding: 3rem;
color: var(--text-muted);
}
.progress-bar {
height: 8px;
background: var(--secondary-100);
border-radius: 4px;
overflow: hidden;
margin-top: 0.5rem;
}
.progress-fill {
height: 100%;
background: linear-gradient(135deg, var(--primary-500), var(--primary-600));
border-radius: 4px;
transition: width 0.3s ease;
}
@media (max-width: 640px) {
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.stats-grid {
grid-template-columns: 1fr 1fr;
}
.logs-table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body>
<div class="container">
<div class="page-header">
<h1 class="page-title">个人中心</h1>
<div class="header-actions">
<a href="/" class="ui-btn ui-btn-secondary">返回首页</a>
<button class="ui-btn ui-btn-danger" onclick="logout()">退出登录</button>
</div>
</div>
<!-- 使用统计 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">今日已用</div>
<div class="stat-value primary" id="todayUsed">-</div>
</div>
<div class="stat-card">
<div class="stat-label">今日剩余</div>
<div class="stat-value success" id="todayRemaining">-</div>
</div>
<div class="stat-card">
<div class="stat-label">每日限额</div>
<div class="stat-value" id="dailyLimit">-</div>
</div>
<div class="stat-card">
<div class="stat-label">累计解析</div>
<div class="stat-value" id="totalCount">-</div>
</div>
</div>
<!-- 用户信息 -->
<div class="info-card">
<h3>账户信息</h3>
<div class="info-row">
<span class="info-label">用户名</span>
<span class="info-value" id="username">-</span>
</div>
<div class="info-row">
<span class="info-label">邮箱</span>
<span class="info-value" id="email">-</span>
</div>
<div class="info-row">
<span class="info-label">用户组</span>
<span class="info-value" id="groupName">-</span>
</div>
<div class="info-row">
<span class="info-label">注册时间</span>
<span class="info-value" id="createdAt">-</span>
</div>
<div class="info-row">
<span class="info-label">今日使用进度</span>
<span class="info-value" id="usageText">-</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="usageProgress" style="width: 0%"></div>
</div>
</div>
<!-- 解析记录 -->
<div class="logs-card">
<div class="logs-header">
<h3>解析记录最近20条</h3>
</div>
<table class="logs-table">
<thead>
<tr>
<th>平台</th>
<th>视频链接</th>
<th>状态</th>
<th>耗时</th>
<th>时间</th>
</tr>
</thead>
<tbody id="logsBody">
<tr>
<td colspan="5" class="empty-state">加载中...</td>
</tr>
</tbody>
</table>
</div>
</div>
<script src="/static/js/ui-components.js"></script>
<script>
async function loadProfile() {
try {
const response = await fetch('/auth/api/profile');
const result = await response.json();
if (!result.success) {
if (response.status === 401) {
window.location.href = '/auth/login';
return;
}
throw new Error(result.message || '加载失败');
}
const data = result.data;
// 更新统计数据
document.getElementById('todayUsed').textContent = data.usage.today_used;
document.getElementById('todayRemaining').textContent = data.usage.today_remaining;
document.getElementById('dailyLimit').textContent = data.usage.daily_limit;
document.getElementById('totalCount').textContent = data.usage.total_parse_count;
// 更新用户信息
document.getElementById('username').textContent = data.user.username;
document.getElementById('email').textContent = data.user.email;
document.getElementById('createdAt').textContent = data.user.created_at || '-';
// 用户组徽章
const groupBadge = getGroupBadge(data.group.name);
document.getElementById('groupName').innerHTML = groupBadge;
// 使用进度
const usagePercent = data.usage.daily_limit > 0
? Math.round((data.usage.today_used / data.usage.daily_limit) * 100)
: 0;
document.getElementById('usageText').textContent = `${data.usage.today_used} / ${data.usage.daily_limit}`;
document.getElementById('usageProgress').style.width = `${Math.min(usagePercent, 100)}%`;
// 更新解析记录
const logsBody = document.getElementById('logsBody');
if (data.parse_logs.length === 0) {
logsBody.innerHTML = '<tr><td colspan="5" class="empty-state">暂无解析记录</td></tr>';
} else {
logsBody.innerHTML = data.parse_logs.map((log, index) => `
<tr>
<td class="platform">${getPlatformName(log.platform)}</td>
<td class="url" title="点击复制链接" data-url="${log.video_url}" onclick="copyUrlFromElement(this)">${log.video_url}</td>
<td><span class="status-badge ${log.status === 'success' ? 'success' : 'failed'}">${log.status === 'success' ? '成功' : '失败'}</span></td>
<td>${log.response_time ? (log.response_time / 1000).toFixed(2) + 's' : '-'}</td>
<td>${log.created_at}</td>
</tr>
`).join('');
}
} catch (error) {
UI.notify('加载失败: ' + error.message, 'error');
}
}
function getGroupBadge(groupName) {
const name = groupName.toLowerCase();
if (name.includes('svip')) {
return `<span class="group-badge svip">${groupName}</span>`;
} else if (name.includes('vip')) {
return `<span class="group-badge vip">${groupName}</span>`;
}
return `<span class="group-badge normal">${groupName}</span>`;
}
function getPlatformName(platform) {
const names = {
'douyin': '抖音',
'tiktok': 'TikTok',
'bilibili': 'B站'
};
return names[platform] || platform;
}
function copyUrlFromElement(element) {
const url = element.getAttribute('data-url');
if (!url) {
UI.notify('无法获取链接', 'error');
return;
}
navigator.clipboard.writeText(url).then(() => {
UI.notify('链接已复制', 'success');
}).catch(() => {
UI.notify('复制失败', 'error');
});
}
async function logout() {
try {
await fetch('/auth/logout', { method: 'POST' });
UI.notify('已退出登录', 'success');
setTimeout(() => {
window.location.href = '/';
}, 1000);
} catch (error) {
UI.notify('退出失败', 'error');
}
}
loadProfile();
</script>
</body>
</html>