feat(pages): 认证页与门户页文案可配置,不再写死

需求:登录页与门户页的内容应可自定义。

实现(配置存数据卷、后台可改、留空回落内置默认,不配置行为不变):
- pages.go:pageStr 读取 pageInfo;renderPortalPage 优先数据卷
  <data>/portal/index.html 整页覆盖,否则用内置模板替换占位符;
  authPageInfo 提供登录页字段
- auth.go:登录页标题/副标题/徽标/页脚改为模板数据(含备案号)
- gateway.go:kind=portal 的首页改由 admin-server /portal-page 渲染
  (静态资源仍由 nginx 提供),使文案可配置
- portal/index.html:加入 <!--PAGE_TITLE--> / <!--HERO_TITLE--> /
  <!--HERO_SUBTITLE--> / <!--FOOTER--> 占位符
- 后台「站点信息」新增六项文案输入(含回填与保存),并提示整页覆盖方式
- README 新增「认证页与门户页文案(可配置,不写死)」含字段表与自检命令

测试:默认回落、配置取值去空白、认证页字段读取、数据卷覆盖优先、空白覆盖忽略。
This commit is contained in:
HomeDash Bot
2026-09-12 09:12:13 +08:00
parent 352aa570ec
commit 472d8d1cd0
7 changed files with 248 additions and 11 deletions

View File

@ -168,6 +168,20 @@ html[data-theme="dark"] .toast{ box-shadow:0 10px 30px rgba(0,0,0,.5) }
<div class="fg"><label>ICP 备案号</label><input id="p-icp" placeholder="京ICP备00000000号-1"></div>
<div class="fg"><label>公安备案号(可选)</label><input id="p-gongan" placeholder="京公网安备00000000000000号"></div>
</div>
<details style="margin:12px 0">
<summary style="cursor:pointer;font-size:13px;color:#475569">认证页与门户页文案(留空=用内置默认)</summary>
<div class="row" style="margin-top:8px">
<div class="fg"><label>登录页标题</label><input id="p-auth-title" placeholder="统一门户"></div>
<div class="fg"><label>登录页副标题</label><input id="p-auth-sub" placeholder="登录后访问受保护的内部服务"></div>
<div class="fg" style="max-width:160px"><label>登录页徽标</label><input id="p-auth-logo" placeholder="JF"></div>
</div>
<div class="row">
<div class="fg"><label>门户页浏览器标题</label><input id="p-portal-title" placeholder="JianF Git - 个人开发环境"></div>
<div class="fg"><label>门户页主标题</label><input id="p-hero-title" placeholder="代码、博客与灵感之站"></div>
<div class="fg"><label>门户页副标题</label><input id="p-hero-sub" placeholder="JianF Git · 代码托管 · 技术博客"></div>
</div>
<p class="hint" style="margin:6px 0 0">整页完全自定义:把 <code>index.html</code> 放进数据卷的 <code>portal/</code> 目录即生效(优先于以上字段)。</p>
</details>
<details style="margin:12px 0">
<summary style="cursor:pointer;font-size:13px;color:#475569">高级:自定义页脚 HTML</summary>
<div class="fg" style="margin-top:8px">
@ -468,6 +482,12 @@ async function loadPageInfo() {
document.getElementById('p-desc').value = PAGE.description || '';
const icp = PAGE.icp || parseIcp(PAGE.footer);
const gongan = PAGE.gongan || parseGongan(PAGE.footer);
const setIf = (id, v) => { const el = document.getElementById(id); if (el) el.value = v || ''; };
setIf('p-auth-title', PAGE.authTitle); setIf('p-auth-sub', PAGE.authSubtitle); setIf('p-auth-logo', PAGE.authLogo);
setIf('p-portal-title', PAGE.portalTitle); setIf('p-hero-title', PAGE.heroTitle); setIf('p-hero-sub', PAGE.heroSubtitle);
document.getElementById('p-icp').value = icp;
document.getElementById('p-gongan').value = gongan;
// 能从备案号字段还原时,高级 HTML 留空,保存时按模板重新生成
@ -490,6 +510,18 @@ document.getElementById('save-info').addEventListener('click', async () => {
gongan: document.getElementById('p-gongan').value,
// 留空 = 交给服务端按备案号生成模板;填写则整段覆盖
footer: document.getElementById('p-footer').value,
authTitle: document.getElementById('p-auth-title').value.trim(),
authSubtitle: document.getElementById('p-auth-sub').value.trim(),
authLogo: document.getElementById('p-auth-logo').value.trim(),
portalTitle: document.getElementById('p-portal-title').value.trim(),
heroTitle: document.getElementById('p-hero-title').value.trim(),
heroSubtitle: document.getElementById('p-hero-sub').value.trim(),
navLinks: (PAGE.navLinks || []).filter(l => l && (l.name || l.url)),
};
const r = await fetch(A + '/pageinfo', {