[开发]首页精选区块插件(home_spotlight)

👑Lv.11 元老 🌞 骨干会员
2026-10-26 00:33:06

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:WWW/plugins/home_spotlight/说明.md。

首页精选区块插件(home_spotlight)

首页顶部精选区块:左侧图片轮播 + 右侧帖子 Tab(推荐 / 置顶 / 最新 / 最热 / 精华),

纯 CSS 切换 tab,与核心「论坛社区」区块并存(不替换),渲染顺序排在公告块之上。

「推荐」位支持积分上墙、到期自动下架。

版本:1.2.0 min_core:1.1.2 sort_order:-1


一、挂载位置(复用核心钩子,零核心改动)

复用核心通用钩子 announcements_display,零核心改动。

项事实
触发点app/Views/home/index_default.php:17、app/Views/forum/index.php:22、app/Views/blog/index.php:20 —— 一次页面渲染共三处
因此渲染钩子第一件事就是「仅首页」门控:$GLOBALS['__view']->currentPath() !== '/' 即 return(其余两页零查询返回)
排序plugin.json 的 sort_order = -1;同钩子上 announcements 为 0、home_members 为 2
页面位置首页中栏最上方:公告块之上、会员区块之上、论坛社区区块之上

★ sort_order 支持负数

app/Helpers/Plugin.php:514 走 (int)($plugin['sort_order'] ?? 0),:516 用

$a['order'] <=> $b['order'] ?: $a['seq'] <=> $b['seq'] 排序 → 负数合法,

故 -1 即可插到所有未声明 sort_order(缺省 0)的插件之前。

实测真实执行顺序(读框架 hookIndex(),非推测):

home_spotlight → announcements → home_members

⚠️ 本机 announcements 未配置公告数据 → 该块不输出 HTML,

所以「精选在公告之上」只能靠钩子真实顺序证明,不能靠页面 offset 比对。

与核心「论坛社区」区块的关系

并存(站长拍板)。首页会出现两份帖子列表:上方 = 精选/推荐帖,下方 = 核心最新帖。

核心视图 index_default.php 一行未改。


二、五个 Tab 的口径

tab数据源口径
recommend 推荐hs_wall(插件独立库)+ 后台手动 tid见 §三,实时计算、不走缓存
pinned 置顶main_index.topic_indexis_pinned = 1,按 last_reply_time DESC, id DESC
latest 最新main_index.topic_index按 create_time DESC, id DESC
hot 最热main_index.topic_index按 view_count DESC, reply_count DESC, id DESC
highlight 精华main_index.topic_index复用核心 is_highlighted 字段,按 last_reply_time DESC, id DESC

  • 已去掉「公告」tab(站长要求):TAB_KEYS 只有上述 5 项。
  • 每榜条数后台可配(list_n,默认 10,范围 1-50)。
  • 权限过滤:所有取数一律先过 Permission::getAuthorizedCategoryIds()

(规范红线:自造权限判定 = 越权高危)。未登录视 group_id = 0。

  • 软删过滤:status = 0 AND deleted_at IS NULL,与 app/Models/Thread::getLatestAllowed() 完全一致。
  • 作者名:拿到 uid 后统一走 User::batchGetUsers()(规范 §11.8.3:插件禁止直查 users 表取展示字段)。

用户名缺失(已注销)的行直接丢弃,避免死链。

  • 排序 / WHERE 片段一律取自白名单常量(BOARD_ORDER / BOARD_WHERE / BOARD_TIME),

绝不把外部输入拼进 SQL。

  • ⚠️ topic_index 禁 status 打头索引(既有索引:idx_topic_index_list (is_pinned DESC, last_reply_time DESC, create_time DESC))。

Tab 可配 + 可排序

后台可多选启用并给每榜一个序号(seq,小者在前):

  • 启用的 tab 按 seq 升序(seq 相同则按 TAB_KEYS 声明顺序兜底)。
  • 一个都不启用 → panelEnabled() 为 false,整个区块不输出。
  • 序号经 Plugin::tabs() 做白名单 + 类型收敛,非法行直接丢弃。

