主题
主题决定前台(用户访问的页面)的外观,管理后台不受主题影响。可以安装他人开发的主题,也可以自己开发:主题可以是用 Laravel Blade 渲染的普通页面,也可以是调用接口的单页应用(SPA)。内置的默认主题就是一个用 Vue 3 开发的单页应用。
开发主题需要熟悉 Laravel、Blade 模板 和用于构建配置表单的 Filament。修改默认主题还需要熟悉 Node.js、Vite、Vue 3、Pinia、Vue Router、Naive UI 和 Tailwind CSS。
安装主题
主题页面列出了已收录的主题。安装主题有两种方式:
- 上传到
themes目录:把主题文件夹上传到程序根目录的themes目录中。使用 Docker 安装时,放到映射的themes目录(见Docker 容器启动) - 在后台导入:在管理后台的「主题管理」页面,点击「导入主题」按钮,上传主题的 zip 压缩包
安装后,在「主题管理」页面的「选择主题」中选择这个主题,按需填写主题配置,点击「保存」即可启用。
在后台导入主题
主题中包含 PHP 代码,导入主题等同于在服务器上执行代码,因此从 2.5.1 起「导入主题」默认禁用。需要在线导入时:
在程序根目录的
.env文件中设置:bashAPP_THEME_IMPORT_ENABLED=true如果执行过
php artisan optimize或php artisan config:cache,再执行php artisan config:clear使设置生效导入完成后,建议把这一项改回
false
压缩包中需要包含主题入口类文件(*Theme.php),可以直接放在压缩包根目录,也可以放在压缩包中唯一的文件夹里。已经存在同名主题时,需要勾选「覆盖已有主题」才能导入。
DANGER
请只安装来源可信的主题。主题中的 PHP 代码拥有和程序相同的权限,可以读取数据库和服务器上的文件。
在「主题管理」页面还可以导出主题(打包为 zip)和删除主题。删除会删除主题的所有文件,并自动切回默认主题;内置的默认主题不能删除。
主题是如何运作的?
程序从两个位置读取主题:
app/Themes/:内置主题(默认主题)themes/:第三方主题和自己开发的主题
一个主题的目录结构如下:
my-test/ # 主题文件夹,名称与主题 id 保持一致
├─ assets/ # 静态资源目录
│ ├─ css/
│ ├─ fonts/
│ ├─ images/
│ └─ js/
├─ controllers/ # PHP 控制器目录(可选)
│ └─ YourController.php
├─ views/ # 视图,文件名以 .blade.php 结尾
│ ├─ components/
│ │ └─ button.blade.php
│ ├─ layouts/
│ │ └─ app.blade.php
│ └─ pages/
│ └─ index.blade.php
├─ README.md # 主题说明
└─ MyTestTheme.php # 主题入口类程序根据文件夹名称推导入口类的类名和命名空间:文件夹 my-test 对应命名空间 Themes\MyTest、入口文件 MyTestTheme.php。启用主题后,程序会把 public/assets 链接到当前主题的 assets 目录,页面中用 /assets/... 引用静态资源。
主题入口类
入口类继承 App\Contracts\ThemeAbstract,用属性声明主题信息,并实现以下方法:
configurable():必须实现,返回后台「主题管理」中的配置表单,见 configurable 方法routes():定义主题的路由,见 routes 方法casts():可选,转换配置值,见 casts 方法
<?php
namespace Themes\MyTest;
use App\Contracts\ThemeAbstract;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Section;
use Illuminate\Support\Facades\Route;
class MyTestTheme extends ThemeAbstract
{
/** @var string 主题唯一标识,与文件夹名称一致 */
public string $id = 'my-test';
/** @var string 主题名称,显示在管理后台中 */
public string $name = '测试主题';
/** @var string|null 主题描述 */
public ?string $description = '这是一个测试主题';
/** @var string 主题作者 */
public string $author = 'Unknown';
/** @var string 主题版本 */
public string $version = '1.0.0';
/** @var string|null 主题官网或仓库地址 */
public ?string $url = '';
/**
* 主题路由
*/
public function routes(): void
{
Route::get('/', fn () => view("{$this->id}::pages.index"));
}
/**
* 后台配置表单
*/
public function configurable(): array
{
return [
Section::make('基础设置')->schema([
TextInput::make('payload.site_title')
->label('网站标题')
->maxLength(100)
->required(),
Textarea::make('payload.site_description')
->label('网站描述')
->maxLength(500)
->rows(3),
]),
];
}
/**
* 配置值转换
*/
public function casts(): array
{
return [];
}
}生命周期
flowchart LR
A[获取系统设置的主题] --> B[检测主题是否存在]
B -- 否 --> Z[结束]
B -- 是 --> C[加载控制器文件]
C --> D[加载主题的视图]
D --> E[注册主题的路由]
E --> F[路由回调]
F --> Z[结束]主题的配置可以在 Blade 视图中通过 ThemeService 读取,getTheme() 的参数是主题 id:
- 读取某一项:
\App\Facades\ThemeService::getTheme('my-test')->getSettings('site_title') - 读取全部:
\App\Facades\ThemeService::getTheme('my-test')->getSettings()
单页应用主题可以请求 GET /api/v2/configs 接口,返回的 data.site 中是当前主题的全部配置。
routes 方法
TIP
Laravel 路由的详细用法见 Laravel 官方文档。
在 routes 方法中定义主题的路由:
<?php
use Illuminate\Support\Facades\Route;
use Themes\MyTest\Controllers\YourController;
public function routes(): void
{
// 访问首页时直接返回视图
Route::get('/', fn () => view("{$this->id}::pages.index"));
// 也可以交给控制器处理
Route::get('/about', [YourController::class, 'about']);
// 单页应用:除 /api 开头以外的所有路径都交给同一个入口
// Route::any('/{any}', [YourController::class, 'index'])->where('any', '^(?!api).*');
}casts 方法
配置表单中有文件上传字段(例如 logo)时,保存的配置值只是文件路径,不是完整的网址。可以在 casts 中转换读取时的值:
<?php
use App\Support\Attribute;
use Illuminate\Support\Facades\Storage;
public function casts(): array
{
return [
'logo' => new Attribute(
// 读取时把文件路径转换为完整网址
get: fn ($value) => $value ? Storage::url($value) : '',
),
];
}getSettings() 返回转换后的值;需要原始值时使用 getRawSettings()。
configurable 方法
configurable 返回后台「主题管理」中的配置表单,使用 Filament 的组件构建,常用的是:
- Filament Forms:输入框、选择框、文件上传等表单字段
- Filament Schemas:分组、栅格等布局组件
所有表单字段的名称都必须以 payload. 开头,例如网站标题的配置项名称为 payload.title,读取时使用 title。最简单的配置表单:
<?php
use Filament\Forms\Components\TextInput;
public function configurable(): array
{
return [
// TextInput 组件文档:https://filamentphp.com/docs/4.x/forms/text-input
TextInput::make('payload.title')
->label('网站标题')
->maxLength(60)
->required(),
];
}在「主题管理」中选择这个主题后,就会出现「网站标题」配置项。
创建新的主题
程序内置了生成主题模板的命令,在程序根目录执行:
php artisan make:theme按提示填写主题 id(小写字母、数字和 -,3 到 30 个字符)、名称、作者、描述,选择是否创建 assets、views、controllers 目录和配置表单的模板类型,命令会在 themes/ 目录中生成主题模板。在此基础上修改即可。
开发完成后,可以提交到主题仓库,见主题页面。
修改默认主题
默认主题是混合部署的单页应用:Laravel 用通配路由把除 /api 以外的所有请求都交给主题入口,返回单页应用的 index.blade.php;页面在浏览器中渲染后,再调用 /api 开头的接口获取数据。因此不需要额外的伪静态规则,刷新页面也不会出现 404。
flowchart TD
A[浏览器访问 URL] --> B[Laravel 路由匹配 /*]
B --> C{是否匹配 api 路径?}
C -- 是 --> D[调用 API 控制器]
C -- 否 --> E[返回 index.blade.php]
E --> F[浏览器渲染 Vue3 前端应用]
F --> G[前端调用 API 接口]
G --> D[API 控制器处理请求]
D --> H[返回 JSON 数据给前端]
H --> F[前端接收并更新页面]默认主题的源码在安装包的 web 目录中。Docker 镜像中没有这个目录,需要时请在授权中心下载与当前版本相同的安装包取得。修改前请在电脑上安装 Node.js 22.13 或以上版本。
在 web 目录中安装依赖:
npm installyarn启动开发服务器,修改代码后页面会自动刷新:
npm run devyarn dev开发服务器默认请求 http://127.0.0.1:8000/api/v2 的接口,可以修改 web/.env 中的 VITE_APP_API_URL 指向您的站点。
修改完成后执行构建:
npm run buildyarn build构建结果会直接写入程序的 app/Themes/Default 目录(assets、views/index.blade.php 和 DefaultTheme.php),把这个目录上传到服务器覆盖即可。
WARNING
默认主题属于程序文件。升级时如果新版本修改了默认主题,您的修改会被覆盖;「修复安装」也会把它恢复为官方文件。请保留修改过的源码,升级后用新版本的 web 目录合并修改并重新构建。需要长期维护的修改,建议做成独立的主题放在 themes 目录中。
