导表
This commit is contained in:
parent
1f8325b1c1
commit
2e489de7a3
74
src/App.tsx
74
src/App.tsx
@ -44,6 +44,8 @@ const metricOptions = [
|
|||||||
{ key: 'dataCount', label: '数据量' },
|
{ key: 'dataCount', label: '数据量' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
const gridMetricOptions = metricOptions.filter((option) => option.key !== 'dataCount');
|
||||||
|
|
||||||
const metricShortLabels: Record<MetricKey, string> = {
|
const metricShortLabels: Record<MetricKey, string> = {
|
||||||
cost: '价',
|
cost: '价',
|
||||||
buildingArea: '建',
|
buildingArea: '建',
|
||||||
@ -729,6 +731,7 @@ function App() {
|
|||||||
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
|
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
|
||||||
const [statisticMenuOpen, setStatisticMenuOpen] = useState(false);
|
const [statisticMenuOpen, setStatisticMenuOpen] = useState(false);
|
||||||
const [metricMenuOpen, setMetricMenuOpen] = useState(false);
|
const [metricMenuOpen, setMetricMenuOpen] = useState(false);
|
||||||
|
const [gridContextMenuPosition, setGridContextMenuPosition] = useState<{ left: number; top: number } | null>(null);
|
||||||
const [activeContentKey, setActiveContentKey] = useState<ContentKey>('geoLocation');
|
const [activeContentKey, setActiveContentKey] = useState<ContentKey>('geoLocation');
|
||||||
const [treeByContent, setTreeByContent] = useState<Record<ContentKey, TreeNode[]>>({
|
const [treeByContent, setTreeByContent] = useState<Record<ContentKey, TreeNode[]>>({
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
@ -979,7 +982,7 @@ function App() {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headerName: '样本统计值',
|
headerName: `样本统计值(${pivotGridRowData.length})`,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
field: 'maxValue',
|
field: 'maxValue',
|
||||||
@ -1036,7 +1039,7 @@ function App() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
[requestMetricKey],
|
[pivotGridRowData.length, requestMetricKey],
|
||||||
);
|
);
|
||||||
const fitPivotGridColumns = useCallback(() => {
|
const fitPivotGridColumns = useCallback(() => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@ -1049,6 +1052,29 @@ function App() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
const exportPivotGrid = useCallback(() => {
|
||||||
|
pivotGridApiRef.current?.exportDataAsCsv({ fileName: '组团统计表.csv' });
|
||||||
|
setGridContextMenuPosition(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!gridContextMenuPosition) return;
|
||||||
|
|
||||||
|
const closeMenu = () => setGridContextMenuPosition(null);
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape') closeMenu();
|
||||||
|
};
|
||||||
|
document.addEventListener('mousedown', closeMenu);
|
||||||
|
document.addEventListener('keydown', handleKeyDown);
|
||||||
|
window.addEventListener('blur', closeMenu);
|
||||||
|
window.addEventListener('resize', closeMenu);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mousedown', closeMenu);
|
||||||
|
document.removeEventListener('keydown', handleKeyDown);
|
||||||
|
window.removeEventListener('blur', closeMenu);
|
||||||
|
window.removeEventListener('resize', closeMenu);
|
||||||
|
};
|
||||||
|
}, [gridContextMenuPosition]);
|
||||||
const selectedNodeKeys = useMemo(
|
const selectedNodeKeys = useMemo(
|
||||||
() => new Set(selectedContentNodes.map((node) => getSelectionKey(node.contentKey, node.id))),
|
() => new Set(selectedContentNodes.map((node) => getSelectionKey(node.contentKey, node.id))),
|
||||||
[selectedContentNodes],
|
[selectedContentNodes],
|
||||||
@ -1616,9 +1642,16 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const togglePivotView = useCallback(() => {
|
const togglePivotView = useCallback(() => {
|
||||||
setChartViewKey((current) => (current === 'trend' ? 'pivot' : 'trend'));
|
setChartViewKey((current) => {
|
||||||
|
if (current === 'trend') {
|
||||||
|
setMetricKey((selectedKey) => selectedKey === 'dataCount' ? metricOptions[0].key : selectedKey);
|
||||||
|
return 'pivot';
|
||||||
|
}
|
||||||
|
return 'trend';
|
||||||
|
});
|
||||||
setMetricMenuOpen(false);
|
setMetricMenuOpen(false);
|
||||||
setStatisticMenuOpen(false);
|
setStatisticMenuOpen(false);
|
||||||
|
setGridContextMenuPosition(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleWorkspaceFullscreen = useCallback(() => {
|
const toggleWorkspaceFullscreen = useCallback(() => {
|
||||||
@ -1889,10 +1922,6 @@ function App() {
|
|||||||
handleToolbarAction(button);
|
handleToolbarAction(button);
|
||||||
};
|
};
|
||||||
|
|
||||||
const suppressBrowserContextMenu = (event: MouseEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new MutationObserver(syncToolbarButtons);
|
const observer = new MutationObserver(syncToolbarButtons);
|
||||||
const initialSyncTimers = [
|
const initialSyncTimers = [
|
||||||
window.setTimeout(syncToolbarButtons, 0),
|
window.setTimeout(syncToolbarButtons, 0),
|
||||||
@ -1902,8 +1931,6 @@ function App() {
|
|||||||
];
|
];
|
||||||
document.addEventListener('keydown', handleKeyDown, true);
|
document.addEventListener('keydown', handleKeyDown, true);
|
||||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||||
document.addEventListener('contextmenu', suppressBrowserContextMenu);
|
|
||||||
frame.addEventListener('contextmenu', suppressBrowserContextMenu);
|
|
||||||
frame.addEventListener('click', handleToolbarClick, true);
|
frame.addEventListener('click', handleToolbarClick, true);
|
||||||
frame.addEventListener('keydown', handleToolbarKeyDown, true);
|
frame.addEventListener('keydown', handleToolbarKeyDown, true);
|
||||||
observer.observe(frame, { childList: true, subtree: true });
|
observer.observe(frame, { childList: true, subtree: true });
|
||||||
@ -1913,8 +1940,6 @@ function App() {
|
|||||||
initialSyncTimers.forEach((timer) => window.clearTimeout(timer));
|
initialSyncTimers.forEach((timer) => window.clearTimeout(timer));
|
||||||
document.removeEventListener('keydown', handleKeyDown, true);
|
document.removeEventListener('keydown', handleKeyDown, true);
|
||||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||||
document.removeEventListener('contextmenu', suppressBrowserContextMenu);
|
|
||||||
frame.removeEventListener('contextmenu', suppressBrowserContextMenu);
|
|
||||||
frame.removeEventListener('click', handleToolbarClick, true);
|
frame.removeEventListener('click', handleToolbarClick, true);
|
||||||
frame.removeEventListener('keydown', handleToolbarKeyDown, true);
|
frame.removeEventListener('keydown', handleToolbarKeyDown, true);
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
@ -2180,7 +2205,7 @@ function App() {
|
|||||||
</button>
|
</button>
|
||||||
{metricMenuOpen ? (
|
{metricMenuOpen ? (
|
||||||
<div className="metric-switcher-menu" role="menu" aria-label="切换纵坐标指标">
|
<div className="metric-switcher-menu" role="menu" aria-label="切换纵坐标指标">
|
||||||
{metricOptions.map((option) => (
|
{(variant === 'grid' ? gridMetricOptions : metricOptions).map((option) => (
|
||||||
<button
|
<button
|
||||||
className="metric-switcher-menu-item"
|
className="metric-switcher-menu-item"
|
||||||
type="button"
|
type="button"
|
||||||
@ -2291,7 +2316,30 @@ function App() {
|
|||||||
{loading || loadError ? <div className="chart-status">{loading ? loadingHint || '加载中' : loadError}</div> : null}
|
{loading || loadError ? <div className="chart-status">{loading ? loadingHint || '加载中' : loadError}</div> : null}
|
||||||
<AgCharts options={chartOptions} />
|
<AgCharts options={chartOptions} />
|
||||||
{chartViewKey === 'pivot' ? (
|
{chartViewKey === 'pivot' ? (
|
||||||
<div className="chart-pivot-grid-panel ag-theme-quartz">
|
<div
|
||||||
|
className="chart-pivot-grid-panel ag-theme-quartz"
|
||||||
|
onContextMenu={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setMetricMenuOpen(false);
|
||||||
|
setStatisticMenuOpen(false);
|
||||||
|
setGridContextMenuPosition({
|
||||||
|
left: Math.max(8, Math.min(event.clientX, window.innerWidth - 132)),
|
||||||
|
top: Math.max(8, Math.min(event.clientY, window.innerHeight - 44)),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{gridContextMenuPosition ? (
|
||||||
|
<div
|
||||||
|
className="grid-context-menu"
|
||||||
|
role="menu"
|
||||||
|
style={gridContextMenuPosition}
|
||||||
|
onMouseDown={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
|
<button type="button" role="menuitem" onClick={exportPivotGrid}>
|
||||||
|
导出表格
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<button
|
<button
|
||||||
className="chart-grid-tool-button chart-grid-tool-button--template"
|
className="chart-grid-tool-button chart-grid-tool-button--template"
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -452,6 +452,39 @@ button {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-context-menu {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 50;
|
||||||
|
min-width: 128px;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid rgba(90, 82, 72, 0.22);
|
||||||
|
border-radius: 3px;
|
||||||
|
background: #fffaf4;
|
||||||
|
box-shadow: 0 6px 18px rgba(69, 54, 36, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-context-menu button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #262a33;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-context-menu button:hover,
|
||||||
|
.grid-context-menu button:focus-visible {
|
||||||
|
color: #0078a8;
|
||||||
|
background: rgba(0, 120, 168, 0.08);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-grid-tool-button {
|
.chart-grid-tool-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 24px;
|
left: 24px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user