update. 完成导航菜单接口
This commit is contained in:
@@ -69,3 +69,35 @@ class PictureLinkUpdateSchema(Schema):
|
||||
url: Optional[str] = None
|
||||
order: Optional[int] = None
|
||||
is_active: Optional[bool] = None
|
||||
|
||||
|
||||
class MainMenuBase(Schema):
|
||||
"""基础菜单字段"""
|
||||
title: str
|
||||
code: str
|
||||
url: str = ""
|
||||
is_external: bool = False
|
||||
is_carousel: bool = False
|
||||
order: int = 0
|
||||
visible: bool = True
|
||||
|
||||
|
||||
class MainMenuCreate(MainMenuBase):
|
||||
"""创建时的 Schema"""
|
||||
parent_id: Optional[int] = None
|
||||
|
||||
|
||||
class MainMenuUpdate(MainMenuBase):
|
||||
"""更新时的 Schema"""
|
||||
parent_id: Optional[int] = None
|
||||
|
||||
|
||||
class MainMenuOut(MainMenuBase):
|
||||
"""输出用的 Schema,包含 ID 和递归的子菜单"""
|
||||
id: int
|
||||
parent_id: Optional[int] = None
|
||||
children: List['MainMenuOut'] = [] # 递归引用自身
|
||||
|
||||
|
||||
# 告诉 Pydantic 如何处理递归模型
|
||||
MainMenuOut.model_rebuild()
|
||||
|
||||
+106
-2
@@ -10,9 +10,9 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from ninja import Router
|
||||
|
||||
from base.models import FriendLink, LinkCategory, PictureLink
|
||||
from base.models import FriendLink, LinkCategory, PictureLink, MainMenu
|
||||
from base.schema import FriendLinkCreateSchema, FriendLinkSchema, LinkCategoryCreateSchema, LinkCategorySchema, \
|
||||
PictureLinkSchema, PictureLinkCreateSchema, PictureLinkUpdateSchema
|
||||
PictureLinkSchema, PictureLinkCreateSchema, PictureLinkUpdateSchema, MainMenuOut, MainMenuCreate, MainMenuUpdate
|
||||
|
||||
# Create your views here.
|
||||
router = Router(tags=['base'])
|
||||
@@ -178,3 +178,107 @@ def delete_picture_link(request, link_id: int):
|
||||
link = get_object_or_404(PictureLink, id=link_id)
|
||||
link.delete()
|
||||
return {"success": True}
|
||||
|
||||
|
||||
def build_menu_tree(menus: List[MainMenu]) -> List[MainMenuOut]:
|
||||
"""
|
||||
将平铺的菜单列表转换为嵌套的树形结构。
|
||||
这是一个高效的 O(n) 算法。
|
||||
"""
|
||||
menu_map = {}
|
||||
root_menus = []
|
||||
|
||||
# 第一步:将所有菜单放入字典,并初始化 children 列表
|
||||
for menu in menus:
|
||||
menu_data = MainMenuOut(
|
||||
id=menu.id,
|
||||
parent_id=menu.parent_id,
|
||||
title=menu.title,
|
||||
code=menu.code,
|
||||
url=menu.url,
|
||||
is_external=menu.is_external,
|
||||
is_carousel=menu.is_carousel,
|
||||
order=menu.order,
|
||||
visible=menu.visible,
|
||||
children=[]
|
||||
)
|
||||
menu_map[menu.id] = menu_data
|
||||
if menu.parent_id is None:
|
||||
root_menus.append(menu_data)
|
||||
|
||||
# 第二步:遍历字典,将子菜单挂载到父菜单上
|
||||
for menu_id, menu_obj in menu_map.items():
|
||||
if menu_obj.parent_id is not None and menu_obj.parent_id in menu_map:
|
||||
parent = menu_map[menu_obj.parent_id]
|
||||
parent.children.append(menu_obj)
|
||||
|
||||
# 第三步:对每个层级的菜单按 'order' 排序
|
||||
def sort_children(menu_list):
|
||||
menu_list.sort(key=lambda x: (x.order, x.title))
|
||||
for menu in menu_list:
|
||||
sort_children(menu.children)
|
||||
|
||||
sort_children(root_menus)
|
||||
return root_menus
|
||||
|
||||
|
||||
@router.get("main-menu/", response=List[MainMenuOut])
|
||||
def get_menu_tree(request):
|
||||
"""
|
||||
获取完整的导航菜单树。
|
||||
GET /api/main-menu/
|
||||
"""
|
||||
all_menus = MainMenu.objects.all().select_related('parent')
|
||||
return build_menu_tree(all_menus)
|
||||
|
||||
|
||||
@router.post("/main-menu/", response=MainMenuOut)
|
||||
def create_menu(request, payload: MainMenuCreate):
|
||||
"""
|
||||
创建一个新的菜单项。
|
||||
POST /api/main-menu/
|
||||
"""
|
||||
parent = None
|
||||
if payload.parent_id:
|
||||
parent = get_object_or_404(MainMenu, id=payload.parent_id)
|
||||
|
||||
menu = MainMenu.objects.create(
|
||||
parent=parent,
|
||||
**payload.dict(exclude={'parent_id'})
|
||||
)
|
||||
return menu
|
||||
|
||||
|
||||
@router.put("/main-menu/{menu_id}", response=MainMenuOut)
|
||||
def update_menu(request, menu_id: int, payload: MainMenuUpdate):
|
||||
"""
|
||||
更新一个菜单项。
|
||||
PUT /api/main-menu/{menu_id}/
|
||||
"""
|
||||
menu = get_object_or_404(MainMenu, id=menu_id)
|
||||
|
||||
# 处理 parent_id
|
||||
if payload.parent_id is not None:
|
||||
if payload.parent_id != menu.parent_id:
|
||||
new_parent = get_object_or_404(MainMenu, id=payload.parent_id)
|
||||
menu.parent = new_parent
|
||||
else:
|
||||
menu.parent = None
|
||||
|
||||
# 更新其他字段
|
||||
for attr, value in payload.dict(exclude={'parent_id'}).items():
|
||||
setattr(menu, attr, value)
|
||||
|
||||
menu.save()
|
||||
return menu
|
||||
|
||||
|
||||
@router.delete("/main-menu/{menu_id}")
|
||||
def delete_menu(request, menu_id: int):
|
||||
"""
|
||||
删除一个菜单项(及其所有子项)。
|
||||
DELETE /api/main-menu/{menu_id}/
|
||||
"""
|
||||
menu = get_object_or_404(MainMenu, id=menu_id)
|
||||
menu.delete() # Django 的级联删除会自动处理子项
|
||||
return {"success": True}
|
||||
|
||||
Reference in New Issue
Block a user