Compare commits
5 Commits
main
...
building-f
| Author | SHA1 | Date | |
|---|---|---|---|
| 178a3e656f | |||
| cfb6afcdce | |||
| 58abe588aa | |||
| 46325ee801 | |||
| c65235f377 |
3
.playwright-mcp/console-2026-05-13T08-06-15-907Z.log
Normal file
3
.playwright-mcp/console-2026-05-13T08-06-15-907Z.log
Normal file
@ -0,0 +1,3 @@
|
||||
[ 6918ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:20102
|
||||
[ 7315ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://127.0.0.1:5173/favicon.ico:0
|
||||
[ 331036ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:20102
|
||||
@ -1 +1,6 @@
|
||||
[ 550538ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:20102
|
||||
[ 300ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:20102
|
||||
[ 1230ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://127.0.0.1:5173/favicon.ico:0
|
||||
[ 40506ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://nest.zwgczx.com/api/v1/zw/getBuildingFunctionCostFilterTree?key=templateLibrary¤ttime=1778660578349&__random__=1778660578349:0
|
||||
[ 40812ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://nest.zwgczx.com/api/v1/zw/getBuildingFunctionCostFilterTree?key=indicatorTree&templateId=3¤ttime=1778660578682&__random__=1778660578682:0
|
||||
[ 52231ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://nest.zwgczx.com/api/v1/zw/getBuildingFunctionCostFilterTree?key=templateLibrary¤ttime=1778660590101&__random__=1778660590101:0
|
||||
[ 53824ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://nest.zwgczx.com/api/v1/zw/getBuildingFunctionCostFilterTree?key=indicatorTree&templateId=3¤ttime=1778660591693&__random__=1778660591693:0
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
[ 136ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:20102
|
||||
[ 595ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://127.0.0.1:5173/favicon.ico:0
|
||||
@ -1,2 +0,0 @@
|
||||
[ 221ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://127.0.0.1:5179/node_modules/.vite/deps/react-dom_client.js?v=eccfa23d:20102
|
||||
[ 1056ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://127.0.0.1:5179/favicon.ico:0
|
||||
84
.playwright-mcp/page-2026-05-13T08-06-23-224Z.yml
Normal file
84
.playwright-mcp/page-2026-05-13T08-06-23-224Z.yml
Normal file
@ -0,0 +1,84 @@
|
||||
- main [ref=e3]:
|
||||
- generic:
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- region "年度费用模板" [ref=e4]:
|
||||
- generic "筛选条件" [ref=e5]:
|
||||
- button "省市区" [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e11]: 省市区
|
||||
- button "自然地理区位" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic [ref=e16]: 自然地理区位
|
||||
- button "设施类别" [ref=e17] [cursor=pointer]:
|
||||
- img [ref=e18]
|
||||
- generic [ref=e22]: 设施类别
|
||||
- button "建筑功能" [ref=e23] [cursor=pointer]:
|
||||
- img [ref=e24]
|
||||
- generic [ref=e27]: 建筑功能
|
||||
- button "建设阶段" [ref=e28] [cursor=pointer]:
|
||||
- img [ref=e29]
|
||||
- generic [ref=e34]: 建设阶段
|
||||
- button "规划形式" [ref=e35] [cursor=pointer]:
|
||||
- img [ref=e36]
|
||||
- generic [ref=e41]: 规划形式
|
||||
- region "年度总费用图表" [ref=e42]:
|
||||
- generic [ref=e43]:
|
||||
- button "纵坐标:造价(元)" [ref=e45] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e46]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e47]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e48]
|
||||
- toolbar "标注" [ref=e49]:
|
||||
- button "库" [ref=e50] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 库
|
||||
- button "树" [ref=e51] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 树
|
||||
- button "均" [ref=e52] [cursor=pointer]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e53]
|
||||
- button "Text Tool" [disabled] [ref=e54]
|
||||
- button "Shape Tool" [disabled] [ref=e55]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e56]
|
||||
- button "全屏(F11)" [ref=e57] [cursor=pointer]
|
||||
- button "Clear annotations" [disabled] [ref=e58]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e59]:
|
||||
- button "缩小" [disabled] [ref=e60]
|
||||
- button "放大" [ref=e61] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e62]
|
||||
- button "右移" [disabled] [ref=e63]
|
||||
- button "重置" [disabled] [ref=e64]
|
||||
- complementary "选择内容" [ref=e65]:
|
||||
- tablist "选择内容切换项" [ref=e66]:
|
||||
- tab "自然地理区位" [selected] [ref=e67] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e68] [cursor=pointer]
|
||||
- tab "建筑功能" [ref=e69] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e70] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e71] [cursor=pointer]
|
||||
- generic [ref=e72]:
|
||||
- generic [ref=e73]: 自然地理区位
|
||||
- generic [ref=e74]: 加载中
|
||||
84
.playwright-mcp/page-2026-05-13T08-22-19-114Z.yml
Normal file
84
.playwright-mcp/page-2026-05-13T08-22-19-114Z.yml
Normal file
@ -0,0 +1,84 @@
|
||||
- main [ref=e3]:
|
||||
- generic:
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- region "年度费用模板" [ref=e4]:
|
||||
- generic "筛选条件" [ref=e5]:
|
||||
- button "省市区" [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e11]: 省市区
|
||||
- button "自然地理区位" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic [ref=e16]: 自然地理区位
|
||||
- button "设施类别" [ref=e17] [cursor=pointer]:
|
||||
- img [ref=e18]
|
||||
- generic [ref=e22]: 设施类别
|
||||
- button "建筑功能" [ref=e23] [cursor=pointer]:
|
||||
- img [ref=e24]
|
||||
- generic [ref=e27]: 建筑功能
|
||||
- button "建设阶段" [ref=e28] [cursor=pointer]:
|
||||
- img [ref=e29]
|
||||
- generic [ref=e34]: 建设阶段
|
||||
- button "规划形式" [ref=e35] [cursor=pointer]:
|
||||
- img [ref=e36]
|
||||
- generic [ref=e41]: 规划形式
|
||||
- region "年度总费用图表" [ref=e42]:
|
||||
- generic [ref=e43]:
|
||||
- button "纵坐标:造价(元)" [ref=e45] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e46]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e47]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e48]
|
||||
- toolbar "标注" [ref=e49]:
|
||||
- button "库" [ref=e50] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 库
|
||||
- button "树" [ref=e51] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 树
|
||||
- button "均" [ref=e52] [cursor=pointer]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e53]
|
||||
- button "Text Tool" [disabled] [ref=e54]
|
||||
- button "Shape Tool" [disabled] [ref=e55]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e56]
|
||||
- button "全屏(F11)" [ref=e57] [cursor=pointer]
|
||||
- button "Clear annotations" [disabled] [ref=e58]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e59]:
|
||||
- button "缩小" [disabled] [ref=e60]
|
||||
- button "放大" [ref=e61] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e62]
|
||||
- button "右移" [disabled] [ref=e63]
|
||||
- button "重置" [disabled] [ref=e64]
|
||||
- complementary "选择内容" [ref=e65]:
|
||||
- tablist "选择内容切换项" [ref=e66]:
|
||||
- tab "自然地理区位" [selected] [ref=e67] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e68] [cursor=pointer]
|
||||
- tab "建筑功能" [ref=e69] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e70] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e71] [cursor=pointer]
|
||||
- generic [ref=e72]:
|
||||
- generic [ref=e73]: 自然地理区位
|
||||
- generic [ref=e74]: 加载中
|
||||
@ -1,80 +0,0 @@
|
||||
- main [ref=e3]:
|
||||
- generic:
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- region "年度费用模板" [ref=e4]:
|
||||
- generic "筛选条件" [ref=e5]:
|
||||
- button "省市区" [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e11]: 省市区
|
||||
- button "自然地理区位" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic [ref=e16]: 自然地理区位
|
||||
- button "设施类别" [ref=e17] [cursor=pointer]:
|
||||
- img [ref=e18]
|
||||
- generic [ref=e22]: 设施类别
|
||||
- button "建设阶段" [ref=e23] [cursor=pointer]:
|
||||
- img [ref=e24]
|
||||
- generic [ref=e29]: 建设阶段
|
||||
- button "规划形式" [ref=e30] [cursor=pointer]:
|
||||
- img [ref=e31]
|
||||
- generic [ref=e36]: 规划形式
|
||||
- region "年度总费用图表" [ref=e37]:
|
||||
- generic [ref=e38]:
|
||||
- button "纵坐标:造价(元)" [ref=e40] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e41]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e42]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e43]
|
||||
- toolbar "标注" [ref=e44]:
|
||||
- button "库" [ref=e45] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 库
|
||||
- button "树" [ref=e46] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 树
|
||||
- button "均" [ref=e47] [cursor=pointer]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e48]
|
||||
- button "Text Tool" [disabled] [ref=e49]
|
||||
- button "Shape Tool" [disabled] [ref=e50]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e51]
|
||||
- button "全屏(F11)" [ref=e52] [cursor=pointer]
|
||||
- button "Clear annotations" [disabled] [ref=e53]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e54]:
|
||||
- button "缩小" [disabled] [ref=e55]
|
||||
- button "放大" [ref=e56] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e57]
|
||||
- button "右移" [disabled] [ref=e58]
|
||||
- button "重置" [disabled] [ref=e59]
|
||||
- complementary "选择内容" [ref=e60]:
|
||||
- tablist "选择内容切换项" [ref=e61]:
|
||||
- tab "自然地理区位" [selected] [ref=e62] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e63] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e64] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e65] [cursor=pointer]
|
||||
- generic [ref=e66]:
|
||||
- generic [ref=e67]: 自然地理区位
|
||||
- generic [ref=e68]: 加载中
|
||||
@ -1,83 +0,0 @@
|
||||
- main [ref=e3]:
|
||||
- generic:
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- generic: 众为数字化管理平台
|
||||
- region "年度费用模板" [ref=e4]:
|
||||
- generic "筛选条件" [ref=e5]:
|
||||
- button "省市区" [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e11]: 省市区
|
||||
- button "自然地理区位" [ref=e12] [cursor=pointer]:
|
||||
- img [ref=e13]
|
||||
- generic [ref=e16]: 自然地理区位
|
||||
- button "设施类别" [ref=e17] [cursor=pointer]:
|
||||
- img [ref=e18]
|
||||
- generic [ref=e22]: 设施类别
|
||||
- button "建设阶段" [ref=e23] [cursor=pointer]:
|
||||
- img [ref=e24]
|
||||
- generic [ref=e29]: 建设阶段
|
||||
- button "规划形式" [ref=e30] [cursor=pointer]:
|
||||
- img [ref=e31]
|
||||
- generic [ref=e36]: 规划形式
|
||||
- region "年度总费用图表" [ref=e37]:
|
||||
- generic [ref=e38]:
|
||||
- button "纵坐标:造价(元)" [ref=e40] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e41]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e42]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e43]
|
||||
- toolbar "标注" [ref=e44]:
|
||||
- button "库" [disabled] [ref=e45] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 库
|
||||
- button "指" [disabled] [ref=e46] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 指
|
||||
- button "均" [disabled] [ref=e47] [cursor=pointer]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e48]
|
||||
- button "Text Tool" [disabled] [ref=e49]
|
||||
- button "Shape Tool" [disabled] [ref=e50]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e51]
|
||||
- button "全屏(F11)" [disabled] [ref=e52] [cursor=pointer]
|
||||
- button "Clear annotations" [disabled] [ref=e53]
|
||||
- button "切换到表格" [disabled] [ref=e54] [cursor=pointer]:
|
||||
- generic:
|
||||
- generic: 趋
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e55]:
|
||||
- button "缩小" [disabled] [ref=e56]
|
||||
- button "放大" [ref=e57] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e58]
|
||||
- button "右移" [disabled] [ref=e59]
|
||||
- button "重置" [disabled] [ref=e60]
|
||||
- complementary "选择内容" [ref=e61]:
|
||||
- tablist "选择内容切换项" [ref=e62]:
|
||||
- tab "自然地理区位" [selected] [ref=e63] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e64] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e65] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e66] [cursor=pointer]
|
||||
- generic [ref=e67]:
|
||||
- generic [ref=e68]: 自然地理区位
|
||||
- generic [ref=e69]: Unexpected token '<', "<!doctype "... is not valid JSON
|
||||
@ -1,10 +1,10 @@
|
||||
# 组团趋势图
|
||||
# 建筑设施对象趋势图
|
||||
|
||||
这是一个基于 `React + AG Charts` 的组团趋势图页面,用来查看不同分类维度下的年度统计趋势,并通过右侧筛选条件联动图表结果。
|
||||
这是一个基于 `React + AG Charts` 的建筑设施对象趋势图页面,用来查看不同分类维度下的年度统计趋势,并通过右侧筛选条件联动图表结果。
|
||||
|
||||
## 功能
|
||||
|
||||
- 左侧展示组团趋势图,支持最低值、最高值、平均值、中位数和数据量切换
|
||||
- 左侧展示建筑设施对象趋势图,支持最低值、最高值、平均值、中位数和数据量切换
|
||||
- 右侧展示分类树,支持按 `自然地理区位 / 设施类别 / 建设阶段 / 规划形式` 选择节点
|
||||
- 筛选条件支持 `省市区`,后端会根据 `uf_xzqy` 展开到区级 `id` 后再过滤
|
||||
- 支持搜索筛选树节点,支持多条件叠加
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<title>AG Chart Service</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="ztChart"></div>
|
||||
<div id="sbChart"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
148
src/App.tsx
148
src/App.tsx
@ -12,7 +12,7 @@ import {
|
||||
} from 'ag-grid-community';
|
||||
import type { AgCartesianChartOptions } from 'ag-charts-community';
|
||||
import { ModuleRegistry } from 'ag-charts-community';
|
||||
import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, PanelRightClose, PanelRightOpen, Waypoints } from 'lucide-react';
|
||||
import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, PanelRightClose, PanelRightOpen, SquareFunction, Waypoints } from 'lucide-react';
|
||||
import {
|
||||
AnnotationsModule,
|
||||
ContextMenuModule,
|
||||
@ -29,6 +29,12 @@ AgGridModuleRegistry.registerModules([AgGridAllCommunityModule]);
|
||||
const API_BASE_URL = 'https://nest.zwgczx.com/api/v1';
|
||||
// const API_BASE_URL = 'http://127.0.0.1:9089/api/v1';
|
||||
|
||||
const API_ROUTES = {
|
||||
filterTree: '/zw/getBuildingFacilityObjectFilterTree',
|
||||
filterTreeSearch: '/zw/getBuildingFacilityObjectFilterTreeSearch',
|
||||
statsBatch: '/zw/getBuildingFacilityObjectStatsBatch',
|
||||
} as const;
|
||||
|
||||
const statisticOptions = [
|
||||
{ key: 'minValue', label: '最低值', shortLabel: '低' },
|
||||
{ key: 'maxValue', label: '最高值', shortLabel: '高' },
|
||||
@ -55,6 +61,7 @@ const metricShortLabels: Record<MetricKey, string> = {
|
||||
const contentOptions = [
|
||||
{ key: 'geoLocation', label: '自然地理区位' },
|
||||
{ key: 'facilityType', label: '设施类别' },
|
||||
{ key: 'buildingFunction', label: '建筑功能' },
|
||||
{ key: 'constructionStage', label: '建设阶段' },
|
||||
{ key: 'planningForm', label: '规划形式' },
|
||||
] as const;
|
||||
@ -65,6 +72,7 @@ const filterOptions = [
|
||||
{ key: 'region', label: '省市区', icon: MapPinned },
|
||||
{ key: 'geoLocation', label: '自然地理区位', icon: LocateFixed },
|
||||
{ key: 'facilityType', label: '设施类别', icon: Building2 },
|
||||
{ key: 'buildingFunction', label: '建筑功能', icon: SquareFunction },
|
||||
{ key: 'constructionStage', label: '建设阶段', icon: Construction },
|
||||
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid },
|
||||
] as const;
|
||||
@ -103,6 +111,17 @@ const contentTreeConfigs = {
|
||||
fieldid: '305426',
|
||||
defaultExpandedLevel: 3,
|
||||
},
|
||||
buildingFunction: {
|
||||
endpoint: '/api/public/browser/data/256',
|
||||
treeid: '95004',
|
||||
fieldid: '305919',
|
||||
defaultExpandedLevel: 0,
|
||||
browserParams: {
|
||||
workflowid: '182032',
|
||||
wfid: '182032',
|
||||
billid: '-1817',
|
||||
},
|
||||
},
|
||||
constructionStage: {
|
||||
endpoint: '/api/public/browser/data/256',
|
||||
treeid: '94007',
|
||||
@ -157,6 +176,7 @@ type StatisticKey = (typeof statisticOptions)[number]['key'];
|
||||
type MetricKey = (typeof metricOptions)[number]['key'];
|
||||
type ContentKey = (typeof contentOptions)[number]['key'];
|
||||
type FilterKey = (typeof filterOptions)[number]['key'];
|
||||
type GroupKey = 'year';
|
||||
type ChartViewKey = 'trend' | 'pivot';
|
||||
type ApiBuildingFunctionStat = {
|
||||
group_key?: string | number | null;
|
||||
@ -568,10 +588,6 @@ function isDefaultTemplateSelection(nodes: SelectedFilterNode[]) {
|
||||
return nodes.length === 1 && nodes[0]?.id === defaultTemplateFilterNode.id;
|
||||
}
|
||||
|
||||
function isSameFilterSelection(a: SelectedFilterNode[], b: SelectedFilterNode[]) {
|
||||
return a.length === b.length && a.every((node, index) => node.id === b[index]?.id);
|
||||
}
|
||||
|
||||
function getDefaultIndicatorTreeFilterNodes(nodes: TreeNode[]): SelectedFilterNode[] {
|
||||
const defaultNode = nodes[0];
|
||||
if (!defaultNode) return [];
|
||||
@ -710,6 +726,7 @@ function App() {
|
||||
const treeInitialLoadStartedRef = useRef<Record<ContentKey, boolean>>({
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
buildingFunction: false,
|
||||
constructionStage: false,
|
||||
planningForm: false,
|
||||
});
|
||||
@ -719,11 +736,13 @@ function App() {
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
buildingFunction: false,
|
||||
constructionStage: false,
|
||||
planningForm: false,
|
||||
});
|
||||
const [statisticKey, setStatisticKey] = useState<StatisticKey>('avgValue');
|
||||
const [metricKey, setMetricKey] = useState<MetricKey>('cost');
|
||||
const [groupKey, setGroupKey] = useState<GroupKey>('year');
|
||||
const [chartViewKey, setChartViewKey] = useState<ChartViewKey>('trend');
|
||||
const [workspaceFullscreen, setWorkspaceFullscreen] = useState(false);
|
||||
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
|
||||
@ -733,18 +752,21 @@ function App() {
|
||||
const [treeByContent, setTreeByContent] = useState<Record<ContentKey, TreeNode[]>>({
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
buildingFunction: [],
|
||||
constructionStage: [],
|
||||
planningForm: [],
|
||||
});
|
||||
const [treeLoadingByContent, setTreeLoadingByContent] = useState<Record<ContentKey, boolean>>({
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
buildingFunction: false,
|
||||
constructionStage: false,
|
||||
planningForm: false,
|
||||
});
|
||||
const [treeErrorByContent, setTreeErrorByContent] = useState<Record<ContentKey, string | null>>({
|
||||
geoLocation: null,
|
||||
facilityType: null,
|
||||
buildingFunction: null,
|
||||
constructionStage: null,
|
||||
planningForm: null,
|
||||
});
|
||||
@ -761,6 +783,7 @@ function App() {
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
buildingFunction: [],
|
||||
constructionStage: [],
|
||||
planningForm: [],
|
||||
});
|
||||
@ -770,6 +793,7 @@ function App() {
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
buildingFunction: false,
|
||||
constructionStage: false,
|
||||
planningForm: false,
|
||||
});
|
||||
@ -779,6 +803,7 @@ function App() {
|
||||
region: null,
|
||||
geoLocation: null,
|
||||
facilityType: null,
|
||||
buildingFunction: null,
|
||||
constructionStage: null,
|
||||
planningForm: null,
|
||||
});
|
||||
@ -788,6 +813,7 @@ function App() {
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
buildingFunction: [],
|
||||
constructionStage: [],
|
||||
planningForm: [],
|
||||
});
|
||||
@ -797,6 +823,7 @@ function App() {
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
buildingFunction: false,
|
||||
constructionStage: false,
|
||||
planningForm: false,
|
||||
});
|
||||
@ -806,6 +833,7 @@ function App() {
|
||||
region: null,
|
||||
geoLocation: null,
|
||||
facilityType: null,
|
||||
buildingFunction: null,
|
||||
constructionStage: null,
|
||||
planningForm: null,
|
||||
});
|
||||
@ -815,6 +843,7 @@ function App() {
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
buildingFunction: [],
|
||||
constructionStage: [],
|
||||
planningForm: [],
|
||||
});
|
||||
@ -829,6 +858,7 @@ function App() {
|
||||
region: 0,
|
||||
geoLocation: 0,
|
||||
facilityType: 0,
|
||||
buildingFunction: 0,
|
||||
constructionStage: 0,
|
||||
planningForm: 0,
|
||||
});
|
||||
@ -861,7 +891,10 @@ function App() {
|
||||
);
|
||||
const indicatorSelectionLabel = appliedFilters.indicatorTree[0]?.label ?? defaultIndicatorTreeNodes[0]?.label ?? '';
|
||||
const activeFilterCount = Object.entries(appliedFilters).reduce((total, [key, nodes]) => (
|
||||
key === 'templateLibrary' && isDefaultTemplateSelection(nodes) ? total : total + nodes.length
|
||||
(key === 'templateLibrary' && isDefaultTemplateSelection(nodes))
|
||||
|| (key === 'indicatorTree' && isDefaultIndicatorTreeSelection(nodes, filterTreeByKey.indicatorTree))
|
||||
? total
|
||||
: total + nodes.length
|
||||
), 0);
|
||||
const chartEmptyText = selectedContentNodes.length === 0
|
||||
? '请选择右侧分类项'
|
||||
@ -1057,6 +1090,14 @@ function App() {
|
||||
() => new Set(draftFilterNodes.map((node) => getFilterSelectionKey(node.filterKey, node.id))),
|
||||
[draftFilterNodes],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultIndicatorTreeNodes.length === 0) return;
|
||||
setAppliedFilters((current) => {
|
||||
if (current.indicatorTree.length > 0) return current;
|
||||
return { ...current, indicatorTree: defaultIndicatorTreeNodes };
|
||||
});
|
||||
}, [defaultIndicatorTreeNodes]);
|
||||
const appliedFilterPayload = useMemo(
|
||||
() => filterOptions
|
||||
.map((option) => ({
|
||||
@ -1083,6 +1124,7 @@ function App() {
|
||||
}
|
||||
const treeParams = {
|
||||
...browserTreeDefaults,
|
||||
...('browserParams' in config ? config.browserParams : {}),
|
||||
treeid: config.treeid,
|
||||
cube_treeid: config.treeid,
|
||||
fieldid: config.fieldid,
|
||||
@ -1140,7 +1182,7 @@ function App() {
|
||||
};
|
||||
|
||||
const fetchRegionFilterTree = async (signal?: AbortSignal) => {
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTree?${buildQuery({ key: 'region' })}`, {
|
||||
const response = await fetch(`${API_BASE_URL}${API_ROUTES.filterTree}?${buildQuery({ key: 'region' })}`, {
|
||||
signal,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
@ -1185,7 +1227,7 @@ function App() {
|
||||
};
|
||||
|
||||
const fetchBackendFilterTreeSearch = async (filterKey: FilterKey, keyword: string, signal?: AbortSignal) => {
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTreeSearch?${buildQuery({
|
||||
const response = await fetch(`${API_BASE_URL}${API_ROUTES.filterTreeSearch}?${buildQuery({
|
||||
key: filterKey,
|
||||
keyword,
|
||||
nodePrefix: isContentFilterKey(filterKey) ? getTreeNodePrefix(filterTreeByKey[filterKey]) : '',
|
||||
@ -1370,6 +1412,10 @@ function App() {
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
ensureFilterTreeLoaded('indicatorTree');
|
||||
}, [selectedTemplateId]);
|
||||
|
||||
const openFilterModal = (filterKey: FilterKey) => {
|
||||
setFilterModalKey(filterKey);
|
||||
if (isIndicatorTreeFilterKey(filterKey) && appliedFilters[filterKey].length === 0 && defaultIndicatorTreeNodes.length > 0) {
|
||||
@ -1386,10 +1432,6 @@ function App() {
|
||||
ensureFilterTreeLoaded(filterKey);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
ensureFilterTreeLoaded('indicatorTree');
|
||||
}, [selectedTemplateId]);
|
||||
|
||||
const closeFilterModal = () => {
|
||||
if (filterModalKey) {
|
||||
filterSearchRequestSeqRef.current[filterModalKey] += 1;
|
||||
@ -1544,22 +1586,18 @@ function App() {
|
||||
if (nextDraftNodes.length === 0) {
|
||||
nextDraftNodes = defaultIndicatorTreeNodes;
|
||||
}
|
||||
if (isDefaultIndicatorTreeSelection(nextDraftNodes, filterTreeByKey.indicatorTree)) {
|
||||
nextDraftNodes = [];
|
||||
}
|
||||
}
|
||||
const shouldReloadIndicatorTree = isTemplateFilterKey(filterModalKey) && !isSameFilterSelection(appliedFilters.templateLibrary, nextDraftNodes);
|
||||
setAppliedFilters((current) => {
|
||||
const nextFilters = {
|
||||
...current,
|
||||
[filterModalKey]: nextDraftNodes,
|
||||
};
|
||||
if (shouldReloadIndicatorTree) {
|
||||
if (isTemplateFilterKey(filterModalKey)) {
|
||||
nextFilters.indicatorTree = [];
|
||||
}
|
||||
return nextFilters;
|
||||
});
|
||||
if (shouldReloadIndicatorTree) {
|
||||
if (isTemplateFilterKey(filterModalKey)) {
|
||||
resetIndicatorTreeState();
|
||||
}
|
||||
setChartDataBySelection({});
|
||||
@ -1579,7 +1617,7 @@ function App() {
|
||||
: isIndicatorTreeFilterKey(filterKey)
|
||||
? defaultIndicatorTreeNodes
|
||||
: [];
|
||||
if (isSameFilterSelection(appliedFilters[filterKey], nextNodes)) {
|
||||
if (appliedFilters[filterKey].length === nextNodes.length && appliedFilters[filterKey].every((node, index) => node.id === nextNodes[index]?.id)) {
|
||||
return;
|
||||
}
|
||||
setAppliedFilters((current) => {
|
||||
@ -1699,14 +1737,14 @@ function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostStatsBatch`, {
|
||||
const response = await fetch(`${API_BASE_URL}${API_ROUTES.statsBatch}`, {
|
||||
method: 'POST',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
groupBy: 'year',
|
||||
groupBy: groupKey,
|
||||
metric: requestMetricKey,
|
||||
templateId: selectedTemplateId,
|
||||
filters: appliedFilterPayload,
|
||||
@ -1753,7 +1791,7 @@ function App() {
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [appliedFilterPayload, chartQueryVersion, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
|
||||
}, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
|
||||
|
||||
useEffect(() => {
|
||||
const frame = chartFrameRef.current;
|
||||
@ -1792,7 +1830,6 @@ function App() {
|
||||
indicatorButton.classList.add('chart-indicator-button');
|
||||
enableCustomToolbarButton(indicatorButton);
|
||||
setButtonAttribute(indicatorButton, 'aria-expanded', String(filterModalKey === 'indicatorTree'));
|
||||
setButtonAttribute(indicatorButton, 'title', '指标树形');
|
||||
}
|
||||
|
||||
const button = getFullscreenButton();
|
||||
@ -1941,7 +1978,7 @@ function App() {
|
||||
}, [chartViewKey, fitPivotGridColumns, metricKey, pivotGridRowData.length, rightPanelCollapsed]);
|
||||
|
||||
const chartOptions = useMemo<AgCartesianChartOptions>(() => {
|
||||
const trendData = groupNames.map((groupName) => {
|
||||
const visibleData = groupNames.map((groupName) => {
|
||||
const row: Record<string, string | number | null> = { groupName };
|
||||
selectedContentNodes.forEach((node) => {
|
||||
const datum = chartDataBySelection[getSelectionKey(node.contentKey, node.id)]?.find((item) => item.groupName === groupName);
|
||||
@ -1949,32 +1986,6 @@ function App() {
|
||||
});
|
||||
return row;
|
||||
});
|
||||
const series = selectedContentNodes.map((node) => ({
|
||||
type: 'line' as const,
|
||||
xKey: 'groupName',
|
||||
yKey: getSeriesValueKey(node.contentKey, node.id),
|
||||
yName: `${node.label} ${seriesValueLabel}`,
|
||||
stroke: node.color,
|
||||
strokeWidth: 2,
|
||||
connectMissingData: true,
|
||||
marker: {
|
||||
enabled: true,
|
||||
fill: node.color,
|
||||
stroke: node.color,
|
||||
size: 5,
|
||||
},
|
||||
interpolation: {
|
||||
type: 'smooth' as const,
|
||||
},
|
||||
tooltip: {
|
||||
renderer: ({ datum, yKey, yName }: { datum: Record<string, unknown>; yKey: string; yName?: string }) => ({
|
||||
title: yName ?? '',
|
||||
data: [
|
||||
{ label: selectedMetric.label, value: formatChartValue(Number(datum[yKey]), metricKey) },
|
||||
],
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
return {
|
||||
theme: {
|
||||
@ -2004,7 +2015,7 @@ function App() {
|
||||
bottom: 18,
|
||||
left: 24,
|
||||
},
|
||||
data: trendData,
|
||||
data: visibleData,
|
||||
zoom: {
|
||||
enabled: true,
|
||||
anchorPointX: 'pointer',
|
||||
@ -2087,7 +2098,32 @@ function App() {
|
||||
] as unknown as NonNullable<NonNullable<AgCartesianChartOptions['annotations']>['toolbar']>['buttons']),
|
||||
},
|
||||
},
|
||||
series,
|
||||
series: selectedContentNodes.map((node) => ({
|
||||
type: 'line',
|
||||
xKey: 'groupName',
|
||||
yKey: getSeriesValueKey(node.contentKey, node.id),
|
||||
yName: `${node.label} ${seriesValueLabel}`,
|
||||
stroke: node.color,
|
||||
strokeWidth: 2,
|
||||
connectMissingData: true,
|
||||
marker: {
|
||||
enabled: true,
|
||||
fill: node.color,
|
||||
stroke: node.color,
|
||||
size: 5,
|
||||
},
|
||||
interpolation: {
|
||||
type: 'smooth',
|
||||
},
|
||||
tooltip: {
|
||||
renderer: ({ datum, yKey, yName }) => ({
|
||||
title: yName,
|
||||
data: [
|
||||
{ label: selectedMetric.label, value: formatChartValue(Number(datum[yKey]), metricKey) },
|
||||
],
|
||||
}),
|
||||
},
|
||||
})),
|
||||
axes: {
|
||||
x: {
|
||||
type: 'category',
|
||||
@ -2162,6 +2198,7 @@ function App() {
|
||||
selectedValueKey,
|
||||
statisticKey,
|
||||
]);
|
||||
|
||||
const renderMetricSwitcher = (variant: 'chart' | 'grid') => (
|
||||
<div className={`metric-switcher metric-switcher--${variant}`}>
|
||||
<button
|
||||
@ -2239,16 +2276,13 @@ function App() {
|
||||
type="button"
|
||||
title="清空全部筛选"
|
||||
onClick={() => {
|
||||
const shouldReloadIndicatorTree = !isDefaultTemplateSelection(appliedFilters.templateLibrary);
|
||||
if (shouldReloadIndicatorTree) {
|
||||
resetIndicatorTreeState();
|
||||
}
|
||||
setAppliedFilters({
|
||||
templateLibrary: getDefaultTemplateFilterNodes(),
|
||||
indicatorTree: [],
|
||||
indicatorTree: defaultIndicatorTreeNodes,
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
buildingFunction: [],
|
||||
constructionStage: [],
|
||||
planningForm: [],
|
||||
});
|
||||
|
||||
@ -2,8 +2,6 @@ import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { AllCommunityModule, ModuleRegistry } from 'ag-charts-community';
|
||||
import App from './App';
|
||||
import 'ag-grid-community/styles/ag-grid.css';
|
||||
import 'ag-grid-community/styles/ag-theme-quartz.css';
|
||||
import './styles.css';
|
||||
|
||||
ModuleRegistry.registerModules([AllCommunityModule]);
|
||||
@ -14,7 +12,7 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const chartContainerId = 'ztChart';
|
||||
const chartContainerId = 'sbChart';
|
||||
|
||||
const getRootRegistry = () => {
|
||||
window.__chartReactRoots ??= new WeakMap<Element, ReturnType<typeof createRoot>>();
|
||||
|
||||
@ -64,7 +64,7 @@ button {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(320px, 3fr);
|
||||
grid-template-columns: minmax(540px, 52vw) 1fr;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
gap: 12px 28px;
|
||||
height: 100vh;
|
||||
@ -250,7 +250,7 @@ button {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 7fr) minmax(320px, 3fr);
|
||||
grid-template-columns: minmax(540px, 52vw) 1fr;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
gap: 12px 28px;
|
||||
width: 100vw;
|
||||
|
||||
@ -1 +1 @@
|
||||
$ vite --host 0.0.0.0 --port "5173"
|
||||
$ vite --host 0.0.0.0 --port "5174"
|
||||
|
||||
10
vite-dev.log
10
vite-dev.log
@ -1,8 +1,8 @@
|
||||
|
||||
[32m[1mVITE[22m v7.3.2[39m [2mready in [0m[1m303[22m[2m[0m ms[22m
|
||||
[32m[1mVITE[22m v7.3.2[39m [2mready in [0m[1m306[22m[2m[0m ms[22m
|
||||
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://localhost:[1m5173[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://100.106.162.120:[1m5173[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.1.155:[1m5173[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.31.112.1:[1m5173[22m/[39m
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://localhost:[1m5174[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://100.106.162.120:[1m5174[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.1.155:[1m5174[22m/[39m
|
||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.31.112.1:[1m5174[22m/[39m
|
||||
[2m[32m ➜[39m[22m[2m press [22m[1mh + enter[22m[2m to show help[22m
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
[32m[1mVITE[22m v7.3.2[39m [2mready in [0m[1m283[22m[2m[0m ms[22m
|
||||
[32m[1mVITE[22m v7.3.2[39m [2mready in [0m[1m307[22m[2m[0m ms[22m
|
||||
|
||||
[32m➜[39m [1mLocal[22m: [36mhttp://127.0.0.1:[1m5173[22m/[39m
|
||||
[2m[32m ➜[39m[22m[2m press [22m[1mh + enter[22m[2m to show help[22m
|
||||
[2m16:10:18[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mhmr update [39m[2m/src/App.tsx[22m
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user