diff --git a/src/App.tsx b/src/App.tsx
index 12330a3..9d9515c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -21,7 +21,6 @@ const statisticOptions = [
{ key: 'maxValue', label: '最高值', shortLabel: '高' },
{ key: 'avgValue', label: '平均值', shortLabel: '均' },
{ key: 'medianValue', label: '中位数', shortLabel: '中' },
- { key: 'dataCount', label: '数据量', shortLabel: '量' },
] as const;
const metricOptions = [
@@ -29,6 +28,7 @@ const metricOptions = [
{ key: 'buildingArea', label: '建筑面积指标(元/m²)' },
{ key: 'builtArea', label: '建造面积指标(元/m²)' },
{ key: 'usableArea', label: '使用面积指标(元/m²)' },
+ { key: 'dataCount', label: '数据量' },
] as const;
const contentOptions = [
@@ -179,8 +179,8 @@ function formatAreaMetricValue(value: number) {
return `${formatNumber(value, fractionDigits)}元/m²`;
}
-function formatChartValue(value: number, metricKey: MetricKey, statisticKey: StatisticKey) {
- if (statisticKey === 'dataCount') {
+function formatChartValue(value: number, metricKey: MetricKey) {
+ if (metricKey === 'dataCount') {
return formatNumber(value, 0);
}
if (metricKey === 'cost') {
@@ -306,7 +306,7 @@ function renderTreeNodes(
{nodes.map((node) => {
const selected = selectedNodeKeys.has(getSelectionKey(contentKey, node.id));
- const color = node.canClick ? getNodeColor(contentKey, node.id) : undefined;
+ const color = getNodeColor(contentKey, node.id);
return (
-
@@ -322,13 +322,10 @@ function renderTreeNodes(
className="content-tree-select"
type="button"
aria-pressed={selected}
- disabled={!node.canClick}
- onClick={() => {
- if (node.canClick) onSelect(node);
- }}
+ onClick={() => onSelect(node)}
title={node.label}
>
- {node.canClick ? : null}
+
{node.label}
{node.loading ? 加载中 : null}
@@ -387,6 +384,9 @@ function App() {
const selectedMetric = metricOptions.find((option) => option.key === metricKey) ?? metricOptions[0];
const activeContent = contentOptions.find((option) => option.key === activeContentKey) ?? contentOptions[0];
const activeTree = treeByContent[activeContentKey];
+ const selectedValueKey = metricKey === 'dataCount' ? 'dataCount' : statisticKey;
+ const requestMetricKey = metricKey === 'dataCount' ? 'cost' : metricKey;
+ const seriesValueLabel = metricKey === 'dataCount' ? selectedMetric.label : selectedStatistic.label;
const selectedNodeKeys = useMemo(
() => new Set(selectedContentNodes.map((node) => getSelectionKey(node.contentKey, node.id))),
[selectedContentNodes],
@@ -536,6 +536,16 @@ function App() {
});
};
+ const handleActiveContentKeyChange = (nextContentKey: ContentKey) => {
+ if (nextContentKey === activeContentKey) return;
+ setActiveContentKey(nextContentKey);
+ setSelectedContentNodes([]);
+ setChartDataBySelection({});
+ setLoadError(null);
+ setLoadingHint('');
+ setLoading(false);
+ };
+
const updateMetricKey = (nextMetricKey: MetricKey) => {
setMetricKey(nextMetricKey);
setChartDataBySelection({});
@@ -599,12 +609,12 @@ function App() {
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify({
- groupBy: groupKey,
- metric: metricKey,
- nodes: selectedContentNodes.map((node) => ({
- key: getSelectionKey(node.contentKey, node.id),
- contentKey: node.contentKey,
+ body: JSON.stringify({
+ groupBy: groupKey,
+ metric: requestMetricKey,
+ nodes: selectedContentNodes.map((node) => ({
+ key: getSelectionKey(node.contentKey, node.id),
+ contentKey: node.contentKey,
nodeId: node.id,
})),
}),
@@ -636,7 +646,7 @@ function App() {
return () => {
controller.abort();
};
- }, [chartQueryVersion, groupKey, metricKey, selectedContentNodes]);
+ }, [chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes]);
useEffect(() => {
const frame = chartFrameRef.current;
@@ -751,7 +761,6 @@ function App() {
}, [statisticMenuOpen]);
const chartOptions = useMemo(() => {
- const isCount = statisticKey === 'dataCount';
const groupNames: string[] = [];
const groupNameSeen = new Set();
selectedContentNodes.forEach((node) => {
@@ -766,7 +775,7 @@ function App() {
const row: Record = { groupName };
selectedContentNodes.forEach((node, index) => {
const datum = chartDataBySelection[getSelectionKey(node.contentKey, node.id)]?.find((item) => item.groupName === groupName);
- row[getSeriesValueKey(index)] = datum?.[statisticKey] ?? null;
+ row[getSeriesValueKey(index)] = datum?.[selectedValueKey] ?? null;
});
return row;
});
@@ -866,7 +875,7 @@ function App() {
type: 'line',
xKey: 'groupName',
yKey: getSeriesValueKey(index),
- yName: `${node.label} ${selectedStatistic.label}`,
+ yName: `${node.label} ${seriesValueLabel}`,
stroke: node.color,
strokeWidth: 2,
marker: {
@@ -882,7 +891,7 @@ function App() {
renderer: ({ datum, yKey, yName }) => ({
title: yName,
data: [
- { label: selectedMetric.label, value: formatChartValue(Number(datum[yKey]), metricKey, statisticKey) },
+ { label: selectedMetric.label, value: formatChartValue(Number(datum[yKey]), metricKey) },
],
}),
},
@@ -915,7 +924,7 @@ function App() {
label: {
color: '#1f2933',
fontSize: 12,
- formatter: ({ value }) => formatChartValue(Number(value), metricKey, statisticKey),
+ formatter: ({ value }) => formatChartValue(Number(value), metricKey),
},
line: {
enabled: false,
@@ -942,9 +951,11 @@ function App() {
},
tooltip: {
enabled: true,
+ mode: 'shared',
+ pagination: true,
},
};
- }, [chartDataBySelection, metricKey, selectedContentNodes, selectedMetric.label, selectedStatistic.label, statisticKey]);
+ }, [chartDataBySelection, metricKey, requestMetricKey, selectedContentNodes, selectedMetric.label, seriesValueLabel, selectedValueKey, statisticKey]);
return (
@@ -1033,7 +1044,7 @@ function App() {
role="tab"
key={option.key}
aria-selected={option.key === activeContentKey}
- onClick={() => setActiveContentKey(option.key)}
+ onClick={() => handleActiveContentKeyChange(option.key)}
>
{option.label}