- rowKey 使用 ?? 替代 ||,避免合法值 0 被当作 falsy - parseJson 增加非字符串类型防御检查 - toTree 增加递归深度限制防止循环引用栈溢出 - saveBatchNodes 增加空名称数组检查 - localStorage.setItem 捕获 QuotaExceededError - 路径参数缺失时显式抛出错误而非静默替换
46 lines
3.0 KiB
TypeScript
46 lines
3.0 KiB
TypeScript
import { Table, Tag, Typography } from 'antd';
|
|
|
|
const labels: Record<string, string> = {
|
|
id: '编号', tenantId: '租户', name: '名称', code: '编码', slug: '短编码', host: '域名', status: '状态', billingStatus: '账务状态',
|
|
createdAt: '创建时间', updatedAt: '更新时间', expiresAt: '到期时间', dueAt: '到期时间', amountCents: '金额', totalAmountCents: '金额',
|
|
balanceCents: '余额', phone: '手机号', email: '邮箱', ownerName: '负责人', provider: '服务商', enabled: '启用', appName: '应用名称',
|
|
appCode: '应用代码', title: '标题', summary: '摘要', action: '动作', targetType: '目标类型', targetId: '目标编号', severity: '级别',
|
|
version: '版本', billingCycle: '计费周期', effectiveAt: '生效时间', questionCount: '题目数', nodeType: '节点类型', sortOrder: '排序',
|
|
};
|
|
|
|
function isScalar(value: unknown) { return value == null || ['string', 'number', 'boolean'].includes(typeof value); }
|
|
function cell(value: unknown, key: string) {
|
|
if (value === null || value === undefined || value === '') return '-';
|
|
if (typeof value === 'boolean') return <Tag color={value ? 'green' : 'default'}>{value ? '是' : '否'}</Tag>;
|
|
if (/status|severity/i.test(key)) return <Tag color={String(value).toLowerCase().includes('active') || String(value).includes('正常') ? 'green' : 'blue'}>{String(value)}</Tag>;
|
|
if (/Cents$/.test(key) && typeof value === 'number') return `¥${(value / 100).toLocaleString('zh-CN', { minimumFractionDigits: 2 })}`;
|
|
return <Typography.Text ellipsis={{ tooltip: String(value) }}>{String(value)}</Typography.Text>;
|
|
}
|
|
|
|
export function rowsFromPayload(payload: unknown): Record<string, unknown>[] {
|
|
if (Array.isArray(payload)) return payload.filter((item) => item && typeof item === 'object') as Record<string, unknown>[];
|
|
if (!payload || typeof payload !== 'object') return [];
|
|
const object = payload as Record<string, unknown>;
|
|
for (const key of ['items', 'data', 'results', 'tenants', 'domains', 'staff', 'alerts', 'logs']) {
|
|
if (Array.isArray(object[key])) return object[key] as Record<string, unknown>[];
|
|
}
|
|
return [object];
|
|
}
|
|
|
|
export function BusinessTable({ payload, loading, onSelect }: { payload: unknown; loading: boolean; onSelect(record: Record<string, unknown> | null): void }) {
|
|
const rows = rowsFromPayload(payload);
|
|
const keys = [...new Set(rows.flatMap((row) => Object.keys(row).filter((key) => isScalar(row[key]))))].slice(0, 9);
|
|
return (
|
|
<Table
|
|
loading={loading}
|
|
dataSource={rows}
|
|
rowKey={(row, index) => String(row.id ?? row.tenantId ?? row.code ?? index)}
|
|
rowSelection={{ type: 'radio', onChange: (_, selected) => onSelect(selected[0] || null) }}
|
|
scroll={{ x: 'max-content' }}
|
|
pagination={{ pageSize: 20, showSizeChanger: true, showTotal: (total) => `共 ${total} 条` }}
|
|
locale={{ emptyText: '暂无业务数据' }}
|
|
columns={keys.map((key) => ({ title: labels[key] || key, dataIndex: key, key, ellipsis: true, render: (value) => cell(value, key) }))}
|
|
/>
|
|
);
|
|
}
|