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` 后再过滤
|
- 筛选条件支持 `省市区`,后端会根据 `uf_xzqy` 展开到区级 `id` 后再过滤
|
||||||
- 支持搜索筛选树节点,支持多条件叠加
|
- 支持搜索筛选树节点,支持多条件叠加
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
<title>AG Chart Service</title>
|
<title>AG Chart Service</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="ztChart"></div>
|
<div id="sbChart"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
148
src/App.tsx
148
src/App.tsx
@ -12,7 +12,7 @@ import {
|
|||||||
} from 'ag-grid-community';
|
} from 'ag-grid-community';
|
||||||
import type { AgCartesianChartOptions } from 'ag-charts-community';
|
import type { AgCartesianChartOptions } from 'ag-charts-community';
|
||||||
import { ModuleRegistry } 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 {
|
import {
|
||||||
AnnotationsModule,
|
AnnotationsModule,
|
||||||
ContextMenuModule,
|
ContextMenuModule,
|
||||||
@ -29,6 +29,12 @@ AgGridModuleRegistry.registerModules([AgGridAllCommunityModule]);
|
|||||||
const API_BASE_URL = 'https://nest.zwgczx.com/api/v1';
|
const API_BASE_URL = 'https://nest.zwgczx.com/api/v1';
|
||||||
// const API_BASE_URL = 'http://127.0.0.1:9089/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 = [
|
const statisticOptions = [
|
||||||
{ key: 'minValue', label: '最低值', shortLabel: '低' },
|
{ key: 'minValue', label: '最低值', shortLabel: '低' },
|
||||||
{ key: 'maxValue', label: '最高值', shortLabel: '高' },
|
{ key: 'maxValue', label: '最高值', shortLabel: '高' },
|
||||||
@ -55,6 +61,7 @@ const metricShortLabels: Record<MetricKey, string> = {
|
|||||||
const contentOptions = [
|
const contentOptions = [
|
||||||
{ key: 'geoLocation', label: '自然地理区位' },
|
{ key: 'geoLocation', label: '自然地理区位' },
|
||||||
{ key: 'facilityType', label: '设施类别' },
|
{ key: 'facilityType', label: '设施类别' },
|
||||||
|
{ key: 'buildingFunction', label: '建筑功能' },
|
||||||
{ key: 'constructionStage', label: '建设阶段' },
|
{ key: 'constructionStage', label: '建设阶段' },
|
||||||
{ key: 'planningForm', label: '规划形式' },
|
{ key: 'planningForm', label: '规划形式' },
|
||||||
] as const;
|
] as const;
|
||||||
@ -65,6 +72,7 @@ const filterOptions = [
|
|||||||
{ key: 'region', label: '省市区', icon: MapPinned },
|
{ key: 'region', label: '省市区', icon: MapPinned },
|
||||||
{ key: 'geoLocation', label: '自然地理区位', icon: LocateFixed },
|
{ key: 'geoLocation', label: '自然地理区位', icon: LocateFixed },
|
||||||
{ key: 'facilityType', label: '设施类别', icon: Building2 },
|
{ key: 'facilityType', label: '设施类别', icon: Building2 },
|
||||||
|
{ key: 'buildingFunction', label: '建筑功能', icon: SquareFunction },
|
||||||
{ key: 'constructionStage', label: '建设阶段', icon: Construction },
|
{ key: 'constructionStage', label: '建设阶段', icon: Construction },
|
||||||
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid },
|
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid },
|
||||||
] as const;
|
] as const;
|
||||||
@ -103,6 +111,17 @@ const contentTreeConfigs = {
|
|||||||
fieldid: '305426',
|
fieldid: '305426',
|
||||||
defaultExpandedLevel: 3,
|
defaultExpandedLevel: 3,
|
||||||
},
|
},
|
||||||
|
buildingFunction: {
|
||||||
|
endpoint: '/api/public/browser/data/256',
|
||||||
|
treeid: '95004',
|
||||||
|
fieldid: '305919',
|
||||||
|
defaultExpandedLevel: 0,
|
||||||
|
browserParams: {
|
||||||
|
workflowid: '182032',
|
||||||
|
wfid: '182032',
|
||||||
|
billid: '-1817',
|
||||||
|
},
|
||||||
|
},
|
||||||
constructionStage: {
|
constructionStage: {
|
||||||
endpoint: '/api/public/browser/data/256',
|
endpoint: '/api/public/browser/data/256',
|
||||||
treeid: '94007',
|
treeid: '94007',
|
||||||
@ -157,6 +176,7 @@ type StatisticKey = (typeof statisticOptions)[number]['key'];
|
|||||||
type MetricKey = (typeof metricOptions)[number]['key'];
|
type MetricKey = (typeof metricOptions)[number]['key'];
|
||||||
type ContentKey = (typeof contentOptions)[number]['key'];
|
type ContentKey = (typeof contentOptions)[number]['key'];
|
||||||
type FilterKey = (typeof filterOptions)[number]['key'];
|
type FilterKey = (typeof filterOptions)[number]['key'];
|
||||||
|
type GroupKey = 'year';
|
||||||
type ChartViewKey = 'trend' | 'pivot';
|
type ChartViewKey = 'trend' | 'pivot';
|
||||||
type ApiBuildingFunctionStat = {
|
type ApiBuildingFunctionStat = {
|
||||||
group_key?: string | number | null;
|
group_key?: string | number | null;
|
||||||
@ -568,10 +588,6 @@ function isDefaultTemplateSelection(nodes: SelectedFilterNode[]) {
|
|||||||
return nodes.length === 1 && nodes[0]?.id === defaultTemplateFilterNode.id;
|
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[] {
|
function getDefaultIndicatorTreeFilterNodes(nodes: TreeNode[]): SelectedFilterNode[] {
|
||||||
const defaultNode = nodes[0];
|
const defaultNode = nodes[0];
|
||||||
if (!defaultNode) return [];
|
if (!defaultNode) return [];
|
||||||
@ -710,6 +726,7 @@ function App() {
|
|||||||
const treeInitialLoadStartedRef = useRef<Record<ContentKey, boolean>>({
|
const treeInitialLoadStartedRef = useRef<Record<ContentKey, boolean>>({
|
||||||
geoLocation: false,
|
geoLocation: false,
|
||||||
facilityType: false,
|
facilityType: false,
|
||||||
|
buildingFunction: false,
|
||||||
constructionStage: false,
|
constructionStage: false,
|
||||||
planningForm: false,
|
planningForm: false,
|
||||||
});
|
});
|
||||||
@ -719,11 +736,13 @@ function App() {
|
|||||||
region: false,
|
region: false,
|
||||||
geoLocation: false,
|
geoLocation: false,
|
||||||
facilityType: false,
|
facilityType: false,
|
||||||
|
buildingFunction: false,
|
||||||
constructionStage: false,
|
constructionStage: false,
|
||||||
planningForm: false,
|
planningForm: false,
|
||||||
});
|
});
|
||||||
const [statisticKey, setStatisticKey] = useState<StatisticKey>('avgValue');
|
const [statisticKey, setStatisticKey] = useState<StatisticKey>('avgValue');
|
||||||
const [metricKey, setMetricKey] = useState<MetricKey>('cost');
|
const [metricKey, setMetricKey] = useState<MetricKey>('cost');
|
||||||
|
const [groupKey, setGroupKey] = useState<GroupKey>('year');
|
||||||
const [chartViewKey, setChartViewKey] = useState<ChartViewKey>('trend');
|
const [chartViewKey, setChartViewKey] = useState<ChartViewKey>('trend');
|
||||||
const [workspaceFullscreen, setWorkspaceFullscreen] = useState(false);
|
const [workspaceFullscreen, setWorkspaceFullscreen] = useState(false);
|
||||||
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
|
const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
|
||||||
@ -733,18 +752,21 @@ function App() {
|
|||||||
const [treeByContent, setTreeByContent] = useState<Record<ContentKey, TreeNode[]>>({
|
const [treeByContent, setTreeByContent] = useState<Record<ContentKey, TreeNode[]>>({
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
facilityType: [],
|
facilityType: [],
|
||||||
|
buildingFunction: [],
|
||||||
constructionStage: [],
|
constructionStage: [],
|
||||||
planningForm: [],
|
planningForm: [],
|
||||||
});
|
});
|
||||||
const [treeLoadingByContent, setTreeLoadingByContent] = useState<Record<ContentKey, boolean>>({
|
const [treeLoadingByContent, setTreeLoadingByContent] = useState<Record<ContentKey, boolean>>({
|
||||||
geoLocation: false,
|
geoLocation: false,
|
||||||
facilityType: false,
|
facilityType: false,
|
||||||
|
buildingFunction: false,
|
||||||
constructionStage: false,
|
constructionStage: false,
|
||||||
planningForm: false,
|
planningForm: false,
|
||||||
});
|
});
|
||||||
const [treeErrorByContent, setTreeErrorByContent] = useState<Record<ContentKey, string | null>>({
|
const [treeErrorByContent, setTreeErrorByContent] = useState<Record<ContentKey, string | null>>({
|
||||||
geoLocation: null,
|
geoLocation: null,
|
||||||
facilityType: null,
|
facilityType: null,
|
||||||
|
buildingFunction: null,
|
||||||
constructionStage: null,
|
constructionStage: null,
|
||||||
planningForm: null,
|
planningForm: null,
|
||||||
});
|
});
|
||||||
@ -761,6 +783,7 @@ function App() {
|
|||||||
region: [],
|
region: [],
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
facilityType: [],
|
facilityType: [],
|
||||||
|
buildingFunction: [],
|
||||||
constructionStage: [],
|
constructionStage: [],
|
||||||
planningForm: [],
|
planningForm: [],
|
||||||
});
|
});
|
||||||
@ -770,6 +793,7 @@ function App() {
|
|||||||
region: false,
|
region: false,
|
||||||
geoLocation: false,
|
geoLocation: false,
|
||||||
facilityType: false,
|
facilityType: false,
|
||||||
|
buildingFunction: false,
|
||||||
constructionStage: false,
|
constructionStage: false,
|
||||||
planningForm: false,
|
planningForm: false,
|
||||||
});
|
});
|
||||||
@ -779,6 +803,7 @@ function App() {
|
|||||||
region: null,
|
region: null,
|
||||||
geoLocation: null,
|
geoLocation: null,
|
||||||
facilityType: null,
|
facilityType: null,
|
||||||
|
buildingFunction: null,
|
||||||
constructionStage: null,
|
constructionStage: null,
|
||||||
planningForm: null,
|
planningForm: null,
|
||||||
});
|
});
|
||||||
@ -788,6 +813,7 @@ function App() {
|
|||||||
region: [],
|
region: [],
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
facilityType: [],
|
facilityType: [],
|
||||||
|
buildingFunction: [],
|
||||||
constructionStage: [],
|
constructionStage: [],
|
||||||
planningForm: [],
|
planningForm: [],
|
||||||
});
|
});
|
||||||
@ -797,6 +823,7 @@ function App() {
|
|||||||
region: false,
|
region: false,
|
||||||
geoLocation: false,
|
geoLocation: false,
|
||||||
facilityType: false,
|
facilityType: false,
|
||||||
|
buildingFunction: false,
|
||||||
constructionStage: false,
|
constructionStage: false,
|
||||||
planningForm: false,
|
planningForm: false,
|
||||||
});
|
});
|
||||||
@ -806,6 +833,7 @@ function App() {
|
|||||||
region: null,
|
region: null,
|
||||||
geoLocation: null,
|
geoLocation: null,
|
||||||
facilityType: null,
|
facilityType: null,
|
||||||
|
buildingFunction: null,
|
||||||
constructionStage: null,
|
constructionStage: null,
|
||||||
planningForm: null,
|
planningForm: null,
|
||||||
});
|
});
|
||||||
@ -815,6 +843,7 @@ function App() {
|
|||||||
region: [],
|
region: [],
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
facilityType: [],
|
facilityType: [],
|
||||||
|
buildingFunction: [],
|
||||||
constructionStage: [],
|
constructionStage: [],
|
||||||
planningForm: [],
|
planningForm: [],
|
||||||
});
|
});
|
||||||
@ -829,6 +858,7 @@ function App() {
|
|||||||
region: 0,
|
region: 0,
|
||||||
geoLocation: 0,
|
geoLocation: 0,
|
||||||
facilityType: 0,
|
facilityType: 0,
|
||||||
|
buildingFunction: 0,
|
||||||
constructionStage: 0,
|
constructionStage: 0,
|
||||||
planningForm: 0,
|
planningForm: 0,
|
||||||
});
|
});
|
||||||
@ -861,7 +891,10 @@ function App() {
|
|||||||
);
|
);
|
||||||
const indicatorSelectionLabel = appliedFilters.indicatorTree[0]?.label ?? defaultIndicatorTreeNodes[0]?.label ?? '';
|
const indicatorSelectionLabel = appliedFilters.indicatorTree[0]?.label ?? defaultIndicatorTreeNodes[0]?.label ?? '';
|
||||||
const activeFilterCount = Object.entries(appliedFilters).reduce((total, [key, nodes]) => (
|
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);
|
), 0);
|
||||||
const chartEmptyText = selectedContentNodes.length === 0
|
const chartEmptyText = selectedContentNodes.length === 0
|
||||||
? '请选择右侧分类项'
|
? '请选择右侧分类项'
|
||||||
@ -1057,6 +1090,14 @@ function App() {
|
|||||||
() => new Set(draftFilterNodes.map((node) => getFilterSelectionKey(node.filterKey, node.id))),
|
() => new Set(draftFilterNodes.map((node) => getFilterSelectionKey(node.filterKey, node.id))),
|
||||||
[draftFilterNodes],
|
[draftFilterNodes],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (defaultIndicatorTreeNodes.length === 0) return;
|
||||||
|
setAppliedFilters((current) => {
|
||||||
|
if (current.indicatorTree.length > 0) return current;
|
||||||
|
return { ...current, indicatorTree: defaultIndicatorTreeNodes };
|
||||||
|
});
|
||||||
|
}, [defaultIndicatorTreeNodes]);
|
||||||
const appliedFilterPayload = useMemo(
|
const appliedFilterPayload = useMemo(
|
||||||
() => filterOptions
|
() => filterOptions
|
||||||
.map((option) => ({
|
.map((option) => ({
|
||||||
@ -1083,6 +1124,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
const treeParams = {
|
const treeParams = {
|
||||||
...browserTreeDefaults,
|
...browserTreeDefaults,
|
||||||
|
...('browserParams' in config ? config.browserParams : {}),
|
||||||
treeid: config.treeid,
|
treeid: config.treeid,
|
||||||
cube_treeid: config.treeid,
|
cube_treeid: config.treeid,
|
||||||
fieldid: config.fieldid,
|
fieldid: config.fieldid,
|
||||||
@ -1140,7 +1182,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchRegionFilterTree = async (signal?: AbortSignal) => {
|
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,
|
signal,
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
@ -1185,7 +1227,7 @@ function App() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchBackendFilterTreeSearch = async (filterKey: FilterKey, keyword: string, signal?: AbortSignal) => {
|
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,
|
key: filterKey,
|
||||||
keyword,
|
keyword,
|
||||||
nodePrefix: isContentFilterKey(filterKey) ? getTreeNodePrefix(filterTreeByKey[filterKey]) : '',
|
nodePrefix: isContentFilterKey(filterKey) ? getTreeNodePrefix(filterTreeByKey[filterKey]) : '',
|
||||||
@ -1370,6 +1412,10 @@ function App() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ensureFilterTreeLoaded('indicatorTree');
|
||||||
|
}, [selectedTemplateId]);
|
||||||
|
|
||||||
const openFilterModal = (filterKey: FilterKey) => {
|
const openFilterModal = (filterKey: FilterKey) => {
|
||||||
setFilterModalKey(filterKey);
|
setFilterModalKey(filterKey);
|
||||||
if (isIndicatorTreeFilterKey(filterKey) && appliedFilters[filterKey].length === 0 && defaultIndicatorTreeNodes.length > 0) {
|
if (isIndicatorTreeFilterKey(filterKey) && appliedFilters[filterKey].length === 0 && defaultIndicatorTreeNodes.length > 0) {
|
||||||
@ -1386,10 +1432,6 @@ function App() {
|
|||||||
ensureFilterTreeLoaded(filterKey);
|
ensureFilterTreeLoaded(filterKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
ensureFilterTreeLoaded('indicatorTree');
|
|
||||||
}, [selectedTemplateId]);
|
|
||||||
|
|
||||||
const closeFilterModal = () => {
|
const closeFilterModal = () => {
|
||||||
if (filterModalKey) {
|
if (filterModalKey) {
|
||||||
filterSearchRequestSeqRef.current[filterModalKey] += 1;
|
filterSearchRequestSeqRef.current[filterModalKey] += 1;
|
||||||
@ -1544,22 +1586,18 @@ function App() {
|
|||||||
if (nextDraftNodes.length === 0) {
|
if (nextDraftNodes.length === 0) {
|
||||||
nextDraftNodes = defaultIndicatorTreeNodes;
|
nextDraftNodes = defaultIndicatorTreeNodes;
|
||||||
}
|
}
|
||||||
if (isDefaultIndicatorTreeSelection(nextDraftNodes, filterTreeByKey.indicatorTree)) {
|
|
||||||
nextDraftNodes = [];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
const shouldReloadIndicatorTree = isTemplateFilterKey(filterModalKey) && !isSameFilterSelection(appliedFilters.templateLibrary, nextDraftNodes);
|
|
||||||
setAppliedFilters((current) => {
|
setAppliedFilters((current) => {
|
||||||
const nextFilters = {
|
const nextFilters = {
|
||||||
...current,
|
...current,
|
||||||
[filterModalKey]: nextDraftNodes,
|
[filterModalKey]: nextDraftNodes,
|
||||||
};
|
};
|
||||||
if (shouldReloadIndicatorTree) {
|
if (isTemplateFilterKey(filterModalKey)) {
|
||||||
nextFilters.indicatorTree = [];
|
nextFilters.indicatorTree = [];
|
||||||
}
|
}
|
||||||
return nextFilters;
|
return nextFilters;
|
||||||
});
|
});
|
||||||
if (shouldReloadIndicatorTree) {
|
if (isTemplateFilterKey(filterModalKey)) {
|
||||||
resetIndicatorTreeState();
|
resetIndicatorTreeState();
|
||||||
}
|
}
|
||||||
setChartDataBySelection({});
|
setChartDataBySelection({});
|
||||||
@ -1579,7 +1617,7 @@ function App() {
|
|||||||
: isIndicatorTreeFilterKey(filterKey)
|
: isIndicatorTreeFilterKey(filterKey)
|
||||||
? defaultIndicatorTreeNodes
|
? defaultIndicatorTreeNodes
|
||||||
: [];
|
: [];
|
||||||
if (isSameFilterSelection(appliedFilters[filterKey], nextNodes)) {
|
if (appliedFilters[filterKey].length === nextNodes.length && appliedFilters[filterKey].every((node, index) => node.id === nextNodes[index]?.id)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAppliedFilters((current) => {
|
setAppliedFilters((current) => {
|
||||||
@ -1699,14 +1737,14 @@ function App() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostStatsBatch`, {
|
const response = await fetch(`${API_BASE_URL}${API_ROUTES.statsBatch}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
groupBy: 'year',
|
groupBy: groupKey,
|
||||||
metric: requestMetricKey,
|
metric: requestMetricKey,
|
||||||
templateId: selectedTemplateId,
|
templateId: selectedTemplateId,
|
||||||
filters: appliedFilterPayload,
|
filters: appliedFilterPayload,
|
||||||
@ -1753,7 +1791,7 @@ function App() {
|
|||||||
return () => {
|
return () => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
};
|
};
|
||||||
}, [appliedFilterPayload, chartQueryVersion, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
|
}, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const frame = chartFrameRef.current;
|
const frame = chartFrameRef.current;
|
||||||
@ -1792,7 +1830,6 @@ function App() {
|
|||||||
indicatorButton.classList.add('chart-indicator-button');
|
indicatorButton.classList.add('chart-indicator-button');
|
||||||
enableCustomToolbarButton(indicatorButton);
|
enableCustomToolbarButton(indicatorButton);
|
||||||
setButtonAttribute(indicatorButton, 'aria-expanded', String(filterModalKey === 'indicatorTree'));
|
setButtonAttribute(indicatorButton, 'aria-expanded', String(filterModalKey === 'indicatorTree'));
|
||||||
setButtonAttribute(indicatorButton, 'title', '指标树形');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const button = getFullscreenButton();
|
const button = getFullscreenButton();
|
||||||
@ -1941,7 +1978,7 @@ function App() {
|
|||||||
}, [chartViewKey, fitPivotGridColumns, metricKey, pivotGridRowData.length, rightPanelCollapsed]);
|
}, [chartViewKey, fitPivotGridColumns, metricKey, pivotGridRowData.length, rightPanelCollapsed]);
|
||||||
|
|
||||||
const chartOptions = useMemo<AgCartesianChartOptions>(() => {
|
const chartOptions = useMemo<AgCartesianChartOptions>(() => {
|
||||||
const trendData = groupNames.map((groupName) => {
|
const visibleData = groupNames.map((groupName) => {
|
||||||
const row: Record<string, string | number | null> = { groupName };
|
const row: Record<string, string | number | null> = { groupName };
|
||||||
selectedContentNodes.forEach((node) => {
|
selectedContentNodes.forEach((node) => {
|
||||||
const datum = chartDataBySelection[getSelectionKey(node.contentKey, node.id)]?.find((item) => item.groupName === groupName);
|
const datum = chartDataBySelection[getSelectionKey(node.contentKey, node.id)]?.find((item) => item.groupName === groupName);
|
||||||
@ -1949,32 +1986,6 @@ function App() {
|
|||||||
});
|
});
|
||||||
return row;
|
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 {
|
return {
|
||||||
theme: {
|
theme: {
|
||||||
@ -2004,7 +2015,7 @@ function App() {
|
|||||||
bottom: 18,
|
bottom: 18,
|
||||||
left: 24,
|
left: 24,
|
||||||
},
|
},
|
||||||
data: trendData,
|
data: visibleData,
|
||||||
zoom: {
|
zoom: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
anchorPointX: 'pointer',
|
anchorPointX: 'pointer',
|
||||||
@ -2087,7 +2098,32 @@ function App() {
|
|||||||
] as unknown as NonNullable<NonNullable<AgCartesianChartOptions['annotations']>['toolbar']>['buttons']),
|
] 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: {
|
axes: {
|
||||||
x: {
|
x: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
@ -2162,6 +2198,7 @@ function App() {
|
|||||||
selectedValueKey,
|
selectedValueKey,
|
||||||
statisticKey,
|
statisticKey,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const renderMetricSwitcher = (variant: 'chart' | 'grid') => (
|
const renderMetricSwitcher = (variant: 'chart' | 'grid') => (
|
||||||
<div className={`metric-switcher metric-switcher--${variant}`}>
|
<div className={`metric-switcher metric-switcher--${variant}`}>
|
||||||
<button
|
<button
|
||||||
@ -2239,16 +2276,13 @@ function App() {
|
|||||||
type="button"
|
type="button"
|
||||||
title="清空全部筛选"
|
title="清空全部筛选"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const shouldReloadIndicatorTree = !isDefaultTemplateSelection(appliedFilters.templateLibrary);
|
|
||||||
if (shouldReloadIndicatorTree) {
|
|
||||||
resetIndicatorTreeState();
|
|
||||||
}
|
|
||||||
setAppliedFilters({
|
setAppliedFilters({
|
||||||
templateLibrary: getDefaultTemplateFilterNodes(),
|
templateLibrary: getDefaultTemplateFilterNodes(),
|
||||||
indicatorTree: [],
|
indicatorTree: defaultIndicatorTreeNodes,
|
||||||
region: [],
|
region: [],
|
||||||
geoLocation: [],
|
geoLocation: [],
|
||||||
facilityType: [],
|
facilityType: [],
|
||||||
|
buildingFunction: [],
|
||||||
constructionStage: [],
|
constructionStage: [],
|
||||||
planningForm: [],
|
planningForm: [],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import { StrictMode } from 'react';
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { AllCommunityModule, ModuleRegistry } from 'ag-charts-community';
|
import { AllCommunityModule, ModuleRegistry } from 'ag-charts-community';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import 'ag-grid-community/styles/ag-grid.css';
|
|
||||||
import 'ag-grid-community/styles/ag-theme-quartz.css';
|
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
|
||||||
ModuleRegistry.registerModules([AllCommunityModule]);
|
ModuleRegistry.registerModules([AllCommunityModule]);
|
||||||
@ -14,7 +12,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartContainerId = 'ztChart';
|
const chartContainerId = 'sbChart';
|
||||||
|
|
||||||
const getRootRegistry = () => {
|
const getRootRegistry = () => {
|
||||||
window.__chartReactRoots ??= new WeakMap<Element, ReturnType<typeof createRoot>>();
|
window.__chartReactRoots ??= new WeakMap<Element, ReturnType<typeof createRoot>>();
|
||||||
|
|||||||
@ -64,7 +64,7 @@ button {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
display: grid;
|
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);
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
gap: 12px 28px;
|
gap: 12px 28px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@ -250,7 +250,7 @@ button {
|
|||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
display: grid;
|
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);
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
gap: 12px 28px;
|
gap: 12px 28px;
|
||||||
width: 100vw;
|
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 [1mLocal[22m: [36mhttp://localhost:[1m5174[22m/[39m
|
||||||
[32m➜[39m [1mNetwork[22m: [36mhttp://100.106.162.120:[1m5173[22m/[39m
|
[32m➜[39m [1mNetwork[22m: [36mhttp://100.106.162.120:[1m5174[22m/[39m
|
||||||
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.1.155:[1m5173[22m/[39m
|
[32m➜[39m [1mNetwork[22m: [36mhttp://192.168.1.155:[1m5174[22m/[39m
|
||||||
[32m➜[39m [1mNetwork[22m: [36mhttp://172.31.112.1:[1m5173[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
|
[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
|
[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
|
[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