180 lines
6.0 KiB
Python
180 lines
6.0 KiB
Python
from django.contrib import admin
|
|
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
|
from django.utils.html import format_html
|
|
from import_export.admin import ImportExportModelAdmin
|
|
|
|
from .models import Department, Role, Menu
|
|
from .models import User
|
|
|
|
|
|
# ========== 菜单管理 ==========
|
|
@admin.register(Menu)
|
|
class MenuAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
'name', 'code', 'menu_type_display', 'path', 'permission_code', 'parent_link', 'order_num', 'status')
|
|
list_filter = ('menu_type', 'status', 'parent')
|
|
search_fields = ('name', 'code', 'permission_code')
|
|
ordering = ('order_num', 'id')
|
|
list_editable = ('order_num', 'status') # 允许在列表页直接编辑
|
|
list_per_page = 20
|
|
|
|
# 自定义字段:显示菜单类型
|
|
def menu_type_display(self, obj):
|
|
types = {0: '目录', 1: '菜单', 2: '按钮'}
|
|
return types.get(obj.menu_type, '未知')
|
|
|
|
menu_type_display.short_description = '类型'
|
|
|
|
# 自定义字段:父菜单链接(可点击)
|
|
def parent_link(self, obj):
|
|
if obj.parent:
|
|
url = f"/admin/users/menu/{obj.parent.id}/change/"
|
|
return format_html('<a href="{}">{}</a>', url, obj.parent.name)
|
|
return '-'
|
|
|
|
parent_link.short_description = '父菜单'
|
|
|
|
|
|
# ========== 角色管理 ==========
|
|
class RoleMenuInline(admin.TabularInline):
|
|
"""
|
|
内联管理:在角色编辑页直接分配菜单
|
|
"""
|
|
model = Role.menus.through # 使用自动创建的中间表
|
|
verbose_name = "菜单权限"
|
|
verbose_name_plural = "菜单权限"
|
|
extra = 1 # 默认显示1个空行
|
|
|
|
# 优化显示:只显示关键字段
|
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
|
if db_field.name == "menu":
|
|
# 可以在这里过滤菜单选项,例如只显示状态为True的
|
|
kwargs["queryset"] = Menu.objects.filter(status=True)
|
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
|
|
|
|
|
@admin.register(Role)
|
|
class RoleAdmin(admin.ModelAdmin):
|
|
list_display = ('name', 'code', 'description', 'user_count', 'menu_count', 'status', 'created_at')
|
|
list_filter = ('status', 'created_at')
|
|
search_fields = ('name', 'code', 'description')
|
|
ordering = ('-created_at',)
|
|
list_per_page = 20
|
|
|
|
# 使用内联管理菜单权限
|
|
inlines = [RoleMenuInline]
|
|
|
|
# 排除 menus 字段,因为已经在内联中处理
|
|
exclude = ('menus',)
|
|
|
|
# 自定义字段:关联用户数量
|
|
def user_count(self, obj):
|
|
count = obj.users.count()
|
|
if count:
|
|
url = f"/admin/users/user/?roles__id__exact={obj.id}"
|
|
return format_html('<a href="{}">{}</a>', url, count)
|
|
return 0
|
|
|
|
user_count.short_description = '用户数'
|
|
|
|
# 自定义字段:关联菜单数量
|
|
def menu_count(self, obj):
|
|
count = obj.menus.count()
|
|
if count:
|
|
url = f"/admin/users/menu/?roles__id__exact={obj.id}"
|
|
return format_html('<a href="{}">{}</a>', url, count)
|
|
return 0
|
|
|
|
menu_count.short_description = '菜单数'
|
|
|
|
|
|
# ========== 用户管理 ==========
|
|
class UserRoleInline(admin.TabularInline):
|
|
"""
|
|
内联管理:在用户编辑页直接分配角色
|
|
"""
|
|
model = User.roles.through
|
|
verbose_name = "角色"
|
|
verbose_name_plural = "角色"
|
|
extra = 1
|
|
|
|
|
|
@admin.register(User)
|
|
class UserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
|
list_display = ('username', 'cname', 'dept', 'phone', 'is_active', 'is_staff')
|
|
list_filter = ('is_active', 'is_staff', 'dept')
|
|
search_fields = ('username', 'cname', 'phone',)
|
|
fieldsets = (
|
|
(None, {'fields': ('username', 'password')}),
|
|
('个人信息', {'fields': ('cname', 'dept')}),
|
|
('权限', {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
|
|
('重要日期', {'fields': ('last_login', 'date_joined')}),
|
|
)
|
|
add_fieldsets = (
|
|
(None, {
|
|
'classes': ('wide',),
|
|
'fields': ('username', 'cname', 'password1', 'password2', 'dept'),
|
|
}),
|
|
)
|
|
ordering = ('username', '-date_joined',)
|
|
filter_horizontal = ('groups', 'user_permissions', 'roles') # 如果 roles 是 ManyToMany
|
|
list_per_page = 20
|
|
# 使用内联管理角色
|
|
inlines = [UserRoleInline]
|
|
|
|
# 排除 roles 字段,因为已经在内联中处理
|
|
exclude = ('roles',)
|
|
|
|
# 自定义字段:显示所有角色名称
|
|
def role_names(self, obj):
|
|
roles = obj.roles.all()
|
|
if roles:
|
|
names = ', '.join([role.name for role in roles])
|
|
return names[:50] + '...' if len(names) > 50 else names
|
|
return '-'
|
|
|
|
role_names.short_description = '角色'
|
|
|
|
|
|
# ========== 部门管理 ==========
|
|
@admin.register(Department)
|
|
class DepartmentAdmin(admin.ModelAdmin):
|
|
list_display = ('name_with_indent', 'parent_link', 'order', 'user_count')
|
|
list_editable = ('order',)
|
|
ordering = ('order', 'id')
|
|
list_per_page = 30
|
|
|
|
def get_queryset(self, request):
|
|
# 优化查询,避免 N+1
|
|
return super().get_queryset(request).select_related('parent')
|
|
|
|
def name_with_indent(self, obj):
|
|
"""根据层级缩进显示部门名称"""
|
|
level = 0
|
|
current = obj.parent
|
|
while current:
|
|
level += 1
|
|
current = current.parent
|
|
indent = "=>" * level
|
|
return format_html('{}{}', indent, obj.name)
|
|
|
|
name_with_indent.short_description = "部门名称"
|
|
name_with_indent.allow_tags = True
|
|
|
|
def parent_link(self, obj):
|
|
if obj.parent:
|
|
url = f"/admin/users/department/{obj.parent.id}/change/"
|
|
return format_html('<a href="{}">{}</a>', url, obj.parent.name)
|
|
return '-'
|
|
|
|
parent_link.short_description = "上级部门"
|
|
|
|
def user_count(self, obj):
|
|
count = obj.user_set.count()
|
|
if count:
|
|
url = f"/admin/users/user/?department__id__exact={obj.id}"
|
|
return format_html('<a href="{}">{}</a>', url, count)
|
|
return 0
|
|
|
|
user_count.short_description = "用户数"
|