[开发]等级徽章实施报告 — 列表页双徽章 + 详情页用户名下方

👑Lv.11 元老 🌞 骨干会员
2026-10-26 01:53:13

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/待办事宜/等级徽章_列表与详情页实施报告.md。

等级徽章实施报告 — 列表页双徽章 + 详情页用户名下方

需求原文:

1. 把级别和信任等级加到首页、论坛、分类页(移动端只显示图标)

2. 把级别和信任等级加到帖子详情页的用户名下面

实施日期:2026-09-17 | 覆盖范围:核心视图 + Xenforo 主题副本 | 改动文件:13 个


一、需求拆解与关键判断

站内存在两套互不相干的等级体系,这是本次改动最容易踩错的地方:

「级别」「信任等级」
业务含义积分等级 Lv.1~Lv.11Discourse 式信任等级 TL0~TL4
数据来源users.level + level_config 表user_level 插件 ul_users.map_lv
代码归属app/Helpers/Points.phpplugins/user_level/Plugin.php
徽章 CSS 类.level-badge / .mn-level-badge.user-level-badge / .user-level-avatar-badge

结论:两枚徽章必须分别从各自的渲染入口产出,不能混用。

另一个关键判断:数据层完全就绪,无需改动任何控制器。

Thread::decorate() / Thread::decorateList() / Post::getByThread() 均已输出 level 字段,

HomeController 亦已补 $t['level'],视图直接取 $thread['level'] / $post['level'] 即可。


二、改动清单

2.1 后端 / 插件(4 个文件)

app/Helpers/Points.php

  • getLevelConfig():由「逐档按需查询」改为首次调用一次性载入整表。

原实现在列表页会被逐行触发,20 行帖子最多产生 11 次查询(N+1);

改为单次 SELECT * FROM level_config 后恒为 1 次。空表时缓存为空数组(非 null),不会反复回源。

  • renderBadge(int $level, string $mode = 'meta'):重写。
  • 输出结构改为 icon / text 双 span,为响应式收敛留出钩子:

```html

<span class="level-badge mn-level-badge[ level-badge--list]" style="background:#xxx;" title="Lv.N 称号">

<span class="level-badge-icon">🌰</span><span class="level-badge-text">Lv.N 称号</span>

</span>

```

  • 内联样式从 6 条属性收敛为 1 条(仅背景色 —— 每档颜色来自 level_config,属数据不属样式),

其余(display / padding / border-radius / font-size / font-weight / line-height /

white-space)全部下沉到 modern.css。

  • 新增 title 属性,窄屏只剩图标时仍可通过长按/悬停看到完整文案。
  • 新增 mode='list' → 追加 level-badge--list 类。
  • 默认 mode='meta' 保证向后兼容:唯一外部调用方 plugins/user_profile/views/front.php:48

无需修改即可继续工作。

plugins/user_level/Plugin.php

  • badgeHtml() 新增 mode='list':输出 user-level-badge--list + icon/text 双 span,

与「级别」徽章同构。原 meta(详情页)与 avatar(头像微徽章)两种模式保持不变。

plugins/user_level/hook/controller_view_before.php

  • 列表模板白名单由 ['forum/index','home/index_default'] 扩为

['forum/index','forum/category','home/index_default'] —— 分类页此前不在白名单内,徽章零输出。

  • 收集循环变量由 3 个扩为 ['latestThreads','normalThreads','pinnedThreads','threads'],兼容各列表视图的不同命名。
  • 列表徽章渲染模式由 avatar(纯图标)改为 list(桌面图标+文字 / 移动端仅图标)。

plugins/user_level/hook/layout_head_end.php

  • $targets 加入 'forum/category'。

这条与上一条必须成对修改 —— 否则分类页会出现「有徽章但没样式」。

2.2 列表视图(核心 3 + 主题 3)

统一改为输出一个徽章容器,把两枚徽章包在一起(便于统一间距与换行):

<?php $ulListBadges = $this->getData('ulListBadges', []); $rowUid = (int)($thread['user_id'] ?? 0); ?>
<?php echo \app\Helpers\Points::renderBadge($level, 'list'); ?><?php echo $ulListBadges[$rowUid] ?? ''; ?>

文件位置容器类
app/views/forum/index.php置顶行 + 普通行(2 处)mn-list-badges
app/views/forum/category.php帖子行(1 处,新增占位)mn-list-badges
app/views/home/index_default.php帖子行(1 处)mn-list-badges
assets/themes/Xenforo/forum/index.php置顶行 + 普通行(2 处,补 $level 赋值 ×2)xen-threadRow-badges
assets/themes/Xenforo/forum/category.php帖子行(1 处)xen-threadRow-badges
assets/themes/Xenforo/home/index_default.php帖子行(1 处,补 $level 赋值)xen-threadRow-badges

Xenforo 主题三个列表视图原先只有信任等级占位,$level 变量未定义(部分循环甚至未赋值),本次一并补齐。

2.3 详情视图(核心 1 + 主题 1)

app/views/thread/show.php(主帖 + 回复,2 处)

调整前:用户名 / 勋章 / 头衔 / 信任等级 挤在同一行,时间在下一行。

调整后:

用户名 + 勋章 + 头衔        ← 第一行(保持原样)
[Lv.N 称号] [TL4 领袖]      ← 第二行(新增 .thread-user-badges)
时间                        ← 第三行

assets/themes/Xenforo/thread/show.php(主帖 + 回复,2 处)

