This commit is contained in:
wintsa committed 2026-08-16 17:06:45 +08:00
1 parent 3a82078bd2
commit 42a07e2bf8
14 files changed
+850 -17

No files matched your search

+122 -17
View File
@@ -12,7 +12,7 @@ import {
} from 'ag-grid-community';
import type { AgCartesianChartOptions } from 'ag-charts-community';
import { ModuleRegistry } from 'ag-charts-community';
import { Building2, CalendarRange, Construction, LayoutGrid, Library, LocateFixed, MapPinned, PanelRightClose, PanelRightOpen, Ruler, SquareFunction, Waypoints } from 'lucide-react';
import { Building2, CalendarRange, Construction, House, LayoutGrid, Library, LocateFixed, MapPinned, PanelRightClose, PanelRightOpen, Ruler, SquareFunction, Waypoints } from 'lucide-react';
import {
AnnotationsModule,
ContextMenuModule,
@@ -75,6 +75,7 @@ const filterOptions = [
{ key: 'geoLocation', label: '自然地理区位', icon: LocateFixed },
{ key: 'facilityType', label: '设施类别', icon: Building2 },
{ key: 'buildingFunction', label: '建筑功能', icon: SquareFunction },
{ key: 'buildingNature', label: '建筑性质', icon: House },
{ key: 'constructionStage', label: '建设阶段', icon: Construction },
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid },
{ key: 'time', label: '时间', icon: CalendarRange },
@@ -140,6 +141,26 @@ const contentTreeConfigs = {
},
} as const;
const buildingNatureBrowserConfig = {
conditionEndpoint: '/api/public/browser/condition/256',
dataEndpoint: '/api/public/browser/data/256',
treeid: '95504',
fieldid: '316390',
searchbrowserid: '36523',
browserParams: {
requestid: '',
isshowsearchtab: '1',
workflowid: '182028',
wfid: '182028',
billid: '-1815',
f_weaver_belongto_userid: '',
f_weaver_belongto_usertype: '',
wf_isagent: '',
wf_beagenter: '',
wfCreater: '',
},
} as const;
const chartLineColors = ['#0078a8', '#d14d72', '#1f8f4d', '#d96f23', '#6b5cc8', '#0d7680', '#9a6b12', '#b24b38'];
const defaultTemplateFilterNode = {
id: '3',
@@ -603,12 +624,16 @@ function isIndicatorTreeFilterKey(filterKey: FilterKey): filterKey is 'indicator
return filterKey === 'indicatorTree';
}
function isBuildingNatureFilterKey(filterKey: FilterKey): filterKey is 'buildingNature' {
return filterKey === 'buildingNature';
}
function isRangeFilterKey(filterKey: FilterKey): filterKey is RangeFilterKey {
return filterKey === 'time' || filterKey === 'area';
}
function isSingleSelectFilterKey(filterKey: FilterKey) {
return isTemplateFilterKey(filterKey) || isIndicatorTreeFilterKey(filterKey);
return isTemplateFilterKey(filterKey) || isIndicatorTreeFilterKey(filterKey) || isBuildingNatureFilterKey(filterKey);
}
function getDefaultTemplateFilterNodes(): SelectedFilterNode[] {
@@ -768,6 +793,7 @@ function App() {
geoLocation: false,
facilityType: false,
buildingFunction: false,
buildingNature: false,
constructionStage: false,
planningForm: false,
time: false,
@@ -818,6 +844,7 @@ function App() {
geoLocation: [],
facilityType: [],
buildingFunction: [],
buildingNature: [],
constructionStage: [],
planningForm: [],
time: [],
@@ -830,6 +857,7 @@ function App() {
geoLocation: false,
facilityType: false,
buildingFunction: false,
buildingNature: false,
constructionStage: false,
planningForm: false,
time: false,
@@ -842,6 +870,7 @@ function App() {
geoLocation: null,
facilityType: null,
buildingFunction: null,
buildingNature: null,
constructionStage: null,
planningForm: null,
time: null,
@@ -854,6 +883,7 @@ function App() {
geoLocation: [],
facilityType: [],
buildingFunction: [],
buildingNature: [],
constructionStage: [],
planningForm: [],
time: [],
@@ -866,6 +896,7 @@ function App() {
geoLocation: false,
facilityType: false,
buildingFunction: false,
buildingNature: false,
constructionStage: false,
planningForm: false,
time: false,
@@ -878,6 +909,7 @@ function App() {
geoLocation: null,
facilityType: null,
buildingFunction: null,
buildingNature: null,
constructionStage: null,
planningForm: null,
time: null,
@@ -890,6 +922,7 @@ function App() {
geoLocation: [],
facilityType: [],
buildingFunction: [],
buildingNature: [],
constructionStage: [],
planningForm: [],
time: [],
@@ -908,6 +941,7 @@ function App() {
geoLocation: 0,
facilityType: 0,
buildingFunction: 0,
buildingNature: 0,
constructionStage: 0,
planningForm: 0,
time: 0,
@@ -1032,7 +1066,7 @@ function App() {
);
const pivotGridPinnedBottomRowData = useMemo<PivotGridRow[]>(
() => {
if (hasTimeFilter) return [];
if (selectedContentNodes.length === 0 || hasTimeFilter) return [];
const datum = chartSummaryBySelection[overallSummaryKey]
?? selectedContentNodes
.map((node) => chartSummaryBySelection[getSelectionKey(node.contentKey, node.id)])
@@ -1243,6 +1277,7 @@ function App() {
[
appliedFilters.area,
appliedFilters.buildingFunction,
appliedFilters.buildingNature,
appliedFilters.constructionStage,
appliedFilters.facilityType,
appliedFilters.geoLocation,
@@ -1302,6 +1337,65 @@ function App() {
return normalizeTreeRows(rows);
};
const fetchBuildingNatureFilterTree = async (nodeId?: string, signal?: AbortSignal) => {
const treeParams = {
...browserTreeDefaults,
...buildingNatureBrowserConfig.browserParams,
treeid: buildingNatureBrowserConfig.treeid,
cube_treeid: buildingNatureBrowserConfig.treeid,
searchbrowserid: buildingNatureBrowserConfig.searchbrowserid,
fieldid: buildingNatureBrowserConfig.fieldid,
};
const requestOptions: RequestInit = {
credentials: 'include',
signal,
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
};
if (!nodeId) {
const conditionResponse = await fetch(
`${buildingNatureBrowserConfig.conditionEndpoint}?${buildQuery(treeParams)}`,
requestOptions,
);
if (!conditionResponse.ok) {
throw new Error(`建筑性质浏览条件加载失败:HTTP ${conditionResponse.status}`);
}
const conditionPayload = await conditionResponse.json() as { status?: boolean; msg?: string };
if (conditionPayload.status === false) {
throw new Error(conditionPayload.msg || '建筑性质浏览条件加载失败');
}
}
const dataParams = nodeId
? {
...treeParams,
type: '2',
id: nodeId,
isVirtual: '',
}
: {
...treeParams,
pageSize: '10',
current: '1',
min: '1',
max: '10',
};
const dataResponse = await fetch(
`${buildingNatureBrowserConfig.dataEndpoint}?${buildQuery(dataParams)}`,
requestOptions,
);
if (!dataResponse.ok) {
throw new Error(`建筑性质浏览数据加载失败:HTTP ${dataResponse.status}`);
}
const dataPayload = await dataResponse.json() as { status?: boolean; msg?: string };
if (dataPayload.status === false) {
throw new Error(dataPayload.msg || '建筑性质浏览数据加载失败');
}
return normalizeTreeRows(pickArray(dataPayload));
};
const normalizeBackendTree = (nodes: TreeNode[]): TreeNode[] => nodes.map((node) => ({
...node,
hasChildren: node.children.length > 0 || node.hasChildren,
@@ -1342,7 +1436,7 @@ function App() {
};
const fetchTemplateLibraryTree = async (signal?: AbortSignal) => {
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTree?${buildQuery({ key: 'templateLibrary' })}`, {
const response = await fetch(`${API_BASE_URL}${API_ROUTES.filterTree}?${buildQuery({ key: 'templateLibrary' })}`, {
signal,
headers: {
'X-Requested-With': 'XMLHttpRequest',
@@ -1357,7 +1451,7 @@ function App() {
};
const fetchIndicatorTree = async (signal?: AbortSignal) => {
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTree?${buildQuery({ key: 'indicatorTree', templateId: selectedTemplateId })}`, {
const response = await fetch(`${API_BASE_URL}${API_ROUTES.filterTree}?${buildQuery({ key: 'indicatorTree', templateId: selectedTemplateId })}`, {
signal,
headers: {
'X-Requested-With': 'XMLHttpRequest',
@@ -1415,6 +1509,12 @@ function App() {
};
const loadFilterTree = async (filterKey: FilterKey, keyword?: string, signal?: AbortSignal) => {
if (isBuildingNatureFilterKey(filterKey)) {
const nodes = filterTreeByKey.buildingNature.length > 0
? filterTreeByKey.buildingNature
: await fetchBuildingNatureFilterTree(undefined, signal);
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes;
}
if (filterKey === 'region') {
const nodes = await fetchRegionFilterTree(signal);
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes;
@@ -1621,7 +1721,7 @@ function App() {
return;
}
if (isTemplateFilterKey(filterKey) || filterKey === 'indicatorTree') {
if (isTemplateFilterKey(filterKey) || filterKey === 'indicatorTree' || isBuildingNatureFilterKey(filterKey)) {
filterSearchTimerRef.current = window.setTimeout(() => {
setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: true }));
setFilterSearchErrorByKey((current) => ({ ...current, [filterKey]: null }));
@@ -1678,10 +1778,13 @@ function App() {
})),
}));
if (node.loaded || !isContentFilterKey(filterModalKey)) return;
if (node.loaded || (!isContentFilterKey(filterModalKey) && !isBuildingNatureFilterKey(filterModalKey))) return;
const currentFilterKey = filterModalKey;
fetchContentTree(currentFilterKey, nodeId)
const childRequest = isBuildingNatureFilterKey(currentFilterKey)
? fetchBuildingNatureFilterTree(nodeId)
: fetchContentTree(currentFilterKey, nodeId);
childRequest
.then((children) => {
setFilterTreeByKey((current) => ({
...current,
@@ -1921,6 +2024,11 @@ function App() {
async function loadStats() {
if (selectedContentNodes.length === 0) {
setChartDataBySelection({});
setChartSummaryBySelection({});
setLoading(false);
setLoadingHint('');
setLoadError(null);
return;
}
setLoading(true);
@@ -1957,21 +2065,17 @@ function App() {
}
const payload = (await response.json()) as { data?: ApiBuildingFunctionStatBatchItem[] };
const results = (payload.data ?? [])
.filter((item) => item.key)
.filter((item) => item.key && item.key !== overallSummaryKey)
.map((item) => [item.key as string, (item.data ?? []).map(normalizeStat).slice(0, 36)] as const);
const summaries = (payload.data ?? [])
.filter((item) => item.key && item.summary)
.map((item) => [item.key as string, normalizeStat(item.summary as ApiBuildingFunctionStat)] as const);
const overallSummary = (payload.data ?? []).find((item) => item.summary)?.summary;
setChartDataBySelection((current) => ({
...current,
...Object.fromEntries(results),
}));
setChartSummaryBySelection((current) => ({
...current,
const overallSummary = (payload.data ?? []).find((item) => item.key === overallSummaryKey)?.summary;
setChartDataBySelection(Object.fromEntries(results));
setChartSummaryBySelection({
...Object.fromEntries(summaries),
...(overallSummary ? { [overallSummaryKey]: normalizeStat(overallSummary) } : {}),
}));
});
} catch (error) {
if (controller.signal.aborted) return;
setLoadError(error instanceof Error ? error.message : '接口请求失败');
@@ -2472,6 +2576,7 @@ function App() {
geoLocation: [],
facilityType: [],
buildingFunction: [],
buildingNature: [],
constructionStage: [],
planningForm: [],
time: [],