[Fatal Error] 修复 /api/v1/* 接口报 “Cannot redeclare function esc_html()” Fatal
根因: api/v1/bootstrap.php 用
class_exists('EscapeService')守卫加载 EscapeService.php,但该文件只定义全局函数(esc_html/esc_attr/esc_js),不定义类,class_exists 永远返回 false触发场景: nginx 未配置 /api/v1/ 专用 rewrite 时,请求经根 index.php → index.inc.php(line 10 已 include EscapeService.php)→ bootstrap.php(再次 include),二次声明触发 Fatal
修复: 守卫改为
function_exists('esc_html'),与 model/misc.func.php 中 esc_* 兜底的写法一致必测: 通过 /api/v1/thread、/api/v1/notify/unread、/api/v1/user/me 等接口验证不再 Fatal
[优化] xnx_avatar_frame「我的头像框」页面整合到「我的」页面布局
背景: 原页面直接 include header+footer,无左侧导航,与个人中心其他子页面(等级/通知/我的帖子)风格不一致
改造: my_frame.htm 改为 include my.layout.inc.htm 公共布局,复用左侧导航 + 移动端 Tab + 用户信息卡片
route/my_frame.php 新增
$active_tab = 'frame',侧边栏「我的头像框」菜单项高亮卡片头部增加「头像框商店」入口按钮,空数据状态也增加商店跳转引导
必测: 访问 /frame-my.htm 确认左侧导航显示且「我的头像框」高亮,PC/移动端布局正常
[Bug 修复] 修复 xnx_avatar_frame 头像框在帖子列表/详情页作者位置不显示
根因: thread_list.inc.htm 和 thread_right.inc.htm 调用
avatar_component_from_data()时未传_uid选项,导致 frame hook 内$_frame_uid=0,无法定位用户佩戴记录修复: 两处调用均补传
array('_uid' => intval(...))优化: hook
avatar_component_frame.php跳过 xs 尺寸(24px 头像框无法辨识,影响视觉),同时显式跳过 uid=0 避免无效查库必测: 帖子列表项作者头像 + 帖子详情右侧作者卡均显示头像框;签到排行榜/抽奖参与名单等 xs 头像位不显示头像框
[Bug 修复] 补齐 xnx_avatar_frame 头像框在多个用户信息位的注入
根因: 同前一条规则——
avatar_component_from_data()第 6 参数漏传_uid,frame hook 拿到 uid=0 跳过注入修复位置:
view/htm/my.layout.inc.htm:134—— my/profile 等所有 my/* 子页面顶部用户信息卡view/htm/user_info_card.inc.htm:20—— user/{uid} 顶部用户信息横排卡plugin/xnx_fcomment/hook/forum_mod_after.htm:35—— 版块右栏最新评论列表头像
必测: my/profile 顶部头像框、user/1 顶部头像框、版块页右栏最新评论头像框均显示
[Bug 修复] 批量补齐 avatar_component_from_data() 调用漏传 _uid 的位置
根因: 同 bugfix_rules.md 第 6 条规则——
avatar_component_from_data()第 6 参数必须包含_uid,否则 frame hook 拿到 uid=0 跳过注入修复位置(核心代码):
view/htm/post_list.inc.htm:32,214 —— 帖子详情一级评论/楼中楼头像
view/htm/user_following.htm:21, user_followers.htm:21 —— 用户主页关注/粉丝列表
view/htm/my_following.htm:52, my_followers.htm:11 —— 我的关注/粉丝列表
view/htm/rank.htm:112,147 —— 排行榜
view/htm/my_notify.htm:76, my_notify.template.htm:23,68 —— 通知列表
view/htm/search.htm:171 —— 用户搜索结果
view/htm/banned.htm:74,141 —— 封禁名单/封禁日志
admin/view/htm/banned_user_list.htm:73 —— 后台封禁用户列表
修复位置(插件):
xnx_fcomment/hook/forum_mod_after.htm:35 —— 版块右栏最新评论
xnx_theme_moments/view/htm/thread_list_moments.inc.htm:56 —— moments 主题列表项
xnx_appcenter/view/htm/appcenter_detail.htm:637,681 —— 应用详情评论/回复
xnx_tag/view/htm/tag.htm:35 —— 标签页帖子列表
xnx_related/view/htm/related_posts.htm:23 —— 相关帖子
xnx_reward/view/htm/reward_best_answer.htm:71 —— 悬赏最佳答案用户卡
额外修复:
xnx_status/hook/index_site_brief_after.htm:54 —— 原代码把
$_xnx_uid误传到第 5 参数(gid 位置),导致用户组图标按 uid 当作 gid 回退到默认;同时未传 _uid 导致头像框不注入。改为第 5 参数传$_xnx_gid,第 6 参数传array('_uid' => $_xnx_uid)
优化: xnx_checkin 签到排行榜头像尺寸由 ‘md’ 改为 ‘sm’,与列表紧凑布局更协调
必测: 各列表/卡片位置头像框正常显示;签到排行榜头像变 sm;首页"感谢贡献用户"卡片头像组角标正确显示
[Bug 修复] 修复签到主页排行榜小卡片未走头像组件导致头像框/用户组图标不注入
根因:
checkin_index.htm的 JS 动态渲染排行榜时直接拼<img src="...">标签,完全绕过 PHP 的avatar_component_from_data()组件,frame hook 和 badges hook 均无法注入修复方案: JS 端无法调用 PHP 的 plugin_hook 机制,改为后端 API 预渲染头像 HTML 字段下发,前端直接 innerHTML
修改:
CheckinService::getRankList()和getTodayCheckinUsers()返回数据补gid/group_icon_class/group_color字段(从 user_read 结果取)api.php的rank和today两个 action 遍历结果调用avatar_component_from_data()生成avatar_html字段(含头像框 hook + 用户组图标)checkin_index.htmJS 端把<img src="...">改为插入u.avatar_html(保留 fallback 兜底)
必测: 签到主页"今日签到/累计签到/连续签到"三个 tab 切换后,排行榜小卡片头像显示头像框 + 用户组图标
[重构] xnx_avatar_frame 头像框渲染去内联 style,改用全局 class + 每框唯一 class
背景: 用户要求"头像框不要直接注入 css style,加个 class,加一个全局 class css 设置,每个头像框提供 css 微调"
改动:
FrameService::renderFrameHtml()移除基础布局内联 style(position/top/left/width/height/pointer-events/z-index),这些样式走全局 frame.css 的.avatar-frame-img/.avatar-frame-cssclass输出时加
.avatar-frame-{id}唯一 class,方便主题/管理员针对具体框做 css 微调(如.avatar-frame-3 { transform: scale(1.2); })CSS 框类型保留
frame_css字段作为内联 style(管理员后台填的微调样式,可信输入不转义);图片框类型不输出任何内联 stylehook/footer_js_after.htm把 frame.css 改为全局加载(移除路由判断),因为头像框会在所有页面渲染(帖子列表/用户主页/签到排行榜等);frame.js + i18n + POST_URL 仍只在 frame 路由加载static/css/frame.css补充注释说明.avatar-frame-{id}唯一 class 用法
必测: 任意页面(帖子列表/详情/用户主页/签到排行榜/我的头像框/头像框商店)头像框正常显示,无内联 style 残留;后台对某个 CSS 框填 frame_css 微调样式生效
[Bug 修复] 修复 xnx_avatar_frame 购买/佩戴/取消佩戴均提示失败且不刷新
根因(与 htmx 无关,是 fetch 请求头缺失):
frame.js 的 fetch 没设
X-Requested-With: XMLHttpRequestheader,也没设Accept: application/json后端 message() 函数检测顺序:
$is_htmx(HX-Request header)→$is_api(X-Requested-With/Accept/X-API-Request)→$ajax,三者都 false 时返回完整 HTML 跳转页面(含 jump() 生成的<script>window.location=...</script>)前端
res.json()解析 HTML 失败 → 走.catch()→ toast 显示"购买失败",且不刷新
修复:
frame.js 的 fetch 加
headers: { 'X-Requested-With': 'XMLHttpRequest' },让 message() 走$is_api分支返回 JSON后端 message() 返回的 code 是字符串(
$arr['code'] = $code.''),前端判断改为data.code === 0 || data.code === '0'buy 成功后跳转到"我的头像框"页(佩戴入口在那),wear/unwear 仍 reload 当前页(更新按钮状态)
同步新增:
hook/footer_js_after.htm 下发
window.XNX_FRAME_MY_URL(供 buy 后跳转用)frame_shop.htm 顶部加"我的头像框"入口按钮(与 my_frame.htm 的"商店"按钮对称)
必测: 商店页点购买 → 提示成功 → 跳到"我的头像框"页;"我的头像框"页点佩戴/取消佩戴 → 提示成功 → 页面刷新按钮状态正确