三、轮播图(左侧)

站内原本没有中栏轮播组件 → 本插件自建一套(新表 hs_slide + 后台传图/链接/排序/启用)。

项说明
图片上传走核心 Upload::file()(规范红线:唯一落盘口)
命名空间★ covers(见下方红线)
缩略图档位md_(800×600);前台取 URL 走 Upload::thumbUrl($rel, 'md_'),缺失自动回退原图 → 零 404
数量上限SLIDE_MAX = 20
自动播放interval 后台可配(默认 5000ms,范围 2000-15000),assets/script.js 的 initRotator() 驱动
控件前后箭头 + 圆点;只有 1 张时前后端都不渲染控件(点了没反应的控件不出现)
交互鼠标悬停暂停、移出恢复;visibilitychange 切后台暂停;点击箭头/圆点后重置计时器
跳转链接白名单:http(s):// 外链 或 站内相对路径(/ 开头);javascript: / data: / 协议相对 // 一律丢弃
关掉轮播后台 carousel_on = 0 或一张图都没有 → 左栏不渲染,右栏自动占满整宽

⚠️⚠️ 命名空间红线(本插件上线时踩过的坑)

Upload::REL_PATTERN(app/Helpers/Upload.php:28)只允许 seed_avatars / covers / pwa:

private const REL_PATTERN = '#^(?:(?:seed_avatars|covers|pwa)/)?(?:[0-9]{4}-[0-9]{2}/)?[A-Za-z0-9_.\-]+$#';

makeRelPath() 对任意 namespace 都会生成该前缀,但 absPath() 会按正则拒绝它

→ Upload::file() 走到「文件保存失败」分支。

症状:后台点「新增轮播图」永远失败,protected/error.log 零告警

(因为 Upload::file() 是正常返回 ['success'=>false,'error'=>'文件保存失败'],不是抛异常)。

根因:最初写成 'namespace' => 'spotlight' → absPath() 返回 null。

修法:改用 'namespace' => 'covers'。

一条断言就能证伪:Upload::absPath(Upload::makeRelPath('png','spotlight')) === null。

孤儿文件保护(不需要额外配置)

核心 Settings::cleanupOrphanAttachments() 会扫 assets/uploads/ 下的孤儿文件,

但它的第 5 步「插件库兜底扫描」会遍历 plugins//data/.sqlite 的全部文本列,

把「整值即路径」的字符串记为「在用」(markUsedFromText() 口径 ①)。

本插件把完整相对路径(covers/2026-10/<32hex>.png)存进 hs_slide.image 单列

→ 会被命中 → 不会被误删。(hs_slide 是新表、插件源码指纹变化 → 必然被重扫,无需清白名单。)

删除轮播图时 Plugin::purgeUpload() 会一并删掉原图 + md_ / thumb_ 派生图

(一律走 Upload::path() 白名单校验,拿不到真实路径就什么都不做 → 防路径穿越 / 误删别人文件)。


四、推荐位与「上墙」

坑位组成

推荐位(slots,默认 10,可配 1-50)
 ├─ ① 后台手动 tid(按填写顺序,去重)—— 永不过期、不显示剩余天数
 └─ ② 上墙记录(未过期)—— 补足到 slots,按 expires_at 倒序;显示「剩 N 天」

  • 手动 tid 解析:英文逗号 / 中文逗号 / 顿号 / 空格 / 换行均可分隔,非纯数字片段丢弃。
  • 已注销 / 已软删 / 无权限版块的帖子不占坑(用 LIMIT slots+10 多取若干条补位)。
  • ★ 「推荐」取数 = 后台指定 tid:topic_index 没有推荐标记字段,故不新增标记,直接由站长填 tid。

上墙购买

