feat: 扩展 ECharts 第二阶段图表支持
新增 tree、treemap、sunburst、graph、sankey、chord、funnel、gauge 和 pictorialBar 系列,并注册对应的 ECharts 按需模块。 完善层级与关系数据校验、节点和边数量限制、默认布局及错误占位规则,同时补充默认 Markdown 与 README 示例。 静态渲染全局关闭 ECharts 动画,修复 Treemap 标签冻结在透明动画帧的问题,并完成快速预览、精确 PDF、容器镜像和 147 项全项目测试验证。
This commit is contained in:
@@ -11,6 +11,9 @@ export interface EChartsSecurityLimits {
|
||||
maxArrayLength: number;
|
||||
maxStringLength: number;
|
||||
maxSeries: number;
|
||||
maxHierarchyNodes: number;
|
||||
maxGraphNodes: number;
|
||||
maxGraphEdges: number;
|
||||
}
|
||||
|
||||
export const defaultEChartsSecurityLimits: EChartsSecurityLimits = {
|
||||
@@ -19,7 +22,10 @@ export const defaultEChartsSecurityLimits: EChartsSecurityLimits = {
|
||||
maxObjectKeys: 1_000,
|
||||
maxArrayLength: 20_000,
|
||||
maxStringLength: 100_000,
|
||||
maxSeries: 32
|
||||
maxSeries: 32,
|
||||
maxHierarchyNodes: 5_000,
|
||||
maxGraphNodes: 5_000,
|
||||
maxGraphEdges: 10_000
|
||||
};
|
||||
|
||||
const forbiddenPropertyNames = new Set([
|
||||
@@ -257,6 +263,86 @@ function assertSupportedSeries(
|
||||
`option.series.${index}.type 暂不支持 ${String(type)}`
|
||||
);
|
||||
}
|
||||
assertSeriesStructureLimits(item, index, type, limits);
|
||||
}
|
||||
}
|
||||
|
||||
function countHierarchyNodes(
|
||||
value: JsonValue | undefined,
|
||||
limit: number,
|
||||
path: string
|
||||
) {
|
||||
if (!Array.isArray(value)) {
|
||||
return;
|
||||
}
|
||||
let count = 0;
|
||||
const pending = [...value];
|
||||
while (pending.length > 0) {
|
||||
const node = pending.pop();
|
||||
count += 1;
|
||||
if (count > limit) {
|
||||
throw new EChartsFenceError(
|
||||
"LIMIT_EXCEEDED",
|
||||
`${path} 层级节点数量不能超过 ${limit}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
node !== null &&
|
||||
!Array.isArray(node) &&
|
||||
typeof node === "object" &&
|
||||
Array.isArray(node.children)
|
||||
) {
|
||||
pending.push(...node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function arrayLength(value: JsonValue | undefined) {
|
||||
return Array.isArray(value) ? value.length : 0;
|
||||
}
|
||||
|
||||
function assertSeriesStructureLimits(
|
||||
series: Record<string, JsonValue>,
|
||||
seriesIndex: number,
|
||||
type: string,
|
||||
limits: EChartsSecurityLimits
|
||||
) {
|
||||
const path = `option.series.${seriesIndex}`;
|
||||
if (
|
||||
type === "tree" ||
|
||||
type === "treemap" ||
|
||||
type === "sunburst"
|
||||
) {
|
||||
countHierarchyNodes(
|
||||
series.data,
|
||||
limits.maxHierarchyNodes,
|
||||
`${path}.data`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
type !== "graph" &&
|
||||
type !== "sankey" &&
|
||||
type !== "chord"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeCount =
|
||||
arrayLength(series.data) + arrayLength(series.nodes);
|
||||
if (nodeCount > limits.maxGraphNodes) {
|
||||
throw new EChartsFenceError(
|
||||
"LIMIT_EXCEEDED",
|
||||
`${path} 节点数量不能超过 ${limits.maxGraphNodes}`
|
||||
);
|
||||
}
|
||||
const edgeCount =
|
||||
arrayLength(series.links) + arrayLength(series.edges);
|
||||
if (edgeCount > limits.maxGraphEdges) {
|
||||
throw new EChartsFenceError(
|
||||
"LIMIT_EXCEEDED",
|
||||
`${path} 边数量不能超过 ${limits.maxGraphEdges}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user