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.
OneAuth/ui/page/stats.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>