""" 后台导航管理视图(通过插件方式开发) URL 前缀变更: - 原蓝图 url_prefix='/nav'(注册到 system_bp → /system/nav/...) - 改为 url_prefix='/system/nav' 直接挂到 app,这样去掉了对 framework 内 system_bp 的依赖 - 与用户原始菜单路径 /system/nav 完全兼容,权限码 system:nav:* 保持不变 """ 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 Nav from applications.schemas import NavManageSchema # 插件级 Blueprint:直接挂到 app,前缀 /system/nav,独立模板目录 dir_path = os.path.dirname(os.path.abspath(__file__)) bp = Blueprint( 'nav', __name__, url_prefix='/system/nav', template_folder=os.path.join(dir_path, '..', 'templates'), ) @bp.get('/') @authorize("system:nav:main") def main(): return render_template('system/nav/main.html') @bp.get('/data') @limiter.limit("60 per minute") @authorize("system:nav:main") def data(): keyword = str_escape(request.args.get('keyword', type=str)) category = str_escape(request.args.get('category', type=str)) query = Nav.query.filter() if keyword: query = query.filter( db.or_( Nav.title.contains(keyword), Nav.url.contains(keyword), Nav.description.contains(keyword), ) ) if category: query = query.filter(Nav.category == category) items = query.order_by(Nav.category.asc(), Nav.sort.asc(), Nav.id.asc()).layui_paginate() return table_api( msg="请求成功", data=curd.model_to_dicts(schema=NavManageSchema, data=items.items), count=items.total, ) @bp.get('/add') @authorize("system:nav:add", log=True) def add(): return render_template('system/nav/add.html') @bp.post('/save') @authorize("system:nav:add", log=True) def save(): req_json = request.get_json(force=True, silent=True) or {} category = str_escape(req_json.get('category')) title = str_escape(req_json.get('title')) url = str_escape(req_json.get('url')) icon = str_escape(req_json.get('icon')) or 'layui-icon-link' if not all([category, title, url]): return fail_api(msg="分类/标题/链接不得为空") try: nav = Nav( category=category, title=title, url=url, description=str_escape(req_json.get('description')), icon=icon, sort=int(req_json.get('sort') or 0), enable=int(req_json.get('enable') or 1), is_external=int(req_json.get('isExternal') or 1), create_by=current_user.username if current_user.is_authenticated else 'admin', ) db.session.add(nav) 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:edit", log=True) def edit(): nav_id = request.args.get('navId', type=int) nav = curd.get_one_by_id(Nav, nav_id) if not nav: return fail_api(msg="导航不存在") return render_template('system/nav/edit.html', nav=nav) @bp.put('/update') @authorize("system:nav:edit", log=True) def update(): req_json = request.get_json(force=True, silent=True) or {} nav_id = req_json.get('navId') nav = curd.get_one_by_id(Nav, nav_id) if not nav: return fail_api(msg="导航不存在") category = str_escape(req_json.get('category')) title = str_escape(req_json.get('title')) url = str_escape(req_json.get('url')) if not all([category, title, url]): return fail_api(msg="分类/标题/链接不得为空") try: nav.category = category nav.title = title nav.url = url nav.description = str_escape(req_json.get('description')) nav.icon = str_escape(req_json.get('icon')) or 'layui-icon-link' nav.sort = int(req_json.get('sort') or 0) nav.enable = int(req_json.get('enable') or 1) nav.is_external = int(req_json.get('isExternal') 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/') @authorize("system:nav:remove", log=True) def remove(nav_id): res = curd.delete_one_by_id(Nav, nav_id) if not res: return fail_api(msg="删除失败") return success_api(msg="删除成功") @bp.put('/enable') @authorize("system:nav:edit", log=True) def enable(): req_json = request.get_json(force=True, silent=True) or {} nav_id = req_json.get('navId') if not nav_id: return fail_api(msg="数据错误") res = curd.enable_status(Nav, nav_id) if not res: return fail_api(msg="操作失败") return success_api(msg="已启用") @bp.put('/disable') @authorize("system:nav:edit", log=True) def dis_enable(): req_json = request.get_json(force=True, silent=True) or {} nav_id = req_json.get('navId') if not nav_id: return fail_api(msg="数据错误") res = curd.disable_status(Nav, nav_id) if not res: return fail_api(msg="操作失败") return success_api(msg="已禁用")