本文目录导读:

我来为你详细介绍ThinkPHP项目中的节点与菜单管理实现方案:
数据库设计
节点表(node)
CREATE TABLE `node` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '节点名称', varchar(50) NOT NULL COMMENT '菜单标题', `pid` int(11) NOT NULL DEFAULT '0' COMMENT '父级ID', `level` tinyint(1) NOT NULL DEFAULT '0' COMMENT '层级', `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序', `icon` varchar(50) DEFAULT NULL COMMENT '图标', `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态', `remark` varchar(255) DEFAULT NULL COMMENT '备注', `create_time` int(11) DEFAULT NULL, `update_time` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `pid` (`pid`), KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='节点表';
角色-节点关联表
CREATE TABLE `role_node` ( `id` int(11) NOT NULL AUTO_INCREMENT, `role_id` int(11) NOT NULL COMMENT '角色ID', `node_id` int(11) NOT NULL COMMENT '节点ID', PRIMARY KEY (`id`), KEY `role_id` (`role_id`), KEY `node_id` (`node_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色节点关联表';
Node模型
<?php
namespace app\common\model;
use think\Model;
use think\model\concern\SoftDelete;
class Node extends Model
{
use SoftDelete;
protected $name = 'node';
protected $deleteTime = 'delete_time';
// 自动写入时间戳
protected $autoWriteTimestamp = true;
// 关联角色
public function roles()
{
return $this->belongsToMany(Role::class, 'role_node');
}
// 子节点
public function children()
{
return $this->hasMany(Node::class, 'pid')->order('sort asc');
}
// 获取树形结构
public static function getTree()
{
$nodes = self::order('sort asc')->select()->toArray();
return self::buildTree($nodes);
}
// 构建树形结构
protected static function buildTree($items, $pid = 0)
{
$tree = [];
foreach ($items as $item) {
if ($item['pid'] == $pid) {
$item['children'] = self::buildTree($items, $item['id']);
$tree[] = $item;
}
}
return $tree;
}
// 获取用户权限节点
public static function getUserNodes($roleIds)
{
$nodeIds = RoleNode::whereIn('role_id', $roleIds)
->column('node_id');
return self::whereIn('id', $nodeIds)
->where('status', 1)
->order('sort asc')
->select()
->toArray();
}
}
?>
菜单管理控制器
<?php
namespace app\admin\controller;
use think\Controller;
use think\Request;
use app\common\model\Node;
class Menu extends Base
{
protected $nodeModel;
public function __construct()
{
parent::__construct();
$this->nodeModel = new Node();
}
// 菜单列表
public function index()
{
$list = Node::getTree();
return view('index', ['list' => $list]);
}
// 添加菜单
public function add()
{
if (request()->isPost()) {
$data = request()->post();
$validate = new \think\Validate([
'name' => 'require|max:50',
'title' => 'require|max:50',
]);
if (!$validate->check($data)) {
return json(['code' => 0, 'msg' => $validate->getError()]);
}
$result = Node::create($data);
if ($result) {
return json(['code' => 1, 'msg' => '添加成功']);
} else {
return json(['code' => 0, 'msg' => '添加失败']);
}
}
$pid = input('pid', 0);
$nodeList = Node::getTree();
return view('add', ['pid' => $pid, 'nodeList' => $nodeList]);
}
// 编辑菜单
public function edit($id)
{
$node = Node::find($id);
if (request()->isPost()) {
$data = request()->post();
$validate = new \think\Validate([
'name' => 'require|max:50',
'title' => 'require|max:50',
]);
if (!$validate->check($data)) {
return json(['code' => 0, 'msg' => $validate->getError()]);
}
$result = $node->save($data);
if ($result) {
return json(['code' => 1, 'msg' => '更新成功']);
} else {
return json(['code' => 0, 'msg' => '更新失败']);
}
}
$nodeList = Node::getTree();
return view('edit', ['node' => $node, 'nodeList' => $nodeList]);
}
// 删除菜单
public function delete($id)
{
// 检查是否有子节点
$hasChildren = Node::where('pid', $id)->find();
if ($hasChildren) {
return json(['code' => 0, 'msg' => '请先删除子菜单']);
}
$result = Node::destroy($id);
if ($result) {
return json(['code' => 1, 'msg' => '删除成功']);
} else {
return json(['code' => 0, 'msg' => '删除失败']);
}
}
// 菜单排序
public function sort()
{
$data = request()->post('sort');
foreach ($data as $id => $sort) {
Node::where('id', $id)->update(['sort' => $sort]);
}
return json(['code' => 1, 'msg' => '排序成功']);
}
}
?>
前端模板
菜单列表模板 (index.html)
<!DOCTYPE html>
<html>
<head>菜单管理</title>
<link rel="stylesheet" href="...">
</head>
<body>
<div class="container-fluid">
<div class="panel panel-default">
<div class="panel-heading">
菜单管理
<a href="{:url('menu/add')}" class="btn btn-primary btn-sm">
<i class="glyphicon glyphicon-plus"></i> 添加菜单
</a>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<th width="50">ID</th>
<th>菜单名称</th>
<th>节点标识</th>
<th>图标</th>
<th>排序</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{volist name="list" id="vo"}
<tr>
<td>{$vo.id}</td>
<td style="padding-left:{$vo.level*20}px">
{if $vo.level > 0}├─{/if}
{$vo.title}
</td>
<td>{$vo.name}</td>
<td>{$vo.icon}</td>
<td>
<input type="number" value="{$vo.sort}"
class="form-control sort-input"
data-id="{$vo.id}"
style="width:60px;display:inline-block">
</td>
<td>
<a href="javascript:;"
onclick="changeStatus({$vo.id}, {$vo.status})">
{if $vo.status == 1}
<span class="label label-success">启用</span>
{else}
<span class="label label-danger">禁用</span>
{/if}
</a>
</td>
<td>
<a href="{:url('menu/edit', ['id' => $vo.id])}"
class="btn btn-info btn-xs">编辑</a>
<a href="javascript:;"
onclick="deleteNode({$vo.id})"
class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{/volist}
</tbody>
</table>
</div>
</div>
</div>
<script>
// 删除节点
function deleteNode(id) {
if (confirm('确定要删除该菜单吗?')) {
$.post('{:url("menu/delete")}', {id: id}, function(res) {
if (res.code == 1) {
alert(res.msg);
location.reload();
} else {
alert(res.msg);
}
}, 'json');
}
}
// 修改状态
function changeStatus(id, status) {
$.post('{:url("menu/status")}', {
id: id,
status: status == 1 ? 0 : 1
}, function(res) {
if (res.code == 1) {
location.reload();
} else {
alert(res.msg);
}
}, 'json');
}
// 排序保存
$('.sort-input').change(function() {
var id = $(this).data('id');
var sort = $(this).val();
$.post('{:url("menu/sort")}', {id: id, sort: sort}, function(res) {
if (res.code == 1) {
alert('排序成功');
}
}, 'json');
});
</script>
</body>
</html>
菜单添加模板 (add.html)
<form id="menuForm">
<div class="form-group">
<label>父级菜单</label>
<select name="pid" class="form-control">
<option value="0">顶级菜单</option>
{volist name="nodeList" id="vo"}
<option value="{$vo.id}" {if $vo.id == $pid}selected{/if}>
{$vo.title}
</option>
{/volist}
</select>
</div>
<div class="form-group">
<label>菜单名称</label>
<input type="text" name="title" class="form-control" required>
</div>
<div class="form-group">
<label>节点标识</label>
<input type="text" name="name" class="form-control"
placeholder="如:admin/user/index">
</div>
<div class="form-group">
<label>图标</label>
<input type="text" name="icon" class="form-control"
placeholder="如:fa-user">
</div>
<div class="form-group">
<label>排序</label>
<input type="number" name="sort" class="form-control" value="0">
</div>
<div class="form-group">
<label>状态</label>
<select name="status" class="form-control">
<option value="1">启用</option>
<option value="0">禁用</option>
</select>
</div>
<button type="submit" class="btn btn-primary">保存</button>
</form>
<script>
$('#menuForm').on('submit', function(e) {
e.preventDefault();
$.post('{:url("menu/add")}', $(this).serialize(), function(res) {
if (res.code == 1) {
alert('添加成功');
location.href = '{:url("menu/index")}';
} else {
alert(res.msg);
}
}, 'json');
});
</script>
后台菜单渲染
// 在Base控制器中获取菜单
public function getMenuList()
{
// 获取用户角色
$roleIds = $this->userInfo->roles()->column('id');
// 获取权限节点
$nodes = Node::getUserNodes($roleIds);
// 如果是超级管理员,获取全部
if ($this->isSuperAdmin()) {
$nodes = Node::where('status', 1)->order('sort asc')->select()->toArray();
}
// 构建菜单树
return Node::buildTree($nodes);
}
前端菜单渲染模板
<!-- 侧边栏菜单 -->
<ul class="sidebar-menu">
<li class="header">功能菜单</li>
{volist name="menuList" id="menu"}
<li class="treeview {if empty($menu['children'])}single{/if}">
<a href="
{if empty($menu['children'])}
{$menu['name']|url}
{else}
javascript:void(0)
{/if}
">
<i class="fa {$menu.icon}"></i>
<span>{$menu.title}</span>
{if !empty($menu['children'])}
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
{/if}
</a>
{if !empty($menu['children'])}
<ul class="treeview-menu">
{volist name="menu['children']" id="sub"}
<li>
<a href="{$sub.name|url}">
<i class="fa fa-circle-o"></i> {$sub.title}
</a>
</li>
{/volist}
</ul>
{/if}
</li>
{/volist}
</ul>
权限验证
<?php
namespace app\admin\controller;
use think\Controller;
class Base extends Controller
{
protected $userInfo;
public function initialize()
{
parent::initialize();
// 验证登录
$this->checkLogin();
// 验证权限
$this->checkAuth();
}
// 权限验证
protected function checkAuth()
{
if (session('is_super_admin')) {
return true;
}
$route = strtolower(request()->module() . '/' .
request()->controller() . '/' .
request()->action());
// 获取用户权限节点
$userNodes = session('user_nodes');
if (!in_array($route, $userNodes)) {
$this->error('没有操作权限,请联系管理员');
}
}
}
?>
这个完整的节点与菜单管理方案包含了数据库设计、后端逻辑、前端模板和权限验证等核心部分,你可以根据实际需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播