123 lines
4.2 KiB
JavaScript
123 lines
4.2 KiB
JavaScript
function element(tag, className, text) {
|
|
const el = document.createElement(tag);
|
|
if (className) el.className = className;
|
|
if (text !== undefined) el.textContent = text;
|
|
return el;
|
|
}
|
|
|
|
function button(label, { active = false, onClick, className = '' } = {}) {
|
|
const el = element('button', className, label);
|
|
el.type = 'button';
|
|
el.dataset.active = String(active);
|
|
if (onClick) el.addEventListener('click', onClick);
|
|
return el;
|
|
}
|
|
|
|
export function createAppShell(root, { onReload }) {
|
|
root.replaceChildren();
|
|
|
|
const header = element('header', 'app-header');
|
|
const brand = element('div', 'app-brand');
|
|
brand.append(element('h1', '', '财报图表'), element('p', '', '选择公司与分析维度,数据更新后可重新加载。'));
|
|
header.append(brand, button('重新加载数据', { onClick: onReload, className: 'reload-button' }));
|
|
|
|
const layout = element('div', 'app-layout');
|
|
const companyNav = element('aside', 'sidebar company-sidebar');
|
|
const content = element('main', 'content');
|
|
const tabs = element('nav', 'tabs');
|
|
const cards = element('div', 'cards');
|
|
content.append(tabs, cards);
|
|
const chartNav = element('aside', 'sidebar chart-sidebar');
|
|
layout.append(companyNav, content, chartNav);
|
|
root.append(header, layout);
|
|
|
|
return { companyNav, tabs, cards, chartNav };
|
|
}
|
|
|
|
export function renderCompanies(container, companies, activeCompanyId, onSelect) {
|
|
container.replaceChildren(element('h2', 'sidebar-title', '公司'));
|
|
const list = element('div', 'company-list');
|
|
companies.forEach((company) => {
|
|
list.append(button(company.name, {
|
|
active: company.id === activeCompanyId,
|
|
onClick: () => onSelect(company.id),
|
|
className: 'company-button',
|
|
}));
|
|
});
|
|
container.append(list);
|
|
}
|
|
|
|
export function renderTabs(container, tabs, activeTabId, onSelect) {
|
|
container.replaceChildren();
|
|
tabs.forEach((tab) => {
|
|
container.append(button(tab.name, {
|
|
active: tab.id === activeTabId,
|
|
onClick: () => onSelect(tab.id),
|
|
className: 'tab-button',
|
|
}));
|
|
});
|
|
}
|
|
|
|
export function renderCards(container, charts, activeViews, { onExport, onSelectView }) {
|
|
container.replaceChildren();
|
|
if (!charts.length) {
|
|
container.append(element('p', 'empty-state', '该分类暂时没有图表。'));
|
|
return;
|
|
}
|
|
|
|
charts.forEach((chart) => {
|
|
const card = element('section', 'card');
|
|
card.id = `card-${chart.id}`;
|
|
const bar = element('div', 'card-bar');
|
|
const heading = element('div', 'card-heading');
|
|
heading.append(element('h2', 'card-title', chart.title));
|
|
|
|
const viewNames = Object.keys(chart.views || {});
|
|
const activeView = activeViews.get(chart.id) || chart.defaultView || viewNames[0];
|
|
const source = chart.views ? chart.views[activeView] : chart.csv;
|
|
heading.append(element('span', 'src', source));
|
|
bar.append(heading);
|
|
|
|
if (viewNames.length > 1) {
|
|
const switcher = element('div', 'view-switcher');
|
|
const labels = { annual: '年度', halfyear: '半年', quarter: '季度' };
|
|
viewNames.forEach((view) => {
|
|
switcher.append(button(labels[view] || view, {
|
|
active: view === activeView,
|
|
onClick: () => onSelectView(chart.id, view),
|
|
className: 'view-button',
|
|
}));
|
|
});
|
|
bar.append(switcher);
|
|
}
|
|
|
|
bar.append(button('导出 PNG', {
|
|
onClick: () => onExport(chart.id, chart.filename || chart.title),
|
|
className: 'export-button',
|
|
}));
|
|
const chartEl = element('div', 'chart');
|
|
chartEl.id = `chart-${chart.id}`;
|
|
chartEl.style.height = `${chart.height || 680}px`;
|
|
card.append(bar, chartEl);
|
|
container.append(card);
|
|
});
|
|
}
|
|
|
|
export function renderAnchors(container, charts, onSelect) {
|
|
container.replaceChildren(element('h2', 'sidebar-title', '当前图表'));
|
|
const list = element('div', 'anchor-list');
|
|
charts.forEach((chart) => {
|
|
container.append(button(chart.title, {
|
|
onClick: () => onSelect(chart.id),
|
|
className: 'anchor-button',
|
|
}));
|
|
});
|
|
if (!charts.length) container.append(element('p', 'sidebar-empty', '暂无图表'));
|
|
}
|
|
|
|
export function showChartError(chartId, message) {
|
|
const chartEl = document.getElementById(`chart-${chartId}`);
|
|
if (!chartEl) return;
|
|
chartEl.replaceChildren(element('div', 'chart-error', message));
|
|
}
|