Zsens Admin 插件开发教程

张先生
张先生 主理圣羽星庭 执序官者社群翘楚勋望元宿
社区管理
插件开发 126 浏览 0 回复

Zsens Admin 插件开发教程

1. 插件是什么

每个插件是一个独立目录:plugins/<插件名>/。系统通过 plugin.json 发现插件,通过入口类完成安装、配置、钩子、路由与菜单。

核心服务:

  • app/service/PluginService.php:安装 / 卸载 / 升级 / 配置 / 静态资源
  • app/service/PluginRouteRegistry.php:注册前后台路由
  • app/service/HookService.php:渲染 hook_slot
  • app/service/PluginMenuService.php:注册后台菜单

安装入口:后台「应用中心」→ 安装 / 升级 / 卸载。

2. 目录结构

plugins/hello/
├── plugin.json              # 必填:插件元信息
├── HelloPlugin.php          # 必填:入口类
├── icon.svg                 # 可选:图标
├── helpers.php              # 可选:模板助手函数
├── routes.php               # 可选:前台/后台路由
├── install.sql              # 可选:建表 SQL(不要写表前缀)
├── static/                  # 可选:静态资源(安装时复制到 public)
├── controller/              # 可选:控制器
│   ├── Index.php            # 前台
│   └── admin/               # 后台
├── service/                 # 可选:业务服务
├── model/                   # 可选:模型
└── view/
    ├── front/               # 前台视图
    └── admin/               # 后台视图

