模块:OWMOMarket:修订间差异
来自Blockland Wiki
更多操作
无编辑摘要 |
无编辑摘要 |
||
| 第4行: | 第4行: | ||
local extData = mw.ext.externalData | local extData = mw.ext.externalData | ||
if not extData or not extData.getDbData then | if not extData or not extData.getDbData then | ||
return "错误:未找到 External Data | return "错误:未找到 External Data 接口" | ||
end | end | ||
-- 1. 获取最新期数 | -- 1. 获取最新期数 | ||
local latest_data | local latest_data = extData.getDbData{ | ||
db = 'wp_db', | db = 'wp_db', | ||
from = 'wp_owmc_currency_rates', | from = 'wp_owmc_currency_rates', | ||
| 第26行: | 第26行: | ||
local old_m = ((old_abs - 1) % 104) + 1 | local old_m = ((old_abs - 1) % 104) + 1 | ||
-- 3. | -- 3. 抓取本期和上月的数据 | ||
local current_rates = extData.getDbData{ | local current_rates = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_rates', where = string.format("year=%d AND month=%d", max_y, max_m), limit = 200, data = 'currency_code=currency_code, rate=rate' } | ||
local old_rates = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_rates', where = string.format("year=%d AND month=%d", old_y, old_m), limit = 200, data = 'currency_code=currency_code, rate=rate' } | |||
local meta_data = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_meta', limit = 200, data = 'currency_code=currency_code, currency_name=currency_name' } | |||
local old_rates = extData.getDbData{ | |||
local meta_data = extData.getDbData{ | |||
-- 4. 数据映射与处理 | -- 4. 数据映射与处理 | ||
local meta_map, old_map = {}, {} | local meta_map, old_map = {}, {} | ||
if meta_data then | if meta_data then for _, r in ipairs(meta_data) do meta_map[r.currency_code] = r.currency_name end end | ||
if old_rates then for _, r in ipairs(old_rates) do old_map[r.currency_code] = tonumber(r.rate) end end | |||
if old_rates then | |||
local stats = {} | local stats = {} | ||
| 第69行: | 第44行: | ||
if c_rate and o_rate and c_rate > 0 then | if c_rate and o_rate and c_rate > 0 then | ||
local val_current = 1 / c_rate | local val_current = 1 / c_rate | ||
local val_old = 1 / o_rate | local val_old = 1 / o_rate | ||
| 第78行: | 第52行: | ||
end | end | ||
table.sort(stats, function(a, b) return a.pct > b.pct end) | table.sort(stats, function(a, b) return a.pct > b.pct end) | ||
-- 5. | -- 5. 渲染 HTML (去掉了固定宽度、边框和阴影,默认撑满 100%) | ||
local html = mw.html.create('div') | local html = mw.html.create('div') | ||
:cssText("width: | :cssText("width: 100%; margin: 0 0 16px 0; background: transparent; font-family: -apple-system, 'Segoe UI', sans-serif;") | ||
local function renderRow(item) | local function renderRow(item) | ||
| 第92行: | 第65行: | ||
local val_str = string.format("%.4f", item.val) | local val_str = string.format("%.4f", item.val) | ||
-- | -- 引入 RemixIcon 图标作为趋势线 | ||
local | local icon_class = is_up and "ri-trend-up-line" or "ri-trend-down-line" | ||
if item.pct == 0 then | if item.pct == 0 then icon_class = "ri-subtract-line" end | ||
local sparkline = string.format('< | local sparkline = string.format('<i class="%s" style="color: %s; font-size: 24px; opacity: 0.8;"></i>', icon_class, color) | ||
return string.format([[ | return string.format([[ | ||
<div style="display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid # | <div style="display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #f1f5f9;"> | ||
<div style="flex: 1; text-align: left;"> | <div style="flex: 1; text-align: left;"> | ||
<div style="font-weight: 900; color: #0f172a; font-size: 14px; font-family: 'Courier New', monospace;">%s</div> | <div style="font-weight: 900; color: #0f172a; font-size: 14px; font-family: 'Courier New', monospace;">%s</div> | ||
<div style="font-size: 11px; color: #64748b; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: | <div style="font-size: 11px; color: #64748b; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 90px;">%s</div> | ||
</div> | </div> | ||
<div style="flex: 0 0 | <div style="flex: 0 0 50px; text-align: center;">%s</div> | ||
<div style="flex: 1; text-align: right;"> | <div style="flex: 1; text-align: right;"> | ||
<div style="font-weight: bold; font-family: 'Courier New', monospace; font-size: 14px; color: #0f172a;">%s</div> | <div style="font-weight: bold; font-family: 'Courier New', monospace; font-size: 14px; color: #0f172a;">%s</div> | ||
| 第112行: | 第85行: | ||
end | end | ||
-- | -- 渲染区块标题样式 | ||
local function renderHeader(title, color) | |||
return mw.html.create('div'):cssText(string.format("font-size: 12px; font-weight: 800; color: %s; text-transform: uppercase; margin-top: 12px; margin-bottom: 4px;", color)):wikitext(title) | |||
end | |||
html:node(renderHeader("Core Assets / 主币", "#94a3b8")) | |||
local cores = {WMC=true, KCK=true, SKV=true, COS=true} | local cores = {WMC=true, KCK=true, SKV=true, COS=true} | ||
for _, item in ipairs(stats) do | for _, item in ipairs(stats) do | ||
| 第119行: | 第96行: | ||
end | end | ||
html:node(renderHeader("Top Gainers / 领涨", "#f43f5e")) | |||
html:node( | |||
local count = 0 | local count = 0 | ||
for _, item in ipairs(stats) do | for _, item in ipairs(stats) do | ||
| 第130行: | 第106行: | ||
end | end | ||
html:node(renderHeader("Top Losers / 领跌", "#10b981")) | |||
html:node( | |||
count = 0 | count = 0 | ||
for i = #stats, 1, -1 do | for i = #stats, 1, -1 do | ||
2026年5月8日 (五) 18:00的版本
此模块的文档可以在模块:OWMOMarket/doc创建
local p = {}
function p.renderSidebar(frame)
local extData = mw.ext.externalData
if not extData or not extData.getDbData then
return "错误:未找到 External Data 接口"
end
-- 1. 获取最新期数
local latest_data = extData.getDbData{
db = 'wp_db',
from = 'wp_owmc_currency_rates',
['order by'] = 'year DESC, month DESC',
limit = 1,
data = 'year=year, month=month'
}
if not latest_data or not latest_data[1] then return "暂无行情数据" end
local max_y = tonumber(latest_data[1].year)
local max_m = tonumber(latest_data[1].month)
-- 2. 计算 8 期前(1 个月前)的期数
local current_abs = max_y * 104 + max_m
local old_abs = current_abs - 8
local old_y = math.floor((old_abs - 1) / 104)
local old_m = ((old_abs - 1) % 104) + 1
-- 3. 抓取本期和上月的数据
local current_rates = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_rates', where = string.format("year=%d AND month=%d", max_y, max_m), limit = 200, data = 'currency_code=currency_code, rate=rate' }
local old_rates = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_rates', where = string.format("year=%d AND month=%d", old_y, old_m), limit = 200, data = 'currency_code=currency_code, rate=rate' }
local meta_data = extData.getDbData{ db = 'wp_db', from = 'wp_owmc_currency_meta', limit = 200, data = 'currency_code=currency_code, currency_name=currency_name' }
-- 4. 数据映射与处理
local meta_map, old_map = {}, {}
if meta_data then for _, r in ipairs(meta_data) do meta_map[r.currency_code] = r.currency_name end end
if old_rates then for _, r in ipairs(old_rates) do old_map[r.currency_code] = tonumber(r.rate) end end
local stats = {}
if current_rates then
for _, row in ipairs(current_rates) do
local code = row.currency_code
local c_rate = tonumber(row.rate)
local o_rate = old_map[code]
if c_rate and o_rate and c_rate > 0 then
local val_current = 1 / c_rate
local val_old = 1 / o_rate
local pct = ((val_current - val_old) / val_old) * 100
table.insert(stats, { code = code, name = meta_map[code] or code, val = val_current, pct = pct })
end
end
end
table.sort(stats, function(a, b) return a.pct > b.pct end)
-- 5. 渲染 HTML (去掉了固定宽度、边框和阴影,默认撑满 100%)
local html = mw.html.create('div')
:cssText("width: 100%; margin: 0 0 16px 0; background: transparent; font-family: -apple-system, 'Segoe UI', sans-serif;")
local function renderRow(item)
local is_up = item.pct >= 0
local color = is_up and "#f43f5e" or "#10b981"
local sign = is_up and "▲ +" or "▼ "
local pct_str = string.format("%.2f%%", item.pct)
local val_str = string.format("%.4f", item.val)
-- 引入 RemixIcon 图标作为趋势线
local icon_class = is_up and "ri-trend-up-line" or "ri-trend-down-line"
if item.pct == 0 then icon_class = "ri-subtract-line" end
local sparkline = string.format('<i class="%s" style="color: %s; font-size: 24px; opacity: 0.8;"></i>', icon_class, color)
return string.format([[
<div style="display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #f1f5f9;">
<div style="flex: 1; text-align: left;">
<div style="font-weight: 900; color: #0f172a; font-size: 14px; font-family: 'Courier New', monospace;">%s</div>
<div style="font-size: 11px; color: #64748b; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 90px;">%s</div>
</div>
<div style="flex: 0 0 50px; text-align: center;">%s</div>
<div style="flex: 1; text-align: right;">
<div style="font-weight: bold; font-family: 'Courier New', monospace; font-size: 14px; color: #0f172a;">%s</div>
<div style="font-size: 11px; font-weight: 800; color: %s; letter-spacing: -0.5px;">%s%s</div>
</div>
</div>
]], item.code, item.name, sparkline, val_str, color, sign, pct_str)
end
-- 渲染区块标题样式
local function renderHeader(title, color)
return mw.html.create('div'):cssText(string.format("font-size: 12px; font-weight: 800; color: %s; text-transform: uppercase; margin-top: 12px; margin-bottom: 4px;", color)):wikitext(title)
end
html:node(renderHeader("Core Assets / 主币", "#94a3b8"))
local cores = {WMC=true, KCK=true, SKV=true, COS=true}
for _, item in ipairs(stats) do
if cores[item.code] then html:node(mw.html.create('div'):wikitext(renderRow(item))) end
end
html:node(renderHeader("Top Gainers / 领涨", "#f43f5e"))
local count = 0
for _, item in ipairs(stats) do
if not cores[item.code] and item.pct > 0 then
html:node(mw.html.create('div'):wikitext(renderRow(item)))
count = count + 1
if count >= 3 then break end
end
end
html:node(renderHeader("Top Losers / 领跌", "#10b981"))
count = 0
for i = #stats, 1, -1 do
local item = stats[i]
if not cores[item.code] and item.pct < 0 then
html:node(mw.html.create('div'):wikitext(renderRow(item)))
count = count + 1
if count >= 3 then break end
end
end
return tostring(html)
end
return p