mirror of https://github.com/veypi/OneAuth.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
8.8 KiB
HTML
324 lines
8.8 KiB
HTML
<!doctype html>
|
|
<html>
|
|
|
|
<head>
|
|
<title>项目与用户管理 Dashboard</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="description" content="统计仪表盘" details="展示系统用户、项目、任务等统计数据和图表">
|
|
<script src="/assets/libs/echarts.min.js"></script>
|
|
</head>
|
|
<style>
|
|
body {
|
|
background-color: var(--bg-color-secondary);
|
|
color: var(--text-color-primary);
|
|
}
|
|
|
|
.dashboard {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
padding: var(--spacing-lg);
|
|
}
|
|
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.header h1 {
|
|
font-size: 28px;
|
|
color: var(--text-color-primary);
|
|
margin: 0;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.header .date {
|
|
color: var(--text-color-secondary);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.stats-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.stat-card {
|
|
background: var(--bg-color-primary);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
transition: transform 0.3s, box-shadow 0.3s;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.stat-card .icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 15px;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.stat-card .value {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
margin-bottom: 5px;
|
|
color: var(--text-color-primary);
|
|
}
|
|
|
|
.stat-card .label {
|
|
color: var(--text-color-secondary);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.stat-card .trend {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 10px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.trend.up {
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.trend.down {
|
|
color: var(--color-danger);
|
|
}
|
|
|
|
.charts-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: var(--spacing-lg);
|
|
margin-bottom: var(--spacing-xl);
|
|
}
|
|
|
|
.chart-container {
|
|
background: var(--bg-color-primary);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.chart-container h2 {
|
|
margin-top: 0;
|
|
font-size: 18px;
|
|
color: var(--text-color-primary);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.chart {
|
|
width: 100%;
|
|
height: 300px;
|
|
}
|
|
|
|
.recent-activities {
|
|
background: var(--bg-color-primary);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--spacing-lg);
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.recent-activities h2 {
|
|
margin-top: 0;
|
|
font-size: 18px;
|
|
color: var(--text-color-primary);
|
|
margin-bottom: var(--spacing-md);
|
|
}
|
|
|
|
.activity-item {
|
|
display: flex;
|
|
padding: 15px 0;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.activity-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.activity-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: var(--bg-color-secondary);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 15px;
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.activity-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.activity-title {
|
|
font-weight: 600;
|
|
margin-bottom: 5px;
|
|
color: var(--text-color-primary);
|
|
}
|
|
|
|
.activity-time {
|
|
color: var(--text-color-secondary);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.badge {
|
|
display: inline-block;
|
|
padding: 3px 8px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.badge-primary {
|
|
background: color-mix(in srgb, var(--color-primary), transparent 90%);
|
|
color: var(--color-primary);
|
|
}
|
|
|
|
.badge-success {
|
|
background: color-mix(in srgb, var(--color-success), transparent 90%);
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.badge-warning {
|
|
background: color-mix(in srgb, var(--color-warning), transparent 90%);
|
|
color: var(--color-warning);
|
|
}
|
|
</style>
|
|
|
|
<body>
|
|
<div class="dashboard">
|
|
<div class="header">
|
|
<div>
|
|
<h1>仪表盘</h1>
|
|
<div class="date">{{ today }}</div>
|
|
</div>
|
|
<v-btn size="sm">
|
|
<i class="fas fa-download"></i> 导出报告
|
|
</v-btn>
|
|
</div>
|
|
|
|
<div class="stats-grid">
|
|
<div class="stat-card" v-for="(stat, index) in stats" :key="index">
|
|
<div class="icon" :style="{background: stat.bgColor, color: stat.color}">
|
|
<i :class="stat.icon"></i>
|
|
</div>
|
|
<div class="value">{{ stat.value }}</div>
|
|
<div class="label">{{ stat.label }}</div>
|
|
<div class="trend" :class="stat.trend > 0 ? 'up' : 'down'">
|
|
<i :class="stat.trend > 0 ? 'fas fa-arrow-up' : 'fas fa-arrow-down'"></i>
|
|
<span style="margin-left: 5px;">{{ Math.abs(stat.trend) }}% 较上周</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="charts-grid">
|
|
<div class="chart-container">
|
|
<h2>用户增长趋势</h2>
|
|
<div id="userChart" class="chart"></div>
|
|
</div>
|
|
<div class="chart-container">
|
|
<h2>项目分布</h2>
|
|
<div id="projectChart" class="chart"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="recent-activities">
|
|
<h2>最近活动</h2>
|
|
<div class="activity-item" v-for="(activity, index) in activities" :key="index">
|
|
<div class="activity-icon">
|
|
<i :class="activity.icon"></i>
|
|
</div>
|
|
<div class="activity-content">
|
|
<div class="activity-title">
|
|
{{ activity.user }} {{ activity.action }}
|
|
<span class="badge" :class="activity.badgeClass">{{ activity.target }}</span>
|
|
</div>
|
|
<div class="activity-time">{{ activity.time }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
<script setup>
|
|
today = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'long' });
|
|
|
|
stats = [
|
|
{ label: '总用户数', value: '12,345', icon: 'fas fa-users', color: '#4a6cf7', bgColor: 'rgba(74, 108, 247, 0.1)', trend: 5.2 },
|
|
{ label: '活跃项目', value: '86', icon: 'fas fa-project-diagram', color: '#28a745', bgColor: 'rgba(40, 167, 69, 0.1)', trend: 2.8 },
|
|
{ label: '待处理任务', value: '34', icon: 'fas fa-tasks', color: '#ffc107', bgColor: 'rgba(255, 193, 7, 0.1)', trend: -1.5 },
|
|
{ label: '系统消息', value: '128', icon: 'fas fa-envelope', color: '#dc3545', bgColor: 'rgba(220, 53, 69, 0.1)', trend: 8.4 }
|
|
];
|
|
|
|
activities = [
|
|
{ user: '张三', action: '创建了新项目', target: 'AI 助手', badgeClass: 'badge-primary', icon: 'fas fa-plus', time: '10 分钟前' },
|
|
{ user: '李四', action: '完成了任务', target: '前端重构', badgeClass: 'badge-success', icon: 'fas fa-check', time: '30 分钟前' },
|
|
{ user: '王五', action: '提交了 Bug', target: '登录问题', badgeClass: 'badge-warning', icon: 'fas fa-bug', time: '1 小时前' },
|
|
{ user: '赵六', action: '更新了文档', target: 'API 接口', badgeClass: 'badge-primary', icon: 'fas fa-file-alt', time: '2 小时前' }
|
|
];
|
|
|
|
// ECharts initialization logic will be in <script> as it needs DOM access
|
|
</script>
|
|
|
|
<script>
|
|
$watch(() => [], () => {
|
|
// Initialize charts after DOM is ready
|
|
// Mock data for charts
|
|
const userChartDom = document.getElementById('userChart');
|
|
if (userChartDom) {
|
|
const userChart = echarts.init(userChartDom);
|
|
userChart.setOption({
|
|
grid: { top: 10, right: 10, bottom: 20, left: 30 },
|
|
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
|
|
yAxis: { type: 'value' },
|
|
series: [{ data: [150, 230, 224, 218, 135, 147, 260], type: 'line', smooth: true, itemStyle: { color: '#4a6cf7' } }]
|
|
});
|
|
}
|
|
|
|
const projectChartDom = document.getElementById('projectChart');
|
|
if (projectChartDom) {
|
|
const projectChart = echarts.init(projectChartDom);
|
|
projectChart.setOption({
|
|
tooltip: { trigger: 'item' },
|
|
series: [
|
|
{
|
|
type: 'pie',
|
|
radius: ['40%', '70%'],
|
|
avoidLabelOverlap: false,
|
|
itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 },
|
|
label: { show: false, position: 'center' },
|
|
emphasis: { label: { show: true, fontSize: '20', fontWeight: 'bold' } },
|
|
labelLine: { show: false },
|
|
data: [
|
|
{ value: 1048, name: 'Web' },
|
|
{ value: 735, name: 'Mobile' },
|
|
{ value: 580, name: 'Desktop' },
|
|
{ value: 484, name: 'AI' },
|
|
{ value: 300, name: 'Other' }
|
|
]
|
|
}
|
|
]
|
|
});
|
|
}
|
|
})
|
|
</script>
|
|
|
|
</html>
|