diff --git a/src/App.tsx b/src/App.tsx index 19049d1..83faee3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,11 +6,13 @@ import { ModuleRegistry as AgGridModuleRegistry, type ColDef, type ColGroupDef, + type GridApi, + type GridReadyEvent, type ValueFormatterParams, } from 'ag-grid-community'; import type { AgCartesianChartOptions } from 'ag-charts-community'; import { ModuleRegistry } from 'ag-charts-community'; -import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, Waypoints } from 'lucide-react'; +import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, PanelRightClose, PanelRightOpen, Waypoints } from 'lucide-react'; import { AnnotationsModule, ContextMenuModule, @@ -704,6 +706,7 @@ function renderFilterTreeNodes( function App() { const workspaceRef = useRef(null); const chartFrameRef = useRef(null); + const pivotGridApiRef = useRef | null>(null); const treeInitialLoadStartedRef = useRef>({ geoLocation: false, facilityType: false, @@ -723,6 +726,7 @@ function App() { const [metricKey, setMetricKey] = useState('cost'); const [chartViewKey, setChartViewKey] = useState('trend'); const [workspaceFullscreen, setWorkspaceFullscreen] = useState(false); + const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false); const [statisticMenuOpen, setStatisticMenuOpen] = useState(false); const [metricMenuOpen, setMetricMenuOpen] = useState(false); const [activeContentKey, setActiveContentKey] = useState('geoLocation'); @@ -837,6 +841,7 @@ function App() { const pivotToggleActionLabel = chartViewKey === 'pivot' ? '切换到趋势图' : '切换到表格'; const pivotToggleTitle = `${chartViewKey === 'pivot' ? '当前表格' : '当前趋势图'},${pivotToggleActionLabel}`; const fullscreenToggleLabel = workspaceFullscreen ? '退出全屏' : '全屏'; + const rightPanelToggleLabel = rightPanelCollapsed ? '展开选择区' : '收起选择区'; const activeContent = contentOptions.find((option) => option.key === activeContentKey) ?? contentOptions[0]; const activeTree = treeByContent[activeContentKey]; const activeFilter = filterOptions.find((option) => option.key === filterModalKey); @@ -937,14 +942,15 @@ function App() { { field: 'year', headerName: '年度', - minWidth: 68, - width: 74, + minWidth: 58, + width: 64, }, { field: 'name', headerName: '名称', flex: 1, - minWidth: 108, + minWidth: 86, + tooltipField: 'name', }, { headerName: '基准阀值', @@ -953,21 +959,21 @@ function App() { field: 'lowValue', headerName: '低值', type: 'numericColumn', - minWidth: 78, + minWidth: 64, valueFormatter, }, { field: 'centerValue', headerName: '中心值', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'highValue', headerName: '高值', type: 'numericColumn', - minWidth: 78, + minWidth: 64, valueFormatter, }, ], @@ -979,49 +985,49 @@ function App() { field: 'maxValue', headerName: '最大值', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'minValue', headerName: '最小值', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'avgValue', headerName: '平均值', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'medianValue', headerName: '中位数', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'standardDeviation', headerName: '标准差', type: 'numericColumn', - minWidth: 78, + minWidth: 68, valueFormatter, }, { field: 'interquartileRange', headerName: '四分位距', type: 'numericColumn', - minWidth: 88, + minWidth: 76, valueFormatter, }, { field: 'coefficientOfVariation', headerName: '变异系数', type: 'numericColumn', - minWidth: 88, + minWidth: 76, valueFormatter: ({ value }: ValueFormatterParams) => ( value == null ? '' : formatNumber(Number(value), 4) ), @@ -1032,6 +1038,17 @@ function App() { }, [requestMetricKey], ); + const fitPivotGridColumns = useCallback(() => { + window.requestAnimationFrame(() => { + pivotGridApiRef.current?.sizeColumnsToFit({ + defaultMinWidth: 58, + columnLimits: [ + { key: 'year', minWidth: 58, maxWidth: 72 }, + { key: 'name', minWidth: 86, maxWidth: 220 }, + ], + }); + }); + }, []); const selectedNodeKeys = useMemo( () => new Set(selectedContentNodes.map((node) => getSelectionKey(node.contentKey, node.id))), [selectedContentNodes], @@ -1916,6 +1933,13 @@ function App() { toggleWorkspaceFullscreen, ]); + useEffect(() => { + if (chartViewKey !== 'pivot') return; + + const timer = window.setTimeout(fitPivotGridColumns, 180); + return () => window.clearTimeout(timer); + }, [chartViewKey, fitPivotGridColumns, metricKey, pivotGridRowData.length, rightPanelCollapsed]); + const chartOptions = useMemo(() => { const trendData = groupNames.map((groupName) => { const row: Record = { groupName }; @@ -2184,7 +2208,11 @@ function App() { ))} -
+
{indicatorSelectionLabel ?
{indicatorSelectionLabel}
: null}
{chartFilterOptions.map((option) => { @@ -2315,7 +2343,17 @@ function App() { sortable: true, resizable: true, filter: true, + minWidth: 58, }} + rowHeight={30} + headerHeight={32} + groupHeaderHeight={32} + onGridReady={(event: GridReadyEvent) => { + pivotGridApiRef.current = event.api; + fitPivotGridColumns(); + }} + onGridSizeChanged={fitPivotGridColumns} + onFirstDataRendered={fitPivotGridColumns} suppressCellFocus overlayNoRowsTemplate={selectedContentNodes.length === 0 ? '请选择右侧分类项' : '暂无透视数据'} /> @@ -2329,7 +2367,21 @@ function App() {
-