模块:CardData:修订间差异
来自夜幕之下
更多操作
更新:生成骨架 HTML,数据由 CardData.js 填充 (via update-page on MediaWiki MCP Server) |
重写:用 mw.html.create 生成完整骨架,对应 Sandbox/2.0 HTML 结构 (via update-page on MediaWiki MCP Server) |
||
| 第1行: | 第1行: | ||
-- Module:CardData | -- Module:CardData | ||
-- | -- 生成卡片页面完整 HTML 骨架,数据由前端 JS 从 Directus API 获取后填充 | ||
-- 用法:{{#invoke:CardData|render|复仇童谣}} | -- 用法:{{#invoke:CardData|render|复仇童谣}} | ||
-- | |||
-- 骨架结构对应 Sandbox/2.0,分为左栏(基础信息)和右栏(技能/觉醒/小传/邀约) | |||
-- JS 会找到所有 [data-cardname] 元素,请求 Directus 后将数据注入各 [data-card-field] 占位节点 | |||
local p = {} | local p = {} | ||
-- ────────────────────────────────────────────── | |||
-- 辅助:创建"标题行"(如「战斗技能 Tactical」) | |||
-- ────────────────────────────────────────────── | |||
local function makeTitle(titleText, subtitleText, extraClass) | |||
local div = mw.html.create("div") | |||
:addClass("card_content-item-title") | |||
if extraClass then div:addClass(extraClass) end | |||
div:wikitext(titleText) | |||
div:tag("span") | |||
:addClass("card_content-item-subtitle") | |||
:wikitext(subtitleText) | |||
return div | |||
end | |||
-- ────────────────────────────────────────────── | |||
-- 辅助:创建分隔线 | |||
-- ────────────────────────────────────────────── | |||
local function makeHr(extraClass) | |||
local div = mw.html.create("div") | |||
:addClass("card_content-item-hr") | |||
if extraClass then div:addClass(extraClass) end | |||
return div | |||
end | |||
-- ────────────────────────────────────────────── | |||
-- 辅助:创建一个技能卡片骨架 | |||
-- skillKey = "normal_attack" | "passive" | "ultimate" | |||
-- ────────────────────────────────────────────── | |||
local function makeSkillCard(skillKey) | |||
-- 外层卡片 | |||
local card = mw.html.create("div") | |||
:addClass("card_content_skill-card") | |||
-- 左侧内容区 | |||
local left = card:tag("div"):addClass("card_content_skill-left") | |||
-- 技能标题行 | |||
local titleWrap = left:tag("div"):addClass("card_content_skill-title") | |||
titleWrap:tag("div"):addClass("card_content_skill-icon") -- 图标占位 | |||
local titleText = titleWrap:tag("div"):addClass("card_content_skill-title-text") | |||
-- 技能名 + 类型 行 | |||
local nameRow = titleText:tag("div") | |||
nameRow:tag("span") | |||
:addClass("card_content_skill-name") | |||
:attr("data-card-field", "skill_" .. skillKey .. ".name") | |||
:wikitext("—") | |||
nameRow:tag("span") | |||
:addClass("card_content_skill-type card_accent--type") | |||
:attr("data-card-field", "skill_" .. skillKey .. ".type_label") | |||
:wikitext("—") | |||
-- meta 行(普攻显示武器;被动显示触发条件;终结技显示欲火消耗) | |||
local metaRow = titleText:tag("div"):addClass("card_content_skill-meta") | |||
if skillKey == "normal_attack" then | |||
metaRow:wikitext("使用武器 ") | |||
metaRow:tag("span") | |||
:addClass("card_content_skill-meta-val card_accent") | |||
:attr("data-card-field", "skill_normal_attack.weapon") | |||
:wikitext("—") | |||
elseif skillKey == "passive" then | |||
metaRow:wikitext("触发条件 ") | |||
metaRow:tag("span") | |||
:addClass("card_content_skill-meta-val") | |||
:attr("data-card-field", "skill_passive.trigger_label") | |||
:wikitext("—") | |||
elseif skillKey == "ultimate" then | |||
metaRow:wikitext("欲火消耗 ") | |||
metaRow:tag("span") | |||
:addClass("card_content_skill-meta-val") | |||
:tag("span") | |||
:addClass("card_accent") | |||
:attr("data-card-field", "skill_ultimate.desire_cost") | |||
:wikitext("—") | |||
end | |||
-- 被动/终结技才有 tags 行 | |||
if skillKey == "passive" or skillKey == "ultimate" then | |||
left:tag("div") | |||
:addClass("card_content_skill-tags") | |||
:attr("data-card-field", "skill_" .. skillKey .. ".tags") | |||
-- JS 会把 tag 列表渲染到这里 | |||
end | |||
-- 技能描述 | |||
left:tag("div") | |||
:addClass("card_content_skill_effect") | |||
:attr("data-card-field", "skill_" .. skillKey .. ".description_html") | |||
:wikitext("—") | |||
-- 右侧升级表格占位(JS 负责生成具体列数和内容) | |||
card:tag("div") | |||
:addClass("card_content_skill-upgrade") | |||
:attr("data-card-field", "skill_" .. skillKey .. ".levels_table") | |||
return card | |||
end | |||
-- ────────────────────────────────────────────── | |||
-- 主渲染函数 | |||
-- ────────────────────────────────────────────── | |||
function p.render(frame) | function p.render(frame) | ||
local name = frame.args[1] or frame.args.name or "" | local name = frame.args[1] or frame.args.name or "" | ||
name = mw.text.trim(name) | name = mw.text.trim(name) | ||
| 第14行: | 第117行: | ||
end | end | ||
-- | -- ── 最外层容器,JS 以此为根节点 ── | ||
-- JS | local root = mw.html.create("div") | ||
:addClass("ron-card") | |||
.. | :attr("data-cardname", name) | ||
.. | |||
-- 全屏卡面图 | |||
root:tag("div") | |||
:addClass("card_fullscreen-img") | |||
:attr("data-card-field", "fullscreen_img") | |||
-- 英雄区占位高度 | |||
root:tag("div") | |||
:addClass("card_hero") | |||
:css("height", "100vh") | |||
-- ── 主内容容器 ── | |||
local container = root:tag("div"):addClass("card_content-container") | |||
local bg = container:tag("div"):addClass("card_content-background") | |||
local inner = bg:tag("div"):addClass("card_content-inner") | |||
local layout = inner:tag("div"):addClass("card_content card_content--layout") | |||
-- ════════════════════════════════ | |||
-- 左栏(sticky) | |||
-- ════════════════════════════════ | |||
local left = layout:tag("div"):addClass("card_content_left") | |||
-- 1. 卡名区 | |||
local nameContainer = left:tag("div"):addClass("card_content_name-container") | |||
local nameRow = nameContainer:tag("div"):addClass("card_content_name-row") | |||
-- 职业图标 + 文字 | |||
nameRow:tag("div") | |||
:addClass("card_content_name-icon") | |||
:attr("data-card-field", "profession_icon") | |||
nameRow:tag("div") | |||
:addClass("card_content_name-classtext") | |||
:attr("data-card-field", "profession.name") | |||
:wikitext("—") | |||
-- 欲望图标 + 文字 | |||
nameRow:tag("div") | |||
:addClass("card_content_name-icon card_content_name-icon--ml") | |||
:attr("data-card-field", "desire_icon") | |||
nameRow:tag("div") | |||
:addClass("card_content_name-classtext") | |||
:attr("data-card-field", "desire.name") | |||
:wikitext("—") | |||
-- 卡名 + 角色名 | |||
nameContainer:tag("div") | |||
:addClass("card_content_name-text") | |||
:attr("data-card-field", "card_title") | |||
-- JS 会把「风格名 · 角色名」填进来 | |||
:wikitext("—") | |||
-- 2. 信息(稀有度等,目前 API 只有 rarity,其他待补) | |||
left:node(makeTitle("信息", "Info", "card_content-item-title--mt")) | |||
left:node(makeHr()) | |||
local infoGrid = left:tag("div"):addClass("card_content_info") | |||
-- 四项情报:JS 填充(data-card-field="info_grid") | |||
infoGrid:attr("data-card-field", "info_grid") | |||
-- 3. 满级属性(API 暂无,先留骨架) | |||
left:node(makeTitle("等级 100", "Lv. Max", "card_content-item-title--mt")) | |||
left:node(makeHr()) | |||
left:tag("div") | |||
:addClass("card_content_attributes") | |||
:attr("data-card-field", "attributes") | |||
:wikitext("—") | |||
-- 4. 映像(API 暂无,先留骨架) | |||
left:node(makeTitle("映像", "Reflection", "card_content-item-title--mt")) | |||
left:node(makeHr()) | |||
left:tag("div") | |||
:addClass("card_content_reflection") | |||
:attr("data-card-field", "reflection") | |||
:wikitext("—") | |||
-- 5. 认知(API 暂无,先留骨架) | |||
left:node(makeTitle("认知", "Cognition", "card_content-item-title--mt")) | |||
left:node(makeHr()) | |||
left:tag("div") | |||
:addClass("card_content_cognition") | |||
:attr("data-card-field", "cognition") | |||
:wikitext("—") | |||
-- ════════════════════════════════ | |||
-- 右栏 | |||
-- ════════════════════════════════ | |||
local right = layout:tag("div"):addClass("card_content_right") | |||
-- ── 2-1. 战斗技能 ── | |||
right:node(makeTitle("战斗技能", "Tactical", "card_content-item-title--mt")) | |||
right:node(makeHr("card_content-item-hr--mb12")) | |||
-- 普攻 | |||
right:node(makeSkillCard("normal_attack")) | |||
right:tag("div"):addClass("card_content_skill-gap") | |||
-- 被动 | |||
right:node(makeSkillCard("passive")) | |||
right:tag("div"):addClass("card_content_skill-gap") | |||
-- 终结技 | |||
right:node(makeSkillCard("ultimate")) | |||
-- ── 2-2. 觉醒 ── | |||
right:node(makeTitle("觉醒", "Feat", "card_content-item-title--mt")) | |||
right:node(makeHr("card_content-item-hr--mb12")) | |||
-- 觉醒列表,JS 根据 feats.stages 数组填充 | |||
right:tag("ul") | |||
:addClass("card_content_feat-ul") | |||
:attr("data-card-field", "feats_list") | |||
-- ── 2-3. 小传 ── | |||
local storySection = right:tag("div"):addClass("card_content_story") | |||
local storyHeader = storySection:tag("div"):addClass("card_content_story-header") | |||
local storyLeft = storyHeader:tag("div"):addClass("card_content_story-header-left") | |||
storyLeft:node(makeTitle("小传", "Story", "card_content-item-title--mt")) | |||
storyLeft:node(makeHr("card_content-item-hr--mb12")) | |||
storyLeft:tag("div") | |||
:addClass("card_content_story-toggle") | |||
:attr("data-collapsed", "false") | |||
:tag("span"):addClass("story-toggle-text"):wikitext("收起"):done() | |||
:tag("span"):addClass("story-toggle-icon"):wikitext("-") | |||
storyHeader:tag("div"):addClass("card_content_story-header-spacer") | |||
-- 时间线,JS 根据 story 数据填充 | |||
storyHeader:tag("div") | |||
:addClass("card_timeline card_content_story-timeline") | |||
:attr("data-card-field", "story_timeline") | |||
:tag("div"):addClass("card_timeline-line") | |||
-- ── 2-4. 邀约 ── | |||
right:tag("div"):css("height", "12px"):css("width", "100%") | |||
right:node(makeTitle("邀约", "Date", "card_content-item-title--mt")) | |||
right:node(makeHr("card_content-item-hr--mb12")) | |||
-- 邀约卡片轨道,JS 填充 | |||
local dateWrap = right:tag("div"):addClass("card_content_date") | |||
dateWrap:tag("div") | |||
:addClass("card_content_date-track") | |||
:tag("ul") | |||
:addClass("card_content_date-list") | |||
:attr("data-card-field", "date_list") | |||
-- 邀约故事面板(点击卡片后展开) | |||
local dateStory = right:tag("div") | |||
:addClass("card_date_story") | |||
:attr("id", "date-story-panel") | |||
:attr("aria-live", "polite") | |||
dateStory:tag("div") | |||
:addClass("card_date_story-scene") | |||
:attr("id", "date-story-scene") | |||
:attr("data-card-field", "date_story_scene") | |||
dateStory:tag("div") | |||
:addClass("card_date_story-body") | |||
:attr("id", "date-story-body") | |||
:attr("data-card-field", "date_story_body") | |||
-- ── 加载提示(JS 完成后移除)── | |||
root:tag("div") | |||
:addClass("ron-card-loading") | |||
:wikitext("⏳ 加载中…") | |||
-- 输出 HTML + CSS 模板 | |||
return tostring(root) .. frame:expandTemplate{ title = "cardcss" } | |||
end | end | ||
return p | return p | ||
2026年3月10日 (二) 23:32的版本
此模块的文档可以在模块:CardData/doc创建
-- Module:CardData
-- 生成卡片页面完整 HTML 骨架,数据由前端 JS 从 Directus API 获取后填充
-- 用法:{{#invoke:CardData|render|复仇童谣}}
--
-- 骨架结构对应 Sandbox/2.0,分为左栏(基础信息)和右栏(技能/觉醒/小传/邀约)
-- JS 会找到所有 [data-cardname] 元素,请求 Directus 后将数据注入各 [data-card-field] 占位节点
local p = {}
-- ──────────────────────────────────────────────
-- 辅助:创建"标题行"(如「战斗技能 Tactical」)
-- ──────────────────────────────────────────────
local function makeTitle(titleText, subtitleText, extraClass)
local div = mw.html.create("div")
:addClass("card_content-item-title")
if extraClass then div:addClass(extraClass) end
div:wikitext(titleText)
div:tag("span")
:addClass("card_content-item-subtitle")
:wikitext(subtitleText)
return div
end
-- ──────────────────────────────────────────────
-- 辅助:创建分隔线
-- ──────────────────────────────────────────────
local function makeHr(extraClass)
local div = mw.html.create("div")
:addClass("card_content-item-hr")
if extraClass then div:addClass(extraClass) end
return div
end
-- ──────────────────────────────────────────────
-- 辅助:创建一个技能卡片骨架
-- skillKey = "normal_attack" | "passive" | "ultimate"
-- ──────────────────────────────────────────────
local function makeSkillCard(skillKey)
-- 外层卡片
local card = mw.html.create("div")
:addClass("card_content_skill-card")
-- 左侧内容区
local left = card:tag("div"):addClass("card_content_skill-left")
-- 技能标题行
local titleWrap = left:tag("div"):addClass("card_content_skill-title")
titleWrap:tag("div"):addClass("card_content_skill-icon") -- 图标占位
local titleText = titleWrap:tag("div"):addClass("card_content_skill-title-text")
-- 技能名 + 类型 行
local nameRow = titleText:tag("div")
nameRow:tag("span")
:addClass("card_content_skill-name")
:attr("data-card-field", "skill_" .. skillKey .. ".name")
:wikitext("—")
nameRow:tag("span")
:addClass("card_content_skill-type card_accent--type")
:attr("data-card-field", "skill_" .. skillKey .. ".type_label")
:wikitext("—")
-- meta 行(普攻显示武器;被动显示触发条件;终结技显示欲火消耗)
local metaRow = titleText:tag("div"):addClass("card_content_skill-meta")
if skillKey == "normal_attack" then
metaRow:wikitext("使用武器 ")
metaRow:tag("span")
:addClass("card_content_skill-meta-val card_accent")
:attr("data-card-field", "skill_normal_attack.weapon")
:wikitext("—")
elseif skillKey == "passive" then
metaRow:wikitext("触发条件 ")
metaRow:tag("span")
:addClass("card_content_skill-meta-val")
:attr("data-card-field", "skill_passive.trigger_label")
:wikitext("—")
elseif skillKey == "ultimate" then
metaRow:wikitext("欲火消耗 ")
metaRow:tag("span")
:addClass("card_content_skill-meta-val")
:tag("span")
:addClass("card_accent")
:attr("data-card-field", "skill_ultimate.desire_cost")
:wikitext("—")
end
-- 被动/终结技才有 tags 行
if skillKey == "passive" or skillKey == "ultimate" then
left:tag("div")
:addClass("card_content_skill-tags")
:attr("data-card-field", "skill_" .. skillKey .. ".tags")
-- JS 会把 tag 列表渲染到这里
end
-- 技能描述
left:tag("div")
:addClass("card_content_skill_effect")
:attr("data-card-field", "skill_" .. skillKey .. ".description_html")
:wikitext("—")
-- 右侧升级表格占位(JS 负责生成具体列数和内容)
card:tag("div")
:addClass("card_content_skill-upgrade")
:attr("data-card-field", "skill_" .. skillKey .. ".levels_table")
return card
end
-- ──────────────────────────────────────────────
-- 主渲染函数
-- ──────────────────────────────────────────────
function p.render(frame)
local name = frame.args[1] or frame.args.name or ""
name = mw.text.trim(name)
if name == "" then
return '<span class="error">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>'
end
-- ── 最外层容器,JS 以此为根节点 ──
local root = mw.html.create("div")
:addClass("ron-card")
:attr("data-cardname", name)
-- 全屏卡面图
root:tag("div")
:addClass("card_fullscreen-img")
:attr("data-card-field", "fullscreen_img")
-- 英雄区占位高度
root:tag("div")
:addClass("card_hero")
:css("height", "100vh")
-- ── 主内容容器 ──
local container = root:tag("div"):addClass("card_content-container")
local bg = container:tag("div"):addClass("card_content-background")
local inner = bg:tag("div"):addClass("card_content-inner")
local layout = inner:tag("div"):addClass("card_content card_content--layout")
-- ════════════════════════════════
-- 左栏(sticky)
-- ════════════════════════════════
local left = layout:tag("div"):addClass("card_content_left")
-- 1. 卡名区
local nameContainer = left:tag("div"):addClass("card_content_name-container")
local nameRow = nameContainer:tag("div"):addClass("card_content_name-row")
-- 职业图标 + 文字
nameRow:tag("div")
:addClass("card_content_name-icon")
:attr("data-card-field", "profession_icon")
nameRow:tag("div")
:addClass("card_content_name-classtext")
:attr("data-card-field", "profession.name")
:wikitext("—")
-- 欲望图标 + 文字
nameRow:tag("div")
:addClass("card_content_name-icon card_content_name-icon--ml")
:attr("data-card-field", "desire_icon")
nameRow:tag("div")
:addClass("card_content_name-classtext")
:attr("data-card-field", "desire.name")
:wikitext("—")
-- 卡名 + 角色名
nameContainer:tag("div")
:addClass("card_content_name-text")
:attr("data-card-field", "card_title")
-- JS 会把「风格名 · 角色名」填进来
:wikitext("—")
-- 2. 信息(稀有度等,目前 API 只有 rarity,其他待补)
left:node(makeTitle("信息", "Info", "card_content-item-title--mt"))
left:node(makeHr())
local infoGrid = left:tag("div"):addClass("card_content_info")
-- 四项情报:JS 填充(data-card-field="info_grid")
infoGrid:attr("data-card-field", "info_grid")
-- 3. 满级属性(API 暂无,先留骨架)
left:node(makeTitle("等级 100", "Lv. Max", "card_content-item-title--mt"))
left:node(makeHr())
left:tag("div")
:addClass("card_content_attributes")
:attr("data-card-field", "attributes")
:wikitext("—")
-- 4. 映像(API 暂无,先留骨架)
left:node(makeTitle("映像", "Reflection", "card_content-item-title--mt"))
left:node(makeHr())
left:tag("div")
:addClass("card_content_reflection")
:attr("data-card-field", "reflection")
:wikitext("—")
-- 5. 认知(API 暂无,先留骨架)
left:node(makeTitle("认知", "Cognition", "card_content-item-title--mt"))
left:node(makeHr())
left:tag("div")
:addClass("card_content_cognition")
:attr("data-card-field", "cognition")
:wikitext("—")
-- ════════════════════════════════
-- 右栏
-- ════════════════════════════════
local right = layout:tag("div"):addClass("card_content_right")
-- ── 2-1. 战斗技能 ──
right:node(makeTitle("战斗技能", "Tactical", "card_content-item-title--mt"))
right:node(makeHr("card_content-item-hr--mb12"))
-- 普攻
right:node(makeSkillCard("normal_attack"))
right:tag("div"):addClass("card_content_skill-gap")
-- 被动
right:node(makeSkillCard("passive"))
right:tag("div"):addClass("card_content_skill-gap")
-- 终结技
right:node(makeSkillCard("ultimate"))
-- ── 2-2. 觉醒 ──
right:node(makeTitle("觉醒", "Feat", "card_content-item-title--mt"))
right:node(makeHr("card_content-item-hr--mb12"))
-- 觉醒列表,JS 根据 feats.stages 数组填充
right:tag("ul")
:addClass("card_content_feat-ul")
:attr("data-card-field", "feats_list")
-- ── 2-3. 小传 ──
local storySection = right:tag("div"):addClass("card_content_story")
local storyHeader = storySection:tag("div"):addClass("card_content_story-header")
local storyLeft = storyHeader:tag("div"):addClass("card_content_story-header-left")
storyLeft:node(makeTitle("小传", "Story", "card_content-item-title--mt"))
storyLeft:node(makeHr("card_content-item-hr--mb12"))
storyLeft:tag("div")
:addClass("card_content_story-toggle")
:attr("data-collapsed", "false")
:tag("span"):addClass("story-toggle-text"):wikitext("收起"):done()
:tag("span"):addClass("story-toggle-icon"):wikitext("-")
storyHeader:tag("div"):addClass("card_content_story-header-spacer")
-- 时间线,JS 根据 story 数据填充
storyHeader:tag("div")
:addClass("card_timeline card_content_story-timeline")
:attr("data-card-field", "story_timeline")
:tag("div"):addClass("card_timeline-line")
-- ── 2-4. 邀约 ──
right:tag("div"):css("height", "12px"):css("width", "100%")
right:node(makeTitle("邀约", "Date", "card_content-item-title--mt"))
right:node(makeHr("card_content-item-hr--mb12"))
-- 邀约卡片轨道,JS 填充
local dateWrap = right:tag("div"):addClass("card_content_date")
dateWrap:tag("div")
:addClass("card_content_date-track")
:tag("ul")
:addClass("card_content_date-list")
:attr("data-card-field", "date_list")
-- 邀约故事面板(点击卡片后展开)
local dateStory = right:tag("div")
:addClass("card_date_story")
:attr("id", "date-story-panel")
:attr("aria-live", "polite")
dateStory:tag("div")
:addClass("card_date_story-scene")
:attr("id", "date-story-scene")
:attr("data-card-field", "date_story_scene")
dateStory:tag("div")
:addClass("card_date_story-body")
:attr("id", "date-story-body")
:attr("data-card-field", "date_story_body")
-- ── 加载提示(JS 完成后移除)──
root:tag("div")
:addClass("ron-card-loading")
:wikitext("⏳ 加载中…")
-- 输出 HTML + CSS 模板
return tostring(root) .. frame:expandTemplate{ title = "cardcss" }
end
return p