# 皮肤(主题)开发指南 ## 目录格式 ``` themes/{slug}/ theme.json # 元数据 assets/ # 静态资源(CSS/图片/预览) style.css preview.svg # 后台卡片预览(建议 16:10) views/ # Blade,命名空间 theme:: layout.blade.php home.blade.php article.blade.php ... ``` ### theme.json 示例 ```json { "name": "my-theme", "title": "我的皮肤", "version": "1.0.0", "description": "说明文字", "slots": "*" } ``` ### 必须声明 `slots` 皮肤应在 `theme.json` 声明支持的注入槽(后台「皮肤」卡片会校验): | 写法 | 含义 | |---|---| | `"slots": "*"` 或 `"all"` | 支持全部标准槽(推荐) | | `"slots": ["head", "sidebar", "my_slot"]` | 显式列表;可混用 `"*"` 再加自定义槽 | | 省略 `slots` | **不合规**:后台标「未声明」;启用时警告 | 校验还会扫 `views/**/*.blade.php` 里的 `@themeslot` / `ThemeSlot::render`(非 default 主题会把未覆盖的 default 视图算进有效槽)。声明了但视图未调用 →「声明与视图不一致」。 ## 视图约定 - 使用 `@extends('theme::layout')` - 控制器传:`$articles` / `$article` / `$categories` / `$settings` / `$seo` - 静态资源 URL:`app(ThemeManager::class)->assetUrl('style.css')` → `/themes/{slug}/style.css` - 文案:`__('frontend.nav.home')` 等,勿写死中英文 ## 内容注入点(ThemeSlot) 主题应在固定位置调用 `@themeslot('槽名')`(或 `ThemeSlot::render('槽名')`)。 插件与后台「注入 / 广告 / 统计」都会写入同一套槽。 ### 同一位置可多条 `Hook::gather('theme.{slot}')` 会按注册顺序把**后台片段 + 各插件**拼成一段 HTML。 同一槽塞 3 个插件广告 + 后台一段,都会出来(纵向叠放,由主题 CSS 决定外观)。 ### 可控边界(换别人做的 theme) | 可控 | 不可强控 | |---|---| | 标准槽名(API):`head` / `sidebar` / … | 像素级宽高、是否做成卡片 | | 主题是否调用了 `@themeslot`(合规皮肤应全挂) | 侧栏在左还是右、主栏实际像素 | | 建议尺寸写在 `ThemeSlot::catalog()` / 后台 helper | 第三方主题漏掉某个槽 → 该注入静默不显示 | 结论:**槽名是硬约定,版式是软建议**。别人做 theme 只要挂齐标准槽,内容就能到「大概那个位置」;精确到「300×250 居中」做不到,也不该做进核心。合规检查可后续做:扫描 views 是否包含全部标准 `@themeslot`。 尺寸/位置说明见 `ThemeSlot::catalog()`(后台「注入」Tab 的 helper 同步显示)。 | 槽名 | 典型用途 | 建议尺寸(软) | |---|---|---| | `head` | 统计 / 额外 CSS·JS | 无版面 | | `body_start` / `body_end` | body 首尾脚本 | 无版面 | | `header_after` | 页头横幅、公告 | 通栏高约 60–120px | | `nav_after` | 导航后临时入口 | 文字链 | | `content_before` / `content_after` | 主栏前后 | 主栏宽约 640–760px | | `article_top` / `article_bottom` | 文章广告 | 主栏宽 × 90–250px | | `sidebar_before` / `sidebar` / `sidebar_after` | 侧栏广告 / 临时链接 | 侧栏约 280–320px,多条叠放 | | `footer_before` / `footer_after` | 页脚附加 | 通栏 | ### 后台怎么填 `/admin` → **站点设置** → Tab「注入 / 广告 / 统计」: 1. **统计代码** → `head` 2. **侧栏广告** → `sidebar` 3. **临时链接 HTML** → `sidebar_after`(可写 `活动`) 4. **文章顶/底广告** → `article_top` / `article_bottom` 也可继续用 **站点片段(Stylevars)** 做纯文本侧栏块;友情链接仍走「友情链接」资源。 ### 插件怎么注入 ```php use App\Domain\Plugin\Hook; use App\Domain\Theme\ThemeSlot; Hook::listen(ThemeSlot::event(ThemeSlot::SIDEBAR), function (string $html): string { return $html.'
'; }); ``` ### 主题里任意位置 在任意 Blade 加一行即可,例如正文中间: ```blade @themeslot('article_top') {{-- 或自定义新槽:插件 listen theme.my_slot,主题写 @themeslot('my_slot') --}} ``` 新槽无需改核心:约定 `theme.{name}` 事件即可。 ## 发布 ```bash php artisan themes:publish # 或后台「皮肤」→ 启用 / 发布静态资源 ``` 资源复制到 `public/themes/{slug}/`。 ## 回退 `ThemeManager` 先注册 `default` views,再 `prepend` 当前主题;缺视图自动回退 default。 ## 切换 后台「皮肤」卡片启用,或改 Settings `general.active_theme`。