feat(ai-analysis): 添加图表渲染和分析API客户端
This commit is contained in:
19
apps/web/components/analysis/AnalysisChart.tsx
Normal file
19
apps/web/components/analysis/AnalysisChart.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import dynamic from 'next/dynamic';
|
||||||
|
import type { UnifiedChartSpec } from '@ftb/shared';
|
||||||
|
import { toEChartsOption } from '@/lib/analysis-chart-renderer';
|
||||||
|
|
||||||
|
const ReactECharts = dynamic(() => import('echarts-for-react'), { ssr: false });
|
||||||
|
|
||||||
|
export function AnalysisChart({ spec }: { spec: UnifiedChartSpec }) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-[260px] rounded-[28px] border border-white/60 bg-white/75 p-4 shadow-[0_18px_60px_rgba(15,23,42,0.08)] backdrop-blur-xl">
|
||||||
|
<div className="mb-3">
|
||||||
|
<h3 className="text-[14px] font-semibold text-[#111827]">{spec.title}</h3>
|
||||||
|
{spec.subtitle && <p className="mt-1 text-[12px] text-[#64748b]">{spec.subtitle}</p>}
|
||||||
|
</div>
|
||||||
|
<ReactECharts option={toEChartsOption(spec)} style={{ height: 240, width: '100%' }} notMerge lazyUpdate />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
apps/web/lib/analysis-api.test.ts
Normal file
26
apps/web/lib/analysis-api.test.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { requestAnalysis } from './analysis-api';
|
||||||
|
import { __resetApiAvailabilityForTests, resolveApiBase } from './api';
|
||||||
|
|
||||||
|
test('requestAnalysis posts to /ai/analysis', async () => {
|
||||||
|
const originalFetch = globalThis.fetch;
|
||||||
|
const calls: string[] = [];
|
||||||
|
const apiBase = resolveApiBase();
|
||||||
|
globalThis.fetch = (async (input: RequestInfo | URL) => {
|
||||||
|
calls.push(String(input));
|
||||||
|
return new Response(JSON.stringify(calls.length === 1 ? {} : { ok: false, code: 'NO_DATA', message: 'no rows' }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
});
|
||||||
|
}) as typeof fetch;
|
||||||
|
|
||||||
|
try {
|
||||||
|
__resetApiAvailabilityForTests();
|
||||||
|
const result = await requestAnalysis({ question: '哪个部门最忙', context: { surface: 'ai_assistant' } }, ['management:view']);
|
||||||
|
assert.equal(result.ok, false);
|
||||||
|
assert.deepEqual(calls, [`${apiBase}/config/ai`, `${apiBase}/ai/analysis`]);
|
||||||
|
} finally {
|
||||||
|
globalThis.fetch = originalFetch;
|
||||||
|
}
|
||||||
|
});
|
||||||
6
apps/web/lib/analysis-api.ts
Normal file
6
apps/web/lib/analysis-api.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { AnalysisRequest, AnalysisResponse } from '@ftb/shared';
|
||||||
|
import { api } from './api';
|
||||||
|
|
||||||
|
export function requestAnalysis(request: AnalysisRequest, permissions: string[] = []): Promise<AnalysisResponse> {
|
||||||
|
return api.post<AnalysisResponse>('/ai/analysis', { ...request, permissions });
|
||||||
|
}
|
||||||
45
apps/web/lib/analysis-chart-renderer.test.ts
Normal file
45
apps/web/lib/analysis-chart-renderer.test.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { toEChartsOption } from './analysis-chart-renderer';
|
||||||
|
import type { UnifiedChartSpec } from '@ftb/shared';
|
||||||
|
|
||||||
|
test('toEChartsOption renders line_area with smooth line and area gradient', () => {
|
||||||
|
const spec: UnifiedChartSpec = {
|
||||||
|
kind: 'line_area',
|
||||||
|
title: '需求完成趋势',
|
||||||
|
dataset: { source: [{ label: '2026-07-01', value: 3 }], x: 'label', y: 'value' },
|
||||||
|
encoding: {
|
||||||
|
x: { field: 'label', label: '日期' },
|
||||||
|
y: { field: 'value', label: '完成数' },
|
||||||
|
value: { field: 'value', label: '完成数' },
|
||||||
|
color: { mode: 'single' },
|
||||||
|
},
|
||||||
|
annotations: [{ type: 'peak', label: '峰值', field: 'value', value: 3 }],
|
||||||
|
stylePreset: 'apple_vision_light',
|
||||||
|
};
|
||||||
|
|
||||||
|
const option: any = toEChartsOption(spec);
|
||||||
|
|
||||||
|
assert.equal(option.series[0].type, 'line');
|
||||||
|
assert.equal(option.series[0].smooth, true);
|
||||||
|
assert.ok(option.series[0].areaStyle);
|
||||||
|
assert.equal(option.xAxis.show, true);
|
||||||
|
assert.equal(option.yAxis.splitLine.show, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('toEChartsOption renders horizontal_bar with rounded bars and single color', () => {
|
||||||
|
const option: any = toEChartsOption({
|
||||||
|
kind: 'horizontal_bar',
|
||||||
|
title: '部门负载',
|
||||||
|
dataset: { source: [{ label: '研发', value: 8 }], label: 'label', value: 'value' },
|
||||||
|
encoding: {
|
||||||
|
value: { field: 'value', label: '待办数' },
|
||||||
|
color: { mode: 'single' },
|
||||||
|
},
|
||||||
|
stylePreset: 'apple_vision_light',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(option.series[0].type, 'bar');
|
||||||
|
assert.deepEqual(option.series[0].itemStyle.borderRadius, [0, 8, 8, 0]);
|
||||||
|
assert.equal(option.color.length, 1);
|
||||||
|
});
|
||||||
124
apps/web/lib/analysis-chart-renderer.ts
Normal file
124
apps/web/lib/analysis-chart-renderer.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import type { UnifiedChartSpec } from '@ftb/shared';
|
||||||
|
import type { EChartsOption } from 'echarts';
|
||||||
|
|
||||||
|
const ACCENT = '#0f172a';
|
||||||
|
const MUTED = '#94a3b8';
|
||||||
|
const RISK = '#f97316';
|
||||||
|
|
||||||
|
export function toEChartsOption(spec: UnifiedChartSpec): EChartsOption {
|
||||||
|
if (spec.kind === 'line_area') return lineAreaOption(spec);
|
||||||
|
if (spec.kind === 'horizontal_bar' || spec.kind === 'stacked_horizontal_bar') return horizontalBarOption(spec);
|
||||||
|
if (spec.kind === 'donut') return donutOption(spec);
|
||||||
|
return numberCardFallbackOption(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lineAreaOption(spec: UnifiedChartSpec): EChartsOption {
|
||||||
|
const xField = spec.dataset.x ?? spec.encoding.x?.field ?? 'label';
|
||||||
|
const yField = spec.dataset.y ?? spec.encoding.y?.field ?? spec.encoding.value?.field ?? 'value';
|
||||||
|
return {
|
||||||
|
color: [ACCENT],
|
||||||
|
grid: { left: 8, right: 8, top: 18, bottom: 24, containLabel: true },
|
||||||
|
tooltip: { trigger: 'axis', borderWidth: 0, backgroundColor: 'rgba(255,255,255,0.92)', textStyle: { color: '#111827' } },
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
show: true,
|
||||||
|
boundaryGap: false,
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisLabel: { color: MUTED, fontSize: 11 },
|
||||||
|
data: spec.dataset.source.map((row) => formatCategory(row[xField])),
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
show: true,
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisLabel: { show: false },
|
||||||
|
splitLine: { show: false },
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'circle',
|
||||||
|
symbolSize: 7,
|
||||||
|
data: spec.dataset.source.map((row) => formatNumeric(row[yField])),
|
||||||
|
lineStyle: { width: 3 },
|
||||||
|
areaStyle: { opacity: 0.14 },
|
||||||
|
markPoint: buildMarkPoints(spec),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function horizontalBarOption(spec: UnifiedChartSpec): EChartsOption {
|
||||||
|
const labelField = spec.dataset.label ?? 'label';
|
||||||
|
const valueField = spec.dataset.value ?? spec.encoding.value?.field ?? 'value';
|
||||||
|
const rows = spec.dataset.source.slice().reverse();
|
||||||
|
return {
|
||||||
|
color: [spec.encoding.color?.mode === 'risk' ? RISK : ACCENT],
|
||||||
|
grid: { left: 8, right: 32, top: 12, bottom: 12, containLabel: true },
|
||||||
|
tooltip: { trigger: 'item', borderWidth: 0, backgroundColor: 'rgba(255,255,255,0.92)' },
|
||||||
|
xAxis: { type: 'value', show: false },
|
||||||
|
yAxis: {
|
||||||
|
type: 'category',
|
||||||
|
axisTick: { show: false },
|
||||||
|
axisLine: { show: false },
|
||||||
|
axisLabel: { color: '#334155', fontSize: 12 },
|
||||||
|
data: rows.map((row) => formatCategory(row[labelField])),
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'bar',
|
||||||
|
data: rows.map((row) => formatNumeric(row[valueField])),
|
||||||
|
barWidth: 12,
|
||||||
|
itemStyle: { borderRadius: [0, 8, 8, 0] },
|
||||||
|
label: { show: true, position: 'right', color: '#64748b', fontSize: 11 },
|
||||||
|
animationDuration: 520,
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function donutOption(spec: UnifiedChartSpec): EChartsOption {
|
||||||
|
const labelField = spec.dataset.label ?? 'label';
|
||||||
|
const valueField = spec.dataset.value ?? spec.encoding.value?.field ?? 'value';
|
||||||
|
return {
|
||||||
|
color: ['#0f172a', '#64748b', '#94a3b8', '#cbd5e1', '#e2e8f0', '#f97316', '#fb923c', '#fed7aa'],
|
||||||
|
tooltip: { trigger: 'item', borderWidth: 0, backgroundColor: 'rgba(255,255,255,0.92)' },
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['62%', '82%'],
|
||||||
|
avoidLabelOverlap: true,
|
||||||
|
label: { color: '#334155', fontSize: 11 },
|
||||||
|
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 },
|
||||||
|
data: spec.dataset.source.map((row) => ({ name: formatCategory(row[labelField]), value: formatNumeric(row[valueField]) })),
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberCardFallbackOption(spec: UnifiedChartSpec): EChartsOption {
|
||||||
|
return horizontalBarOption({ ...spec, kind: 'horizontal_bar' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMarkPoints(spec: UnifiedChartSpec) {
|
||||||
|
if (!spec.annotations?.length) return undefined;
|
||||||
|
return {
|
||||||
|
symbolSize: 42,
|
||||||
|
label: { fontSize: 10 },
|
||||||
|
data: spec.annotations.map((item) => ({
|
||||||
|
type: item.type === 'peak' ? 'max' as const : undefined,
|
||||||
|
name: item.label,
|
||||||
|
value: item.value,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCategory(value: string | number | null): string {
|
||||||
|
return value == null ? '' : String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumeric(value: string | number | null): number {
|
||||||
|
if (typeof value === 'number') return value;
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
const parsed = Number(value);
|
||||||
|
return Number.isFinite(parsed) ? parsed : 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@
|
|||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@ftb/shared": "workspace:*",
|
"@ftb/shared": "workspace:*",
|
||||||
|
"echarts": "^6.1.0",
|
||||||
|
"echarts-for-react": "^3.0.6",
|
||||||
"lucide-react": "^1.17.0",
|
"lucide-react": "^1.17.0",
|
||||||
"next": "^14.2.0",
|
"next": "^14.2.0",
|
||||||
"pinyin-pro": "^3.28.1",
|
"pinyin-pro": "^3.28.1",
|
||||||
|
|||||||
48
pnpm-lock.yaml
generated
48
pnpm-lock.yaml
generated
@@ -102,6 +102,12 @@ importers:
|
|||||||
'@ftb/shared':
|
'@ftb/shared':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/shared
|
version: link:../../packages/shared
|
||||||
|
echarts:
|
||||||
|
specifier: ^6.1.0
|
||||||
|
version: 6.1.0
|
||||||
|
echarts-for-react:
|
||||||
|
specifier: ^3.0.6
|
||||||
|
version: 3.0.6(echarts@6.1.0)(react@18.3.1)
|
||||||
lucide-react:
|
lucide-react:
|
||||||
specifier: ^1.17.0
|
specifier: ^1.17.0
|
||||||
version: 1.17.0(react@18.3.1)
|
version: 1.17.0(react@18.3.1)
|
||||||
@@ -582,28 +588,24 @@ packages:
|
|||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@next/swc-linux-arm64-musl@14.2.33':
|
'@next/swc-linux-arm64-musl@14.2.33':
|
||||||
resolution: {integrity: sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==}
|
resolution: {integrity: sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-gnu@14.2.33':
|
'@next/swc-linux-x64-gnu@14.2.33':
|
||||||
resolution: {integrity: sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==}
|
resolution: {integrity: sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@next/swc-linux-x64-musl@14.2.33':
|
'@next/swc-linux-x64-musl@14.2.33':
|
||||||
resolution: {integrity: sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==}
|
resolution: {integrity: sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@next/swc-win32-arm64-msvc@14.2.33':
|
'@next/swc-win32-arm64-msvc@14.2.33':
|
||||||
resolution: {integrity: sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==}
|
resolution: {integrity: sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==}
|
||||||
@@ -1348,6 +1350,15 @@ packages:
|
|||||||
eastasianwidth@0.2.0:
|
eastasianwidth@0.2.0:
|
||||||
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
|
||||||
|
|
||||||
|
echarts-for-react@3.0.6:
|
||||||
|
resolution: {integrity: sha512-4zqLgTGWS3JvkQDXjzkR1k1CHRdpd6by0988TWMJgnvDytegWLbeP/VNZmMa+0VJx2eD7Y632bi2JquXDgiGJg==}
|
||||||
|
peerDependencies:
|
||||||
|
echarts: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||||
|
react: ^15.0.0 || >=16.0.0
|
||||||
|
|
||||||
|
echarts@6.1.0:
|
||||||
|
resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==}
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||||
|
|
||||||
@@ -2555,6 +2566,9 @@ packages:
|
|||||||
sisteransi@1.0.5:
|
sisteransi@1.0.5:
|
||||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||||
|
|
||||||
|
size-sensor@1.0.3:
|
||||||
|
resolution: {integrity: sha512-+k9mJ2/rQMiRmQUcjn+qznch260leIXY8r4FyYKKyRBO/s5UoeMAHGkCJyE1R/4wrIhTJONfyloY55SkE7ve3A==}
|
||||||
|
|
||||||
slash@3.0.0:
|
slash@3.0.0:
|
||||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2822,6 +2836,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
|
resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
tslib@2.3.0:
|
||||||
|
resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==}
|
||||||
|
|
||||||
tslib@2.8.1:
|
tslib@2.8.1:
|
||||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
@@ -3013,6 +3030,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
zrender@6.1.0:
|
||||||
|
resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==}
|
||||||
|
|
||||||
zustand@4.5.7:
|
zustand@4.5.7:
|
||||||
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
||||||
engines: {node: '>=12.7.0'}
|
engines: {node: '>=12.7.0'}
|
||||||
@@ -4428,6 +4448,18 @@ snapshots:
|
|||||||
|
|
||||||
eastasianwidth@0.2.0: {}
|
eastasianwidth@0.2.0: {}
|
||||||
|
|
||||||
|
echarts-for-react@3.0.6(echarts@6.1.0)(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
echarts: 6.1.0
|
||||||
|
fast-deep-equal: 3.1.3
|
||||||
|
react: 18.3.1
|
||||||
|
size-sensor: 1.0.3
|
||||||
|
|
||||||
|
echarts@6.1.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
zrender: 6.1.0
|
||||||
|
|
||||||
ee-first@1.1.1: {}
|
ee-first@1.1.1: {}
|
||||||
|
|
||||||
electron-to-chromium@1.5.368: {}
|
electron-to-chromium@1.5.368: {}
|
||||||
@@ -5824,6 +5856,8 @@ snapshots:
|
|||||||
|
|
||||||
sisteransi@1.0.5: {}
|
sisteransi@1.0.5: {}
|
||||||
|
|
||||||
|
size-sensor@1.0.3: {}
|
||||||
|
|
||||||
slash@3.0.0: {}
|
slash@3.0.0: {}
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
@@ -6061,6 +6095,8 @@ snapshots:
|
|||||||
minimist: 1.2.8
|
minimist: 1.2.8
|
||||||
strip-bom: 3.0.0
|
strip-bom: 3.0.0
|
||||||
|
|
||||||
|
tslib@2.3.0: {}
|
||||||
|
|
||||||
tslib@2.8.1: {}
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
turbo@2.9.16:
|
turbo@2.9.16:
|
||||||
@@ -6254,6 +6290,10 @@ snapshots:
|
|||||||
|
|
||||||
yocto-queue@0.1.0: {}
|
yocto-queue@0.1.0: {}
|
||||||
|
|
||||||
|
zrender@6.1.0:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.3.0
|
||||||
|
|
||||||
zustand@4.5.7(@types/react@18.3.31)(react@18.3.1):
|
zustand@4.5.7(@types/react@18.3.31)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
use-sync-external-store: 1.6.0(react@18.3.1)
|
use-sync-external-store: 1.6.0(react@18.3.1)
|
||||||
|
|||||||
Reference in New Issue
Block a user