This repository has been archived on 2026-06-21. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MetaLab/templates/MetaLab-2026/static/js/shortcode.js
Victor_Jay a8a52f5fd0 refactor: 提取公共工具函数到 shared/utils.js,消除 DRY 重复
- escapeHtml 8 处定义合并为 shared/utils.js 1 处:删除 shortcode.js、posts/show.html、settings/index.html、comments.js、energy.js、common.js 中的重复定义
- formatTime 6 处定义合并为 1 处:统一 ISO 时间格式化逻辑
- api() 5 处定义收敛:前台页面使用 shared/utils.js 统一 api(method,url,data),admin 使用 common.js 的 api 对象
- showToast 3 处定义合并为 1 处:自包含内联样式,前后台通用
- admin/posts.js api 调用迁移为 api.post()
2026-06-02 19:50:52 +08:00

130 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Shortcode 卡片渲染器 — 配合 [zone:type:params] Markdown 扩展语法
*
* 工作流程:
* 1. 后端 ShortcodeService 将 MD 中的 [zone:type:params] 替换为占位 <div class="zone-card">
* 2. Vditor.preview() 渲染 MD占位 div 原样保留
* 3. 本脚本扫描所有 .zone-card根据 data-zone-* 属性渲染对应卡片 UI
*
* 添加新卡片类型:
* 1. 在 cardRenderers 中注册类型标识和渲染函数
* 2. 渲染函数接收 id 参数,返回 HTML 字符串
* 3. 如果后端 API 已可用,在渲染函数中 fetch 数据;否则展示静态占位卡片
*
* 后端 shortcode 语法参考: /docs/vditor-migration-plan.md
*/
(function () {
'use strict';
// =========================================================================
// 卡片渲染函数注册表
//
// 每个渲染函数签名为: function(id: string) → html: string
// id 来自 [zone:type:id] 中的 id 部分
// =========================================================================
var cardRenderers = {
/**
* 活动卡片 — [zone:event:活动ID]
* TODO: 接入活动 API 后改为 fetch 动态数据
*/
event: function (id) {
return '' +
'<div class="zone-card-event">' +
' <div class="zone-card-event-icon">🎪</div>' +
' <div class="zone-card-event-body">' +
' <div class="zone-card-event-title">官方活动</div>' +
' <div class="zone-card-event-id">活动 ID: ' + escapeHtml(id) + '</div>' +
' </div>' +
' <a class="zone-card-event-link" href="/events/' + escapeHtml(id) + '" target="_blank">查看详情 →</a>' +
'</div>';
},
/**
* 游戏卡片 — [zone:game:游戏slug]
* TODO: 接入游戏数据库 API 后改为 fetch 动态数据
*/
game: function (id) {
return '' +
'<div class="zone-card-game">' +
' <div class="zone-card-game-icon">🎮</div>' +
' <div class="zone-card-game-body">' +
' <div class="zone-card-game-title">相关游戏</div>' +
' <div class="zone-card-game-slug">' + escapeHtml(id) + '</div>' +
' </div>' +
' <a class="zone-card-game-link" href="/games/' + escapeHtml(id) + '" target="_blank">查看 →</a>' +
'</div>';
},
/**
* 投票组件 — [zone:poll:投票ID]
* ※预留:后端投票 API 未实现,当前展示占位卡片
*/
poll: function (id) {
return '' +
'<div class="zone-card-poll">' +
' <div class="zone-card-poll-icon">📊</div>' +
' <div class="zone-card-poll-body">' +
' <div class="zone-card-poll-title">社区投票</div>' +
' <div class="zone-card-poll-id">投票 ID: ' + escapeHtml(id) + '</div>' +
' </div>' +
' <span class="zone-card-poll-pending">即将开放</span>' +
'</div>';
},
/**
* 资源卡片 — [zone:resource:资源ID]
* ※预留:后端资源 API 未实现,当前展示占位卡片
*/
resource: function (id) {
return '' +
'<div class="zone-card-resource">' +
' <div class="zone-card-resource-icon">📦</div>' +
' <div class="zone-card-resource-body">' +
' <div class="zone-card-resource-title">推荐资源</div>' +
' <div class="zone-card-resource-id">资源 ID: ' + escapeHtml(id) + '</div>' +
' </div>' +
' <a class="zone-card-resource-link" href="/resources/' + escapeHtml(id) + '" target="_blank">查看 →</a>' +
'</div>';
}
};
// =========================================================================
// 工具函数 — escapeHtml 由 shared/utils.js 统一提供
// =========================================================================
// =========================================================================
// 主渲染逻辑
// =========================================================================
/**
* renderShortcodes — 扫描容器内所有 .zone-card 占位并渲染
* @param {HTMLElement} container — 包含 shortcode 占位的 DOM 容器
*/
window.renderShortcodes = function (container) {
if (!container) return;
var placeholders = container.querySelectorAll('.zone-card');
for (var i = 0; i < placeholders.length; i++) {
var el = placeholders[i];
var type = el.getAttribute('data-zone-type');
var id = el.getAttribute('data-zone-id');
if (!type || !id) continue;
var renderer = cardRenderers[type];
if (!renderer) continue;
// 渲染卡片 HTML 并替换占位 div
try {
el.outerHTML = renderer(id);
} catch (e) {
// 渲染失败时保留占位文本
console.warn('[Shortcode] render failed for type=' + type + ' id=' + id, e);
}
}
};
})();