项说明
入口首页「推荐」Tab 底部的「我要上墙」按钮 → 跳到 /spotlight/wall 上墙页挑自己的帖子
独立实现★ 不与 home_members 复用:不同路由、不同表、不同配置、不同 flash 键(站长要求)
显示条件登录 + 开放购买 + 坑位未满(任一不满足 → 首页按钮不渲染)
提交前确认assets/script.js 读 form[data-hs-confirm] 弹 window.confirm,确认后置灰按钮 + 标记已提交(防双击重复扣分)
扣分复用核心 Points::deduct() —— 条件 UPDATE 原子防透支 + points_log 流水 + syncLevel() 等级同步 + 扣分通知。插件不写自己的扣分逻辑
单价 0免费上墙,完全不走 Points::deduct()(不产生流水、不发通知);文案改显示「免费上墙」
时效默认 7 天(1-365 可配),到期由查询侧过滤自动下架(无定时任务)
重复上墙已在墙上 → 拒绝,并提示剩余天数
本人限制只能上自己的帖子(row['uid'] !== $uid → 拒绝)
坑位已满拒绝,前台自动隐藏入口

顺序:先占位、后扣分、扣分失败回滚占位

$db->beginTransaction();
// ① 条件 INSERT 原子占位(SQLite 无 FOR UPDATE → 用 WHERE NOT EXISTS 等价防重复)
INSERT INTO hs_wall (...) SELECT ... WHERE NOT EXISTS (
    SELECT 1 FROM hs_wall WHERE tid = :tid2 AND expires_at > :now2
)
if ($stmt->rowCount() === 0) { $db->rollBack(); return already; }   // 并发时只有一方能插入
// ② 扣分(核心库独立事务);返回 false = 余额不足 → 回滚占位
if ($price > 0 && !Points::deduct(...)) { $db->rollBack(); return no_points; }
$db->commit();

  • points 字段记录成交价快照(price 之后被后台改动不影响历史记录)。
  • 余额不足的文案带具体数字:积分不足:上墙需 {need} 积分,你当前只有 {have} 积分。
  • 后台「下架」是管理动作,不退款(Plugin::removeWall())。

结果提示(PRG + 一次性 flash)

POST /spotlight/wall/buy  →  写 $_SESSION['hs_wall_flash']  →  302 回 /spotlight/wall(PRG)
GET  /spotlight/wall      →  FrontController::wall() 读 flash、渲染 .mn-alert、**立即 unset**
GET  /                    →  announcements_display 钩子读 $_SESSION['hs_flash']、渲染、**立即 unset**

  • 首页那条 flash 渲染段刻意放在 panelEnabled() 之前 → 即使管理员随后关掉了面板,用户也能看到自己刚操作的结果。
  • 必须 unset:Csrf::verify() 不消费 token,不 unset 则 F5 会重复提示。
  • ⚠️ Csrf::verifyOrDie 在未登录时跳 /login(302)、已登录但 token 过期时 403 ——

两条都是核心行为,不是本插件的分支。


五、缓存机制(性能)

首页每个访客请求都渲染,不能在渲染路径做跨版块聚合。

渲染钩子(仅首页)
  └─ 读 hs_cache(1 次查询,全表 4 行)
       ├─ 数据新鲜 → 直接渲染
       └─ 已过期   → 仍渲染旧数据(不阻塞),置 $GLOBALS['hs_need_refresh']

route_after_dispatch(index.php:441,响应产出之后)
  └─ 标志为真 → 重算 4 榜 + 回写(4 次聚合 + 1 次 upsert)

  • 正常请求的 hook 查询数 = 1(hs_cache)+ 1(batchGetUsers)+ 1(推荐位实时 hs_wall)= 3 次

