文章详情

幻影二级域名分发系统 V6 门户模板开发文档

文档版本:6.0.5
适用范围:系统内置 Vue 门户、可安装的 portal 静态模板、门户首页、文章详情页和开放 API 文档页


1. 门户模板运行方式

系统支持内置模板和外置模板,两者对访客使用相同的公开地址:

页面 公开地址 是否需要登录
门户首页 / 否
首页兼容地址 /home 否,外置模板会统一回到 /
开放 API 文档 /portal/api-docs 否
文章详情 /portal/article/{id} 否
用户登录 /user/login 否
用户注册 /user/register 否
后台登录 /admin/login 否

外置模板文件虽然存放在:

/portal-templates/{plugin_code}/

但这个地址只是系统内部的静态资源目录,不是门户公开地址。模板代码、导航菜单、文章链接和对外文档中都不要展示或跳转到该目录。

1.1 模板选择流程

门户入口通过以下公开接口获取当前模板:

GET /api/v1/portal/template

内置模板返回示例:

{
“code”: 200,
“message”: “获取门户模板成功”,
“data”: {
“code”: “builtin”,
“name”: “默认门户模板”,
“mode”: “builtin”,
“entry_url”: “”
},
“request_id”: “…”
}

外置模板返回示例:

{
“code”: 200,
“message”: “获取门户模板成功”,
“data”: {
“code”: “portal-bento”,
“name”: “portal-bento”,
“mode”: “external”,
“entry_url”: “/portal-templates/portal-bento/index.html”
},
“request_id”: “…”
}

字段说明:

字段 取值 说明
code builtin 或插件编号 当前模板唯一编号
name 模板名称 用于加载提示和 iframe 标题
mode builtin / external builtin 加载系统 Vue 组件,external 加载插件入口
entry_url 空或模板入口 系统内部使用,模板自身不要再次跳转到该地址

1.2 外置模板加载机制

当前系统按以下顺序加载外置模板:

  1. 后端读取 portal.active_template 配置。
  2. 检查 plugins/portal/{plugin_code}/index.html 是否存在。
  3. 访问 /、/portal/api-docs 或 /portal/article/{id} 时,后端优先返回当前模板的 index.html。
  4. 后端把入口 HTML 中的 ./assets/... 和 assets/... 自动映射到 /portal-templates/{plugin_code}/assets/...。
  5. 浏览器地址保持公开页面地址,不再跳转到模板存储目录。
  6. 如果部署环境由 Nginx 直接提供主前端,主前端会在当前页面内兼容加载外置模板,浏览器顶层地址仍保持不变。

因此模板绝对不能执行下面的代码:

// 错误:会再次暴露模板存储地址
window.location.href = ‘/portal-templates/portal-bento/index.html’;

2. 模板目录和清单

最小模板结构:

portal-template/
├── manifest.json
├── index.html
└── assets/
├── app.js
├── app.css
└── images/

manifest.json 示例:

{
“plugin_code”: “portal-bento”,
“plugin_type”: “portal”,
“name”: “Bento 网格门户模板”,
“version”: “1.0.0”,
“description”: “Bento 网格风格企业门户模板”,
“author”: “幻影二级域名分发系统”,
“runtime”: “static”,
“entry”: “index.html”,
“minimum_system_version”: “6.0.5”,
“capabilities”: [
“site_info”,
“announcements”,
“article_categories”,
“articles”
]
}

清单字段说明:

字段 必填 固定值或格式 说明
plugin_code 是 小写字母、数字、-、_ 模板唯一编号,不能与其他 portal 模板重复
plugin_type 是 portal 必须是门户插件类型
name 是 字符串 后台展示名称
version 是 如 1.0.0 每次发布升级版本号
description 建议 字符串 模板说明
author 建议 字符串 作者或厂商
runtime 是 static 当前门户模板运行方式
entry 是 index.html 模板入口文件
minimum_system_version 建议 如 6.0.5 最低兼容系统版本
capabilities 建议 字符串数组 声明模板使用的公开能力

压缩包根目录必须直接出现 manifest.json 和 index.html,不要在 ZIP 最外层再套一层同名文件夹。


3. 通用接口约定

门户只调用公开接口,统一使用相对地址:

fetch(‘/api/v1/site/home’);

禁止写死开发地址或线上域名:

