docs: 追加经验#19 — AdminSidebarInit items vs item 属性名陷阱 + 检查清单更新

feat/b-verification-clean
Council 2026-04-25 19:40:49 +08:00
parent 44553442e0
commit 6c18dd38f4
1 changed files with 68 additions and 0 deletions

View File

@ -342,6 +342,73 @@ class Index
---
### 19. AdminSidebarInit 子菜单:`items`(复数)不是 `item`(单数)
**症状**sidebar 显示"VR票务"顶级菜单,但子菜单项(场馆配置/座位模板/电子票等)全部丢失,控制台无报错。
**根因**`Hook.php` 的 `AdminSidebarInit` 里,子菜单数组的 key 写成 `item`
```php
// ❌ 错误ShopXO 渲染器不认识 `item`
$params[] = [
'id' => 'plugins-vr_ticket',
'name' => 'VR票务',
'item' => [ // ← 单数ShopXO sidebar 不认
['name' => '场馆配置', 'url' => ..., 'is_show' => 1],
['name' => '座位模板', 'url' => ..., 'is_show' => 1],
]
];
```
ShopXO 后台 sidebar 模板(`app/admin/view/default/public/menu.html`)渲染逻辑:
```html
{{foreach $left_menu as $v}}
{{if empty($v['items'])}} <!-- 无子菜单,渲染为顶级单项 -->
{{else /}} <!-- 有子菜单,渲染为可折叠父项 -->
<ul class="admin-sidebar-sub">
{{foreach $v.items as $vs}} ... {{/foreach}}
</ul>
{{/if}}
{{/foreach}}
```
ShopXO 渲染器检查的是 **`items`(复数)**,不是 `item`(单数)。`item` 被当作普通属性忽略,所有子菜单项静默丢失。
**修复**
```php
// ✅ 正确:`items` 复数
$params[] = [
'id' => 'plugins-vr_ticket',
'name' => 'VR票务',
'items' => [ // ← 复数ShopXO sidebar 渲染器认识
['name' => '场馆配置', 'url' => ..., 'is_show' => 1],
['name' => '座位模板', 'url' => ..., 'is_show' => 1],
]
];
```
**调试方法**:在浏览器控制台执行:
```javascript
// 提取 sidebar 中所有菜单项 URL
const items = document.querySelectorAll('a.menu-item');
const vr = [];
items.forEach(a => {
const t = a.textContent.trim();
if(t.includes('VR')||t.includes('票务')||t.includes('场馆')||t.includes('座位'))
vr.push(a.href + ' => ' + t);
});
console.log(vr.join('\n'));
```
对比 `Hook.php` 中注册的所有 URL即可发现哪些子菜单项未出现在 DOM 中。
**教训**ShopXO 大量使用复数形式约定(`items`、`menus`、`params`),遇到"数据明明传了但不渲染"的问题时,优先检查属性名的单复数是否与渲染器约定一致。
---
## 📌 开发前检查清单
接手本插件时,逐项确认以下内容:
@ -351,6 +418,7 @@ class Index
- [ ] 引用 JS/CSS 前先查 ShopXO 是否已自带(`public/static/common/lib/`),优先本地文件而非 CDN
- [ ] 插件模板使用 `$public_host` 时,控制器已显式传递(不依赖框架自动赋值)
- [ ] Hook.php 返回数组包含 `id`、`url`、`name`、`is_show`
- [ ] Hook.php `AdminSidebarInit` 子菜单数组的 key 是 `items`(复数),不是 `item`(单数)
- [ ] Admin.php 有缓存锁机制保护 `initialize()` 免于每次请求检查表
- [ ] 改字段名之前查过 Service 层源码或实际表结构
- [ ] 插件视图路径使用 `../../../plugins/vr_ticket/view/...` 前缀