### [瓜奇主题文字格子广告插件8码库原创](https://8maoku.com/article/3273) **Published:** 2026-09-20T00:49:33 **Author:** zenghe **Excerpt:** 把页面上一块区域变成一面 18 格的文字广告墙:访客点格子自助下单、按月付费、付款后自动上架,支持预约锁位与到… ## 这个插件解决什么问题 中小站长想赚广告位这份钱,通常卡在三件事上:没有现成的广告位管理系统;卖格子全靠人工对接——来人问、聊价格、发收款码、手动挂链接;广告到期没人记得下架,旧的占着位置、新的进不来。格子广告本来就是碎片生意,用人工方式做,一天卖三格就要耗掉半天。 「广告文字墙」要解决的就是这件事:把格子广告的售卖、收款、上架、到期回收整条链路自动化。站长只需要在后台配一次规格和价格,剩下的交给访客自助走完。 ## 插件概览 - **插件名称**:广告文字墙 - **插件标识**:`guaqi/ad-text-wall` - **当前版本**:v1.0.3 - **运行环境**:瓜奇(GuaQi)框架运行时插件 - **适用前端**:Nuxt SSR 站点,客户端与服务端同构渲染 - **上线状态**:已在 8号码库(8maoku.com)首页启用运行 ## 核心功能 ### 一面墙 18 个格子,行列都能配 默认 6 列 3 行,合计 18 个可售格子。列数与行数都写在后台配置里,想做成 8 格的小墙或者 24 格的大墙,改配置即可,不用动代码。 ### 六色轮换,空格子也不难看 格子底色按蓝、橙、青、紫、粉、绿六色循环铺开。一个广告都没卖出去的时候,整面墙看上去依然是一块排布整齐的色块区,而不是一片「待填充」的荒芜感。对首屏观感来说,这一点比想象中重要。 ### 访客自助下单,全程无人值守 点空格子 → 填广告文字和跳转链接 → 选租期看实时报价 → 确认并付款 → 扫码支付。整条链路访客自己走完,站长不用回消息、不用发收款码。游客身份也能直接下单支付(后台可关),不需要先注册登录。 ### 预约锁位,不会两个人买到同一格 访客点开空格子会生成一次预约,预约有效期 20 分钟;下单后若未完成支付,格子锁定 3 小时,超时自动释放回可租状态。避免「你还在填单,格子被别人买走了」这种最伤体验的情况。 ### 到期自动回收,格子不永久占坑 广告租期结束后,格子状态会在渲染前自动归一为「可购买」,重新进入可租池。不需要站长手动下架,也不会因为历史记录还留在库里,就让一个格子永远挂着旧广告占位置。 ## 访客租一个格子,共五步 - **点格子**:点击任意一个可购买的格子,弹出下单窗。 - **填内容**:填写要展示的广告文字与跳转链接。 - **选租期**:选择租几个月,系统按单价实时算出总价。 - **确认并付款**:核对文字、链接与金额,提交订单。 - **扫码支付**:走瓜奇框架自带支付链路,弹出支付二维码;支付完成后格子即时上架展示。 ## 格子的四种状态 - **可购买**:空格子,任何人都能点开下单。 - **已预约**:有人正在填单或等待付款,暂时锁定,其他人点开能看到提示。 - **已上架**:付款完成,展示广告文字,可点击跳转。 - **已过期**:租期结束,自动降级回「可购买」。 ## 后台配置 网格规格、单价、租期、是否允许游客付款,都放在表单定义的 `config.ad_text_wall` 里,改配置就能调整售卖策略: ``` "ad_text_wall": { "wall_layout": "text_wall_6", // 网格布局,6 列 "wall_rows": "3", // 行数,3 行(共 18 格) "text_align": "center", // 格子内文字对齐方式 "slot_monthly_price": { // 每格每月单价,支持多币种 "CNY": "5", "USD": "0.70", "EUR": "0.64" }, "minimum_months": "1", // 最短租期,单位:月 "allow_guest_pay": "1", // 是否允许游客直接下单付款 "purchase_description": { // 下单窗中的购买说明 "zh-hans": "", "en": "", "es": "" } } ``` 其中 `wall_layout` 与 `wall_rows` 决定卖多少个格子,`slot_monthly_price` 决定卖多少钱,`minimum_months` 决定起租门槛。三个参数组合起来,就是一个完整的价格策略,不需要改任何代码。 ## 技术规格 - **多语言**:简体中文 / English / Español 三语,配置与前台文案按语言分开存放,切换语言不会出现空白项。 - **移动端适配**:输入框字号不低于 16px,避免 iOS 自动放大页面;按钮点击区域不小于 44px;窄屏下网格转为横向滚动。 - **输出安全**:访客提交的广告文字一律按文本节点渲染,不使用 v-html。即使有人在文字里塞进标签,也不会被当作 HTML 执行。 - **同构渲染**:客户端与服务端共用同一份组件源码,避免服务端渲染与水合结果不一致。 ## 安装与上线提醒 下面三条都是实际部署时踩过的坑,提前知道能省不少时间: ### 1\. 支付表单的标题不能留空 瓜奇会把支付表单的标题当作支付网关的「商品名」提交。标题为空时,支付宝会直接拒单,返回 `ACQ.INVALID_PARAMETER:商品订单的商品标题不能为空`。而这条错误在前端会被统一收敛成一句「操作失败,请稍后重试」,很容易被误判成插件故障、白白排查半天插件代码。给表单帖补一个标题(例如「广告位招租」)即可恢复。 ### 2\. 升级同一插件,走「更新安装包」而不是「安装插件」 同一个插件 id 上传新版本时,「安装插件」会新增一条独立的包记录,导致同插件多版本并存;若旧版本还挂在某个前端上处于启用状态,就会触发「A runtime plugin must use one revision across all frontends」,启用过程卡在半路。正确做法是走插件卡片菜单里的「更新安装包」,它会把原本启用该插件的站点一起带进更新流程。 ### 3\. 安装、启用、删除都在后台完成 运行时插件的安装与启用走的是会话级校验,应用密码接口只能读取插件目录、写不了。所以这一步没法用脚本批量处理,在后台的运行时插件页操作即可。 ## 常见问题 - **格子不够卖怎么办?**把 `wall_rows` 调大即可扩容;也可以提高 `slot_monthly_price` 控量。 - **只想让登录用户购买?**把 `allow_guest_pay` 关掉,就只有登录用户能下单。 - **广告到期需要手动下架吗?**不需要,过期状态会在渲染前自动降级为可购买。 - **能改格子配色吗?**可以,格子配色由插件运行时样式控制,在站点全局自定义 CSS 中覆盖对应的颜色变量即可生效,不需要改插件包。 ## 小结 「广告文字墙」把一件原本很零碎的生意做成了自助流程:配一次价格,之后每卖出一格就是净收入,售出、收款、上架、到期回收都不需要人工介入。对流量不大但稳定的中小站点来说,这是把首页某块位置变成现金流的最省事的方式。 本文介绍的插件为 8号码库原创,目前已在 8maoku.com 首页实际运行,可按自身站点情况调整网格与定价后再投入使用。 ![](https://www.q0di.top/wp-content/uploads/2026/09/QQ_1789865666106.png) /\* ============================================================================= \* 源码安全检测报告 · 展示样式 \* 说明:本文件同时供两处使用—— \* 1. 客户端运行时插件组件(node/client/components/0/index.js) \* 2. WordPress 服务端正文注入(wordpress/plugin.php 内联打印) \* 因此类名与结构必须保持一致,改这里就等于同时改两边。 \* 尺寸统一用 em,随模块缩放设置联动;颜色优先取 element-plus / 瓜奇主题变量, \* 并带固定色兜底,保证脱离主题也不花屏。不使用 color-mix 等新特性。 \* ========================================================================== \*/ .gqssr { --gqssr-primary: var(--el-color-primary, #00c29a); --gqssr-high: #e5484d; --gqssr-medium: #d98a00; --gqssr-low: #3b82f6; --gqssr-info: #8b8fa3; --gqssr-clean: #16a34a; --gqssr-line: var(--el-border-color, #e4e7ed); --gqssr-fg: var(--el-text-color-primary, #1f2329); --gqssr-fg-2: var(--el-text-color-regular, #4e5969); --gqssr-fg-3: var(--el-text-color-secondary, #86909c); --gqssr-bg: var(--el-bg-color, #ffffff); --gqssr-bg-2: var(--el-fill-color-light, #f7f8fa); --gqssr-code-bg: var(--el-fill-color, #f0f1f3); --gqssr-mono: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, "Liberation Mono", monospace; box-sizing: border-box; display: flex; flex-direction: column; gap: 1em; width: 100%; max-width: 100%; min-width: 0; font-size: 1em; line-height: 1.6; color: var(--gqssr-fg); } .gqssr \*, .gqssr \*::before, .gqssr \*::after { box-sizing: border-box; } /\* ---------------------------------------------------------------- 头部 \*/ .gqssr-head { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.75em 1em; padding-bottom: 0.875em; border-bottom: 1px solid var(--gqssr-line); } .gqssr-head-main { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5em; flex: 1 1 16em; min-width: 0; } .gqssr-title { margin: 0; padding: 0; border: 0; font-size: 1.0625em; font-weight: 600; line-height: 1.4; color: var(--gqssr-fg); } .gqssr-badge { display: inline-flex; align-items: center; flex: 0 0 auto; padding: 0.125em 0.5em; border-radius: 0.375em; font-size: 0.75em; font-weight: 600; line-height: 1.7; white-space: nowrap; color: #fff; background: var(--gqssr-info); } .gqssr-badge--high { background: var(--gqssr-high); } .gqssr-badge--medium { background: #d98a00; color: #fff; } .gqssr-badge--low { background: var(--gqssr-low); } .gqssr-badge--info { background: var(--gqssr-info); } .gqssr-badge--clean { background: var(--gqssr-clean); } .gqssr-chip { display: inline-flex; align-items: center; gap: 0.25em; flex: 0 0 auto; padding: 0.125em 0.5em; border-radius: 0.375em; font-size: 0.75em; font-weight: 600; line-height: 1.7; color: var(--gqssr-primary); background: var(--el-color-primary-light-9, rgba(0, 194, 154, 0.12)); border: 1px solid var(--el-color-primary-light-7, rgba(0, 194, 154, 0.34)); } .gqssr-meta { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 0.375em 1em; font-size: 0.8125em; color: var(--gqssr-fg-3); min-width: 0; } .gqssr-meta span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; } .gqssr-meta b { font-weight: 600; color: var(--gqssr-fg-2); } /\* ---------------------------------------------------------------- 评分环 \*/ .gqssr-score { position: relative; flex: 0 0 auto; width: 4.5em; height: 4.5em; } .gqssr-score svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); } .gqssr-score .gqssr-ring-bg { fill: none; stroke: var(--el-fill-color, #eff0f1); stroke-width: 7; } .gqssr-score .gqssr-ring-fg { fill: none; stroke: var(--gqssr-info); stroke-width: 7; stroke-linecap: round; } .gqssr-score-inner { position: absolute; top: 0; right: 0; bottom: 0; left: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; line-height: 1; } .gqssr-score-num { font-size: 1.375em; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--gqssr-fg); } .gqssr-score-unit { margin-top: 0.125em; font-size: 0.625em; color: var(--gqssr-fg-3); } .gqssr\[data-level="high"\] .gqssr-ring-fg { stroke: var(--gqssr-high); } .gqssr\[data-level="medium"\] .gqssr-ring-fg { stroke: var(--gqssr-medium); } .gqssr\[data-level="low"\] .gqssr-ring-fg { stroke: var(--gqssr-low); } .gqssr\[data-level="info"\] .gqssr-ring-fg { stroke: var(--gqssr-info); } .gqssr\[data-level="clean"\] .gqssr-ring-fg { stroke: var(--gqssr-clean); } /\* ---------------------------------------------------------------- 概览统计 \*/ .gqssr-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.5em; } .gqssr-stat { display: flex; flex-direction: column; align-items: center; gap: 0.125em; padding: 0.625em 0.25em; border-radius: 0.5em; background: var(--gqssr-bg-2); border: 1px solid var(--gqssr-line); min-width: 0; } .gqssr-stat-num { font-size: 1.125em; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; } .gqssr-stat-label { font-size: 0.75em; color: var(--gqssr-fg-3); white-space: nowrap; } .gqssr-stat--high { border-color: rgba(229, 72, 77, 0.32); } .gqssr-stat--high .gqssr-stat-num { color: var(--gqssr-high); } .gqssr-stat--medium { border-color: rgba(217, 138, 0, 0.34); } .gqssr-stat--medium .gqssr-stat-num { color: var(--gqssr-medium); } .gqssr-stat--low { border-color: rgba(59, 130, 246, 0.32); } .gqssr-stat--low .gqssr-stat-num { color: var(--gqssr-low); } .gqssr-stat--info .gqssr-stat-num { color: var(--gqssr-info); } .gqssr-struct { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375em 0.75em; font-size: 0.8125em; color: var(--gqssr-fg-3); } .gqssr-struct b { font-weight: 600; color: var(--gqssr-fg-2); } .gqssr-struct-label { font-weight: 600; color: var(--gqssr-fg-2); } /\* ---------------------------------------------------------------- 命中明细 \*/ .gqssr-findings { display: flex; flex-direction: column; gap: 0.5em; min-width: 0; } .gqssr-findings-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25em 0.625em; font-size: 0.875em; font-weight: 600; color: var(--gqssr-fg); } .gqssr-findings-head em { font-style: normal; font-weight: 400; font-size: 0.875em; color: var(--gqssr-fg-3); } /\* 区块标题:命中明细与后门特征排查共用(.gqssr-findings-head 是旧类名,保留兼容) \*/ .gqssr-sec-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25em 0.625em; font-size: 0.875em; font-weight: 600; color: var(--gqssr-fg); } .gqssr-sec-head em { font-style: normal; font-weight: 400; font-size: 0.875em; color: var(--gqssr-fg-3); } /\* ---------------------------------------------------------------- 按类别 \*/ /\* 把「风险项(参与评分)」与「加固建议 / 来源情报(不扣分)」分开摆, 报告读起来才是客观的 —— 后者不是缺陷,混在一起会让人以为问题很多。 \*/ .gqssr-cats { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375em 0.625em; font-size: 0.8125em; } .gqssr-cats-label { font-weight: 600; color: var(--gqssr-fg-2); } .gqssr-cats-list { display: flex; flex-wrap: wrap; gap: 0.375em; min-width: 0; } .gqssr-cat { display: inline-flex; align-items: baseline; gap: 0.25em; padding: 0.125em 0.5em; border-radius: 0.375em; background: var(--gqssr-bg-2); border: 1px solid var(--gqssr-line); color: var(--gqssr-fg-2); white-space: nowrap; } .gqssr-cat b { font-weight: 700; font-variant-numeric: tabular-nums; } .gqssr-cat i { font-style: normal; font-size: 0.875em; color: var(--gqssr-fg-3); } .gqssr-cat--risk { border-color: rgba(229, 72, 77, 0.32); } .gqssr-cat--risk b { color: var(--gqssr-high); } .gqssr-cat--harden { border-color: rgba(217, 138, 0, 0.34); } .gqssr-cat--harden b { color: var(--gqssr-medium); } .gqssr-cat--intel { border-color: rgba(139, 143, 163, 0.34); } .gqssr-cat--intel b { color: var(--gqssr-info); } .gqssr-cat-groups { display: flex; flex-direction: column; gap: 0.75em; min-width: 0; } .gqssr-cat-group { display: flex; flex-direction: column; gap: 0.375em; min-width: 0; } .gqssr-cat-group-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25em 0.5em; font-size: 0.8125em; font-weight: 600; color: var(--gqssr-fg-2); } .gqssr-cat-group-head i { font-style: normal; font-weight: 400; font-size: 0.875em; color: var(--gqssr-fg-3); } .gqssr-cat-group--risk .gqssr-cat-group-head span { color: var(--gqssr-high); } .gqssr-cat-group--harden .gqssr-cat-group-head span { color: var(--gqssr-medium); } .gqssr-cat-group--intel .gqssr-cat-group-head span { color: var(--gqssr-info); } /\* ---------------------------------------------------------------- 后门特征排查 \*/ /\* 未命中项也要逐条写出来:只列命中项的报告,读的人无法分辨 是「查过没发现」还是「压根没查」。 \*/ .gqssr-backdoor { display: flex; flex-direction: column; gap: 0.5em; min-width: 0; } .gqssr-bd-list { margin: 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.375em 0.625em; } .gqssr-bd-item { display: flex; align-items: center; gap: 0.375em; padding: 0.375em 0.5em; border-radius: 0.375em; background: var(--gqssr-bg-2); border: 1px solid transparent; font-size: 0.8125em; min-width: 0; } .gqssr-bd-item--clear { border-color: rgba(22, 163, 74, 0.22); } .gqssr-bd-item--hit { border-color: rgba(229, 72, 77, 0.38); background: rgba(229, 72, 77, 0.07); } .gqssr-bd-mark { flex: 0 0 auto; width: 1.125em; height: 1.125em; fill: none; stroke: var(--gqssr-clean); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; } .gqssr-bd-mark path:first-child { fill: rgba(22, 163, 74, 0.14); } .gqssr-bd-item--hit .gqssr-bd-mark { stroke: var(--gqssr-high); } .gqssr-bd-item--hit .gqssr-bd-mark path:first-child { fill: rgba(229, 72, 77, 0.14); } .gqssr-bd-title { flex: 1 1 auto; min-width: 0; color: var(--gqssr-fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .gqssr-bd-state { flex: 0 0 auto; font-style: normal; font-size: 0.875em; color: var(--gqssr-clean); } .gqssr-bd-item--hit .gqssr-bd-state { color: var(--gqssr-high); font-weight: 600; } /\* 评分口径说明 \*/ .gqssr-basis { margin: 0; font-size: 0.75em; line-height: 1.6; color: var(--gqssr-fg-3); } .gqssr-hit-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 0.5em; } .gqssr-hit { position: relative; margin: 0; padding: 0.625em 0.75em 0.625em 1.125em; border-radius: 0.5em; background: var(--gqssr-bg-2); list-style: none; min-width: 0; overflow: hidden; } .gqssr-hit::before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 3px; background: var(--gqssr-info); } .gqssr-hit--high::before { background: var(--gqssr-high); } .gqssr-hit--medium::before { background: var(--gqssr-medium); } .gqssr-hit--low::before { background: var(--gqssr-low); } .gqssr-hit--info::before { background: var(--gqssr-info); } .gqssr-hit-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25em 0.5em; min-width: 0; } .gqssr-hit-head strong { font-size: 0.875em; font-weight: 600; color: var(--gqssr-fg); } .gqssr-hit-head code { font-family: var(--gqssr-mono); font-size: 0.75em; padding: 0.0625em 0.375em; border-radius: 0.25em; color: var(--gqssr-fg-2); background: var(--gqssr-code-bg); word-break: break-all; } .gqssr-tag { font-size: 0.6875em; font-weight: 600; padding: 0.0625em 0.375em; border-radius: 0.25em; color: var(--gqssr-high); background: rgba(229, 72, 77, 0.12); } .gqssr-hit-desc { margin: 0.25em 0 0; font-size: 0.8125em; color: var(--gqssr-fg-2); } .gqssr-hit-code { display: block; margin: 0.375em 0 0; padding: 0.375em 0.5em; border-radius: 0.375em; background: var(--gqssr-code-bg); font-family: var(--gqssr-mono); font-size: 0.75em; line-height: 1.5; color: var(--gqssr-fg-2); white-space: pre-wrap; word-break: break-all; overflow-x: auto; max-width: 100%; } .gqssr-hit-fix { margin: 0.375em 0 0; font-size: 0.8125em; color: var(--gqssr-fg-2); } .gqssr-hit-fix b { font-weight: 600; color: var(--gqssr-primary); } /\* ---------------------------------------------------------------- 页脚 \*/ .gqssr-foot { font-size: 0.75em; line-height: 1.6; color: var(--gqssr-fg-3); padding-top: 0.75em; border-top: 1px dashed var(--gqssr-line); } /\* ---------------------------------------------------------------- 窄屏 \*/ @media (max-width: 767px) { .gqssr-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); } .gqssr-head { flex-direction: column; } .gqssr-score { align-self: flex-start; } /\* 窄屏下后门排查改成单列,8 项不至于挤成两行小字 \*/ .gqssr-bd-list { grid-template-columns: minmax(0, 1fr); } } 低风险 ### 源码安全检测报告 已检测 93/100 检测引擎 **srcscan/1.4**报告编号 **SC-20260922-9CFAC8**检测时间 **2026-09-22 09:38:15**内容发布 **2026-09-20 08:49:33**瓜奇主题文字格子广告插件8码库原创 0高危 1中危 0低危 0提示 结构概览文件 **8**体积 **461.7 KB**代码文件 **3**代码行 **13,346** 按类别来源情报 **1**_不扣分_ 后门特征排查_8 项特征全部未发现_ - 请求参数直接进入执行函数_未发现_ - 请求参数直接进入文件包含_未发现_ - 变量函数执行请求参数_未发现_ - 请求参数批量覆盖变量_未发现_ - 编码载荷后执行(混淆特征)_未发现_ - 回连裸 IP 地址_未发现_ - 配置预加载文件(隐蔽后门手法)_未发现_ - 向文件写入 PHP 代码_未发现_ 命中明细_共 1 项_ 来源情报_不扣分_ - **DEBUG\_CHANNEL\_EXPOSED**`wordpress\plugin.php:522` ``` $runtime['pay_trace'] = get_option(self::TRACE_OPTION, []); ``` 本报告由 8号码库源码安全检测引擎(srcscan)静态扫描生成,用于上架前风险筛查,不构成安全保证。 **Tags:** 广告位出租, 文字广告墙, 瓜奇插件 **Categories:** 插件模块 ---