该主题的楼层结构本来就是「.xen-message-username → .xen-message-role」上下两行,

信任等级已在 role 行,只需补级别徽章,用 .xen-message-badges 把两枚包在一起。

2.4 样式(4 个文件)

文件新增内容
assets/css/modern.css.level-badge 基类、.level-badge .level-badge-icon、.mn-list-badges、.thread-user-badges;@media (max-width:768px) 内 .level-badge--list .level-badge-text { display:none } + .level-badge--list 收窄内边距
assets/themes/Xenforo/xenforo.css.xen-threadRow-badges、.xen-message-badges
plugins/user_level/assets/style.css.user-level-badge--list 系列 + @media (max-width:768px) 内隐藏 .user-level-badge-text
plugins/user_profile/assets/style.css回归修复:.up-badges .level-badge { border-radius:10px; font-weight:600; line-height:1.6; }

最后一条是本次唯一的「副作用修补」:renderBadge 原先内联 border-radius:10px(胶囊形),

改为类驱动后是 4px(与列表行 .mn-level-badge 一致的直角圆角)。

个人主页 .up-badges 行内与 .up-badge(10px 胶囊)并排,若不覆盖会出现两枚徽章圆角不一致。

故按页面语境在插件内覆盖回 10px。

2.5 配置与缓存(2 项)

  • user_level_cfg 的 show.list:"0" → "1"

这是列表信任等级徽章的总开关,为 "0" 时整个列表页零输出。

通过 Plugin::saveCfg() 写入(会一并清 Settings 缓存 + PageCache::invalidate())。

  • sw.js 的 CACHE_VERSION:flinthub-static-v1.0.0 → flinthub-static-v1.0.1

本次涉及 CSS 变更,按 sw.js 头部规则必须递增,否则老用户拿到旧样式。


三、验证结果

3.1 静态检查

项目结果
php -l(12 个 PHP 文件)全绿
modern.css 花括号平衡593 / 593
xenforo.css 花括号平衡377 / 377
plugins/user_level/assets/style.css109 / 109
plugins/user_profile/assets/style.css43 / 43
protected/error.log零新增

3.2 渲染实测(抓取 HTML 计数)

清空 data/runtime/pages/*.html 后逐页抓取:

页面徽章容器级别徽章信任等级徽章
/(首页社区模式)101010
/forum161616
/forum/category/1666
/?preview_theme=Xenforo101010
/forum?preview_theme=Xenforo161616
/forum/category/1?preview_theme=Xenforo666
/thread/6617(1 主帖 + 16 回复)1717
/thread/66?preview_theme=Xenforo171717
/u/1(个人主页,回归检查)—1—

实测输出样本(核心首页):


  
    👑Lv.11 社区元老
  
  
    💎社区领袖
  

实测输出样本(核心详情页主帖):

flinthub …3 枚勋章…
…时间…

3.3 移动端收敛的确定性验证

「移动端只显示图标」无法靠 HTML 静态抓取证明,改用两项结构性论证:

  1. 括号深度分析:确认 .level-badge--list .level-badge-text { display:none }

确实位于 @media (max-width:768px) 块内

(modern.css 2126 行开 → 2303 行闭,规则在第 2194 行,深度 = 1)。

  1. 选择器唯一性:全库检索 level-badge-text / user-level-badge-text,

针对这两个选择器的 display 声明各只有一处,无特异性冲突、无后置覆盖。

→ 断点生效时文字必然隐藏,行为是确定的。

两枚徽章使用同一断点口径(768px),与核心 .mn-hide-mobile 工具类一致,窄屏会同步收敛。


四、过程中修复的真实缺陷

#问题后果处理
1forum/category 不在插件列表模板白名单分类页信任等级徽章零输出白名单 + CSS 注入白名单同步补
2show.list = "0"三个列表页的信任等级徽章全部零输出改为 "1"
3Xenforo 列表视图 $level 未定义级别徽章无法渲染三处补齐 $level = (int)($thread['level'] ?? 1)
4Xenforo 论坛页置顶循环漏赋值 $level/forum?preview_theme=Xenforo HTTP 500<br>renderBadge(): Argument #1 ($level) must be of type int, null given补赋值后复测 200
5renderBadge 圆角由 10px 变 4px个人主页徽章行圆角不一致插件内按页面语境覆盖回 10px

第 4 项值得记一笔:它源于本机 Edit 偶发「报成功但未落盘」(伴随 EBUSY)。

本次任务共中 4 处(3 个 Xenforo 视图 + 1 条 CSS 规则),全部靠逐文件 grep 复扫才发现。

结论:批量编辑后必须复扫,只信 Edit 返回值会漏。


五、遗留与建议

  1. 未覆盖的列表页:search/index、tags/show、blog/index、message/inbox|sent

同样渲染作者名,本次不在需求范围内。如需统一,只需把对应模板名加入

controller_view_before.php 的 $listTemplates 与 layout_head_end.php 的 $targets,

再在视图内加容器即可 —— 机制已通用。

  1. 未做浏览器截图验证:本机 agent-browser 未安装 Chromium(open 静默退出 1),

截图链路走不通,故改用「HTML 结构计数 + CSS 括号深度/特异性分析」。

若需视觉确认,建议在装了 Chromium 的环境跑一次 390px / 1440px 双视口截图。

  1. 发布包:本次涉及 CSS 与 sw.js 变更,若要走 update/N/ 流程发布,

需新建 update/4(version.php → V1.0.4)并把 sw.js 一并放入包内。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 1 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录