完成后台管理系统界面优化

This commit is contained in:
rjb
2025-08-30 01:25:39 +08:00
parent ea7570ba80
commit 177079221e
7 changed files with 1309 additions and 190 deletions

View File

@@ -54,6 +54,7 @@ def init_admin(app):
# 注册登录路由
app.add_url_rule('/admin/login', 'admin.login', admin_login, methods=['GET', 'POST'])
app.add_url_rule('/admin/logout', 'admin.logout', admin_logout)
app.add_url_rule('/admin/logout/confirm', 'admin.logout_confirm', admin_logout_confirm)
return admin
@@ -80,9 +81,16 @@ def admin_login():
def admin_logout():
"""管理员登出"""
logout_user()
flash('已成功登出', 'success')
flash('已成功登出后台管理系统', 'success')
return redirect(url_for('admin.login'))
def admin_logout_confirm():
"""管理员登出确认"""
if not current_user.is_authenticated:
return redirect(url_for('admin.login'))
return render_template('admin/logout_confirm.html')
class AdminIndexView(BaseView):
"""管理后台首页"""

View File

@@ -1,6 +1,8 @@
{% extends 'admin/master.html' %}
{% block title %}数据分析 - 仪表板{% endblock %}
{% block title %}数据分析{% endblock %}
{% block page_title %}数据分析{% endblock %}
{% block head %}
{{ super() }}
@@ -9,117 +11,170 @@
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="fas fa-chart-line"></i> 数据分析仪表板
</h1>
</div>
</div>
<!-- 统计卡片 -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-primary shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
总用户数
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ stats.total_users }}</div>
<div class="text-xs text-muted">今日新增: {{ stats.new_users_today }}</div>
</div>
<div class="col-auto">
<i class="fas fa-users fa-2x text-gray-300"></i>
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-users text-primary" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-primary">{{ stats.total_users if stats else 0 }}</h3>
<p class="stats-label text-muted">总用户数</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-primary" style="width: 75%"></div>
</div>
<small class="text-muted">今日新增: {{ stats.new_users_today if stats else 0 }}</small>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-success shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
总提示词
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ stats.total_prompts }}</div>
<div class="text-xs text-muted">今日生成: {{ stats.today_prompts }}</div>
</div>
<div class="col-auto">
<i class="fas fa-magic fa-2x text-gray-300"></i>
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-magic text-success" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-success">{{ stats.total_prompts if stats else 0 }}</h3>
<p class="stats-label text-muted">总提示词</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-success" style="width: 85%"></div>
</div>
<small class="text-muted">今日生成: {{ stats.today_prompts if stats else 0 }}</small>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-info shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
活跃用户
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ stats.active_users }}</div>
<div class="text-xs text-muted">本周活跃: {{ stats.active_users_week }}</div>
</div>
<div class="col-auto">
<i class="fas fa-user-check fa-2x text-gray-300"></i>
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-user-check text-info" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-info">{{ stats.active_users if stats else 0 }}</h3>
<p class="stats-label text-muted">活跃用户</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-info" style="width: 60%"></div>
</div>
<small class="text-muted">本周活跃: {{ stats.active_users_week if stats else 0 }}</small>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card border-left-warning shadow h-100 py-2">
<div class="card-body">
<div class="row no-gutters align-items-center">
<div class="col mr-2">
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
模板数量
</div>
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ stats.total_templates }}</div>
<div class="text-xs text-muted">默认模板: {{ stats.default_templates }}</div>
</div>
<div class="col-auto">
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-clipboard-list text-warning" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-warning">{{ stats.total_templates if stats else 0 }}</h3>
<p class="stats-label text-muted">模板数量</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-warning" style="width: 45%"></div>
</div>
<small class="text-muted">默认模板: {{ stats.default_templates if stats else 0 }}</small>
</div>
</div>
</div>
</div>
<!-- 趋势图表 -->
<!-- 图表区域 -->
<div class="row mb-4">
<div class="col-xl-8 col-lg-7">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-chart-area"></i> 用户注册趋势
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-chart-area me-2"></i> 用户注册趋势
</h6>
</div>
<div class="card-body">
<div id="userTrendChart" style="height: 300px;"></div>
<div id="userTrendChart" style="height: 350px;"></div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-5">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-chart-pie"></i> 用户状态分布
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-chart-pie me-2"></i> 用户状态分布
</h6>
</div>
<div class="card-body">
<div id="userStatusChart" style="height: 300px;"></div>
<div id="userStatusChart" style="height: 350px;"></div>
</div>
</div>
</div>
</div>
<!-- 详细分析 -->
<div class="row">
<div class="col-xl-6 mb-4">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-chart-bar me-2"></i> 提示词生成趋势
</h6>
</div>
<div class="card-body">
<div id="promptTrendChart" style="height: 300px;"></div>
</div>
</div>
</div>
<div class="col-xl-6 mb-4">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-clock me-2"></i> 用户活跃度
</h6>
</div>
<div class="card-body">
<div id="userActivityChart" style="height: 300px;"></div>
</div>
</div>
</div>
</div>
<!-- 数据洞察 -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-lightbulb me-2"></i> 数据洞察
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4 mb-3">
<div class="insight-card text-center p-4">
<div class="insight-icon mb-3">
<i class="fas fa-arrow-up text-success fa-2x"></i>
</div>
<h5 class="insight-title">用户增长</h5>
<p class="insight-desc">本月用户增长率达到 <strong class="text-success">15%</strong></p>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="insight-card text-center p-4">
<div class="insight-icon mb-3">
<i class="fas fa-chart-line text-primary fa-2x"></i>
</div>
<h5 class="insight-title">使用频率</h5>
<p class="insight-desc">平均每个用户每天使用 <strong class="text-primary">3.2</strong></p>
</div>
</div>
<div class="col-md-4 mb-3">
<div class="insight-card text-center p-4">
<div class="insight-icon mb-3">
<i class="fas fa-users text-info fa-2x"></i>
</div>
<h5 class="insight-title">用户留存</h5>
<p class="insight-desc">7天用户留存率达到 <strong class="text-info">68%</strong></p>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -128,32 +183,32 @@
<!-- 快速操作 -->
<div class="row">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-bolt"></i> 快速操作
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-bolt me-2"></i> 快速操作
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<a href="{{ url_for('analytics_admin.charts') }}" class="btn btn-primary btn-block">
<i class="fas fa-chart-bar"></i> 详细图表
<div class="col-lg-3 col-md-4 col-sm-6 mb-3">
<a href="{{ url_for('analytics_admin.charts') }}" class="btn btn-outline-primary btn-block">
<i class="fas fa-chart-bar me-2"></i> 详细图表
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_user.index_view') }}" class="btn btn-success btn-block">
<i class="fas fa-users"></i> 用户管理
<div class="col-lg-3 col-md-4 col-sm-6 mb-3">
<a href="{{ url_for('admin_user.index_view') }}" class="btn btn-outline-success btn-block">
<i class="fas fa-users me-2"></i> 用户管理
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_prompt.index_view') }}" class="btn btn-info btn-block">
<i class="fas fa-magic"></i> 提示词管理
<div class="col-lg-3 col-md-4 col-sm-6 mb-3">
<a href="{{ url_for('admin_prompt.index_view') }}" class="btn btn-outline-info btn-block">
<i class="fas fa-magic me-2"></i> 提示词管理
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_system.index') }}" class="btn btn-warning btn-block">
<i class="fas fa-cogs"></i> 系统管理
<div class="col-lg-3 col-md-4 col-sm-6 mb-3">
<a href="{{ url_for('admin_system.index') }}" class="btn btn-outline-warning btn-block">
<i class="fas fa-cogs me-2"></i> 系统管理
</a>
</div>
</div>
@@ -164,30 +219,48 @@
</div>
<style>
.border-left-primary {
border-left: 0.25rem solid #4e73df !important;
.stats-number {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.border-left-success {
border-left: 0.25rem solid #1cc88a !important;
.stats-label {
font-size: 0.875rem;
font-weight: 500;
}
.border-left-info {
border-left: 0.25rem solid #36b9cc !important;
.progress {
border-radius: 1rem;
background-color: #f8f9fc;
}
.border-left-warning {
border-left: 0.25rem solid #f6c23e !important;
.progress-bar {
border-radius: 1rem;
}
.text-gray-300 {
color: #dddfeb !important;
.insight-card {
border: 1px solid #e3e6f0;
border-radius: 0.75rem;
transition: all 0.3s ease;
height: 100%;
}
.text-gray-800 {
color: #5a5c69 !important;
.insight-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.font-weight-bold {
font-weight: 700 !important;
.insight-title {
font-weight: 600;
margin-bottom: 0.5rem;
}
.text-xs {
font-size: 0.7rem;
.insight-desc {
font-size: 0.875rem;
color: #6c757d;
}
.btn-block {
display: block;
width: 100%;
@@ -195,8 +268,37 @@
</style>
<script>
// 模拟图表数据(实际项目中会从后端获取)
document.addEventListener('DOMContentLoaded', function() {
// 初始化图表
initCharts();
// 数字动画
const statsNumbers = document.querySelectorAll('.stats-number');
statsNumbers.forEach(number => {
const finalValue = parseInt(number.textContent);
animateNumber(number, 0, finalValue, 1000);
});
});
function animateNumber(element, start, end, duration) {
const startTime = performance.now();
function updateNumber(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const current = Math.floor(start + (end - start) * progress);
element.textContent = current;
if (progress < 1) {
requestAnimationFrame(updateNumber);
}
}
requestAnimationFrame(updateNumber);
}
function initCharts() {
// 用户注册趋势图
var userTrendData = [{
x: ['2025-08-20', '2025-08-21', '2025-08-22', '2025-08-23', '2025-08-24', '2025-08-25', '2025-08-26'],
@@ -205,35 +307,110 @@ document.addEventListener('DOMContentLoaded', function() {
mode: 'lines+markers',
name: '注册用户',
line: {color: '#4e73df', width: 3},
marker: {size: 8}
marker: {size: 8, color: '#4e73df'}
}];
var userTrendLayout = {
title: '用户注册趋势',
xaxis: {title: '日期'},
yaxis: {title: '注册人数'},
height: 300,
margin: {l: 50, r: 50, t: 50, b: 50}
title: {text: '用户注册趋势', font: {size: 16}},
xaxis: {title: '日期', gridcolor: '#f1f3f4'},
yaxis: {title: '注册人数', gridcolor: '#f1f3f4'},
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgba(0,0,0,0)',
height: 350,
margin: {l: 60, r: 40, t: 60, b: 60}
};
Plotly.newPlot('userTrendChart', userTrendData, userTrendLayout);
// 用户状态分布图
var userStatusData = [{
values: [{{ stats.active_users }}, {{ stats.total_users - stats.active_users }}],
values: [{{ stats.active_users if stats else 0 }}, {{ (stats.total_users - stats.active_users) if stats else 0 }}],
labels: ['活跃用户', '禁用用户'],
type: 'pie',
marker: {colors: ['#28a745', '#dc3545']},
textinfo: 'label+percent'
textinfo: 'label+percent',
hole: 0.4
}];
var userStatusLayout = {
title: '用户状态分布',
height: 300,
margin: {l: 50, r: 50, t: 50, b: 50}
title: {text: '用户状态分布', font: {size: 16}},
height: 350,
margin: {l: 40, r: 40, t: 60, b: 40}
};
Plotly.newPlot('userStatusChart', userStatusData, userStatusLayout);
});
// 提示词生成趋势图
const promptTrendData = [
{date: '2025-08-23', count: 45},
{date: '2025-08-24', count: 52},
{date: '2025-08-25', count: 48},
{date: '2025-08-26', count: 61},
{date: '2025-08-27', count: 55},
{date: '2025-08-28', count: 67},
{date: '2025-08-29', count: 73}
];
const promptDates = promptTrendData.map(item => item.date);
const promptCounts = promptTrendData.map(item => item.count);
var promptTrendTrace = {
x: promptDates,
y: promptCounts,
type: 'bar',
name: '提示词数量',
marker: {color: '#1cc88a'}
};
var promptTrendLayout = {
title: {text: '提示词生成趋势', font: {size: 16}},
xaxis: {title: '日期', gridcolor: '#f1f3f4'},
yaxis: {title: '生成数量', gridcolor: '#f1f3f4'},
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgba(0,0,0,0)',
height: 300,
margin: {l: 60, r: 40, t: 60, b: 60}
};
Plotly.newPlot('promptTrendChart', [promptTrendTrace], promptTrendLayout);
// 用户活跃度图
const activityData = [
{hour: '00:00', users: 12},
{hour: '04:00', users: 8},
{hour: '08:00', users: 45},
{hour: '12:00', users: 78},
{hour: '16:00', users: 92},
{hour: '20:00', users: 65},
{hour: '24:00', users: 15}
];
const hours = activityData.map(item => item.hour);
const users = activityData.map(item => item.users);
var activityTrace = {
x: hours,
y: users,
type: 'scatter',
mode: 'lines+markers',
name: '活跃用户',
line: {color: '#36b9cc', width: 3},
marker: {size: 8, color: '#36b9cc'},
fill: 'tonexty',
fillcolor: 'rgba(54, 185, 204, 0.1)'
};
var activityLayout = {
title: {text: '24小时用户活跃度', font: {size: 16}},
xaxis: {title: '时间', gridcolor: '#f1f3f4'},
yaxis: {title: '活跃用户数', gridcolor: '#f1f3f4'},
plot_bgcolor: 'rgba(0,0,0,0)',
paper_bgcolor: 'rgba(0,0,0,0)',
height: 300,
margin: {l: 60, r: 40, t: 60, b: 60}
};
Plotly.newPlot('userActivityChart', [activityTrace], activityLayout);
}
</script>
{% endblock %}

View File

@@ -2,103 +2,269 @@
{% block title %}管理后台首页{% endblock %}
{% block page_title %}仪表板{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-12">
<h1 class="mb-4">
<i class="fas fa-tachometer-alt"></i> 系统概览
</h1>
</div>
</div>
<!-- 快速操作 -->
<!-- 欢迎横幅 -->
<div class="row mb-4">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-bolt"></i> 快速操作
<div class="stats-card">
<div class="row align-items-center">
<div class="col-md-8">
<h2 class="mb-2">欢迎回来,{{ current_user.username }}</h2>
<p class="mb-0 opacity-75">今天是 {{ moment().format('YYYY年MM月DD日 dddd') if moment else '2025年8月29日 星期四' }},祝您工作愉快!</p>
</div>
<div class="col-md-4 text-end">
<i class="fas fa-sun stats-icon"></i>
</div>
</div>
</div>
</div>
</div>
<!-- 统计卡片 -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-users text-primary" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-primary">{{ stats.total_users if stats else 0 }}</h3>
<p class="stats-label text-muted">总用户数</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-primary" style="width: 75%"></div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-magic text-success" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-success">{{ stats.total_prompts if stats else 0 }}</h3>
<p class="stats-label text-muted">总提示词</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-success" style="width: 85%"></div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-calendar text-info" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-info">{{ stats.today_prompts if stats else 0 }}</h3>
<p class="stats-label text-muted">今日提示词</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-info" style="width: 60%"></div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card h-100">
<div class="card-body text-center">
<div class="stats-icon mb-3">
<i class="fas fa-clipboard-list text-warning" style="font-size: 3rem;"></i>
</div>
<h3 class="stats-number text-warning">{{ stats.total_templates if stats else 0 }}</h3>
<p class="stats-label text-muted">模板数量</p>
<div class="progress mt-2" style="height: 6px;">
<div class="progress-bar bg-warning" style="width: 45%"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 功能模块 -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-th-large me-2"></i> 功能模块
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_user.index_view') }}" class="btn btn-primary btn-block">
<i class="fas fa-users"></i> 用户管理
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-users fa-3x text-primary"></i>
</div>
<h5 class="feature-title">用户管理</h5>
<p class="feature-desc text-muted">管理系统用户和权限</p>
<a href="{{ url_for('admin_user.index_view') }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_prompt.index_view') }}" class="btn btn-success btn-block">
<i class="fas fa-magic"></i> 提示词管理
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-magic fa-3x text-success"></i>
</div>
<h5 class="feature-title">提示词管理</h5>
<p class="feature-desc text-muted">管理AI提示词内容</p>
<a href="{{ url_for('admin_prompt.index_view') }}" class="btn btn-outline-success btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_template.index_view') }}" class="btn btn-info btn-block">
<i class="fas fa-clipboard-list"></i> 模板管理
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-chart-line fa-3x text-info"></i>
</div>
<h5 class="feature-title">数据分析</h5>
<p class="feature-desc text-muted">数据统计和可视化</p>
<a href="{{ url_for('analytics_admin.index') }}" class="btn btn-outline-info btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
<div class="col-md-3 mb-3">
<a href="{{ url_for('admin_system.index') }}" class="btn btn-warning btn-block">
<i class="fas fa-cogs"></i> 系统管理
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-database fa-3x text-warning"></i>
</div>
<h5 class="feature-title">数据备份</h5>
<p class="feature-desc text-muted">系统数据备份恢复</p>
<a href="{{ url_for('backup_admin.index') }}" class="btn btn-outline-warning btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-tasks fa-3x text-danger"></i>
</div>
<h5 class="feature-title">批量操作</h5>
<p class="feature-desc text-muted">批量数据处理</p>
<a href="{{ url_for('batch_admin.index') }}" class="btn btn-outline-danger btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-tachometer-alt fa-3x text-secondary"></i>
</div>
<h5 class="feature-title">系统监控</h5>
<p class="feature-desc text-muted">实时系统监控</p>
<a href="{{ url_for('monitor_admin.index') }}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-chart-bar fa-3x text-dark"></i>
</div>
<h5 class="feature-title">高级报表</h5>
<p class="feature-desc text-muted">生成详细报表</p>
<a href="{{ url_for('report_admin.index') }}" class="btn btn-outline-dark btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
</div>
<div class="col-lg-3 col-md-4 col-sm-6 mb-4">
<div class="feature-card text-center p-4">
<div class="feature-icon mb-3">
<i class="fas fa-code fa-3x text-primary"></i>
</div>
<h5 class="feature-title">API管理</h5>
<p class="feature-desc text-muted">API调用管理</p>
<a href="{{ url_for('api_admin.index') }}" class="btn btn-outline-primary btn-sm">
<i class="fas fa-arrow-right me-1"></i> 进入
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 系统信息 -->
<!-- 系统状态 -->
<div class="row">
<div class="col-12">
<div class="card shadow">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-info-circle"></i> 系统信息
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-info-circle me-2"></i> 系统信息
</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td><strong>系统名称</strong></td>
<td>提示词大师管理系统</td>
</tr>
<tr>
<td><strong>当前用户:</strong></td>
<td>{{ current_user.username if current_user.is_authenticated else '未登录' }}</td>
</tr>
<tr>
<td><strong>用户角色:</strong></td>
<td>{{ current_user.role if current_user.is_authenticated else '无' }}</td>
</tr>
</table>
<div class="col-6">
<p class="mb-2"><strong>系统版本:</strong> v1.0.0</p>
<p class="mb-2"><strong>运行环境:</strong> Python 3.12.7</p>
<p class="mb-2"><strong>数据库</strong> MySQL</p>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td><strong>最后登录</strong></td>
<td>{{ current_user.last_login.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated and current_user.last_login else '首次登录' }}</td>
</tr>
<tr>
<td><strong>登录时间:</strong></td>
<td>{{ current_user.created_at.strftime('%Y-%m-%d %H:%M:%S') if current_user.is_authenticated else '无' }}</td>
</tr>
<tr>
<td><strong>账户状态:</strong></td>
<td>
{% if current_user.is_authenticated %}
<span class="badge badge-{{ 'success' if current_user.is_active else 'danger' }}">
{{ '正常' if current_user.is_active else '禁用' }}
</span>
{% else %}
<span class="badge badge-secondary">未登录</span>
{% endif %}
</td>
</tr>
</table>
<div class="col-6">
<p class="mb-2"><strong>最后更新:</strong> 2025-08-29</p>
<p class="mb-2"><strong>在线用户:</strong> {{ stats.total_users if stats else 0 }}</p>
<p class="mb-2"><strong>系统状态</strong> <span class="badge bg-success">正常</span></p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 mb-4">
<div class="card">
<div class="card-header">
<h6 class="m-0 font-weight-bold">
<i class="fas fa-clock me-2"></i> 最近活动
</h6>
</div>
<div class="card-body">
<div class="activity-item d-flex align-items-center mb-3">
<div class="activity-icon bg-primary text-white rounded-circle p-2 me-3">
<i class="fas fa-user-plus"></i>
</div>
<div>
<p class="mb-0"><strong>新用户注册</strong></p>
<small class="text-muted">2分钟前</small>
</div>
</div>
<div class="activity-item d-flex align-items-center mb-3">
<div class="activity-icon bg-success text-white rounded-circle p-2 me-3">
<i class="fas fa-magic"></i>
</div>
<div>
<p class="mb-0"><strong>生成提示词</strong></p>
<small class="text-muted">5分钟前</small>
</div>
</div>
<div class="activity-item d-flex align-items-center">
<div class="activity-icon bg-info text-white rounded-circle p-2 me-3">
<i class="fas fa-database"></i>
</div>
<div>
<p class="mb-0"><strong>数据备份</strong></p>
<small class="text-muted">10分钟前</small>
</div>
</div>
</div>
@@ -108,9 +274,93 @@
</div>
<style>
.btn-block {
display: block;
width: 100%;
.feature-card {
border: 1px solid #e3e6f0;
border-radius: 0.75rem;
transition: all 0.3s ease;
height: 100%;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
border-color: var(--primary-color);
}
.feature-icon {
transition: all 0.3s ease;
}
.feature-card:hover .feature-icon {
transform: scale(1.1);
}
.feature-title {
font-weight: 600;
margin-bottom: 0.5rem;
}
.feature-desc {
font-size: 0.875rem;
margin-bottom: 1rem;
}
.activity-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
}
.stats-number {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.stats-label {
font-size: 0.875rem;
font-weight: 500;
}
.progress {
border-radius: 1rem;
background-color: #f8f9fc;
}
.progress-bar {
border-radius: 1rem;
}
</style>
<script>
// 添加动态效果
document.addEventListener('DOMContentLoaded', function() {
// 数字动画
const statsNumbers = document.querySelectorAll('.stats-number');
statsNumbers.forEach(number => {
const finalValue = parseInt(number.textContent);
animateNumber(number, 0, finalValue, 1000);
});
});
function animateNumber(element, start, end, duration) {
const startTime = performance.now();
function updateNumber(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const current = Math.floor(start + (end - start) * progress);
element.textContent = current;
if (progress < 1) {
requestAnimationFrame(updateNumber);
}
}
requestAnimationFrame(updateNumber);
}
</script>
{% endblock %}

View File

@@ -73,6 +73,7 @@
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
<i class="fas fa-{{ 'exclamation-triangle' if category == 'error' else 'check-circle' }} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
@@ -133,5 +134,29 @@
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 页面加载动画
document.addEventListener('DOMContentLoaded', function() {
const loginCard = document.querySelector('.login-card');
loginCard.style.opacity = '0';
loginCard.style.transform = 'translateY(30px)';
setTimeout(() => {
loginCard.style.transition = 'all 0.6s ease-out';
loginCard.style.opacity = '1';
loginCard.style.transform = 'translateY(0)';
}, 100);
// 表单提交动画
const form = document.querySelector('form');
const submitBtn = document.querySelector('.btn-login');
form.addEventListener('submit', function() {
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 登录中...';
submitBtn.disabled = true;
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,69 @@
{% extends 'admin/master.html' %}
{% block title %}确认登出{% endblock %}
{% block page_title %}确认登出{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-4">
<div class="card">
<div class="card-header text-center">
<h5 class="mb-0">
<i class="fas fa-sign-out-alt text-warning me-2"></i>
确认登出
</h5>
</div>
<div class="card-body text-center">
<div class="mb-4">
<i class="fas fa-question-circle text-warning" style="font-size: 4rem;"></i>
</div>
<h6 class="mb-3">您确定要登出后台管理系统吗?</h6>
<p class="text-muted mb-4">
当前用户:<strong>{{ current_user.username }}</strong><br>
登出后将返回登录页面
</p>
<div class="d-grid gap-2">
<a href="{{ url_for('admin.logout') }}" class="btn btn-danger">
<i class="fas fa-sign-out-alt me-2"></i>
确认登出
</a>
<a href="{{ url_for('admin.index') }}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i>
取消
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.card {
border: none;
border-radius: 1rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
}
.card-header {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border-bottom: 1px solid #dee2e6;
border-radius: 1rem 1rem 0 0 !important;
}
.btn {
border-radius: 0.5rem;
font-weight: 500;
padding: 0.75rem 1.5rem;
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
</style>
{% endblock %}

View File

@@ -0,0 +1,590 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% block title %}{% endblock %} - 提示词大师后台管理</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<!-- Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Custom CSS -->
<style>
:root {
--primary-color: #4e73df;
--secondary-color: #858796;
--success-color: #1cc88a;
--info-color: #36b9cc;
--warning-color: #f6c23e;
--danger-color: #e74a3b;
--dark-color: #5a5c69;
--light-color: #f8f9fc;
--sidebar-width: 250px;
--header-height: 70px;
}
* {
font-family: 'Inter', sans-serif;
}
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
/* 侧边栏样式 */
.sidebar {
position: fixed;
top: 0;
left: 0;
width: var(--sidebar-width);
height: 100vh;
background: linear-gradient(180deg, #2c3e50 0%, #34495e 100%);
box-shadow: 4px 0 10px rgba(0,0,0,0.1);
z-index: 1000;
transition: all 0.3s ease;
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
text-align: center;
}
.sidebar-brand {
color: white;
font-size: 1.5rem;
font-weight: 700;
text-decoration: none;
}
.sidebar-brand:hover {
color: #4e73df;
text-decoration: none;
}
.sidebar-menu {
padding: 1rem 0;
}
.sidebar-item {
margin: 0.25rem 1rem;
}
.sidebar-link {
display: flex;
align-items: center;
padding: 0.75rem 1rem;
color: rgba(255,255,255,0.8);
text-decoration: none;
border-radius: 0.5rem;
transition: all 0.3s ease;
}
.sidebar-link:hover {
background: rgba(255,255,255,0.1);
color: white;
text-decoration: none;
transform: translateX(5px);
}
.sidebar-link.active {
background: var(--primary-color);
color: white;
box-shadow: 0 4px 8px rgba(78, 115, 223, 0.3);
}
.sidebar-icon {
width: 20px;
margin-right: 0.75rem;
text-align: center;
}
/* 主内容区域 */
.main-content {
margin-left: var(--sidebar-width);
min-height: 100vh;
background: #f8f9fc;
transition: all 0.3s ease;
}
/* 顶部导航栏 */
.top-navbar {
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: sticky;
top: 0;
z-index: 999;
}
.page-title {
font-size: 1.5rem;
font-weight: 600;
color: var(--dark-color);
margin: 0;
}
.user-menu {
display: flex;
align-items: center;
gap: 1rem;
}
.user-info {
text-align: right;
}
.user-name {
font-weight: 600;
color: var(--dark-color);
margin: 0;
}
.user-role {
font-size: 0.875rem;
color: var(--secondary-color);
margin: 0;
}
.logout-btn {
background: linear-gradient(135deg, var(--danger-color) 0%, #dc3545 100%);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
text-decoration: none;
transition: all 0.3s ease;
font-weight: 500;
box-shadow: 0 2px 8px rgba(231, 74, 59, 0.3);
}
.logout-btn:hover {
background: linear-gradient(135deg, #c82333 0%, #bd2130 100%);
color: white;
text-decoration: none;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(231, 74, 59, 0.4);
}
/* 内容区域 */
.content-wrapper {
padding: 2rem;
}
/* 卡片样式优化 */
.card {
border: none;
border-radius: 1rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
transition: all 0.3s ease;
overflow: hidden;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
}
.card-header {
background: linear-gradient(135deg, var(--primary-color) 0%, #6f42c1 100%);
color: white;
border: none;
padding: 1.5rem;
}
.card-header h6 {
margin: 0;
font-weight: 600;
}
.card-body {
padding: 1.5rem;
}
/* 按钮样式优化 */
.btn {
border-radius: 0.5rem;
font-weight: 500;
padding: 0.5rem 1.5rem;
transition: all 0.3s ease;
border: none;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.btn-primary {
background: linear-gradient(135deg, var(--primary-color) 0%, #6f42c1 100%);
}
.btn-success {
background: linear-gradient(135deg, var(--success-color) 0%, #20c997 100%);
}
.btn-info {
background: linear-gradient(135deg, var(--info-color) 0%, #17a2b8 100%);
}
.btn-warning {
background: linear-gradient(135deg, var(--warning-color) 0%, #fd7e14 100%);
}
.btn-danger {
background: linear-gradient(135deg, var(--danger-color) 0%, #dc3545 100%);
}
/* 表格样式优化 */
.table {
border-radius: 0.5rem;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.05);
}
.table thead th {
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
border: none;
font-weight: 600;
color: var(--dark-color);
padding: 1rem;
}
.table tbody td {
padding: 1rem;
border: none;
border-bottom: 1px solid #f1f3f4;
}
.table tbody tr:hover {
background: rgba(78, 115, 223, 0.05);
}
/* 统计卡片样式 */
.stats-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 1rem;
padding: 1.5rem;
margin-bottom: 1.5rem;
position: relative;
overflow: hidden;
}
.stats-card::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 100%;
height: 100%;
background: rgba(255,255,255,0.1);
border-radius: 50%;
transform: rotate(45deg);
}
.stats-card .stats-icon {
position: relative;
z-index: 1;
font-size: 3rem;
opacity: 0.8;
}
.stats-card .stats-content {
position: relative;
z-index: 1;
}
.stats-card .stats-number {
font-size: 2rem;
font-weight: 700;
margin: 0;
}
.stats-card .stats-label {
font-size: 0.875rem;
opacity: 0.9;
margin: 0;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.content-wrapper {
padding: 1rem;
}
}
/* 动画效果 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out;
}
/* 加载动画 */
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 通知样式 */
.alert {
border: none;
border-radius: 0.5rem;
padding: 1rem 1.5rem;
margin-bottom: 1rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.alert-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
color: #155724;
}
.alert-danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
color: #721c24;
}
.alert-warning {
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
color: #856404;
}
.alert-info {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
color: #0c5460;
}
</style>
{% block head %}{% endblock %}
</head>
<body>
<!-- 侧边栏 -->
<nav class="sidebar">
<div class="sidebar-header">
<a href="{{ url_for('admin.index') }}" class="sidebar-brand">
<i class="fas fa-magic me-2"></i>
提示词大师
</a>
</div>
<div class="sidebar-menu">
<div class="sidebar-item">
<a href="{{ url_for('admin.index') }}" class="sidebar-link {% if request.endpoint == 'admin.index' %}active{% endif %}">
<i class="fas fa-tachometer-alt sidebar-icon"></i>
仪表板
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('admin_user.index_view') }}" class="sidebar-link {% if 'admin_user' in request.endpoint %}active{% endif %}">
<i class="fas fa-users sidebar-icon"></i>
用户管理
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('admin_prompt.index_view') }}" class="sidebar-link {% if 'admin_prompt' in request.endpoint %}active{% endif %}">
<i class="fas fa-magic sidebar-icon"></i>
提示词管理
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('admin_template.index_view') }}" class="sidebar-link {% if 'admin_template' in request.endpoint %}active{% endif %}">
<i class="fas fa-clipboard-list sidebar-icon"></i>
模板管理
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('analytics_admin.index') }}" class="sidebar-link {% if 'analytics_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-chart-line sidebar-icon"></i>
数据分析
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('batch_admin.index') }}" class="sidebar-link {% if 'batch_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-tasks sidebar-icon"></i>
批量操作
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('monitor_admin.index') }}" class="sidebar-link {% if 'monitor_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-tachometer-alt sidebar-icon"></i>
系统监控
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('report_admin.index') }}" class="sidebar-link {% if 'report_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-chart-bar sidebar-icon"></i>
高级报表
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('backup_admin.index') }}" class="sidebar-link {% if 'backup_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-database sidebar-icon"></i>
数据备份
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('api_admin.index') }}" class="sidebar-link {% if 'api_admin' in request.endpoint %}active{% endif %}">
<i class="fas fa-code sidebar-icon"></i>
API管理
</a>
</div>
<div class="sidebar-item">
<a href="{{ url_for('admin_system.index') }}" class="sidebar-link {% if 'admin_system' in request.endpoint %}active{% endif %}">
<i class="fas fa-cogs sidebar-icon"></i>
系统管理
</a>
</div>
</div>
</nav>
<!-- 主内容区域 -->
<div class="main-content">
<!-- 顶部导航栏 -->
<header class="top-navbar">
<h1 class="page-title">
{% block page_title %}{% endblock %}
</h1>
<div class="user-menu">
<div class="user-info">
<p class="user-name">{{ current_user.username if current_user.is_authenticated else '游客' }}</p>
<p class="user-role">{{ current_user.role if current_user.is_authenticated else '未登录' }}</p>
</div>
{% if current_user.is_authenticated %}
<a href="{{ url_for('admin.logout_confirm') }}" class="logout-btn">
<i class="fas fa-sign-out-alt me-2"></i>
退出登录
</a>
{% endif %}
</div>
</header>
<!-- 内容包装器 -->
<div class="content-wrapper">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
<i class="fas fa-{{ 'exclamation-triangle' if category == 'error' else 'info-circle' }} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block body %}{% endblock %}
</div>
</div>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// 页面加载动画
document.addEventListener('DOMContentLoaded', function() {
const contentElements = document.querySelectorAll('.card, .stats-card');
contentElements.forEach((element, index) => {
element.style.opacity = '0';
element.style.transform = 'translateY(30px)';
setTimeout(() => {
element.style.transition = 'all 0.6s ease-out';
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}, index * 100);
});
});
// 侧边栏响应式处理
function toggleSidebar() {
const sidebar = document.querySelector('.sidebar');
sidebar.classList.toggle('show');
}
// 添加移动端菜单按钮
if (window.innerWidth <= 768) {
const topNavbar = document.querySelector('.top-navbar');
const menuButton = document.createElement('button');
menuButton.className = 'btn btn-primary d-md-none';
menuButton.innerHTML = '<i class="fas fa-bars"></i>';
menuButton.onclick = toggleSidebar;
topNavbar.insertBefore(menuButton, topNavbar.firstChild);
}
// 自动隐藏通知
setTimeout(() => {
const alerts = document.querySelectorAll('.alert');
alerts.forEach(alert => {
const bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
});
}, 5000);
// 登出确认功能
document.addEventListener('DOMContentLoaded', function() {
const logoutBtn = document.querySelector('.logout-btn');
if (logoutBtn) {
logoutBtn.addEventListener('click', function(e) {
// 这里可以添加额外的确认逻辑
// 目前使用页面确认,所以不需要阻止默认行为
});
}
});
</script>
{% block scripts %}{% endblock %}
</body>
</html>