打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:CardData:修订间差异

来自夜幕之下
Rin留言 | 贡献
新建 CardData 模块,从 Directus API 获取卡片数据 (via create-page on MediaWiki MCP Server)
 
Rin留言 | 贡献
重构:Lua 只生成 HTML 骨架,数据由前端 JS 填充 (via update-page on MediaWiki MCP Server)
第1行: 第1行:
-- Module:CardData
-- Module:CardData
-- 从 Directus API 获取「夜幕之下」卡片数据并渲染
-- 生成卡片 HTML 骨架,实际数据由前端 JS 从 Directus API 获取并填充
-- 用法:{{#invoke:CardData|render|复仇童谣}}
-- 用法:{{#invoke:CardData|render|复仇童谣}}


local p = {}
local p = {}
-- ==============================
-- 配置
-- ==============================
local API_BASE = "https://data.saltedkiss.org/items/cards"
-- 需要获取的字段,去掉所有 user 相关信息
local FIELDS = table.concat({
    "stylename",
    "rarity",
    "character.name",
    "profession.name",
    "desire.name",
    "skill_normal_attack.name",
    "skill_normal_attack.type",
    "skill_normal_attack.weapon",
    "skill_normal_attack.description",
    "skill_normal_attack.levels.*",
    "skill_passive.name",
    "skill_passive.type",
    "skill_passive.trigger_type",
    "skill_passive.trigger_value",
    "skill_passive.description",
    "skill_passive.levels.*",
    "skill_ultimate.name",
    "skill_ultimate.type",
    "skill_ultimate.desire_cost",
    "skill_ultimate.description",
    "skill_ultimate.levels.*",
    "feats.stages"
}, ",")
-- ==============================
-- 内部函数:通过卡名获取数据
-- ==============================
local function fetchCardByName(name)
    local url = API_BASE
        .. "?fields=" .. FIELDS
        .. "&filter[stylename][_eq]=" .. mw.uri.encode(name)
    -- 发起 HTTP 请求
    local ok, result = pcall(mw.http.get, url)
    if not ok or not result then
        return nil, "HTTP 请求失败,请检查 $wgAllowExternalReqs 是否开启"
    end
    -- 解析 JSON
    local ok2, data = pcall(mw.text.jsonDecode, result)
    if not ok2 or not data then
        return nil, "JSON 解析失败"
    end
    if not data.data or #data.data == 0 then
        return nil, "找不到卡片:" .. name
    end
    return data.data[1]
end
-- ==============================
-- 内部函数:安全取值(避免 nil 报错)
-- ==============================
local function safe(val, fallback)
    if val ~= nil and val ~= "" then
        return tostring(val)
    end
    return fallback or "—"
end
-- ==============================
-- 内部函数:渲染单个技能区块
-- ==============================
local function renderSkill(skill)
    if not skill then return "" end
    local lines = {}
    table.insert(lines, "; " .. safe(skill.name))
    table.insert(lines, ": 类型:" .. safe(skill.type))
    -- 普通攻击有武器
    if skill.weapon then
        table.insert(lines, ": 武器:" .. safe(skill.weapon))
    end
    -- 被动有触发条件
    if skill.trigger_type then
        table.insert(lines, ": 触发:" .. safe(skill.trigger_type) .. " × " .. safe(skill.trigger_value))
    end
    -- 终结技有欲望消耗
    if skill.desire_cost then
        table.insert(lines, ": 欲望消耗:" .. safe(skill.desire_cost))
    end
    table.insert(lines, ": " .. safe(skill.description))
    -- 升级数值
    if skill.levels and #skill.levels > 0 then
        for _, lv in ipairs(skill.levels) do
            table.insert(lines, ":; 升级效果:" .. safe(lv.name))
            if lv.levels then
                table.insert(lines, ":: " .. table.concat(lv.levels, " / "))
            end
        end
    end
    return table.concat(lines, "\n")
end
-- ==============================
-- 内部函数:渲染觉醒区块
-- ==============================
local function renderFeats(feats)
    if not feats or #feats == 0 then return "" end
    local lines = {}
    table.insert(lines, "=== 觉醒 ===")
    for _, feat in ipairs(feats) do
        if feat.stages then
            for _, stage in ipairs(feat.stages) do
                local stageName = stage.extra_name or ("觉醒 " .. tostring(stage.stage))
                table.insert(lines, "; " .. stageName)
                -- 属性加成
                if stage.stat_boosts then
                    for _, boost in ipairs(stage.stat_boosts) do
                        table.insert(lines, ": " .. safe(boost.type) .. " +" .. safe(boost.value))
                    end
                end
                -- 技能效果
                if stage.value then
                    for _, v in ipairs(stage.value) do
                        table.insert(lines, ": " .. safe(v.description))
                    end
                end
            end
        end
    end
    return table.concat(lines, "\n")
end
-- ==============================
-- 对外入口:render
-- ==============================


function p.render(frame)
function p.render(frame)
     -- 支持 {{#invoke:CardData|render|卡名}} 或 {{#invoke:CardData|render|name=卡名}}
     -- 支持位置参数或具名参数
     local name = frame.args[1] or frame.args.name
     local name = frame.args[1] or frame.args.name or ""
    if not name or name == "" then
        return '<span style="color:red">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>'
    end
 
     name = mw.text.trim(name)
     name = mw.text.trim(name)


     local card, err = fetchCardByName(name)
     if name == "" then
    if not card then
         return '<span class="error">错误:请提供卡片名称,例如 {{#invoke:CardData|render|复仇童谣}}</span>'
         return '<span style="color:red">错误:' .. (err or "未知错误") .. '</span>'
     end
     end


     -- 开始拼装输出
     -- 生成骨架容器,data-cardname 供 JS 读取
    local lines = {}
     -- JS 会找到所有 .ron-card 元素,根据 data-cardname 请求 Directus,然后填充内容
 
     return '<div class="ron-card" data-cardname="'
     -- 基本信息
        .. mw.text.encode(name)
     table.insert(lines, "== " .. card.stylename .. " ==")
        .. '"><div class="ron-card-loading">⏳ 加载中…</div></div>'
    table.insert(lines, "; 稀有度:" .. safe(card.rarity))
    table.insert(lines, "; 角色:" .. safe(card.character and card.character.name))
    table.insert(lines, "; 职业:" .. safe(card.profession and card.profession.name))
    table.insert(lines, "; 欲望:" .. safe(card.desire and card.desire.name))
 
    -- 技能
    table.insert(lines, "=== 普通攻击 ===")
    table.insert(lines, renderSkill(card.skill_normal_attack))
 
    table.insert(lines, "=== 被动技能 ===")
    table.insert(lines, renderSkill(card.skill_passive))
 
    table.insert(lines, "=== 终结技 ===")
    table.insert(lines, renderSkill(card.skill_ultimate))
 
    -- 觉醒
    table.insert(lines, renderFeats(card.feats))
 
    return table.concat(lines, "\n")
end
end


return p
return p

2026年3月10日 (二) 23:08的版本

此模块的文档可以在模块:CardData/doc创建

-- Module:CardData
-- 生成卡片 HTML 骨架,实际数据由前端 JS 从 Directus API 获取并填充
-- 用法:{{#invoke:CardData|render|复仇童谣}}

local p = {}

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

    -- 生成骨架容器,data-cardname 供 JS 读取
    -- JS 会找到所有 .ron-card 元素,根据 data-cardname 请求 Directus,然后填充内容
    return '<div class="ron-card" data-cardname="'
        .. mw.text.encode(name)
        .. '"><div class="ron-card-loading">⏳ 加载中…</div></div>'
end

return p