(规范 §11.4 第 24 条红线 ≤ 5)。

  • 冷启动(首次启用 / 缓存被清):渲染钩子内同步算一次,保证首屏不留空面板。
  • TTL = 300 秒。
  • 推荐位与上墙状态不走缓存,每次实时算(hs_wall 行数极少)。
  • ⚠️ 游客走核心 PageCache(整页永久缓存) → 上墙成功 / 后台保存配置 / 轮播图增删改,

四处都必须调 Plugin::invalidatePageCache(),否则游客看到的还是旧首页。

Windows 下 PageCache::invalidate() 可能因文件占用静默失败 → 插件补了一层 glob() + @unlink()。

登录用户不走 PageCache。


六、tab 实现:纯 CSS,零 JS

  • N 个 <input type="radio" class="hs-radio"> 前置为 .hs-tabs / .hs-panes 的兄弟节点。
  • 显隐 / 激活态由 :checked ~ … 控制;对应关系靠 **:nth-of-type(N)(radio,按 input 计数)

/ :nth-child(N)(label、pane)建立,不依赖 id 语义**。

  • ⚠️ 因此 面板内必须零 <form>(否则隐藏域会打乱 input 的 nth-of-type 序号)。

上墙入口在面板里是 <a> 链接,不是表单。

  • ⚠️ radio 的 name / id 带渲染序号(Plugin::nextSeq())→ 同一页面若出现多份面板,

两份不会串成一个 radio group。

  • 视觉隐藏用 clip: rect(0 0 0 0) 而不是 display:none → 保留键盘聚焦与方向键切换。
  • 服务端一次渲染全部 pane,切换零请求。

七、文件结构

plugins/home_spotlight/
├── plugin.json                              # hooks: init_after / announcements_display
│                                            #   / route_after_dispatch / route_register
│                                            #   / admin_route_register / layout_head_end
│                                            #   sort_order = -1(排在公告之上)
├── Plugin.php                               # 独立库 / 建表 / 生命周期 / 配置 / 5 tab 取数
│                                            #   / 缓存读写 / 推荐位 / 上墙 / 轮播图 CRUD
│                                            #   + 末尾 class_alias(目录名含下划线,规范 §4.1)
├── FrontController.php                      # GET /spotlight/wall · POST /spotlight/wall/buy
├── AdminController.php                      # 继承 app\Controllers\Admin\BaseController(鉴权)
├── views/admin.php                          # 后台四分区页(基础设置 / 轮播图 / 推荐与上墙 / 上墙记录)
├── views/wall.php                           # 前台上墙页(挑自己的帖子)
├── hook/
│   ├── init_after.php                       # 惰性建表兜底(戳文件门控)
│   ├── announcements_display.php            # 精选区块渲染 + flash 渲染(三触发钩子,仅首页)
│   ├── route_after_dispatch.php             # 榜单 TTL 懒刷新
│   ├── route_register.php                   # 前台路由
│   ├── admin_route_register.php             # 后台路由(不写 /admin 前缀)
│   └── layout_head_end.php                  # 注入 style.css + script.js(defer)
├── assets/
│   ├── style.css                            # 前台区块 + 上墙页 + 后台局部(颜色只用既有 --mn-* / --c-*)
│   └── script.js                            # 轮播 + 二次确认/防重复提交 + 后台分区切换
├── lang/{zh,zh_tw,en}.php                   # 90 键 × 3 语,键集合与键序完全一致
└── data/
    ├── home_spotlight.sqlite                # 独立库(hs_slide / hs_wall / hs_cache)
    ├── schema.version                       # 数据层版本号戳文件(比对**内容**)
    └── .htaccess / web.config               # 自动生成,防 Web 直读

可调常量(Plugin.php 顶部):LIST_N_DEFAULT/MIN/MAX = 10/1/50、TTL = 300、

SLOTS_MIN/MAX = 1/50、DAYS_MIN/MAX = 1/365、PRICE_MAX = 1000000、

INTERVAL_DEFAULT/MIN/MAX = 5000/2000/15000、SLIDE_MAX = 20。

