feat: 增加 DCF 等估值工具

This commit is contained in:
R524809
2026-10-22 15:43:17 +08:00
parent f646ee6718
commit a46de853a0
22 changed files with 2645 additions and 3 deletions
+20 -1
View File
@@ -1,6 +1,8 @@
import { loadCSV } from './csv.js';
import { getChartBuilder } from './charts/index.js';
import { downloadCanvas, mergeImages } from './merge.js';
import { renderStockReturnCalculator, valuationMenu } from './tools/stock-return-calculator.js';
import { renderDCFCalculator } from './tools/dcf-calculator.js';
import {
createAppShell,
renderAnchors,
@@ -33,6 +35,7 @@ let state;
let renderVersion = 0;
let cacheToken = Date.now();
let paramTimer;
let disposeTool;
function validateManifest(value) {
if (!Array.isArray(value?.companies) || value.companies.length === 0) {
@@ -89,6 +92,7 @@ function validateControls(companyId, tab) {
}
function getCompany(companyId) {
if (companyId === valuationMenu.id) return valuationMenu;
return manifest.companies.find((company) => company.id === companyId) || manifest.companies[0];
}
@@ -402,18 +406,31 @@ function renderCurrent() {
const company = getCompany(state.companyId);
const tab = getTab(company, state.tabId);
const charts = chartsForTab(company, tab);
shell.layout.classList.toggle('tools-layout', company.kind === 'tools');
const version = ++renderVersion;
// 整屏重渲染会带上最新参数把图都画一遍,这里再触发一次参数刷新纯属重复劳动
clearTimeout(paramTimer);
disposeTool?.();
disposeTool = undefined;
disposeAll();
renderCompanies(shell.companyNav, manifest.companies, company.id, (companyId) => {
renderCompanies(shell.companyNav, [...manifest.companies, valuationMenu], company.id, (companyId) => {
updateHash({ companyId });
});
renderTabs(shell.tabs, company.tabs, tab.id, (tabId) => {
updateHash({ companyId: company.id, tabId });
});
renderTabControls(shell.params, tab.controls, paramValues(company, tab), applyTabParam);
if (company.kind === 'tools') {
if (tab.id === 'dcf') {
disposeTool = renderDCFCalculator(shell.cards, manifest.companies.filter(item => ['ppmt', 'moutai', 'catl'].includes(item.id)));
} else {
renderStockReturnCalculator(shell.cards);
}
shell.chartNav.hidden = true;
return;
}
shell.chartNav.hidden = false;
renderCards(shell.cards, charts, activeViews, {
onExport: exportPNG,
isCarted: (instanceId) => cart.has(instanceId),
@@ -467,6 +484,8 @@ window.addEventListener('hashchange', () => {
let resizeTimer;
window.addEventListener('resize', () => {
if (!shell) return;
// Calculator layout is responsive CSS; rebuilding it would interrupt focused inputs.
if (getCompany(state.companyId).kind === 'tools') return;
clearTimeout(resizeTimer);
resizeTimer = setTimeout(renderCurrent, 200);
});