命名约定:

  • 目录名 = plugin.jsonname(小写英文,如 badge
  • 入口类 = plugins\<name>\<Entry>,例如 plugins\badge\BadgePlugin
  • 命名空间遵循 Composer PSR-4:plugins\plugins/

3. plugin.json

{
  "name": "hello",
  "title": "Hello 示例",
  "version": "1.0.0",
  "author": "Zsens Admin",
  "description": "演示最小可安装插件",
  "icon": "mdi-hand-wave",
  "tag": "示例",
  "entry": "HelloPlugin"
}

字段说明:

  • name:插件唯一标识,需与目录名一致
  • title:后台展示名称
  • version:版本号,安装/升级写入数据库
  • author:开发者
  • description:简介
  • icon:Material Design 图标名(可写 mdi-xxx
  • tag:分类标签,如「内容」「运营」
  • entry:入口类短名;省略时默认 Ucfirst(name)Plugin

可选扩展字段(如 typenamespacerequire)可写在 JSON 里作说明,核心不会自动做依赖校验,需要插件自己在代码里判断。

文件请使用 UTF-8 保存,避免中文乱码。

4. 入口类生命周期

最小可用入口(钩子型,参考 copyright):

<?php
declare(strict_types=1);

namespace plugins\hello;

use app\service\PluginService;

class HelloPlugin
{
    public function name(): string
    {
        return 'hello';
    }

    public function defaultConfig(): array
    {
        return [
            'enabled' => '1',
        ];
    }

    public function configSchema(): array
    {
        return [
            [
                'name'  => 'enabled',
                'title' => '启用插件',
                'tip'   => '关闭后前台不输出内容',
                'type'  => 'switch',
                'value' => '1',
            ],
        ];
    }

    public function install(): void
    {
        // 可选:PluginService::runSqlFile(__DIR__ . '/install.sql');
    }

    public function uninstall(): void
    {
        // 可选:删表、清菜单
    }

    public static function enabled(): bool
    {
        return PluginService::isInstalled('hello')
            && (int) PluginService::getConfigValue('hello', 'enabled', 1) === 1;
    }

    public function renderSlot(string $slot, array $ctx = []): string
    {
        if ($slot !== 'plaza_feed_top' || !self::enabled()) {
            return '';
        }
        return '<div class="hello-plugin">Hello,这是我的第一个插件</div>';
    }
}

常见方法:

  • install() / uninstall():安装、卸载
  • defaultConfig():默认配置(值建议用字符串)
  • configSchema():后台配置表单;只有 schema 里的字段才会被保存
  • renderSlot():前台插槽输出
  • hookPriority($slot):同插槽排序(数字越小越靠前)
  • renderAssets():全局 CSS/JS
  • ensureSchema():热路径补表/改表,兼容升级

启用有两层:

  1. 数据库 plugin.status = 1PluginService::isInstalled('hello')
  2. 配置项如 enabled → 插件自己的 enabled() 判断前台是否输出

5. 配置项类型

configSchema 常见 type

  • switch:开关(存 "0" / "1"
  • text / number / textarea / password
  • select / checkbox_group(选项放在 extra 的 JSON 字符串里)
  • 可用 tipshow_when 控制说明与显隐

读取配置:

$cfg = PluginService::getConfig('hello');
$on  = (int) PluginService::getConfigValue('hello', 'enabled', 1) === 1;

6. 钩子(推荐扩展方式)

模板中:

{:hook_slot('plaza_feed_top')}
{:hook_slot('topic_after_content', ['topic' => $topic])}
{:plugin_assets()}

系统会调用所有已安装插件的 renderSlot($slot, $ctx),拼成 HTML。

常用插槽:

  • plaza_feed_top / plaza_feed_slider:广场信息流顶部 / 轮播
  • topic_after_content / topic_before_replies:帖子正文后 / 回复前
  • compose_body / compose_extra / compose_assets:发帖编辑区
  • reply_user_badges / topic_author_badges / user_home_badges:勋章展示
  • member_nav_extra / member_asset_tabs:用户中心导航 / 资产 Tab
  • bar_actions_before_post:顶栏发帖前按钮区
  • auth_login_extra / auth_register_fields:登录/注册扩展
  • admin_topic_form:后台主题表单扩展

参考实现:copyright(最小钩子)、badge(多插槽)、vote(发帖+详情)、ads(广告位)。

7. 路由

plugins/<name>/routes.php 示例:

<?php
use think\facade\Route;

return [
    'front' => static function (): void {
        Route::get('hello', '\plugins\hello\controller\Index@index')->completeMatch();
    },
    'admin' => static function (): void {
        // 挂在 /admin 分组下,实际路径为 /admin/hello
        Route::get('hello', '\plugins\hello\controller\admin\Index@index');
    },
];

复杂前台路由建议做成 service/FrontRoute.php(参考 messagepayment):

  • routes():命名路由 → 路径
  • url($name, $vars):生成链接
  • register():统一 Route::get/post

前台控制器常继承 plugins\community\controller\FrontBase

8. 后台页面

  1. controller/admin/Base 继承后台基类,视图引擎指向核心 view/admin/
  2. 用绝对路径加载插件 HTML:plugins/<name>/view/admin/xxx.html
  3. 列表接口返回 Layui 风格:{code, msg, count, rows}
  4. PluginMenuService::register(...) 挂到插件菜单下

9. 数据库

install.sql不带前缀的表名,安装时由 PluginService::applyPrefix() 自动加 DB_PREFIX

CREATE TABLE IF NOT EXISTS `hello_log` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `content` varchar(255) NOT NULL DEFAULT '',
  `create_time` int unsigned NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  • 升级可用 CREATE IF NOT EXISTS / ALTER(常放在 ensureSchema()
  • 卸载再做 DROP TABLE

10. 静态资源

  • 源目录:plugins/<name>/static/
  • 安装/升级后复制到:public/static/<name>/
  • 页面引用:/static/<name>/xxx.css

注意:改了 static/ 不会自动同步,需要后台「升级」插件,或手动复制到 public/static/<name>/

11. helpers.php

<?php
if (!function_exists('hello_url')) {
    function hello_url(string $path = ''): string
    {
        if (!\app\service\PluginService::isInstalled('hello')) {
            return '#';
        }
        return '/hello' . ($path !== '' ? '/' . ltrim($path, '/') : '');
    }
}

模板:{:hello_url()}。命名建议:{插件名}_{动作}

helpers.php 即使未安装也可能被加载,必须加 function_existsisInstalled 保护。

12. 五分钟最小插件

  1. 建目录 plugins/hello/
  2. 写好 plugin.json + HelloPlugin.php
  3. 后台应用中心安装「Hello 示例」
  4. 打开社区广场,看 plaza_feed_top 是否出现 Hello 区块
  5. 在配置里关掉「启用」,前台应不再输出

13. 对照现有插件

  • copyright:最小钩子插件
  • ads:后台 CRUD + helpers + SQL + 菜单
  • badge:前台+后台 + 多插槽 + ensureSchema + static
  • message / payment:FrontRoute + 命名路由 + *_url
  • vote:发帖扩展 + 详情插槽 + 前端静态资源
  • community:宿主布局、插槽挂载点

14. 常见坑

  1. 目录名 / name / 入口类不一致 → 安装报「插件入口无效」
  2. 只拷了文件未安装 → 路由和钩子不生效
  3. 只改了 enabled,忘了检查 isInstalled
  4. install.sql 手写了表前缀 → 前缀可能被加两次
  5. 改了 static 不升级 → 仍用旧的 public 资源
  6. helpers.php 无守卫 → 未安装也会执行报错
  7. 配置不在 configSchema 里 → 保存时被丢掉
  8. plugin.json 非 UTF-8 → 中文乱码
  9. require 字段不会自动拦截安装,需代码自检
  10. 前台 URL 后缀要用 FrontRoute 正确生成

15. 开发检查清单

  • plugin.jsonname/title/version/author/entry 齐全
  • 入口类命名空间、文件名正确
  • install / uninstall 可重复、可干净卸载
  • 配置有 defaultConfig + configSchema
  • 前台输出同时判断「已安装」+「已启用」
  • SQL 无表前缀;静态资源路径正确
  • helpers 有保护;UTF-8 保存
评论0
回复 · 0
还没有回复
微信客服 微信客服