改动 ddlStatements() 时必须同步递增 SCHEMA_VERSION(当前 hs-schema-1),否则老站点戳文件命中、新表建不出来(规范 §11.15)。


八、数据表(插件独立库)

hs_slide  id / image / title / url / sort_order / enabled / created_at
          idx_hs_slide_list(enabled, sort_order, id)
hs_wall   id / tid / user_id / points / started_at / expires_at / created_at
          idx_hs_wall_tid(tid, expires_at) · idx_hs_wall_exp(expires_at)
hs_cache  ckey(PK) / cdata / updated_at      -- ckey ∈ {pinned, latest, hot, highlight}

  • hs_wall.tid 指向核心 main_index.topic_index.id(跨库引用,无外键)。
  • points = 成交价快照;expires_at 为 Y-m-d H:i:s 文本,与核心时间列口径一致。
  • 建表走 Plugin::ensureSchema()(= Plugin::db($name)->exec($ddl)),

索引 DDL 必须排在 CREATE TABLE 之后。

  • ⚠️ 手工删表后必须一并删掉 data/schema.version,否则戳文件命中不会重建。

九、路由

方法路径控制器门禁
GET/spotlight/wallFrontController::wallrequireLogin()(未登录 302 跳 /login)
POST/spotlight/wall/buyFrontController::buy同上 + Csrf::verifyOrDie($_POST['_token']) + PRG
GET/admin/home-spotlightAdminController::index继承 Admin\BaseController(构造函数鉴权 + 二次验证)
POST/admin/home-spotlight/saveAdminController::save同上 + CSRF
POST/admin/home-spotlight/slide/addAdminController::slideAdd同上 + CSRF
POST/admin/home-spotlight/slide/saveAdminController::slideSave同上 + CSRF
POST/admin/home-spotlight/slide/deleteAdminController::slideDelete同上 + CSRF
POST/admin/home-spotlight/removeAdminController::remove同上 + CSRF

  • 后台路由不写 /admin 前缀(核心挂载点自动拼接,规范 §11.22)。
  • plugin.json 权限:route:admin + system:settings(缺后者 Settings::update() 会被拒绝 + error_log)。
  • ⚠️ 视图里 form action 一律写成 <?php echo $bp; ?>/admin/home-spotlight/... 字面量拼接,

不要抽成 $slideActions = $bp . '/admin/...' 变量 —— plugin_form_action_audit.php

无法回溯「两个变量相加」的赋值,会报疑似 404 假阳性。


十、设计约束(改动前必读)

  • 零内联:面板输出无 style="" / on*="" / <script>。

图标写字面量实体 + 核心字号类 mn-fs-12,不用 $view->icon()(其返回值恒带内联 style)。

用到的 9 个码点(f164 f08d f017 f06d f005 f053 f054 f067 f03e)均已在子集字体中,

新增码点须重跑 build_font_subset.py(否则静默方块)。

  • 颜色只能用既有变量:前台 --mn-(--mn-danger / --mn-spectrum- 全站从未定义,已避开);

后台 --c-*。style.css 在 layout_head_end(main.php:49)注入 → **加载早于核心

admin.css(:55) / modern.css(:60),故覆盖核心属性必须提高特异性**

(例:.mn-section.hs-section 而非 .hs-section),不打 !important 补丁;

收窄输入框宽度用 max-width(不与核心 width:100% 正面冲突)。

  • ⚠️⚠️ 窄屏改 flex-direction 后必须同时改 align-items:A 段基础规则

.mn-section.hs-section { align-items: flex-start } 是给桌面横排两栏写的;

竖排后交叉轴由「垂直」翻成「水平」,flex-start 的语义随之变成

「子项按自身内容宽度收缩 + 左对齐」→ .hs-left 根本拿不到整宽,

里面 .hs-rotator 的 width:100% 落空,再被 aspect-ratio: 3/2 + min-height: 180px

反向传递出固定 min-width = 180×3/2 = 270px → 轮播图贴不到右侧

