+ {(() => {
+ const series = financials?.series ?? {};
+ // 统一 period:优先 p.period;若仅有 year 则映射到 `${year}1231`
+ const toPeriod = (p: any): string | null => {
+ if (!p) return null;
+ if (p.period) return String(p.period);
+ if (p.year) return `${p.year}1231`;
+ return null;
+ };
+
+ const displayedKeys = [
+ 'roe', 'roa', 'roic', 'grossprofit_margin', 'netprofit_margin', 'revenue', 'tr_yoy', 'n_income',
+ 'dt_netprofit_yoy', 'n_cashflow_act', 'c_pay_acq_const_fiolta', '__free_cash_flow',
+ 'dividend_amount', 'repurchase_amount', 'total_assets', 'total_hldr_eqy_exc_min_int', 'goodwill',
+ '__sell_rate', '__admin_rate', '__rd_rate', '__other_fee_rate', '__tax_rate', '__depr_ratio',
+ '__money_cap_ratio', '__inventories_ratio', '__ar_ratio', '__prepay_ratio', '__fix_assets_ratio',
+ '__lt_invest_ratio', '__goodwill_ratio', '__other_assets_ratio', '__ap_ratio', '__adv_ratio',
+ '__st_borr_ratio', '__lt_borr_ratio', '__operating_assets_ratio', '__interest_bearing_debt_ratio',
+ 'invturn_days', 'arturn_days', 'payturn_days', 'fa_turn', 'assets_turn',
+ 'employees', '__rev_per_emp', '__profit_per_emp', '__salary_per_emp',
+ 'close', 'total_mv', 'pe', 'pb', 'holder_num'
+ ];
+
+ const displayedSeries = Object.entries(series)
+ .filter(([key]) => displayedKeys.includes(key))
+ .map(([, value]) => value);
+
+ const allPeriods = Array.from(
+ new Set(
+ (displayedSeries.flat() as any[])
+ .map((p) => toPeriod(p))
+ .filter((v): v is string => Boolean(v))
+ )
+ ).sort((a, b) => b.localeCompare(a)); // 最新在左(按 YYYYMMDD 排序)
+
+ if (allPeriods.length === 0) {
+ return
暂无可展示的数据
;
+ }
+ const periods = allPeriods.slice(0, 10);
+
+ const getValueByPeriod = (points: any[] | undefined, period: string): number | null => {
+ if (!points) return null;
+ const hit = points.find((pp) => toPeriod(pp) === period);
+ const v = hit?.value;
+ if (v == null) return null;
+ const num = typeof v === 'number' ? v : Number(v);
+ return Number.isFinite(num) ? num : null;
+ };
+ return (
+
+
+
+ 指标
+ {periods.map((p) => (
+ {formatReportPeriod(p)}
+ ))}
+
+
+
+ {(() => {
+ // 指定显示顺序(tushareParam)
+ const ORDER: Array<{ key: string; label?: string }> = [
+ { key: 'roe' },
+ { key: 'roa' },
+ { key: 'roic' },
+ { key: 'grossprofit_margin' },
+ { key: 'netprofit_margin' },
+ { key: 'revenue' },
+ { key: 'tr_yoy' },
+ { key: 'n_income' },
+ { key: 'dt_netprofit_yoy' },
+ { key: 'n_cashflow_act' },
+ { key: 'c_pay_acq_const_fiolta' },
+ { key: '__free_cash_flow', label: '自由现金流' },
+ { key: 'dividend_amount', label: '分红' },
+ { key: 'repurchase_amount', label: '回购' },
+ { key: 'total_assets' },
+ { key: 'total_hldr_eqy_exc_min_int' },
+ { key: 'goodwill' },
+ ];
+
+ // 在表格顶部插入"主要指标"行
+ const summaryRow = (
+
+ 主要指标
+ {periods.map((p) => (
+
+ ))}
+
+ );
+
+ const PERCENT_KEYS = new Set([
+ 'roe','roa','roic','grossprofit_margin','netprofit_margin','tr_yoy','dt_netprofit_yoy',
+ // Add all calculated percentage rows
+ '__sell_rate', '__admin_rate', '__rd_rate', '__other_fee_rate', '__tax_rate', '__depr_ratio',
+ '__money_cap_ratio', '__inventories_ratio', '__ar_ratio', '__prepay_ratio',
+ '__fix_assets_ratio', '__lt_invest_ratio', '__goodwill_ratio', '__other_assets_ratio',
+ '__ap_ratio', '__adv_ratio', '__st_borr_ratio', '__lt_borr_ratio',
+ '__operating_assets_ratio', '__interest_bearing_debt_ratio'
+ ]);
+ const rows = ORDER.map(({ key, label }) => {
+ const points = series[key] as any[] | undefined;
+
+ return (
+
+
+ {label || metricDisplayMap[key] || key}
+
+ {periods.map((p) => {
+ const v = getValueByPeriod(points, p);
+
+ const groupName = metricGroupMap[key];
+ const rawNum = typeof v === 'number' ? v : (v == null ? null : Number(v));
+ if (rawNum == null || Number.isNaN(rawNum)) {
+ return -;
+ }
+ if (PERCENT_KEYS.has(key)) {
+ const perc = Math.abs(rawNum) <= 1 && key !== 'tax_to_ebt' && key !== '__tax_rate' ? rawNum * 100 : rawNum;
+ const text = Number.isFinite(perc) ? numberFormatter.format(perc) : '-';
+ const isGrowthRow = key === 'tr_yoy' || key === 'dt_netprofit_yoy';
+ if (isGrowthRow) {
+ const isNeg = typeof perc === 'number' && perc < 0;
+ const isHighGrowth = typeof perc === 'number' && perc > 30;
+
+ let content = `${text}%`;
+ if (key === 'dt_netprofit_yoy' && typeof perc === 'number' && perc > 1000) {
+ content = `${(perc / 100).toFixed(1)}x`;
+ }
+
+ let tableCellClassName = 'text-right p-2';
+ let spanClassName = 'italic';
+
+ if (isNeg) {
+ tableCellClassName += ' bg-red-100';
+ spanClassName += ' text-red-600';
+ } else if (isHighGrowth) {
+ tableCellClassName += ' bg-green-100';
+ spanClassName += ' text-green-800 font-bold';
+ } else {
+ spanClassName += ' text-blue-600';
+ }
+
+ return (
+
+ {content}
+
+ );
+ }
+ const isHighlighted = (key === 'roe' && typeof perc === 'number' && perc > 12.5) ||
+ (key === 'grossprofit_margin' && typeof perc === 'number' && perc > 35) ||
+ (key === 'netprofit_margin' && typeof perc === 'number' && perc > 15);
+
+ if (isHighlighted) {
+ return (
+
+ {`${text}%`}
+
+ );
+ }
+ return (
+ {`${text}%`}
+ );
+ } else {
+ const isFinGroup = groupName === 'income' || groupName === 'balancesheet' || groupName === 'cashflow';
+ const scaled = key === 'total_mv'
+ ? rawNum / 10000
+ : (isFinGroup || key === '__free_cash_flow' || key === 'repurchase_amount' ? rawNum / 1e8 : rawNum);
+ const formatter = key === 'total_mv' ? integerFormatter : numberFormatter;
+ const text = Number.isFinite(scaled) ? formatter.format(scaled) : '-';
+ if (key === '__free_cash_flow') {
+ const isNeg = typeof scaled === 'number' && scaled < 0;
+ return (
+
+ {isNeg ? {text} : text}
+
+ );
+ }
+ return (
+ {text}
+ );
+ }
+ })}
+
+ );
+ });
+
+ // =========================
+ // 费用指标分组
+ // =========================
+ const feeHeaderRow = (
+
+ 费用指标
+ {periods.map((p) => (
+
+ ))}
+
+ );
+
+ const feeRows = [
+ { key: '__sell_rate', label: '销售费用率' },
+ { key: '__admin_rate', label: '管理费用率' },
+ { key: '__rd_rate', label: '研发费用率' },
+ { key: '__other_fee_rate', label: '其他费用率' },
+ { key: '__tax_rate', label: '所得税率' },
+ { key: '__depr_ratio', label: '折旧费用占比' },
+ ].map(({ key, label }) => (
+
+ {label}
+ {periods.map((p) => {
+ const points = series[key] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+
+ if (v == null || !Number.isFinite(v)) {
+ return -;
+ }
+ const rateText = numberFormatter.format(v);
+ const isNegative = v < 0;
+ return (
+
+ {isNegative ? {rateText}% : `${rateText}%`}
+
+ );
+ })}
+
+ ));
+
+ // =========================
+ // 资产占比分组
+ // =========================
+ const assetHeaderRow = (
+
+ 资产占比
+ {periods.map((p) => (
+
+ ))}
+
+ );
+
+ const ratioCell = (value: number | null, keyStr: string) => {
+ if (value == null || !Number.isFinite(value)) {
+ return -;
+ }
+ const text = numberFormatter.format(value);
+ const isNegative = value < 0;
+ const isHighRatio = value > 30;
+
+ let cellClassName = "text-right p-2";
+ if (isHighRatio) {
+ cellClassName += " bg-red-100";
+ } else if (isNegative) {
+ cellClassName += " bg-red-100";
+ }
+
+ return (
+
+ {isNegative ? {text}% : `${text}%`}
+
+ );
+ };
+
+ const assetRows = [
+ { key: '__money_cap_ratio', label: '现金占比' },
+ { key: '__inventories_ratio', label: '库存占比' },
+ { key: '__ar_ratio', label: '应收款占比' },
+ { key: '__prepay_ratio', label: '预付款占比' },
+ { key: '__fix_assets_ratio', label: '固定资产占比' },
+ { key: '__lt_invest_ratio', label: '长期投资占比' },
+ { key: '__goodwill_ratio', label: '商誉占比' },
+ { key: '__other_assets_ratio', label: '其他资产占比' },
+ { key: '__ap_ratio', label: '应付款占比' },
+ { key: '__adv_ratio', label: '预收款占比' },
+ { key: '__st_borr_ratio', label: '短期借款占比' },
+ { key: '__lt_borr_ratio', label: '长期借款占比' },
+ { key: '__operating_assets_ratio', label: '运营资产占比' },
+ { key: '__interest_bearing_debt_ratio', label: '有息负债率' },
+ ].map(({ key, label }) => (
+
+ {label}
+ {periods.map((p) => {
+ const points = series[key] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ return ratioCell(v, p);
+ })}
+
+ ));
+
+ // =========================
+ // 周转能力分组
+ // =========================
+ const turnoverHeaderRow = (
+
+ 周转能力
+ {periods.map((p) => (
+
+ ))}
+
+ );
+
+ const turnoverItems: Array<{ key: string; label: string }> = [
+ { key: 'invturn_days', label: '存货周转天数' },
+ { key: 'arturn_days', label: '应收款周转天数' },
+ { key: 'payturn_days', label: '应付款周转天数' },
+ { key: 'fa_turn', label: '固定资产周转率' },
+ { key: 'assets_turn', label: '总资产周转率' },
+ ];
+
+ const turnoverRows = turnoverItems.map(({ key, label }) => (
+
+ {label}
+ {periods.map((p) => {
+ const points = series[key] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ const value = typeof v === 'number' ? v : (v == null ? null : Number(v));
+
+ if (value == null || !Number.isFinite(value)) {
+ return -;
+ }
+ const text = numberFormatter.format(value);
+ if (key === 'arturn_days' && value > 90) {
+ return (
+ {text}
+ );
+ }
+ return {text};
+ })}
+
+ ));
+
+ return [
+ summaryRow,
+ ...rows,
+ feeHeaderRow,
+ ...feeRows,
+ assetHeaderRow,
+ ...assetRows,
+ turnoverHeaderRow,
+ ...turnoverRows,
+ // =========================
+ // 人均效率分组
+ // =========================
+ (
+
+ 人均效率
+ {periods.map((p) => (
+
+ ))}
+
+ ),
+ // 员工人数(整数千分位)
+ (
+
+ 员工人数
+ {periods.map((p) => {
+ const points = series['employees'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ return {integerFormatter.format(Math.round(v))};
+ })}
+
+ ),
+ // 人均创收 = 收入 / 员工人数(万元)
+ (
+
+ 人均创收(万元)
+ {periods.map((p) => {
+ const points = series['__rev_per_emp'] as any[] | undefined;
+ const val = getValueByPeriod(points, p);
+ if (val == null) {
+ return -;
+ }
+ return {numberFormatter.format(val)};
+ })}
+
+ ),
+ // 人均创利 = 净利润 / 员工人数(万元)
+ (
+
+ 人均创利(万元)
+ {periods.map((p) => {
+ const points = series['__profit_per_emp'] as any[] | undefined;
+ const val = getValueByPeriod(points, p);
+ if (val == null) {
+ return -;
+ }
+ return {numberFormatter.format(val)};
+ })}
+
+ ),
+ // 人均工资 = 支付给职工以及为职工支付的现金 / 员工人数(万元)
+ (
+
+ 人均工资(万元)
+ {periods.map((p) => {
+ const points = series['__salary_per_emp'] as any[] | undefined;
+ const val = getValueByPeriod(points, p);
+ if (val == null) {
+ return -;
+ }
+ return {numberFormatter.format(val)};
+ })}
+
+ ),
+ // =========================
+ // 市场表现分组
+ // =========================
+ (
+
+ 市场表现
+ {periods.map((p) => (
+
+ ))}
+
+ ),
+ // 股价(收盘价)
+ (
+
+ 股价
+ {periods.map((p) => {
+ const points = series['close'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ return {numberFormatter.format(v)};
+ })}
+
+ ),
+ // 市值(按亿为单位显示:乘以10000并整数千分位)
+ (
+
+ 市值(亿元)
+ {periods.map((p) => {
+ const points = series['total_mv'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ const scaled = v / 10000; // 转为亿元
+ return {integerFormatter.format(Math.round(scaled))};
+ })}
+
+ ),
+ // PE
+ (
+
+ PE
+ {periods.map((p) => {
+ const points = series['pe'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ return {numberFormatter.format(v)};
+ })}
+
+ ),
+ // PB
+ (
+
+ PB
+ {periods.map((p) => {
+ const points = series['pb'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ return {numberFormatter.format(v)};
+ })}
+
+ ),
+ // 股东户数
+ (
+
+ 股东户数
+ {periods.map((p) => {
+ const points = series['holder_num'] as any[] | undefined;
+ const v = getValueByPeriod(points, p);
+ if (v == null) {
+ return -;
+ }
+ return {integerFormatter.format(Math.round(v))};
+ })}
+
+ ),
+ ];
+ })()}
+
+
+ );
+ })()}
+