🍔 插件菜单
ThinkAdmin 提供完善的插件菜单配置机制,支持自动生成菜单数据,简化插件集成流程。
📚 基础概念
🤔 基本介绍
插件菜单是插件在后台显示的菜单项,用于展示插件的功能入口。
简单理解:就像插件的"导航栏",告诉用户这个插件有哪些功能,点击菜单可以访问对应的功能页面。
实际例子:
// 插件菜单定义
public static function menu(): array
{
return [
[
'name' => '账号管理',
'subs' => [
['name' => '账号列表', 'node' => 'plugin-account/index'],
['name' => '账号配置', 'node' => 'plugin-account/config'],
],
],
];
}🤔 使用优势
问题1:插件功能无法访问
不使用插件菜单时,用户无法找到插件的功能入口:
// ❌ 不使用插件菜单:用户不知道插件有哪些功能
// 需要手动输入 URL 访问:/admin/plugin-account/index
// 用户体验差,容易忘记功能入口问题2:菜单管理困难
不使用插件菜单时,需要手动在后台添加菜单:
// ❌ 需要手动在后台添加菜单
// 系统管理 → 菜单管理 → 添加菜单
// 每个插件都要手动添加,工作量大使用插件菜单的优势:
// ✅ 使用插件菜单:自动生成菜单
// 1. 在 Service 类中定义菜单
// 2. 插件安装时自动写入菜单
// 3. 用户可以直接看到插件的功能入口🎯 核心功能
功能1:自动生成菜单
插件安装时,自动生成菜单数据:
// 插件安装时,自动调用 menu() 方法
// 自动写入 system_menu 数据表
// 用户可以直接看到插件的菜单功能2:权限控制
支持菜单项的权限控制:
// 菜单项关联权限节点
['name' => '账号列表', 'node' => 'plugin-account/index']
// 只有拥有该权限的用户才能看到菜单功能3:动态生成
菜单由插件服务类动态生成:
// 使用 appCode 动态获取插件编码
$code = app(static::class)->appCode;
['node' => "{$code}/config/index"]
// 避免硬编码,提高灵活性🚀 主要功能
- 自动生成: 插件能够通过配置自动生成菜单数据
- 标准定义: 提供标准的菜单定义方式
- 无缝集成: 菜单数据自动集成到系统菜单体系
- 权限控制: 支持菜单项的权限控制
- 灵活配置: 支持自定义菜单显示方式和属性
- 统一管理: 通过插件中心统一管理所有插件菜单
📋 菜单类型
ThinkAdmin 支持两种菜单类型,各有特点:
1. 全局菜单
写入 system_menu 数据表的菜单,显示在系统主菜单中。
特点:
- 存储方式: 写入
system_menu数据表 - 管理工具: 使用
Phinx数据库迁移工具 - 灵活管理: 可以方便地创建、修改或删除菜单结构
- 数据持久: 菜单数据持久化存储
使用场景:
- 系统核心功能菜单
- 需要持久化存储的菜单
- 需要手动管理的菜单
2. 插件专属菜单
由插件服务注册类动态生成的菜单,显示在插件专属空间。
特点:
- 显示位置: 插件专属空间左侧菜单
- 动态生成: 由插件服务注册类动态生成
- 功能模块: 清晰反映插件的功能模块
- 返回入口: 提供返回插件中心的入口
使用场景:
- 插件功能菜单
- 需要动态生成的菜单
- 插件专属功能入口
⚙️ 配置方式
Service 类配置
在 Service 类中实现 menu() 方法,返回菜单配置数组。
方法要求:
- 方法实现: 在 Service 类中实现
menu方法 - 配置返回: 返回包含菜单项信息的数组或对象
- 结构描述: 描述插件的菜单结构、权限要求等
- 自动调用: 插件安装时系统自动调用
菜单配置选项
支持多种菜单配置选项,满足不同需求。
配置选项:
- 显示方式: 自定义菜单项的显示方式
- 权限控制: 设置菜单项的权限要求
- 业务需求: 适应不同的应用场景和业务需求
- 灵活扩展: 支持菜单配置的灵活扩展
菜单分为两种类型
为了提高插件的集成性和易用性,系统优化了全局菜单的写入方式,并改进了插件专属空间的菜单管理。
首先,系统实现了全局菜单的数据库写入功能。通过将菜单数据写入 system_menu 数据表,我们可以更加灵活地管理整个应用的菜单结构。为了实现这一功能,我们借助了强大的数据库迁移工具 Phinx。通过 Phinx 我们可以方便地创建、修改或删除数据表结构,确保菜单数据的正确存储和访问。 其次,系统改进了插件专属空间的菜单管理。当通过插件中心进入插件专属空间时,左侧将展示当前插件的专属菜单。这些菜单项将清晰地反映出插件的功能模块,并提供返回插件中心的入口。为了实现这一功能,我们要求开发者在插件服务注册类中定义菜单。通过这种方式,插件能够动态地生成和管理自己的菜单项,无需手动修改配置文件或数据表。
插件中心统一入口
在安装 插件中心 后,系统进一步提升了插件管理的便捷性和统一性。用户可以通过插件中心的统一入口,直接访问各个应用插件的独立管理菜单。 具体来说,每个应用插件的 Service 类中定义的 menu 方法,负责生成该插件的专属管理菜单。当插件中心被安装并启用后,它会自动扫描并集成所有已安装插件的管理菜单。这样,用户只需通过插件中心的统一入口,即可一站式地管理和操作所有插件。
这一优化不仅简化了插件管理的流程,还提高了管理的效率。用户无需在不同的地方查找和访问各个插件的管理界面,只需在插件中心即可完成所有操作。同时,由于菜单是由插件自身定义的,因此可以确保菜单的准确性和完整性,避免了因手动配置而可能产生的错误。
写入全局菜单数据
在数据库脚本的执行过程中,系统引入了自动写入菜单数据的机制。具体而言,通过调用应用插件 Service 中的 menu 方法,我们可以获取到插件的菜单配置信息。利用 think\admin\extend\PhinxExtend::write2menu() 方法,我们将这些配置信息自动写入到 system_menu 数据表中,从而实现了菜单的自动生成。
温馨提示: 当卸载应用插件时,并不会自动删除与之相关的菜单项。这可能导致在卸载插件后,系统中仍然残留有无效或不再需要的菜单项,需要管理员手动去系统菜单管理界面进行删除操作。
📝 菜单配置案例
Service 类菜单定义
插件的 Service 类必须继承 think\admin\Plugin 并实现静态方法 menu():
<?php
declare(strict_types=1);
namespace app\wechat;
use think\admin\Plugin;
/**
* 组件注册服务
* @class Service
* @package app\wechat
*/
class Service extends Plugin
{
/**
* 定义插件名称
* @var string
*/
protected $appName = '微信管理';
/**
* 定义安装包名
* @var string
*/
protected $package = 'zoujingli/think-plugs-wechat';
/**
* 定义插件菜单(静态方法)
* @return array
*/
public static function menu(): array
{
$code = app(static::class)->appCode; // 获取插件编码
return [
[
'name' => '微信管理',
'subs' => [
['name' => '微信接口配置', 'icon' => 'layui-icon layui-icon-set', 'node' => "{$code}/config/options"],
['name' => '微信支付配置', 'icon' => 'layui-icon layui-icon-rmb', 'node' => "{$code}/config/payment"],
],
],
[
'name' => '微信定制',
'subs' => [
['name' => '微信粉丝管理', 'icon' => 'layui-icon layui-icon-username', 'node' => "{$code}/fans/index"],
['name' => '微信图文管理', 'icon' => 'layui-icon layui-icon-template-1', 'node' => "{$code}/news/index"],
['name' => '微信菜单配置', 'icon' => 'layui-icon layui-icon-cellphone', 'node' => "{$code}/menu/index"],
['name' => '回复规则管理', 'icon' => 'layui-icon layui-icon-engine', 'node' => "{$code}/keys/index"],
['name' => '关注自动回复', 'icon' => 'layui-icon layui-icon-release', 'node' => "{$code}/auto/index"],
],
],
];
}
}数据库迁移脚本
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use think\admin\extend\PhinxExtend;
class InstallWechatData extends Migrator
{
/**
* 安装微信插件数据
*/
public function up()
{
// 写入菜单数据
PhinxExtend::write2menu([
'name' => '微信管理',
'subs' => [
['name' => '公众号配置', 'node' => 'plugin-wechat/config/index'],
['name' => '菜单管理', 'node' => 'plugin-wechat/menu/index'],
['name' => '素材管理', 'node' => 'plugin-wechat/material/index'],
['name' => '用户管理', 'node' => 'plugin-wechat/fans/index'],
['name' => '消息管理', 'node' => 'plugin-wechat/keys/index'],
]
]);
}
/**
* 卸载微信插件数据
*/
public function down()
{
// 删除菜单数据
PhinxExtend::remove2menu('plugin-wechat');
}
}菜单配置参数说明
| 参数 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| name | string | 是 | 菜单名称 | '微信管理' |
| node | string | 是 | 权限节点(子菜单必填) | 'wechat/config/index' 或 "{$code}/config/index" |
| icon | string | 否 | 菜单图标(LayUI 图标类) | 'layui-icon layui-icon-set' |
| subs | array | 否 | 子菜单数组 | [['name' => '...', 'node' => '...']] |
| sort | int | 否 | 排序权重 | 100 |
| status | int | 否 | 状态(1=启用,0=禁用) | 1 |
菜单结构说明
菜单支持两级结构:
- 一级菜单:包含
name和subs(子菜单数组) - 二级菜单:包含
name、node(权限节点)和可选的icon
注意事项:
menu()方法必须是静态方法(public static function menu())- 使用
app(static::class)->appCode动态获取插件编码,避免硬编码 - 权限节点格式:
{插件编码}/{控制器}/{方法},如:plugin-account/master/index
参考链接
- Service 类菜单定义:think-plugs-wechat Service.php
- 数据库迁移脚本:install_wechat_data.php