(实测只占卡片 73.5%、右侧空 26.5%)。正解 = 窄屏把 align-items 改 stretch。

⚠️ 只改 .hs-left 的 padding 是无效的 —— 宽度压根没给到那一层(1.1.0 曾这样误修过一轮)。

  • 渲染钩子 $this 不可用(闭包作用域,规范 §11.24):用 htmlspecialchars()、Upload::url()、

I18n::get()、$GLOBALS['__view']->timeAgo()。

  • 多触发钩子禁止声明顶层函数/类(规范 §9.0):announcements_display 三触发,

一旦声明会被降级为 include_once,第二/三处入口静默消失。

  • 目录名含下划线必须 class_alias(规范 §4.1):核心按目录名拼 \Plugin\home_spotlight\Plugin,

与命名空间 Plugin\HomeSpotlight 不同标识符 → 生命周期方法会静默跳过、卸载后数据表残留。

  • 后台 markup 必须留在 <main class="admin-content"> 内(放 </main> 后 = 无 UI、零日志)。
  • HTML 不允许表单嵌套 → 设置表单包住「基础设置」+「推荐位与上墙」两个 pane;

「轮播图」与「上墙记录」各自独立成 pane 并放在设置表单之外

(否则浏览器丢弃内层 form,表现为「点了没反应」)。

  • 轮播图列表不用 <table>:每张图是自成一体的 <form> 卡片(表单不能横跨 <tr>)。
  • 两处配置写入点:AdminController::save() 与 Plugin::activate();

新增配置键后必须清一次 protected/settings_cache.php(该文件不自动补新键)。

  • Settings 是 string 语义:布尔开关保存时必须归一成 '0'/'1',绝不能存空串(空串会回落默认值)。
  • 改 hooks 清单后必须 \app\Helpers\Plugin::refresh() 重建 plugins/plugins_cache.json。

十一、验证记录(2026-10-14)

套件结果
语法:php -l(8.2.9nts,13 个 PHP 文件)+ json 解析 + node --check script.js全绿
plugin_case_lint.py(Linux 大小写精确匹配)全绿(59 个插件目录)
php_var_cjk_check.php(全角标点紧跟 $var)全绿(14 文件)
fh_i18n_check.php三语一致 90 键;代码引用键缺 0;总 63 PASS / 0 FAIL
plugin_form_action_audit.php5 个 POST 表单全部命中已注册 POST 路由(修正前 4 个假阳性)
图标字体子集9 个码点全部命中(FA 448 码点),无需重跑子集构建
数据层 / 渲染顺序探针 hs_data_probe.php56 PASS / 0 FAIL —— 含 ★钩子真实顺序 home_spotlight → announcements → home_members
HTTP 端到端冒烟 hs_smoke.sh(真实 php -S)55 PASS / 0 FAIL —— 见下
守卫protected/error.log 零新增;测试后 hs_slide=0、hs_wall=1(站长原有,未动)、assets/uploads/covers/2026-10/ 已清空并 rmdir、冒烟会话按精确 SID 删除

HTTP 冒烟覆盖点:首页 200 含区块/tab/文案/样式/脚本 · 脚本真的调用 initRotator() ·

游客首页(PageCache 路径)也含区块 · /forum 与 /blog 不渲染区块 ·

后台 200 且 main 区无原始 i18n 键 · 未登录访问后台/上墙页 302 · 缺 CSRF 403 ·

★轮播图上传成功(DB 新增行 + 原图落盘 + md_ 缩略图生成 + 命名空间 covers/ + 标题/链接入库 + 首页引用到该图)。

未验证项(交给站长自测):双主题(默认浅色 + 暗夜星辰)下的区块观感、

与上方/下方区块的视觉协调度、轮播动画手感、移动端窄屏收敛效果。

1.2.0(2026-10-15)

