video-shuoshan/prototype/admin-dashboard.html
zyc ffe92f7b15 Initial commit: 即梦视频生成平台
- web/: React + Vite + TypeScript 前端
- backend/: Django + DRF + SimpleJWT 后端
- prototype/: HTML 设计原型
- docs/: PRD 和设计评审文档
- test: 单元测试 + E2E 极限测试
2026-03-13 09:59:33 +08:00

491 lines
21 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>仪表盘 — Jimeng Admin</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-page: #0a0a0f;
--bg-sidebar: #111118;
--bg-card: #16161e;
--border: #2a2a38;
--primary: #00b8e6;
--primary-dim: rgba(0,184,230,0.12);
--text-1: #ffffff;
--text-2: #8a8a9a;
--text-3: #4a4a5a;
--hover: rgba(255,255,255,0.06);
--success: #34d399;
--danger: #f87171;
--warning: #fbbf24;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Noto Sans SC', 'Space Grotesk', system-ui, sans-serif;
background: var(--bg-page);
color: var(--text-1);
height: 100vh;
overflow: hidden;
}
.layout { display: flex; height: 100vh; }
/* Sidebar */
.sidebar {
width: 240px;
min-width: 240px;
background: var(--bg-sidebar);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 0;
}
.sidebar-logo {
padding: 20px 24px;
display: flex;
align-items: center;
gap: 10px;
border-bottom: 1px solid var(--border);
}
.sidebar-logo svg { flex-shrink: 0; }
.sidebar-logo span { font-size: 15px; font-weight: 600; letter-spacing: -0.3px; }
.sidebar-nav { flex: 1; padding: 12px 8px; display: flex; flex-direction: column; gap: 2px; }
.nav-item {
display: flex; align-items: center; gap: 12px;
padding: 10px 16px; border-radius: 8px;
font-size: 14px; color: var(--text-2);
text-decoration: none; transition: all 0.15s;
cursor: pointer;
}
.nav-item:hover { background: var(--hover); color: var(--text-1); }
.nav-item.active {
background: rgba(255,255,255,0.08);
color: var(--text-1); font-weight: 500;
}
.nav-item svg { opacity: 0.6; }
.nav-item.active svg, .nav-item:hover svg { opacity: 1; }
.sidebar-footer {
padding: 16px;
border-top: 1px solid var(--border);
}
.sidebar-footer a {
display: flex; align-items: center; gap: 8px;
font-size: 13px; color: var(--text-3);
text-decoration: none; padding: 8px 12px;
border-radius: 6px; transition: all 0.15s;
}
.sidebar-footer a:hover { color: var(--text-2); background: var(--hover); }
/* Main content */
.main { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
.topbar {
display: flex; align-items: center; justify-content: space-between;
padding: 16px 32px; border-bottom: 1px solid var(--border);
flex-shrink: 0; background: var(--bg-page);
position: sticky; top: 0; z-index: 10;
backdrop-filter: blur(12px);
}
.topbar-left h1 { font-size: 18px; font-weight: 600; }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.admin-badge {
font-size: 12px; color: var(--primary);
padding: 4px 12px; background: var(--primary-dim);
border-radius: 6px; font-weight: 500;
}
.topbar-btn {
padding: 6px 14px; background: transparent;
border: 1px solid var(--border); border-radius: 6px;
color: var(--text-2); font-size: 13px; cursor: pointer;
transition: all 0.15s;
}
.topbar-btn:hover { background: var(--hover); color: var(--text-1); }
.content { padding: 28px 32px; flex: 1; }
/* Time range selector */
.time-range {
display: flex; gap: 4px; background: rgba(255,255,255,0.04);
border-radius: 8px; padding: 3px;
}
.time-range button {
padding: 6px 14px; border: none; border-radius: 6px;
background: transparent; color: var(--text-2); font-size: 12px;
cursor: pointer; transition: all 0.15s; font-weight: 500;
}
.time-range button.active {
background: var(--primary); color: #fff;
}
.time-range button:hover:not(.active) { color: var(--text-1); }
/* Stat cards */
.stat-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 24px;
position: relative;
overflow: hidden;
}
.stat-card::after {
content: '';
position: absolute;
top: 0; right: 0;
width: 80px; height: 80px;
border-radius: 0 0 0 80px;
opacity: 0.04;
}
.stat-card:nth-child(1)::after { background: var(--primary); }
.stat-card:nth-child(2)::after { background: var(--success); }
.stat-card:nth-child(3)::after { background: var(--warning); }
.stat-card:nth-child(4)::after { background: #a78bfa; }
.stat-icon {
width: 36px; height: 36px; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
margin-bottom: 14px;
}
.stat-label { font-size: 13px; color: var(--text-2); margin-bottom: 6px; }
.stat-value {
font-family: 'JetBrains Mono', 'Space Grotesk', monospace;
font-size: 28px; font-weight: 700; letter-spacing: -1px;
margin-bottom: 8px;
}
.stat-trend {
display: inline-flex; align-items: center; gap: 4px;
font-size: 12px; font-weight: 500;
padding: 2px 8px; border-radius: 4px;
}
.stat-trend.up { color: var(--success); background: rgba(52,211,153,0.1); }
.stat-trend.down { color: var(--danger); background: rgba(248,113,113,0.1); }
/* Chart cards */
.chart-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
}
.chart-header {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 20px;
}
.chart-title { font-size: 15px; font-weight: 600; }
/* SVG Chart styles */
.chart-grid-line { stroke: rgba(255,255,255,0.04); stroke-width: 1; }
.chart-axis-label { fill: var(--text-3); font-size: 10px; font-family: 'JetBrains Mono', monospace; }
.chart-line { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.chart-area { fill: url(#areaGradient); }
.chart-dot { fill: var(--primary); }
.chart-tooltip-bg { fill: #1e1e2e; stroke: var(--border); rx: 6; }
.chart-tooltip-text { fill: var(--text-1); font-size: 11px; font-family: 'JetBrains Mono', monospace; }
/* Bar chart */
.bar-item {
display: flex; align-items: center; gap: 12px;
padding: 8px 0;
}
.bar-rank {
font-family: 'JetBrains Mono', monospace;
font-size: 12px; color: var(--text-3);
width: 20px; text-align: center;
}
.bar-rank.top { color: var(--primary); font-weight: 600; }
.bar-username {
width: 80px; font-size: 13px; color: var(--text-2);
text-overflow: ellipsis; overflow: hidden; white-space: nowrap;
}
.bar-track {
flex: 1; height: 24px; background: rgba(255,255,255,0.03);
border-radius: 4px; overflow: hidden; position: relative;
}
.bar-fill {
height: 100%; border-radius: 4px;
background: linear-gradient(90deg, var(--primary), rgba(0,184,230,0.6));
transition: width 0.6s cubic-bezier(0.16, 1, 0.3, 1);
display: flex; align-items: center; justify-content: flex-end;
padding-right: 8px;
}
.bar-fill-value {
font-family: 'JetBrains Mono', monospace;
font-size: 11px; color: #fff; font-weight: 500;
white-space: nowrap;
}
/* Scrollbar */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
/* Load animation */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-in { animation: fadeUp 0.4s ease-out forwards; opacity: 0; }
.delay-1 { animation-delay: 0.05s; }
.delay-2 { animation-delay: 0.1s; }
.delay-3 { animation-delay: 0.15s; }
.delay-4 { animation-delay: 0.2s; }
.delay-5 { animation-delay: 0.3s; }
.delay-6 { animation-delay: 0.4s; }
</style>
</head>
<body>
<div class="layout">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-logo">
<svg width="24" height="24" viewBox="0 0 28 28" fill="none">
<path d="M4 8L14 2L24 8V20L14 26L4 20V8Z" fill="#00b8e6" opacity="0.9"/>
<path d="M14 2L24 8L14 14L4 8L14 2Z" fill="#33ccf0"/>
</svg>
<span>Jimeng Admin</span>
</div>
<nav class="sidebar-nav">
<a href="admin-dashboard.html" class="nav-item active">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
仪表盘
</a>
<a href="admin-users.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
用户管理
</a>
<a href="admin-records.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
消费记录
</a>
<a href="admin-settings.html" class="nav-item">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
系统设置
</a>
</nav>
<div class="sidebar-footer">
<a href="video-generation.html">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
返回首页
</a>
</div>
</aside>
<!-- Main -->
<div class="main">
<header class="topbar">
<div class="topbar-left">
<h1>仪表盘</h1>
</div>
<div class="topbar-right">
<div class="time-range">
<button>今日</button>
<button>近7天</button>
<button class="active">近30天</button>
<button>自定义</button>
</div>
<span class="admin-badge">Admin</span>
<button class="topbar-btn">退出</button>
</div>
</header>
<div class="content">
<!-- Stats Grid -->
<div class="grid grid-cols-4 gap-4 mb-6 max-lg:grid-cols-2">
<div class="stat-card animate-in delay-1">
<div class="stat-icon" style="background: var(--primary-dim);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#00b8e6" stroke-width="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
</div>
<div class="stat-label">总用户数</div>
<div class="stat-value">1,234</div>
<span class="stat-trend up">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/></svg>
+12.3%
</span>
</div>
<div class="stat-card animate-in delay-2">
<div class="stat-icon" style="background: rgba(52,211,153,0.12);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#34d399" stroke-width="2"><path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg>
</div>
<div class="stat-label">今日新增用户</div>
<div class="stat-value">+23</div>
<span class="stat-trend up">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/></svg>
+15.0%
</span>
</div>
<div class="stat-card animate-in delay-3">
<div class="stat-icon" style="background: rgba(251,191,36,0.12);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fbbf24" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</div>
<div class="stat-label">今日消费秒数</div>
<div class="stat-value">4,560<span style="font-size:14px;color:var(--text-2);font-weight:400">s</span></div>
<span class="stat-trend down">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" style="transform:rotate(180deg)"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/></svg>
-5.2%
</span>
</div>
<div class="stat-card animate-in delay-4">
<div class="stat-icon" style="background: rgba(167,139,250,0.12);">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#a78bfa" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
</div>
<div class="stat-label">本月消费秒数</div>
<div class="stat-value">89,010<span style="font-size:14px;color:var(--text-2);font-weight:400">s</span></div>
<span class="stat-trend up">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="23 6 13.5 15.5 8.5 10.5 1 18"/></svg>
+8.7%
</span>
</div>
</div>
<!-- Trend Chart -->
<div class="chart-card mb-6 animate-in delay-5">
<div class="chart-header">
<div class="chart-title">消费趋势近30天</div>
<div style="font-size:12px;color:var(--text-3)">单位:秒</div>
</div>
<svg viewBox="0 0 800 280" style="width:100%;height:auto" id="trendChart">
<defs>
<linearGradient id="areaGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#00b8e6" stop-opacity="0.25"/>
<stop offset="100%" stop-color="#00b8e6" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- Grid lines -->
<line x1="50" y1="20" x2="50" y2="240" class="chart-grid-line"/>
<line x1="50" y1="240" x2="780" y2="240" class="chart-grid-line"/>
<line x1="50" y1="185" x2="780" y2="185" class="chart-grid-line" stroke-dasharray="4,4"/>
<line x1="50" y1="130" x2="780" y2="130" class="chart-grid-line" stroke-dasharray="4,4"/>
<line x1="50" y1="75" x2="780" y2="75" class="chart-grid-line" stroke-dasharray="4,4"/>
<line x1="50" y1="20" x2="780" y2="20" class="chart-grid-line" stroke-dasharray="4,4"/>
<!-- Y axis labels -->
<text x="44" y="244" text-anchor="end" class="chart-axis-label">0</text>
<text x="44" y="189" text-anchor="end" class="chart-axis-label">50</text>
<text x="44" y="134" text-anchor="end" class="chart-axis-label">100</text>
<text x="44" y="79" text-anchor="end" class="chart-axis-label">150</text>
<text x="44" y="24" text-anchor="end" class="chart-axis-label">200</text>
<!-- X axis labels -->
<text x="74" y="260" class="chart-axis-label">3/1</text>
<text x="196" y="260" class="chart-axis-label">3/6</text>
<text x="318" y="260" class="chart-axis-label">3/11</text>
<text x="440" y="260" class="chart-axis-label">3/16</text>
<text x="562" y="260" class="chart-axis-label">3/21</text>
<text x="684" y="260" class="chart-axis-label">3/26</text>
<text x="756" y="260" class="chart-axis-label">3/30</text>
<!-- Area fill -->
<path class="chart-area" d="
M74,190 L98,175 L122,180 L146,160 L170,155 L194,145 L218,150
L242,140 L266,120 L290,130 L314,105 L338,95 L362,110
L386,85 L410,75 L434,90 L458,80 L482,95 L506,70
L530,60 L554,75 L578,55 L602,65 L626,50 L650,60
L674,45 L698,55 L722,40 L746,50 L756,48
L756,240 L74,240 Z"/>
<!-- Line -->
<path class="chart-line" d="
M74,190 L98,175 L122,180 L146,160 L170,155 L194,145 L218,150
L242,140 L266,120 L290,130 L314,105 L338,95 L362,110
L386,85 L410,75 L434,90 L458,80 L482,95 L506,70
L530,60 L554,75 L578,55 L602,65 L626,50 L650,60
L674,45 L698,55 L722,40 L746,50 L756,48"/>
<!-- Dots on key points -->
<circle cx="74" cy="190" r="3" class="chart-dot"/>
<circle cx="314" cy="105" r="3" class="chart-dot"/>
<circle cx="506" cy="70" r="3" class="chart-dot"/>
<circle cx="722" cy="40" r="3" class="chart-dot"/>
<circle cx="756" cy="48" r="3" class="chart-dot"/>
<!-- Hover tooltip example -->
<g style="opacity:0.9">
<rect x="690" y="8" width="80" height="36" class="chart-tooltip-bg" stroke-width="1" rx="6"/>
<text x="730" y="23" text-anchor="middle" class="chart-tooltip-text" font-weight="500">3/28</text>
<text x="730" y="37" text-anchor="middle" class="chart-tooltip-text" fill="#00b8e6">188s</text>
</g>
</svg>
</div>
<!-- Ranking Chart -->
<div class="chart-card animate-in delay-6">
<div class="chart-header">
<div class="chart-title">用户消费排行 Top 10</div>
<div style="font-size:12px;color:var(--text-3)">本月累计消费秒数</div>
</div>
<div style="display:flex;flex-direction:column;gap:4px">
<div class="bar-item">
<span class="bar-rank top">1</span>
<span class="bar-username">zhang_wei</span>
<div class="bar-track"><div class="bar-fill" style="width:100%"><span class="bar-fill-value">2,340s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank top">2</span>
<span class="bar-username">li_ming</span>
<div class="bar-track"><div class="bar-fill" style="width:81%"><span class="bar-fill-value">1,890s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank top">3</span>
<span class="bar-username">wang_fang</span>
<div class="bar-track"><div class="bar-fill" style="width:67%"><span class="bar-fill-value">1,560s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">4</span>
<span class="bar-username">chen_jie</span>
<div class="bar-track"><div class="bar-fill" style="width:58%"><span class="bar-fill-value">1,350s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">5</span>
<span class="bar-username">liu_yang</span>
<div class="bar-track"><div class="bar-fill" style="width:49%"><span class="bar-fill-value">1,140s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">6</span>
<span class="bar-username">zhao_lei</span>
<div class="bar-track"><div class="bar-fill" style="width:42%"><span class="bar-fill-value">980s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">7</span>
<span class="bar-username">huang_mei</span>
<div class="bar-track"><div class="bar-fill" style="width:35%"><span class="bar-fill-value">820s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">8</span>
<span class="bar-username">sun_qiang</span>
<div class="bar-track"><div class="bar-fill" style="width:28%"><span class="bar-fill-value">650s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">9</span>
<span class="bar-username">wu_xia</span>
<div class="bar-track"><div class="bar-fill" style="width:21%"><span class="bar-fill-value">490s</span></div></div>
</div>
<div class="bar-item">
<span class="bar-rank">10</span>
<span class="bar-username">zhou_min</span>
<div class="bar-track"><div class="bar-fill" style="width:15%"><span class="bar-fill-value">350s</span></div></div>
</div>
</div>
</div>
<!-- Footer -->
<div style="text-align:center;padding:24px 0 8px;font-size:11px;color:var(--text-3)">
Jimeng Clone Admin v3.0
</div>
</div>
</div>
</div>
<script>
// Time range button interaction
document.querySelectorAll('.time-range button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelector('.time-range .active')?.classList.remove('active');
btn.classList.add('active');
});
});
</script>
</body>
</html>