Files
pear-admin-flask/plugins/navManager/view/nav_category.py
T
bwstudio 0ffc103e46 feat(plugins): 4 个业务插件代码 + 前台公开页面改造
plugins/navManager:导航 CRUD + 导航分类 CRUD(独立 blueprint)
plugins/friendManager:友情链接 CRUD
plugins/aboutManager:关于本站 CRUD
plugins/siteStats:访问统计主页 + JSON 接口 + /site/* GET 埋点 + nav 卡片点击埋点

applications/view/public/nav.py:导航聚合页 / 分类详情 / JSON 接口,支持 visit_count 注入
applications/view/public/about.py:关于本站公开页
applications/view/public/friend.py:友情链接公开页

templates/public/base.html:响应式首页(搜索框 / 分类抽屉 / 5 主题切换 / footer 隐私小字)
templates/public/index.html / category.html:卡片右下角访问次数
templates/public/about.html / friend.html:关于 + 友链前台页

docs/plugins-development.md:插件开发完整指南(生命周期 + 4 个示例 + FAQ + framework 迁移)

scripts/:探针与端到端验证脚本(probe_*/verify_*/test_*)
2026-09-06 18:18:06 +08:00

164 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
导航分类管理后台视图 — navManager 插件内的子模块。
URL/system/nav-category
权限:system:nav-category:*
"""
import os
from flask import Blueprint, render_template, request
from flask_login import current_user
from applications.common import curd
from applications.common.utils.http import table_api, fail_api, success_api
from applications.common.utils.rights import authorize
from applications.common.utils.validate import str_escape
from applications.extensions import db
from applications.extensions.init_limit import limiter
from applications.models import NavCategory
from applications.schemas import NavCategorySchema
dir_path = os.path.dirname(os.path.abspath(__file__))
bp = Blueprint(
'nav_category', __name__,
url_prefix='/system/nav-category',
template_folder=os.path.join(dir_path, '..', 'templates'),
)
@bp.get('/')
@authorize("system:nav-category:main")
def main():
return render_template('system/nav/category_main.html')
@bp.get('/data')
@limiter.limit("60 per minute")
@authorize("system:nav-category:main")
def data():
keyword = str_escape(request.args.get('keyword', type=str))
query = NavCategory.query.filter()
if keyword:
query = query.filter(
db.or_(
NavCategory.name.contains(keyword),
NavCategory.description.contains(keyword),
)
)
items = query.order_by(NavCategory.sort.desc(), NavCategory.id.asc()).layui_paginate()
return table_api(
msg='请求成功',
data=curd.model_to_dicts(schema=NavCategorySchema, data=items.items),
count=items.total,
)
@bp.get('/add')
@authorize("system:nav-category:add", log=True)
def add():
return render_template('system/nav/category_add.html')
@bp.post('/save')
@authorize("system:nav-category:add", log=True)
def save():
req_json = request.get_json(force=True, silent=True) or {}
name = str_escape(req_json.get('name'))
if not name:
return fail_api(msg='分类名不能为空')
if NavCategory.query.filter_by(name=name).first():
return fail_api(msg=f'分类"{name}"已存在')
try:
cat = NavCategory(
name=name,
icon=str_escape(req_json.get('icon')) or 'layui-icon-list',
description=str_escape(req_json.get('description')),
sort=int(req_json.get('sort') or 0),
enable=int(req_json.get('enable') or 1),
)
db.session.add(cat)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f'新增失败:{e}')
return success_api(msg='新增成功')
@bp.get('/edit')
@authorize("system:nav-category:edit", log=True)
def edit():
cat_id = request.args.get('catId', type=int)
cat = curd.get_one_by_id(NavCategory, cat_id)
if not cat:
return fail_api(msg='分类不存在')
return render_template('system/nav/category_edit.html', cat=cat)
@bp.put('/update')
@authorize("system:nav-category:edit", log=True)
def update():
req_json = request.get_json(force=True, silent=True) or {}
cat_id = req_json.get('catId')
cat = curd.get_one_by_id(NavCategory, cat_id)
if not cat:
return fail_api(msg='分类不存在')
name = str_escape(req_json.get('name'))
if not name:
return fail_api(msg='分类名不能为空')
# name 是否被其他行占用
other = NavCategory.query.filter(NavCategory.name == name, NavCategory.id != cat_id).first()
if other:
return fail_api(msg=f'分类"{name}"已被占用')
try:
cat.name = name
cat.icon = str_escape(req_json.get('icon')) or 'layui-icon-list'
cat.description = str_escape(req_json.get('description'))
cat.sort = int(req_json.get('sort') or 0)
cat.enable = int(req_json.get('enable') or 1)
db.session.commit()
except Exception as e:
db.session.rollback()
return fail_api(msg=f'更新失败:{e}')
return success_api(msg='更新成功')
@bp.delete('/remove/<int:cat_id>')
@authorize("system:nav-category:remove", log=True)
def remove(cat_id):
"""删除分类时不会动 Nav 记录,但 Nav.category 字段会孤立成 '未分组' 显示"""
cat = curd.get_one_by_id(NavCategory, cat_id)
if cat is None:
return fail_api(msg='分类不存在')
res = curd.delete_one_by_id(NavCategory, cat_id)
if not res:
return fail_api(msg='删除失败')
return success_api(msg='删除成功(分类下导航已自动归入「未分组」)')
@bp.put('/enable')
@authorize("system:nav-category:edit", log=True)
def enable():
req_json = request.get_json(force=True, silent=True) or {}
cat_id = req_json.get('catId')
if not cat_id:
return fail_api(msg='数据错误')
res = curd.enable_status(NavCategory, cat_id)
if not res:
return fail_api(msg='操作失败')
return success_api(msg='已启用')
@bp.put('/disable')
@authorize("system:nav-category:edit", log=True)
def dis_enable():
req_json = request.get_json(force=True, silent=True) or {}
cat_id = req_json.get('catId')
if not cat_id:
return fail_api(msg='数据错误')
res = curd.disable_status(NavCategory, cat_id)
if not res:
return fail_api(msg='操作失败')
return success_api(msg='已禁用')