套件结果
CSS 花括号深度解析(剥离注释后计深)深度 0、无负深度(16,930 B / 698 行)
css_fontface_e2e.py(真解析 @font-face + 字体文件可达)全部通过(FA 448 / Tabler 447 码点,图标零方块)
HTTP 端到端冒烟 hs_smoke.sh(真实 php -S)58 PASS / 0 FAIL(新增 3 条移动端样式回归断言)
HTTP 实测?v= == 本地 filemtime(缓存已破);新写法断言全中、旧写法残留 0
守卫protected/error.log 零新增;covers/2026-10/ 回到 6 个文件(冒烟测试图零残留)

本版改动:移动端三处修复,只动 assets/style.css,零逻辑改动 ——

  1. 轮播图通栏 —— 根因是 align-items 换轴(详见 §十),窄屏改 stretch 修复;
  2. 帖子标题回到单行省略号截断 —— 撤掉 1.1.0 引入的「独占一行 + 最多折两行」;
  3. 列表项保持「标签-标题-作者-时间」同一行,窄屏放不下时去掉「时间」(作者名保留)。

⚠️ 「标签」不是本插件的元素 —— 它由核心插件 forum_required_tag 的前端脚本

(BADGE_SELECTOR = 'a[href*="/thread/"], …')把标题开头的 [前缀] 原地替换成

<span class="frt-title-badge"> 并 insertBefore 进 .hs-item-title 的内部首位。

所以「标签 + 标题」天然共处同一个 <a>:标签永远可见,只有后面的标题文本被省略号截断

—— 本插件不需要自己渲染标签,也不该把标题改成 -webkit-box(会与 inline-block 子元素打架)。

未验证项(交给站长自测):窄屏收敛后的实际观感(轮播图是否真的通栏、列表是否确为单行)。


十二、已知取舍

  • 无定时任务:到期靠查询侧过滤(expires_at > now),hs_wall 里会留历史行

(后台「上墙记录」页可见,带「已过期」徽章)。这是刻意的:便于管理员追溯,且避免在 2 核 2G 上跑定时清理。

  • 下架不退款:后台「下架」是管理动作,不退积分、不删 points_log。
  • 与核心论坛区块并存 → 首页会有两份帖子列表。站长已确认接受(上方精选、下方最新)。
  • 「推荐」= 后台填 tid,不做站内自动推荐算法(topic_index 无推荐标记字段)。
  • 无独立「更多」页:区块只出现在首页,5 个 tab 各最多 list_n 条。
  • 轮播图不生成 thumb_:只生成 md_(800×600)。轮播区域约 300px 宽,

md_ 足够且能复用正文图档位;原图不删,便于日后换大尺寸容器。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 瀏覽 7 次 | 回覆 1 次

全部回覆 (1)

🌳Lv.4 中级 🌛 见习会员
2026-10-26 02:01:45
「左侧图片轮播 + 右侧帖子 Tab,与核心论坛社区区块并存、不替换,渲染顺序排在公告块之上」——「并存不替换」这个选择很重要:首页结构是站长的核心资产,插件强行替换会让主题作者和站长都很难受。

三点:

1. sort_order = -1 排在公告之上,意味着首页最顶部是精选轮播。移动端上轮播占的高度很大,用户要滚一屏才能看到帖子列表。建议在移动端降低轮播高度或默认折叠。
2. 「推荐位支持积分上墙、到期自动下架」——上墙是站长收入来源之一,但推荐位如果被付费内容长期占据,会挤掉真正的好帖。建议限制同时上墙数量(比如最多 3 个),并保留至少一个运营位。
3. 五个 Tab(推荐 / 置顶 / 最新 / 最热 / 精华)纯 CSS 切换,意味着五份数据首屏全查。如果每个都要排序(最热要按 reply_count 排),得确认索引齐全,否则首屏会明显变慢。

1.2.0 复用 announcements_display 钩子、零核心改动,这个挂载方式选对了。
#1 樓

請 登入