migration: preserve frontend shell pages and styles
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
window.XiaobaiPageModules.register("themes", ["themeLibraryView"], {
|
||||
enter: ["loadThemes"],
|
||||
});
|
||||
|
||||
/* PRESERVATION-SOURCE-BEGIN app.js:2482-2583 */
|
||||
async function loadThemeLibrary(force = false) {
|
||||
if (state.themeLoading) return;
|
||||
state.themeLoading = true;
|
||||
const button = document.querySelector("#themeRefreshButton");
|
||||
button.disabled = true;
|
||||
setText("themeDateLabel", "正在整理题材库");
|
||||
try {
|
||||
const query = new URLSearchParams({ trade_date: elements.tradeDate.value });
|
||||
if (force) query.set("force", "1");
|
||||
state.themeLibrary = await apiRequest(`/api/themes?${query}`);
|
||||
renderThemeLibrary();
|
||||
const available = (state.themeLibrary.items || []).some((item) => item.code === state.selectedThemeCode);
|
||||
if (!available) state.selectedThemeCode = "";
|
||||
const initialCode = state.selectedThemeCode || state.themeLibrary.items?.[0]?.code || "";
|
||||
if (initialCode) await selectTheme(initialCode, true);
|
||||
} catch (error) {
|
||||
setText("themeDateLabel", error.message || "题材数据暂不可用");
|
||||
renderEmptyState("themeDirectory", error.message || "题材数据加载失败");
|
||||
showToast(error.message || "题材数据加载失败");
|
||||
} finally {
|
||||
state.themeLoading = false;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function renderThemeLibrary() {
|
||||
const payload = state.themeLibrary;
|
||||
if (!payload) return;
|
||||
const summary = payload.summary || {};
|
||||
setText("themeDateLabel", `${payload.meta?.carried_forward ? "最近有效行情" : "行情日期"} ${payload.meta?.trade_date || "--"}`);
|
||||
document.querySelector("#themeSummary").innerHTML = [
|
||||
["收录题材", number(summary.theme_count), "个", ""],
|
||||
["当日上涨", number(summary.up_count), "个", "up"],
|
||||
["当日下跌", number(summary.down_count), "个", "down"],
|
||||
["人气题材", number(summary.hot_count), "个", "warning"],
|
||||
].map(([label, value, unit, tone]) => `<div><span>${label}</span><strong class="${tone}">${value}<small>${unit}</small></strong></div>`).join("");
|
||||
renderThemeDirectory();
|
||||
}
|
||||
|
||||
function renderThemeDirectory() {
|
||||
let items = [...(state.themeLibrary?.items || [])];
|
||||
if (state.themeQuery) {
|
||||
items = items.filter((item) => `${item.code} ${item.name}`.toLocaleLowerCase("zh-CN").includes(state.themeQuery));
|
||||
}
|
||||
setText("themeResultCount", `${items.length} 个`);
|
||||
document.querySelector("#themeDirectory").innerHTML = items.map((item, index) => {
|
||||
const active = item.code === state.selectedThemeCode;
|
||||
return `
|
||||
<button type="button" class="theme-directory-item-v2 ${active ? "active" : ""}" data-theme-code="${escapeHtml(item.code)}" aria-pressed="${active}">
|
||||
<span class="theme-rank-v2">${index + 1}</span>
|
||||
<span class="theme-directory-copy-v2"><strong class="market-preview-trigger" data-market-preview-type="theme" data-market-preview-id="${escapeHtml(item.code)}" title="悬停预览题材行情">${escapeHtml(item.name)}</strong><small>${number(item.member_count)} 只成分${item.hot_rank ? ` · 人气第 ${number(item.hot_rank)}` : ""}</small></span>
|
||||
<b class="${changeClass(item.change)}">${item.has_quote ? `${signed(item.change)}%` : "--"}</b>
|
||||
</button>`;
|
||||
}).join("") || emptyStateHtml("没有匹配的题材");
|
||||
}
|
||||
|
||||
async function selectTheme(code, keepSelection = false) {
|
||||
if (!code) return;
|
||||
state.selectedThemeCode = code;
|
||||
if (!keepSelection) renderThemeDirectory();
|
||||
document.querySelector("#themeDetailEmpty").hidden = false;
|
||||
document.querySelector("#themeDetailContent").hidden = true;
|
||||
setText("themeDetailEmpty", "正在读取题材详情");
|
||||
try {
|
||||
const query = new URLSearchParams({ code, trade_date: elements.tradeDate.value });
|
||||
state.themeDetail = await apiRequest(`/api/themes/detail?${query}`);
|
||||
renderThemeDetail();
|
||||
} catch (error) {
|
||||
setText("themeDetailEmpty", error.message || "题材详情加载失败");
|
||||
showToast(error.message || "题材详情加载失败");
|
||||
}
|
||||
}
|
||||
|
||||
function renderThemeDetail() {
|
||||
const payload = state.themeDetail;
|
||||
if (!payload) return;
|
||||
const theme = payload.theme || {};
|
||||
const summary = payload.summary || {};
|
||||
document.querySelector("#themeDetailEmpty").hidden = true;
|
||||
document.querySelector("#themeDetailContent").hidden = false;
|
||||
setText("themeDetailName", theme.name || "--");
|
||||
setText("themeDetailCode", `${theme.code || "--"} · ${payload.meta?.trade_date || "--"}`);
|
||||
setText("themeDetailChange", `${signed(theme.change)}%`);
|
||||
document.querySelector("#themeDetailChange").className = changeClass(theme.change);
|
||||
document.querySelector("#themeDetailMetrics").innerHTML = [
|
||||
["成分股", `${number(summary.member_count)} 只`, ""],
|
||||
["有行情", `${number(summary.quoted_count)} 只`, ""],
|
||||
["上涨", `${number(summary.up_count)} 只`, "up"],
|
||||
["下跌", `${number(summary.down_count)} 只`, "down"],
|
||||
["换手率", `${formatNumber(theme.turnover_rate, 2)}%`, ""],
|
||||
].map(([label, value, tone]) => `<div><span>${label}</span><strong class="${tone}">${value}</strong></div>`).join("");
|
||||
setText("themeMemberCount", `有行情 ${number(summary.quoted_count)} / ${number(summary.member_count)}`);
|
||||
const body = document.querySelector("#themeMemberTableBody");
|
||||
body.innerHTML = (payload.members || []).map((row, index) => `
|
||||
<tr data-code="${escapeHtml(row.code)}"><td class="row-number num muted">${index + 1}</td>
|
||||
<td><span class="stock-cell"><strong class="stock-name sname">${escapeHtml(row.name)}</strong><small class="stock-code scode">${escapeHtml(row.code)}</small></span></td>
|
||||
<td class="number num ${changeClass(row.change)}">${row.has_quote ? signed(row.change) : ""}</td>
|
||||
<td class="number num">${row.has_quote ? formatNumber(row.price, 2) : ""}</td><td class="number num">${row.has_quote ? formatNumber(row.amount_billion, 2) : ""}</td></tr>`).join("");
|
||||
bindStockRows(body);
|
||||
renderThemeDirectory();
|
||||
}
|
||||
|
||||
/* PRESERVATION-SOURCE-END app.js:2482-2583 */
|
||||
Reference in New Issue
Block a user