// 错误
fetch(‘http://127.0.0.1:8080/api/v1/site/home’);
fetch(‘https://某个固定域名/api/v1/site/home’);

所有接口使用统一响应结构:

{
“code”: 200,
“message”: “请求成功”,
“data”: {},
“request_id”: “20260916T011544.511616700Z”
}

判断成功时兼容 code === 0 和 code === 200:

async function request(path) {
const response = await fetch(`/api/v1${path}`, {
headers: { Accept: ‘application/json’ }
});
const result = await response.json();
if (result.code !== 0 && result.code !== 200) {
throw new Error(result.message || ‘请求失败’);
}
return result.data;
}

门户不得读取或保存用户 Token、管理员 Token、API 秘钥、数据库账号和插件私密配置。


4. 全站公共配置

三个门户页面都必须请求:

GET /api/v1/site/home

完整返回示例:

{
“code”: 200,
“message”: “获取站点信息成功”,
“data”: {
“name”: “幻影二级域名分发系统”,
“title”: “幻影DNS”,
“keywords”: “二级域名,域名解析”,
“description”: “稳定、清晰、可追踪的二级域名解析服务”,
“announcement”: “欢迎使用幻影二级域名分发系统”,
“logo”: “/uploads/site/logo.png”,
“ico”: “/uploads/site/favicon.ico”,
“footer”: “幻影科技版权所有”,
“icp_number”: “浙ICP备00000000号”,
“police_number”: “浙公网安备00000000000000号”
},
“request_id”: “…”
}

字段与页面位置:

后端字段 页面使用位置 空值处理
name 顶部品牌名称、页脚品牌名称 回退“幻影二级域名分发系统”
title 浏览器标题基础文字 回退 name
keywords <meta name="keywords"> 空值时删除该 meta
description Hero 描述、页脚说明、SEO 描述 空值时使用模板自己的简短默认文案
announcement 首页短提示文字 空值时隐藏,不占位
logo 顶部和页脚 Logo 空值时显示文字 Logo 或模板默认图标
ico <link rel="icon"> 空值时保留系统默认 favicon
footer 页脚版权主体 回退 name
icp_number ICP 备案链接文字 空值时整项隐藏
police_number 公安备案链接文字 空值时整项隐藏

建议封装:

const site = await request(‘/site/home’);
const siteName = site.name || ‘幻影二级域名分发系统’;
const baseTitle = site.title || siteName;

document.title = baseTitle;
document.querySelector(‘[data-site-name]’).textContent = siteName;


5. 首页 /

5.1 首页数据对应关系

页面区域 数据来源 使用字段
顶部导航 /site/home name、logo
Hero /site/home name、description、announcement
公告 /announcements?position=home 公告字段
文章分类 /article-categories id、name、sort
文章卡片 /articles id、category_id、category_name、title、summary、时间字段
页脚 /site/home footer、description、icp_number、police_number

功能介绍、产品优势、FAQ 和开发流程属于模板展示内容,可以由模板提供;网站名称、Logo、文章、公告、版权和备案信息不能写死。

5.2 公告接口

GET /api/v1/announcements?position=home

返回列表字段:

字段 类型 说明
id number 公告编号
title string 公告标题
summary string 公告摘要
content string 公告 HTML 内容
type string 公告类型
positions string[] 展示位置,首页应包含 home
display_mode string modal 表示弹窗,其他值按模板样式展示
sort_weight number 排序权重,接口已经按权重排序
allow_close boolean 是否允许关闭
display_once boolean 是否只展示一次
repeat_interval number 再次展示间隔,单位分钟
start_time string? 生效时间
end_time string? 结束时间
published_at string? 发布时间
created_at string 创建时间

接口只返回已发布且处于有效时间范围内的公告。模板不需要再次判断后台状态,但应按 display_mode、allow_close 和重复展示规则处理。

5.3 文章分类接口

GET /api/v1/article-categories

返回示例:

{
“code”: 200,
“data”: {
“list”: [
{ “id”: 1, “name”: “官方通知”, “sort”: 0 }
]
}
}

分类选择器必须以 id 作为筛选值,以 name 作为显示文字。模板可以在前端额外增加“全部分类”,但不要把“全部分类”提交成真实分类编号。

5.4 文章列表接口

全部文章:

GET /api/v1/articles

按分类筛选:

GET /api/v1/articles?category_id=1

文章列表字段:

字段 类型 卡片用途
id number 文章详情编号和列表 key
category_id number? 当前分类筛选
category_name string? 分类标签
title string 卡片标题
summary string 卡片摘要
published_at string? 优先显示的发布时间
created_at string 没有发布时间时的回退时间

列表接口不返回正文 content。点击文章后必须再调用详情接口,不能把 summary 当作正文。

文章点击支持两种方式:

// 独立详情页,推荐
window.top.location.href = `/portal/article/${article.id}`;

// 首页弹窗
const detail = await request(`/articles/${article.id}`);
openArticleDialog(detail);

如果使用弹窗,必须提供关闭按钮、正文滚动区域、加载状态和请求失败提示。

5.5 首页固定链接

按钮 地址
登录 /user/login
免费注册 /user/register
API 文档 /portal/api-docs
创建 API 秘钥 /user/api-keys
提交工单 /user/workorder

外置模板建议使用普通链接并指定顶层窗口:

<a href=”/user/login” target=”_top”>登录</a>
<a href=”/user/register” target=”_top”>免费注册</a>
<a href=”/portal/api-docs” target=”_top”>API 文档</a>

这样在后端直出模式和前端兼容承载模式下都能正确离开模板页面。


6. 文章详情页 /portal/article/{id}

{id} 是文章列表返回的 id,不是分类编号。

请求接口:

GET /api/v1/articles/{id}

详情字段:

字段 类型 页面位置
id number 当前文章编号
category_id number? 分类跳转或筛选
category_name string? 标题上方分类标签
title string 页面 H1 和浏览器标题
summary string 标题下方摘要,可为空
content string 文章 HTML 正文
published_at string? 优先展示的发布时间
created_at string 发布时间回退值
updated_at string 最后更新时间

浏览器标题格式:

{文章标题} – {site.title 或 site.name}

Vue Router 获取编号示例:

const articleId = Number(route.params.id);
if (!Number.isInteger(articleId) || articleId <= 0) {
throw new Error(‘文章编号不正确’);
}
const article = await request(`/articles/${articleId}`);

原生 JS 获取编号示例:

const portalPath = normalizePortalLocation();
const match = portalPath.match(/^\/portal\/article\/(\d+)$/);
const articleId = match ? Number(match[1]) : 0;

正文可以使用 innerHTML 或 Vue v-html 渲染,但模板自身不得拼接脚本,也不得执行正文中的任意 JavaScript。正文区域至少需要适配标题、段落、列表、引用、代码块、表格和响应式图片。

错误状态:

情况 页面处理
ID 不是正整数 显示“文章编号不正确”并提供返回首页
接口返回 404 显示“文章不存在或已下线”
接口返回其他错误 显示接口 message 和重试按钮

7. 开放 API 文档页 /portal/api-docs

API 文档页使用 /site/home 的以下字段:

字段 用途
name 顶部品牌
title 浏览器标题后缀
description 文档页副标题和页脚说明
logo 顶部 Logo
footer 页脚版权
icp_number ICP 备案
police_number 公安备案

浏览器标题格式:

开放 API 文档 – {site.title 或 site.name}

接口基础地址必须从当前访问站点生成:

const apiBaseUrl = `${window.location.origin}/api/v1`;

页面应展示以下开放接口:

方法 地址 功能
GET /api/v1/open/user 获取 API 秘钥所属用户、余额和会员信息
GET /api/v1/open/domains 获取当前用户可使用的域名
GET /api/v1/open/domains/{domain_id}/lines 获取指定域名的解析线路
GET /api/v1/open/records 获取解析记录列表
POST /api/v1/open/records 添加解析记录
PUT /api/v1/open/records/{record_id} 修改解析记录
DELETE /api/v1/open/records/{record_id} 删除解析记录

认证请求头示例:

X-API-Key: phk_your_api_key
Idempotency-Key: unique-request-id

文档示例只允许使用 phk_your_api_key,不能出现真实秘钥。API 文档页面只负责展示说明,不应自动请求需要秘钥的 /open/* 接口。


8. 路由兼容规范

外置模板必须能够识别三种公开路径:

/
/portal/api-docs
/portal/article/{id}

为兼容统一后端直出和旧部署方式,推荐在模板启动前规范化路径:

function normalizePortalLocation() {
const hashPath = window.location.hash.startsWith(‘#/’)
? window.location.hash.slice(1)
: ”;
const storageEntry = /^\/portal-templates\/[^/]+\/(?:index\.html)?$/;

if (hashPath) {
window.history.replaceState(null, ”, hashPath);
return hashPath;
}
if (storageEntry.test(window.location.pathname)) {
window.history.replaceState(null, ”, ‘/’);
return ‘/’;
}
return window.location.pathname;
}

const initialPortalPath = normalizePortalLocation();

Vue 3 路由示例:

import { createRouter, createWebHistory } from ‘vue-router’;

normalizePortalLocation();

const router = createRouter({
history: createWebHistory(),
routes: [
{ path: ‘/’, component: HomePage },
{ path: ‘/portal/api-docs’, component: ApiDocsPage },
{ path: ‘/portal/article/:id’, component: ArticlePage }
]
});

不要使用模板目录作为 Router base,也不要把下面的地址注册成公开路由:

/portal-templates/portal-bento/
/portal-templates/portal-bento/#/

模板内部进入用户中心、后台、登录、注册和门户公开页面时,使用绝对路径和顶层跳转;页面内锚点滚动可以留在模板内部。


9. 静态资源与构建配置

HTML 中推荐使用相对资源:

<script type=”module” src=”./assets/app.js”></script>
<link rel=”stylesheet” href=”./assets/app.css”>
<img src=”./assets/images/hero.webp” alt=””>

系统从根路径提供模板入口时,会把这些地址映射到当前模板自己的资源目录。

Vite 模板必须设置相对资源基础路径:

import { defineConfig } from ‘vite’;
import vue from ‘@vitejs/plugin-vue’;

export default defineConfig({
base: ‘./’,
plugins: [vue()],
build: {
outDir: ‘dist’,
emptyOutDir: true
}
});

禁止:

  • 把资源写成开发机绝对路径。
  • 写死 /assets/app.js,这可能与主程序资源重名。
  • 引用 node_modules 中未打包的文件。
  • 依赖开发服务器才能加载的模块。
  • 把 sourcemap、缓存目录、源码依赖和整个主系统前端打入模板包。

10. 标题、SEO 和 favicon

三个页面加载站点配置后分别设置标题:

const baseTitle = site.title || site.name || ‘幻影二级域名分发系统’;

// 首页
document.title = baseTitle;

// API 文档
document.title = `开放 API 文档 – ${baseTitle}`;

// 文章详情
document.title = `${article.title} – ${baseTitle}`;

更新 SEO:

function updateMeta(name, content) {
let element = document.querySelector(`meta[name=”${name}”]`);
if (!content) {
element?.remove();
return;
}
if (!element) {
element = document.createElement(‘meta’);
element.setAttribute(‘name’, name);
document.head.appendChild(element);
}
element.setAttribute(‘content’, content);
}

updateMeta(‘keywords’, site.keywords);
updateMeta(‘description’, site.description);

favicon 使用 site.ico,Logo 使用 site.logo。图片地址可能是绝对 URL,也可能是 /uploads/... 相对站点地址,模板不要自行拼接固定域名。


11. 加载、空数据和错误状态

每个动态区域都必须分别处理:

状态 要求
加载中 显示稳定尺寸的骨架或加载提示,不让布局跳动
空数据 分类为空、文章为空、公告为空时正常隐藏或显示空状态
请求失败 显示简短错误信息和重试按钮,不让整个页面白屏
图片失败 Logo 回退文字品牌,内容图片保持占位
文章不存在 显示返回首页按钮

站点配置接口失败时可以使用模板默认品牌文字继续显示;文章详情接口失败时不能伪造文章内容。


12. 安全限制

  • 只调用 /api/v1/site/home、文章、分类、公告等公开接口。
  • 不调用 /api/v1/admin/*。
  • 不读取 localStorage 中的登录 Token。
  • 不在模板中保存 API 秘钥、AppSecret、数据库信息和授权密钥。
  • 不在浏览器中实现扣费、权限判断、实名认证或违规检测业务。
  • 不执行文章和公告内容中的 <script>。
  • 外部链接使用 rel="noopener noreferrer"。
  • 所有用户输入和 URL 参数在展示前进行类型检查。

13. 本地开发和打包

开发阶段可以独立启动模板,也可以放入系统插件目录测试。发布前目录应整理为:

dist/
├── manifest.json
├── index.html
└── assets/

打包 ZIP 时压缩 dist 内部内容,而不是压缩 dist 文件夹本身。

安装后的文件位置:

plugins/portal/{plugin_code}/manifest.json
plugins/portal/{plugin_code}/index.html
plugins/portal/{plugin_code}/assets/…

启用模板后依次验证:

/
/portal/api-docs
/portal/article/一个真实文章ID
/user/login
/user/register

浏览器地址不得出现:

/portal-templates/{plugin_code}/
/portal-templates/{plugin_code}/#/

14. 发布检查清单

  • ☐ ZIP 根目录直接包含 manifest.json 和 index.html。
  • ☐ plugin_type 为 portal,runtime 为 static。
  • ☐ 首页、API 文档和文章详情三个公开地址都能直接刷新。
  • ☐ 浏览器顶层地址不会跳转到 /portal-templates/。
  • ☐ JS、CSS、图片均从当前模板目录加载,没有 404 和 MIME 错误。
  • ☐ 登录、注册、用户中心链接使用绝对路径并跳转顶层窗口。
  • ☐ 网站名称、标题、描述、Logo、favicon 来自 /site/home。
  • ☐ 页脚版权、ICP 备案和公安备案来自 /site/home。
  • ☐ 文章分类来自 /article-categories,没有写死后台分类。
  • ☐ 文章列表不把摘要当正文,详情通过 /articles/{id} 获取。
  • ☐ 公告使用 position=home,并处理展示方式和关闭规则。
  • ☐ API 文档没有真实秘钥和敏感信息。
  • ☐ 请求失败、空数据和文章不存在时不会白屏。
  • ☐ 桌面端、平板和移动端没有横向溢出或文字遮挡。
  • ☐ 发布包不包含 node_modules、缓存、源码和主系统无关文件。

15. 系统内对应实现位置

功能 文件
门户入口和兼容承载 ele-admin-plus-ts/src/views/portal/entry.vue
门户公开路由 ele-admin-plus-ts/src/router/routes.ts
模板选择接口 backend/cmd/server/portal_template.go
根路径模板分发 backend/cmd/server/install.go
站点配置接口 backend/cmd/server/site.go
文章、分类和公告接口 backend/cmd/server/content.go
内置门户首页 ele-admin-plus-ts/src/views/portal/index.vue
内置文章详情 ele-admin-plus-ts/src/views/portal/article/index.vue
内置 API 文档 ele-admin-plus-ts/src/views/portal/api-docs/index.vue
Bento 模板源码参考 首页门户模板/portal-bento/
赏

微信扫一扫

支付宝扫一扫

版权:

转载请注明出处:https://www.52hyjs.com/317.html

相关推荐
腾讯云微信H5人脸验证开通教程
开通地址:自助接入 - 人脸核身 - 控制台 1.点击创建业务流程 2.应用场景 选择微信H5  项目名称自己起  使用渠道 移动端  外部…
幻影二级域名分发系统V6.0系列入门配置流程
一文即可看懂我们程序有哪些功能,需要配置哪些东西?怎么好用、易用、快捷! 安装教程请看:[xx_insert_post station_ar…
幻影二级域名分发系统6.0系列首页模板开发文档
幻影二级域名分发系统 V6 门户模板开发文档 文档版本:6.0.5 适用范围:系统内置 Vue 门户、可安装的 portal 静态模板、门户…
幻影轻量级WAF安全网关安装部署教程
当前教程是基于宝塔面板进行搭建的,没有安装任何环境的 没有网站的 宝塔面板,原则上是支持和网站   Nginx 共存的  只不过是需要修改接…
幻影二级域名分发系统《付费版》6.0安装教程
幻影二级域名分发系统最新6.0系列是使用GO 作为后端进行开发的,所以没有那么多环境使用限制,其他系统运行模式同理,这里基于宝塔搭建为教程!…
发表评论
暂无评论

还没有评论呢,快来抢沙发~

点击联系客服

在线时间:8:00-16:00

客服QQ

419437697

客服电话

400-888-8888

客服邮箱

admin@52hyjs.com