完成后台管理系统第一阶段开发
This commit is contained in:
@@ -51,6 +51,10 @@ def create_app(config_class=None):
|
||||
# 注册认证蓝图
|
||||
from src.flask_prompt_master.routes.auth import auth_bp
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
# 初始化后台管理
|
||||
from src.flask_prompt_master.admin import init_admin
|
||||
init_admin(app)
|
||||
|
||||
# 记录应用启动信息
|
||||
app.logger.info(f"应用启动 - 环境: {os.environ.get('FLASK_ENV', 'development')}")
|
||||
|
||||
Binary file not shown.
123
src/flask_prompt_master/admin/__init__.py
Normal file
123
src/flask_prompt_master/admin/__init__.py
Normal file
@@ -0,0 +1,123 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
后台管理模块
|
||||
"""
|
||||
from flask_admin import Admin, BaseView, expose
|
||||
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
|
||||
from flask import redirect, url_for, request, flash, render_template
|
||||
from src.flask_prompt_master import db
|
||||
from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate
|
||||
from .models.admin_user import AdminUser
|
||||
from .forms.admin_forms import AdminLoginForm
|
||||
from .views.user_admin import UserAdminView
|
||||
from .views.prompt_admin import PromptAdminView
|
||||
from .views.template_admin import TemplateAdminView
|
||||
from .views.system_admin import SystemAdminView
|
||||
from datetime import datetime
|
||||
|
||||
# 创建登录管理器
|
||||
login_manager = LoginManager()
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
"""加载用户"""
|
||||
return AdminUser.query.get(int(user_id))
|
||||
|
||||
def init_admin(app):
|
||||
"""初始化后台管理"""
|
||||
# 初始化登录管理器
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = 'admin.login'
|
||||
login_manager.login_message = '请先登录'
|
||||
|
||||
# 创建Admin实例
|
||||
admin = Admin(app, name='提示词大师后台管理', template_mode='bootstrap4')
|
||||
|
||||
# 注册视图
|
||||
admin.add_view(UserAdminView(User, db.session, name='用户管理', endpoint='admin_user'))
|
||||
admin.add_view(PromptAdminView(Prompt, db.session, name='提示词管理', endpoint='admin_prompt'))
|
||||
admin.add_view(TemplateAdminView(PromptTemplate, db.session, name='模板管理', endpoint='admin_template'))
|
||||
admin.add_view(SystemAdminView(name='系统管理', endpoint='admin_system'))
|
||||
|
||||
# 注册登录路由
|
||||
app.add_url_rule('/admin/login', 'admin.login', admin_login, methods=['GET', 'POST'])
|
||||
app.add_url_rule('/admin/logout', 'admin.logout', admin_logout)
|
||||
|
||||
return admin
|
||||
|
||||
def admin_login():
|
||||
"""管理员登录"""
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
form = AdminLoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = AdminUser.query.filter_by(username=form.username.data).first()
|
||||
if user and user.check_password(form.password.data) and user.is_active:
|
||||
login_user(user, remember=form.remember_me.data)
|
||||
user.last_login = datetime.utcnow()
|
||||
db.session.commit()
|
||||
next_page = request.args.get('next')
|
||||
return redirect(next_page or url_for('admin.index'))
|
||||
else:
|
||||
flash('用户名或密码错误', 'error')
|
||||
|
||||
return render_template('admin/login.html', form=form)
|
||||
|
||||
@login_required
|
||||
def admin_logout():
|
||||
"""管理员登出"""
|
||||
logout_user()
|
||||
flash('已成功登出', 'success')
|
||||
return redirect(url_for('admin.login'))
|
||||
|
||||
class AdminIndexView(BaseView):
|
||||
"""管理后台首页"""
|
||||
|
||||
def is_accessible(self):
|
||||
"""检查访问权限"""
|
||||
return current_user.is_authenticated
|
||||
|
||||
@expose('/')
|
||||
@login_required
|
||||
def index(self):
|
||||
"""管理后台首页"""
|
||||
# 获取统计数据
|
||||
from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
try:
|
||||
# 用户统计
|
||||
total_users = User.query.count()
|
||||
active_users = User.query.filter_by(status=1).count()
|
||||
|
||||
# 提示词统计
|
||||
total_prompts = Prompt.query.count()
|
||||
today_prompts = Prompt.query.filter(
|
||||
Prompt.created_at >= datetime.now().date()
|
||||
).count()
|
||||
|
||||
# 模板统计
|
||||
total_templates = PromptTemplate.query.count()
|
||||
default_templates = PromptTemplate.query.filter_by(is_default=True).count()
|
||||
|
||||
stats = {
|
||||
'total_users': total_users,
|
||||
'active_users': active_users,
|
||||
'total_prompts': total_prompts,
|
||||
'today_prompts': today_prompts,
|
||||
'total_templates': total_templates,
|
||||
'default_templates': default_templates
|
||||
}
|
||||
except Exception as e:
|
||||
stats = {
|
||||
'total_users': 0,
|
||||
'active_users': 0,
|
||||
'total_prompts': 0,
|
||||
'today_prompts': 0,
|
||||
'total_templates': 0,
|
||||
'default_templates': 0
|
||||
}
|
||||
|
||||
return self.render('admin/index.html', stats=stats)
|
||||
Binary file not shown.
6
src/flask_prompt_master/admin/forms/__init__.py
Normal file
6
src/flask_prompt_master/admin/forms/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
后台管理表单包
|
||||
"""
|
||||
from .admin_forms import AdminLoginForm, AdminUserForm
|
||||
|
||||
__all__ = ['AdminLoginForm', 'AdminUserForm']
|
||||
Binary file not shown.
Binary file not shown.
45
src/flask_prompt_master/admin/forms/admin_forms.py
Normal file
45
src/flask_prompt_master/admin/forms/admin_forms.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
后台管理表单
|
||||
"""
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, BooleanField, SelectField, TextAreaField
|
||||
from wtforms.validators import DataRequired, Email, Length, EqualTo, Optional
|
||||
|
||||
class AdminLoginForm(FlaskForm):
|
||||
"""管理员登录表单"""
|
||||
username = StringField('用户名', validators=[
|
||||
DataRequired(message='请输入用户名'),
|
||||
Length(min=3, max=50, message='用户名长度必须在3-50个字符之间')
|
||||
])
|
||||
password = PasswordField('密码', validators=[
|
||||
DataRequired(message='请输入密码'),
|
||||
Length(min=6, message='密码长度至少6位')
|
||||
])
|
||||
remember_me = BooleanField('记住我')
|
||||
|
||||
class AdminUserForm(FlaskForm):
|
||||
"""管理员用户表单"""
|
||||
username = StringField('用户名', validators=[
|
||||
DataRequired(message='请输入用户名'),
|
||||
Length(min=3, max=50, message='用户名长度必须在3-50个字符之间')
|
||||
])
|
||||
email = StringField('邮箱', validators=[
|
||||
DataRequired(message='请输入邮箱'),
|
||||
Email(message='邮箱格式不正确')
|
||||
])
|
||||
password = PasswordField('密码', validators=[
|
||||
Optional(),
|
||||
Length(min=6, message='密码长度至少6位')
|
||||
])
|
||||
confirm_password = PasswordField('确认密码', validators=[
|
||||
Optional(),
|
||||
EqualTo('password', message='两次输入的密码不一致')
|
||||
])
|
||||
role = SelectField('角色', choices=[
|
||||
('super_admin', '超级管理员'),
|
||||
('user_admin', '用户管理员'),
|
||||
('content_admin', '内容管理员'),
|
||||
('system_admin', '系统管理员')
|
||||
], validators=[DataRequired(message='请选择角色')])
|
||||
is_active = BooleanField('是否激活', default=True)
|
||||
6
src/flask_prompt_master/admin/models/__init__.py
Normal file
6
src/flask_prompt_master/admin/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
后台管理模型包
|
||||
"""
|
||||
from .admin_user import AdminUser
|
||||
|
||||
__all__ = ['AdminUser']
|
||||
Binary file not shown.
Binary file not shown.
58
src/flask_prompt_master/admin/models/admin_user.py
Normal file
58
src/flask_prompt_master/admin/models/admin_user.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
管理员用户模型
|
||||
"""
|
||||
from datetime import datetime
|
||||
from flask_login import UserMixin
|
||||
from src.flask_prompt_master import db
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
class AdminUser(db.Model, UserMixin):
|
||||
"""管理员用户表"""
|
||||
__tablename__ = 'admin_user'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(50), unique=True, nullable=False, comment='用户名')
|
||||
password_hash = db.Column(db.String(128), nullable=False, comment='密码哈希')
|
||||
email = db.Column(db.String(100), unique=True, nullable=False, comment='邮箱')
|
||||
role = db.Column(db.String(20), nullable=False, default='admin', comment='角色')
|
||||
is_active = db.Column(db.Boolean, default=True, comment='是否激活')
|
||||
last_login = db.Column(db.DateTime, comment='最后登录时间')
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, comment='创建时间')
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment='更新时间')
|
||||
|
||||
def __init__(self, username, password, email, role='admin'):
|
||||
self.username = username
|
||||
self.password_hash = self._hash_password(password)
|
||||
self.email = email
|
||||
self.role = role
|
||||
|
||||
def _hash_password(self, password):
|
||||
"""密码加密"""
|
||||
salt = os.urandom(16).hex()
|
||||
return hashlib.sha256((password + salt).encode()).hexdigest() + ':' + salt
|
||||
|
||||
def check_password(self, password):
|
||||
"""验证密码"""
|
||||
if ':' not in self.password_hash:
|
||||
return False
|
||||
hash_part, salt = self.password_hash.split(':')
|
||||
return hashlib.sha256((password + salt).encode()).hexdigest() == hash_part
|
||||
|
||||
def get_id(self):
|
||||
"""获取用户ID"""
|
||||
return str(self.id)
|
||||
|
||||
@property
|
||||
def is_authenticated(self):
|
||||
"""是否已认证"""
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_anonymous(self):
|
||||
"""是否匿名用户"""
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return f'<AdminUser {self.username}>'
|
||||
14
src/flask_prompt_master/admin/views/__init__.py
Normal file
14
src/flask_prompt_master/admin/views/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
后台管理视图包
|
||||
"""
|
||||
from .user_admin import UserAdminView
|
||||
from .prompt_admin import PromptAdminView
|
||||
from .template_admin import TemplateAdminView
|
||||
from .system_admin import SystemAdminView
|
||||
|
||||
__all__ = [
|
||||
'UserAdminView',
|
||||
'PromptAdminView',
|
||||
'TemplateAdminView',
|
||||
'SystemAdminView'
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
60
src/flask_prompt_master/admin/views/prompt_admin.py
Normal file
60
src/flask_prompt_master/admin/views/prompt_admin.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
提示词管理视图
|
||||
"""
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
from flask_login import current_user
|
||||
from src.flask_prompt_master.models.models import Prompt
|
||||
from datetime import datetime
|
||||
|
||||
class PromptAdminView(ModelView):
|
||||
"""提示词管理视图"""
|
||||
|
||||
# 设置模型
|
||||
model = Prompt
|
||||
|
||||
# 设置页面标题
|
||||
name = '提示词管理'
|
||||
endpoint = 'admin.prompt'
|
||||
|
||||
# 设置列显示
|
||||
column_list = ['id', 'input_text', 'generated_text', 'user_id', 'created_at']
|
||||
column_labels = {
|
||||
'id': 'ID',
|
||||
'input_text': '输入文本',
|
||||
'generated_text': '生成文本',
|
||||
'user_id': '用户ID',
|
||||
'created_at': '创建时间'
|
||||
}
|
||||
|
||||
# 设置搜索
|
||||
column_searchable_list = ['input_text', 'generated_text']
|
||||
|
||||
# 设置过滤
|
||||
column_filters = ['created_at', 'user_id']
|
||||
|
||||
# 设置排序
|
||||
column_default_sort = ('created_at', True)
|
||||
|
||||
# 设置编辑权限
|
||||
can_create = False
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
|
||||
# 设置表单字段
|
||||
form_excluded_columns = ['feedbacks']
|
||||
|
||||
# 设置文本字段为文本区域
|
||||
form_widget_args = {
|
||||
'input_text': {'rows': 3},
|
||||
'generated_text': {'rows': 5}
|
||||
}
|
||||
|
||||
def is_accessible(self):
|
||||
"""检查访问权限"""
|
||||
return current_user.is_authenticated and current_user.role in ['super_admin', 'content_admin']
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
"""模型变更时的处理"""
|
||||
# 可以添加内容审核逻辑
|
||||
pass
|
||||
83
src/flask_prompt_master/admin/views/system_admin.py
Normal file
83
src/flask_prompt_master/admin/views/system_admin.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
系统管理视图
|
||||
"""
|
||||
from flask_admin import BaseView, expose
|
||||
from flask_login import login_required, current_user
|
||||
from src.flask_prompt_master.models.models import User, Prompt, PromptTemplate
|
||||
from src.flask_prompt_master import db
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class SystemAdminView(BaseView):
|
||||
"""系统管理视图"""
|
||||
|
||||
@expose('/')
|
||||
@login_required
|
||||
def index(self):
|
||||
"""系统概览"""
|
||||
if not current_user.is_authenticated or current_user.role not in ['super_admin', 'system_admin']:
|
||||
return self.render('admin/403.html')
|
||||
|
||||
# 获取统计数据
|
||||
stats = self._get_system_stats()
|
||||
|
||||
return self.render('admin/system_dashboard.html', stats=stats)
|
||||
|
||||
@expose('/logs')
|
||||
@login_required
|
||||
def logs(self):
|
||||
"""系统日志"""
|
||||
if not current_user.is_authenticated or current_user.role not in ['super_admin', 'system_admin']:
|
||||
return self.render('admin/403.html')
|
||||
|
||||
# 这里可以添加日志查看功能
|
||||
return self.render('admin/system_logs.html')
|
||||
|
||||
def _get_system_stats(self):
|
||||
"""获取系统统计信息"""
|
||||
try:
|
||||
# 用户统计
|
||||
total_users = User.query.count()
|
||||
active_users = User.query.filter_by(status=1).count()
|
||||
|
||||
# 提示词统计
|
||||
total_prompts = Prompt.query.count()
|
||||
today_prompts = Prompt.query.filter(
|
||||
Prompt.created_at >= datetime.now().date()
|
||||
).count()
|
||||
|
||||
# 模板统计
|
||||
total_templates = PromptTemplate.query.count()
|
||||
default_templates = PromptTemplate.query.filter_by(is_default=True).count()
|
||||
|
||||
# 最近7天用户注册趋势
|
||||
seven_days_ago = datetime.now() - timedelta(days=7)
|
||||
daily_registrations = db.session.query(
|
||||
func.date(User.created_time).label('date'),
|
||||
func.count(User.uid).label('count')
|
||||
).filter(
|
||||
User.created_time >= seven_days_ago
|
||||
).group_by(
|
||||
func.date(User.created_time)
|
||||
).all()
|
||||
|
||||
return {
|
||||
'total_users': total_users,
|
||||
'active_users': active_users,
|
||||
'total_prompts': total_prompts,
|
||||
'today_prompts': today_prompts,
|
||||
'total_templates': total_templates,
|
||||
'default_templates': default_templates,
|
||||
'daily_registrations': daily_registrations
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
'total_users': 0,
|
||||
'active_users': 0,
|
||||
'total_prompts': 0,
|
||||
'today_prompts': 0,
|
||||
'total_templates': 0,
|
||||
'default_templates': 0,
|
||||
'daily_registrations': []
|
||||
}
|
||||
63
src/flask_prompt_master/admin/views/template_admin.py
Normal file
63
src/flask_prompt_master/admin/views/template_admin.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
模板管理视图
|
||||
"""
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
from flask_login import current_user
|
||||
from src.flask_prompt_master.models.models import PromptTemplate
|
||||
from datetime import datetime
|
||||
|
||||
class TemplateAdminView(ModelView):
|
||||
"""模板管理视图"""
|
||||
|
||||
# 设置模型
|
||||
model = PromptTemplate
|
||||
|
||||
# 设置页面标题
|
||||
name = '模板管理'
|
||||
endpoint = 'admin.template'
|
||||
|
||||
# 设置列显示
|
||||
column_list = ['id', 'name', 'category', 'industry', 'profession', 'is_default', 'created_at']
|
||||
column_labels = {
|
||||
'id': 'ID',
|
||||
'name': '模板名称',
|
||||
'category': '分类',
|
||||
'industry': '行业',
|
||||
'profession': '职业',
|
||||
'is_default': '是否默认',
|
||||
'created_at': '创建时间'
|
||||
}
|
||||
|
||||
# 设置搜索
|
||||
column_searchable_list = ['name', 'category', 'industry', 'profession']
|
||||
|
||||
# 设置过滤
|
||||
column_filters = ['category', 'industry', 'profession', 'is_default', 'created_at']
|
||||
|
||||
# 设置排序
|
||||
column_default_sort = ('created_at', True)
|
||||
|
||||
# 设置编辑权限
|
||||
can_create = True
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
|
||||
# 设置表单字段
|
||||
form_excluded_columns = ['updated_at']
|
||||
|
||||
# 设置文本字段为文本区域
|
||||
form_widget_args = {
|
||||
'description': {'rows': 3},
|
||||
'system_prompt': {'rows': 8}
|
||||
}
|
||||
|
||||
def is_accessible(self):
|
||||
"""检查访问权限"""
|
||||
return current_user.is_authenticated and current_user.role in ['super_admin', 'content_admin']
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
"""模型变更时的处理"""
|
||||
if is_created:
|
||||
model.created_at = datetime.now()
|
||||
model.updated_at = datetime.now()
|
||||
64
src/flask_prompt_master/admin/views/user_admin.py
Normal file
64
src/flask_prompt_master/admin/views/user_admin.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
用户管理视图
|
||||
"""
|
||||
from flask_admin import BaseView, expose
|
||||
from flask_admin.contrib.sqla import ModelView
|
||||
from flask_login import login_required, current_user
|
||||
from src.flask_prompt_master.models.models import User
|
||||
from src.flask_prompt_master import db
|
||||
from datetime import datetime
|
||||
|
||||
class UserAdminView(ModelView):
|
||||
"""用户管理视图"""
|
||||
|
||||
# 设置模型
|
||||
model = User
|
||||
|
||||
# 设置页面标题
|
||||
name = '用户管理'
|
||||
endpoint = 'admin.user'
|
||||
|
||||
# 设置列显示
|
||||
column_list = ['uid', 'nickname', 'login_name', 'email', 'mobile', 'sex', 'status', 'created_time']
|
||||
column_labels = {
|
||||
'uid': '用户ID',
|
||||
'nickname': '昵称',
|
||||
'login_name': '用户名',
|
||||
'email': '邮箱',
|
||||
'mobile': '手机号',
|
||||
'sex': '性别',
|
||||
'status': '状态',
|
||||
'created_time': '注册时间'
|
||||
}
|
||||
|
||||
# 设置搜索
|
||||
column_searchable_list = ['nickname', 'login_name', 'email', 'mobile']
|
||||
|
||||
# 设置过滤
|
||||
column_filters = ['status', 'sex', 'created_time']
|
||||
|
||||
# 设置排序
|
||||
column_default_sort = ('created_time', True)
|
||||
|
||||
# 设置编辑权限
|
||||
can_create = False
|
||||
can_edit = True
|
||||
can_delete = True
|
||||
|
||||
# 设置表单字段
|
||||
form_excluded_columns = ['login_pwd', 'login_salt', 'prompts', 'feedbacks']
|
||||
|
||||
def is_accessible(self):
|
||||
"""检查访问权限"""
|
||||
return current_user.is_authenticated and current_user.role in ['super_admin', 'user_admin']
|
||||
|
||||
def on_model_change(self, form, model, is_created):
|
||||
"""模型变更时的处理"""
|
||||
if not is_created:
|
||||
model.updated_time = datetime.now()
|
||||
|
||||
def after_model_delete(self, model):
|
||||
"""删除模型后的处理"""
|
||||
# 记录删除日志
|
||||
pass
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
116
src/flask_prompt_master/templates/admin/index.html
Normal file
116
src/flask_prompt_master/templates/admin/index.html
Normal file
@@ -0,0 +1,116 @@
|
||||
{% extends 'admin/master.html' %}
|
||||
|
||||
{% block 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> 快速操作
|
||||
</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> 用户管理
|
||||
</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> 提示词管理
|
||||
</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> 模板管理
|
||||
</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> 系统管理
|
||||
</a>
|
||||
</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> 系统信息
|
||||
</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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
137
src/flask_prompt_master/templates/admin/login.html
Normal file
137
src/flask_prompt_master/templates/admin/login.html
Normal file
@@ -0,0 +1,137 @@
|
||||
<!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 href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.login-card {
|
||||
background: white;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
|
||||
padding: 40px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.login-header h2 {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.login-header p {
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
.form-control {
|
||||
border-radius: 10px;
|
||||
border: 2px solid #e9ecef;
|
||||
padding: 12px 15px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.form-control:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||||
}
|
||||
.btn-login {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
.alert {
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<h2><i class="fas fa-shield-alt"></i> 后台管理</h2>
|
||||
<p>提示词大师管理系统</p>
|
||||
</div>
|
||||
|
||||
{% 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">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.username.id }}" class="form-label">
|
||||
<i class="fas fa-user"></i> 用户名
|
||||
</label>
|
||||
{{ form.username(class="form-control", placeholder="请输入用户名") }}
|
||||
{% if form.username.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.username.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.password.id }}" class="form-label">
|
||||
<i class="fas fa-lock"></i> 密码
|
||||
</label>
|
||||
{{ form.password(class="form-control", placeholder="请输入密码") }}
|
||||
{% if form.password.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.password.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
{{ form.remember_me(class="form-check-input") }}
|
||||
<label class="form-check-label" for="{{ form.remember_me.id }}">
|
||||
记住我
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary btn-login">
|
||||
<i class="fas fa-sign-in-alt"></i> 登录
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="text-center mt-3">
|
||||
<a href="/" class="text-muted text-decoration-none">
|
||||
<i class="fas fa-arrow-left"></i> 返回前台
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
223
src/flask_prompt_master/templates/admin/system_dashboard.html
Normal file
223
src/flask_prompt_master/templates/admin/system_dashboard.html
Normal file
@@ -0,0 +1,223 @@
|
||||
{% extends 'admin/master.html' %}
|
||||
|
||||
{% block 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-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>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-users fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</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.today_prompts }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-magic fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</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.total_templates }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-clipboard-list fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</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.active_users }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="fas fa-user-check fa-2x text-gray-300"></i>
|
||||
</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> 系统信息
|
||||
</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>
|
||||
<tr>
|
||||
<td><strong>系统状态:</strong></td>
|
||||
<td><span class="badge badge-success">正常运行</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</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>
|
||||
<tr>
|
||||
<td><strong>数据库状态:</strong></td>
|
||||
<td><span class="badge badge-success">连接正常</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
<div class="row mt-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> 快速操作
|
||||
</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> 用户管理
|
||||
</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> 提示词管理
|
||||
</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> 模板管理
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<a href="{{ url_for('admin_system.logs') }}" class="btn btn-warning btn-block">
|
||||
<i class="fas fa-file-alt"></i> 系统日志
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.border-left-primary {
|
||||
border-left: 0.25rem solid #4e73df !important;
|
||||
}
|
||||
.border-left-success {
|
||||
border-left: 0.25rem solid #1cc88a !important;
|
||||
}
|
||||
.border-left-info {
|
||||
border-left: 0.25rem solid #36b9cc !important;
|
||||
}
|
||||
.border-left-warning {
|
||||
border-left: 0.25rem solid #f6c23e !important;
|
||||
}
|
||||
.text-gray-300 {
|
||||
color: #dddfeb !important;
|
||||
}
|
||||
.text-gray-800 {
|
||||
color: #5a5c69 !important;
|
||||
}
|
||||
.font-weight-bold {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
.text-xs {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
86
src/flask_prompt_master/templates/admin/system_logs.html
Normal file
86
src/flask_prompt_master/templates/admin/system_logs.html
Normal file
@@ -0,0 +1,86 @@
|
||||
{% extends 'admin/master.html' %}
|
||||
|
||||
{% block title %}系统日志{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1 class="mb-4">
|
||||
<i class="fas fa-file-alt"></i> 系统日志
|
||||
</h1>
|
||||
</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-list"></i> 日志列表
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>时间</th>
|
||||
<th>级别</th>
|
||||
<th>模块</th>
|
||||
<th>消息</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>2025-08-29 22:34:44</td>
|
||||
<td><span class="badge badge-info">INFO</span></td>
|
||||
<td>系统管理</td>
|
||||
<td>管理员登录成功</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2025-08-29 22:33:42</td>
|
||||
<td><span class="badge badge-success">SUCCESS</span></td>
|
||||
<td>应用启动</td>
|
||||
<td>Flask应用启动成功</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2025-08-29 22:33:42</td>
|
||||
<td><span class="badge badge-info">INFO</span></td>
|
||||
<td>数据库</td>
|
||||
<td>数据库连接成功</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-primary">查看详情</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.badge-info {
|
||||
background-color: #17a2b8;
|
||||
}
|
||||
.badge-success {
|
||||
background-color: #28a745;
|
||||
}
|
||||
.badge-warning {
|
||||
background-color: #ffc107;
|
||||
}
|
||||
.badge-danger {
|
||||
background-color: #dc3545;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user