fix
This commit is contained in:
parent
7845ae8eb0
commit
53c1b6f523
2
.playwright-mcp/console-2026-05-12T03-32-51-357Z.log
Normal file
2
.playwright-mcp/console-2026-05-12T03-32-51-357Z.log
Normal file
@ -0,0 +1,2 @@
|
||||
[ 1001ms] [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
|
||||
[ 1337ms] [ERROR] Failed to load resource: the server responded with a status of 404 (Not Found) @ http://127.0.0.1:5173/favicon.ico:0
|
||||
8
.playwright-mcp/console-2026-05-12T03-33-46-269Z.log
Normal file
8
.playwright-mcp/console-2026-05-12T03-33-46-269Z.log
Normal file
@ -0,0 +1,8 @@
|
||||
[ 79ms] [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
|
||||
[ 255622ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 255658ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 268128ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 268149ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 282018ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 282037ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 282064ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
4
.playwright-mcp/console-2026-05-12T03-38-57-686Z.log
Normal file
4
.playwright-mcp/console-2026-05-12T03-38-57-686Z.log
Normal file
@ -0,0 +1,4 @@
|
||||
[ 47ms] [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
|
||||
[ 194ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 244ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
[ 293ms] [WARNING] AG Charts - Option `annotations.toolbar.buttons[0].value` cannot be set to `"reset"`; expecting a keyword such as 'line-menu', 'fibonacci-menu', 'text-menu', 'shape-menu', 'measurer-menu', 'line', 'horizontal-line', 'vertical-line', 'parallel-channel', 'disjoint-channel', 'fibonacci-retracement', 'fibonacci-retracement-trend-based', 'text', 'comment', 'callout', 'note' or 'clear', ignoring. @ http://127.0.0.1:5173/node_modules/.vite/deps/chunk-XDC3NMYR.js?v=ae174d51:834
|
||||
1
.playwright-mcp/console-2026-05-12T03-39-25-518Z.log
Normal file
1
.playwright-mcp/console-2026-05-12T03-39-25-518Z.log
Normal file
@ -0,0 +1 @@
|
||||
[ 26ms] [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
|
||||
2
.playwright-mcp/console-2026-05-12T03-41-17-981Z.log
Normal file
2
.playwright-mcp/console-2026-05-12T03-41-17-981Z.log
Normal file
@ -0,0 +1,2 @@
|
||||
[ 32ms] [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
|
||||
[ 371068ms] [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
.playwright-mcp/console-2026-05-12T03-48-27-372Z.log
Normal file
1
.playwright-mcp/console-2026-05-12T03-48-27-372Z.log
Normal file
@ -0,0 +1 @@
|
||||
[ 99ms] [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
|
||||
5
.playwright-mcp/console-2026-05-12T03-50-50-051Z.log
Normal file
5
.playwright-mcp/console-2026-05-12T03-50-50-051Z.log
Normal file
@ -0,0 +1,5 @@
|
||||
[ 77ms] [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
|
||||
[ 1186370ms] [ERROR] The final argument passed to %s changed size between renders. The order and size of this array must remain constant.
|
||||
|
||||
Previous: %s
|
||||
Incoming: %s useEffect [false] [[object Object], , [object Object], false] @ http://127.0.0.1:5173/node_modules/.vite/deps/react-dom_client.js?v=ae174d51:5627
|
||||
1
.playwright-mcp/console-2026-05-12T04-17-37-255Z.log
Normal file
1
.playwright-mcp/console-2026-05-12T04-17-37-255Z.log
Normal file
@ -0,0 +1 @@
|
||||
[ 854ms] [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
.playwright-mcp/console-2026-05-12T04-19-14-036Z.log
Normal file
1
.playwright-mcp/console-2026-05-12T04-19-14-036Z.log
Normal file
@ -0,0 +1 @@
|
||||
[ 88ms] [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
|
||||
5
.playwright-mcp/console-2026-05-12T04-27-12-424Z.log
Normal file
5
.playwright-mcp/console-2026-05-12T04-27-12-424Z.log
Normal file
@ -0,0 +1,5 @@
|
||||
[ 219ms] [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
|
||||
[ 139728ms] [ERROR] Failed to load resource: the server responded with a status of 401 () @ https://nest.zwgczx.com/api/v1/zw/getBuildingFunctionCostFilterTree?key=templateLibrary¤ttime=1778560172021&__random__=1778560172021:0
|
||||
[ 142705ms] [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=1778560175114&__random__=1778560175114:0
|
||||
[ 304362ms] [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
|
||||
[ 307069ms] [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=1778560339458&__random__=1778560339458:0
|
||||
81
.playwright-mcp/page-2026-05-12T03-32-52-698Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-32-52-698Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- button "全屏(F11)" [ref=e55] [cursor=pointer]
|
||||
- generic [ref=e57]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e58]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e59]
|
||||
- toolbar "标注" [ref=e60]:
|
||||
- button "均" [disabled] [ref=e61]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e62]
|
||||
- button "Text Tool" [disabled] [ref=e63]
|
||||
- button "Shape Tool" [disabled] [ref=e64]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e65]
|
||||
- button "Clear annotations" [disabled] [ref=e66]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e67]:
|
||||
- button "缩小" [disabled] [ref=e68]
|
||||
- button "放大" [ref=e69] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e70]
|
||||
- button "右移" [disabled] [ref=e71]
|
||||
- button "重置" [disabled] [ref=e72]
|
||||
- complementary "选择内容" [ref=e73]:
|
||||
- tablist "选择内容切换项" [ref=e74]:
|
||||
- tab "自然地理区位" [selected] [ref=e75] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e76] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e77] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e78] [cursor=pointer]
|
||||
- generic [ref=e79]:
|
||||
- generic [ref=e80]: 自然地理区位
|
||||
- generic [ref=e81]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-33-46-589Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-33-46-589Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- button "全屏(F11)" [ref=e55] [cursor=pointer]
|
||||
- generic [ref=e57]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e58]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e59]
|
||||
- toolbar "标注" [ref=e60]:
|
||||
- button "均" [disabled] [ref=e61]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e62]
|
||||
- button "Text Tool" [disabled] [ref=e63]
|
||||
- button "Shape Tool" [disabled] [ref=e64]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e65]
|
||||
- button "Clear annotations" [disabled] [ref=e66]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e67]:
|
||||
- button "缩小" [disabled] [ref=e68]
|
||||
- button "放大" [ref=e69] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e70]
|
||||
- button "右移" [disabled] [ref=e71]
|
||||
- button "重置" [disabled] [ref=e72]
|
||||
- complementary "选择内容" [ref=e73]:
|
||||
- tablist "选择内容切换项" [ref=e74]:
|
||||
- tab "自然地理区位" [selected] [ref=e75] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e76] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e77] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e78] [cursor=pointer]
|
||||
- generic [ref=e79]:
|
||||
- generic [ref=e80]: 自然地理区位
|
||||
- generic [ref=e81]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-38-57-987Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-38-57-987Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e55]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e56]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e57]
|
||||
- toolbar "标注" [ref=e58]:
|
||||
- button "全屏(F11)" [disabled] [ref=e59]
|
||||
- button "均" [disabled] [ref=e60]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e61]
|
||||
- button "Text Tool" [disabled] [ref=e62]
|
||||
- button "Shape Tool" [disabled] [ref=e63]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e64]
|
||||
- button "Clear annotations" [disabled] [ref=e65]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e66]:
|
||||
- button "缩小" [disabled] [ref=e67]
|
||||
- button "放大" [ref=e68] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e69]
|
||||
- button "右移" [disabled] [ref=e70]
|
||||
- button "重置" [disabled] [ref=e71]
|
||||
- complementary "选择内容" [ref=e72]:
|
||||
- tablist "选择内容切换项" [ref=e73]:
|
||||
- tab "自然地理区位" [selected] [ref=e74] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e75] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e76] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e77] [cursor=pointer]
|
||||
- generic [ref=e78]:
|
||||
- generic [ref=e79]: 自然地理区位
|
||||
- generic [ref=e80]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-39-25-782Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-39-25-782Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e55]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e56]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e57]
|
||||
- toolbar "标注" [ref=e58]:
|
||||
- button "全屏(F11)" [disabled] [ref=e59]
|
||||
- button "均" [disabled] [ref=e60]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e61]
|
||||
- button "Text Tool" [disabled] [ref=e62]
|
||||
- button "Shape Tool" [disabled] [ref=e63]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e64]
|
||||
- button "Clear annotations" [disabled] [ref=e65]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e66]:
|
||||
- button "缩小" [disabled] [ref=e67]
|
||||
- button "放大" [ref=e68] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e69]
|
||||
- button "右移" [disabled] [ref=e70]
|
||||
- button "重置" [disabled] [ref=e71]
|
||||
- complementary "选择内容" [ref=e72]:
|
||||
- tablist "选择内容切换项" [ref=e73]:
|
||||
- tab "自然地理区位" [selected] [ref=e74] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e75] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e76] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e77] [cursor=pointer]
|
||||
- generic [ref=e78]:
|
||||
- generic [ref=e79]: 自然地理区位
|
||||
- generic [ref=e80]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-41-18-222Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-41-18-222Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e55]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e56]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e57]
|
||||
- toolbar "标注" [ref=e58]:
|
||||
- button "全屏(F11)" [disabled] [ref=e59]
|
||||
- button "均" [disabled] [ref=e60]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e61]
|
||||
- button "Text Tool" [disabled] [ref=e62]
|
||||
- button "Shape Tool" [disabled] [ref=e63]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e64]
|
||||
- button "Clear annotations" [disabled] [ref=e65]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e66]:
|
||||
- button "缩小" [disabled] [ref=e67]
|
||||
- button "放大" [ref=e68] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e69]
|
||||
- button "右移" [disabled] [ref=e70]
|
||||
- button "重置" [disabled] [ref=e71]
|
||||
- complementary "选择内容" [ref=e72]:
|
||||
- tablist "选择内容切换项" [ref=e73]:
|
||||
- tab "自然地理区位" [selected] [ref=e74] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e75] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e76] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e77] [cursor=pointer]
|
||||
- generic [ref=e78]:
|
||||
- generic [ref=e79]: 自然地理区位
|
||||
- generic [ref=e80]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-48-27-706Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-48-27-706Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e55]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e56]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e57]
|
||||
- toolbar "标注" [ref=e58]:
|
||||
- button "均" [disabled] [ref=e59]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e60]
|
||||
- button "Text Tool" [disabled] [ref=e61]
|
||||
- button "Shape Tool" [disabled] [ref=e62]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e63]
|
||||
- button "全屏(F11)" [disabled] [ref=e64]
|
||||
- button "Clear annotations" [disabled] [ref=e65]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e66]:
|
||||
- button "缩小" [disabled] [ref=e67]
|
||||
- button "放大" [ref=e68] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e69]
|
||||
- button "右移" [disabled] [ref=e70]
|
||||
- button "重置" [disabled] [ref=e71]
|
||||
- complementary "选择内容" [ref=e72]:
|
||||
- tablist "选择内容切换项" [ref=e73]:
|
||||
- tab "自然地理区位" [selected] [ref=e74] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e75] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e76] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e77] [cursor=pointer]
|
||||
- generic [ref=e78]:
|
||||
- generic [ref=e79]: 自然地理区位
|
||||
- generic [ref=e80]: 加载中
|
||||
81
.playwright-mcp/page-2026-05-12T03-50-50-349Z.yml
Normal file
81
.playwright-mcp/page-2026-05-12T03-50-50-349Z.yml
Normal file
@ -0,0 +1,81 @@
|
||||
- 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 "模板库 1" [pressed] [ref=e6] [cursor=pointer]:
|
||||
- img [ref=e7]
|
||||
- generic [ref=e9]: 模板库
|
||||
- strong [ref=e10]: "1"
|
||||
- button "指标树形" [ref=e11] [cursor=pointer]:
|
||||
- img [ref=e12]
|
||||
- generic [ref=e19]: 指标树形
|
||||
- button "省市区" [ref=e20] [cursor=pointer]:
|
||||
- img [ref=e21]
|
||||
- generic [ref=e25]: 省市区
|
||||
- button "自然地理区位" [ref=e26] [cursor=pointer]:
|
||||
- img [ref=e27]
|
||||
- generic [ref=e30]: 自然地理区位
|
||||
- button "设施类别" [ref=e31] [cursor=pointer]:
|
||||
- img [ref=e32]
|
||||
- generic [ref=e36]: 设施类别
|
||||
- button "建设阶段" [ref=e37] [cursor=pointer]:
|
||||
- img [ref=e38]
|
||||
- generic [ref=e43]: 建设阶段
|
||||
- button "规划形式" [ref=e44] [cursor=pointer]:
|
||||
- img [ref=e45]
|
||||
- generic [ref=e50]: 规划形式
|
||||
- region "年度总费用图表" [ref=e51]:
|
||||
- generic [ref=e52]:
|
||||
- button "纵坐标:造价(元)" [ref=e54] [cursor=pointer]: 造价(元)
|
||||
- generic [ref=e55]:
|
||||
- figure "图表,共有0个系列":
|
||||
- generic [ref=e56]:
|
||||
- img "interactive chart":
|
||||
- generic:
|
||||
- img
|
||||
- img
|
||||
- region [ref=e57]
|
||||
- toolbar "标注" [ref=e58]:
|
||||
- button "均" [disabled] [ref=e59]:
|
||||
- generic: 均
|
||||
- button "Line Tool" [disabled] [ref=e60]
|
||||
- button "Text Tool" [disabled] [ref=e61]
|
||||
- button "Shape Tool" [disabled] [ref=e62]
|
||||
- button "Fibonacci Tool" [disabled] [ref=e63]
|
||||
- button "全屏(F11)" [disabled] [ref=e64]
|
||||
- button "Clear annotations" [disabled] [ref=e65]
|
||||
- status:
|
||||
- generic: 请选择右侧分类项
|
||||
- toolbar "缩放" [ref=e66]:
|
||||
- button "缩小" [disabled] [ref=e67]
|
||||
- button "放大" [ref=e68] [cursor=pointer]
|
||||
- button "左移" [disabled] [ref=e69]
|
||||
- button "右移" [disabled] [ref=e70]
|
||||
- button "重置" [disabled] [ref=e71]
|
||||
- complementary "选择内容" [ref=e72]:
|
||||
- tablist "选择内容切换项" [ref=e73]:
|
||||
- tab "自然地理区位" [selected] [ref=e74] [cursor=pointer]
|
||||
- tab "设施类别" [ref=e75] [cursor=pointer]
|
||||
- tab "建设阶段" [ref=e76] [cursor=pointer]
|
||||
- tab "规划形式" [ref=e77] [cursor=pointer]
|
||||
- generic [ref=e78]:
|
||||
- generic [ref=e79]: 自然地理区位
|
||||
- generic [ref=e80]: 加载中
|
||||
80
.playwright-mcp/page-2026-05-12T04-17-38-470Z.yml
Normal file
80
.playwright-mcp/page-2026-05-12T04-17-38-470Z.yml
Normal file
@ -0,0 +1,80 @@
|
||||
- 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]:
|
||||
- generic:
|
||||
- generic: 库
|
||||
- button "树" [disabled] [ref=e46]:
|
||||
- generic:
|
||||
- generic: 树
|
||||
- button "均" [disabled] [ref=e47]:
|
||||
- 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]
|
||||
- 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]: 加载中
|
||||
BIN
fullscreen-button-position.png
Normal file
BIN
fullscreen-button-position.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
393
src/App.tsx
393
src/App.tsx
@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AgCharts } from 'ag-charts-react';
|
||||
import type { AgCartesianChartOptions } from 'ag-charts-community';
|
||||
import { ModuleRegistry } from 'ag-charts-community';
|
||||
import { Building2, Construction, LayoutGrid, LocateFixed, MapPinned } from 'lucide-react';
|
||||
import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, Waypoints } from 'lucide-react';
|
||||
import {
|
||||
AnnotationsModule,
|
||||
ContextMenuModule,
|
||||
@ -41,12 +41,15 @@ const contentOptions = [
|
||||
] as const;
|
||||
|
||||
const filterOptions = [
|
||||
{ key: 'templateLibrary', label: '模板库', icon: Library },
|
||||
{ key: 'indicatorTree', label: '指标树形', icon: Waypoints },
|
||||
{ key: 'region', label: '省市区', icon: MapPinned },
|
||||
{ key: 'geoLocation', label: '自然地理区位', icon: LocateFixed },
|
||||
{ key: 'facilityType', label: '设施类别', icon: Building2 },
|
||||
{ key: 'constructionStage', label: '建设阶段', icon: Construction },
|
||||
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid },
|
||||
] as const;
|
||||
const chartFilterOptions = filterOptions.filter((option) => option.key !== 'templateLibrary' && option.key !== 'indicatorTree');
|
||||
|
||||
const browserTreeDefaults = {
|
||||
treetype: '256',
|
||||
@ -96,6 +99,11 @@ const contentTreeConfigs = {
|
||||
} as const;
|
||||
|
||||
const chartLineColors = ['#0078a8', '#d14d72', '#1f8f4d', '#d96f23', '#6b5cc8', '#0d7680', '#9a6b12', '#b24b38'];
|
||||
const defaultTemplateFilterNode = {
|
||||
id: '3',
|
||||
filterKey: 'templateLibrary',
|
||||
label: '默认模板',
|
||||
} as const;
|
||||
|
||||
// const mockGeoLocationPayload = {
|
||||
// checkStrictly: true,
|
||||
@ -291,6 +299,60 @@ function normalizeTreeRows(rows: unknown[]): TreeNode[] {
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeFlatTemplateLibraryRows(rows: unknown[]): TreeNode[] {
|
||||
return rows
|
||||
.filter((row): row is Record<string, unknown> => !!row && typeof row === 'object')
|
||||
.map((row, index) => {
|
||||
const id = readText(row, ['id', 'mainid', 'mbid']) || `node-${index}`;
|
||||
const label = readText(row, ['mbmc', 'label', 'name', 'title', 'text']) || id;
|
||||
return createFilterTreeNode(id, label);
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeFlatIndicatorRows(rows: unknown[]): TreeNode[] {
|
||||
const sourceRows = rows.filter((row): row is Record<string, unknown> => !!row && typeof row === 'object');
|
||||
const rowsById = new Map<string, Record<string, unknown>>();
|
||||
const childrenByParent = new Map<string, string[]>();
|
||||
|
||||
sourceRows.forEach((row, index) => {
|
||||
const id = readText(row, ['zbid', 'zjzbk', 'id']) || `node-${index}`;
|
||||
if (!id) return;
|
||||
rowsById.set(id, row);
|
||||
|
||||
const parentId = readText(row, ['sj', 'parentId', 'parentid', 'pid', 'pId']) || '';
|
||||
const children = childrenByParent.get(parentId) || [];
|
||||
children.push(id);
|
||||
childrenByParent.set(parentId, children);
|
||||
});
|
||||
|
||||
const buildNode = (id: string): TreeNode => {
|
||||
const row = rowsById.get(id) || {};
|
||||
const children = (childrenByParent.get(id) || [])
|
||||
.filter((childId) => rowsById.has(childId))
|
||||
.map(buildNode);
|
||||
const label = readText(row, ['label', 'name', 'title', 'text', 'zbbh', 'mbmc']) || id;
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
children,
|
||||
hasChildren: children.length > 0,
|
||||
canClick: true,
|
||||
expanded: children.length > 0,
|
||||
loading: false,
|
||||
loaded: true,
|
||||
};
|
||||
};
|
||||
|
||||
return Array.from(rowsById.keys())
|
||||
.filter((id) => {
|
||||
const row = rowsById.get(id);
|
||||
if (!row) return false;
|
||||
const parentId = readText(row, ['sj', 'parentId', 'parentid', 'pid', 'pId']) || '';
|
||||
return !parentId || parentId === '0' || parentId === '0_0' || !rowsById.has(parentId);
|
||||
})
|
||||
.map(buildNode);
|
||||
}
|
||||
|
||||
const regionFieldKeys = {
|
||||
provinceId: ['provinceId', 'province_id', 'provinceid', 'sfid', 'sf_id', 'shengId', 'sheng_id', 'sheng'],
|
||||
provinceName: ['provinceName', 'province_name', 'province', 'sfmc', 'sf', 'shengName', 'sheng_name', 'shengmc'],
|
||||
@ -384,12 +446,56 @@ function getFilterSelectionKey(filterKey: FilterKey, nodeId: string) {
|
||||
return `${filterKey}:${nodeId}`;
|
||||
}
|
||||
|
||||
function getSeriesValueKey(index: number) {
|
||||
return `amount${index}`;
|
||||
function getSeriesValueKey(contentKey: ContentKey, nodeId: string) {
|
||||
return `amount_${getSelectionKey(contentKey, nodeId).replace(/[^a-zA-Z0-9_]/g, '_')}`;
|
||||
}
|
||||
|
||||
function compareGroupNames(a: string, b: string) {
|
||||
const numberA = Number(a);
|
||||
const numberB = Number(b);
|
||||
if (Number.isFinite(numberA) && Number.isFinite(numberB)) {
|
||||
return numberA - numberB;
|
||||
}
|
||||
return a.localeCompare(b, 'zh-CN', { numeric: true });
|
||||
}
|
||||
|
||||
function isContentFilterKey(filterKey: FilterKey): filterKey is ContentKey {
|
||||
return filterKey !== 'region';
|
||||
return Object.prototype.hasOwnProperty.call(contentTreeConfigs, filterKey);
|
||||
}
|
||||
|
||||
function isTemplateFilterKey(filterKey: FilterKey): filterKey is 'templateLibrary' {
|
||||
return filterKey === 'templateLibrary';
|
||||
}
|
||||
|
||||
function isIndicatorTreeFilterKey(filterKey: FilterKey): filterKey is 'indicatorTree' {
|
||||
return filterKey === 'indicatorTree';
|
||||
}
|
||||
|
||||
function isSingleSelectFilterKey(filterKey: FilterKey) {
|
||||
return isTemplateFilterKey(filterKey) || isIndicatorTreeFilterKey(filterKey);
|
||||
}
|
||||
|
||||
function getDefaultTemplateFilterNodes(): SelectedFilterNode[] {
|
||||
return [{ ...defaultTemplateFilterNode }];
|
||||
}
|
||||
|
||||
function isDefaultTemplateSelection(nodes: SelectedFilterNode[]) {
|
||||
return nodes.length === 1 && nodes[0]?.id === defaultTemplateFilterNode.id;
|
||||
}
|
||||
|
||||
function getDefaultIndicatorTreeFilterNodes(nodes: TreeNode[]): SelectedFilterNode[] {
|
||||
const defaultNode = nodes[0];
|
||||
if (!defaultNode) return [];
|
||||
return [{
|
||||
id: defaultNode.id,
|
||||
filterKey: 'indicatorTree',
|
||||
label: defaultNode.label,
|
||||
}];
|
||||
}
|
||||
|
||||
function isDefaultIndicatorTreeSelection(nodes: SelectedFilterNode[], treeNodes: TreeNode[]) {
|
||||
const defaultNode = treeNodes[0];
|
||||
return Boolean(defaultNode) && nodes.length === 1 && nodes[0]?.id === defaultNode?.id;
|
||||
}
|
||||
|
||||
function filterTreeNodesByKeyword(nodes: TreeNode[], keyword: string): TreeNode[] {
|
||||
@ -516,6 +622,8 @@ function App() {
|
||||
planningForm: false,
|
||||
});
|
||||
const filterTreeInitialLoadStartedRef = useRef<Record<FilterKey, boolean>>({
|
||||
templateLibrary: false,
|
||||
indicatorTree: false,
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
@ -553,6 +661,8 @@ function App() {
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
const [loadingHint, setLoadingHint] = useState('');
|
||||
const [filterTreeByKey, setFilterTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({
|
||||
templateLibrary: [],
|
||||
indicatorTree: [],
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
@ -560,6 +670,8 @@ function App() {
|
||||
planningForm: [],
|
||||
});
|
||||
const [filterTreeLoadingByKey, setFilterTreeLoadingByKey] = useState<Record<FilterKey, boolean>>({
|
||||
templateLibrary: false,
|
||||
indicatorTree: false,
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
@ -567,6 +679,8 @@ function App() {
|
||||
planningForm: false,
|
||||
});
|
||||
const [filterTreeErrorByKey, setFilterTreeErrorByKey] = useState<Record<FilterKey, string | null>>({
|
||||
templateLibrary: null,
|
||||
indicatorTree: null,
|
||||
region: null,
|
||||
geoLocation: null,
|
||||
facilityType: null,
|
||||
@ -574,6 +688,8 @@ function App() {
|
||||
planningForm: null,
|
||||
});
|
||||
const [filterSearchTreeByKey, setFilterSearchTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({
|
||||
templateLibrary: [],
|
||||
indicatorTree: [],
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
@ -581,6 +697,8 @@ function App() {
|
||||
planningForm: [],
|
||||
});
|
||||
const [filterSearchLoadingByKey, setFilterSearchLoadingByKey] = useState<Record<FilterKey, boolean>>({
|
||||
templateLibrary: false,
|
||||
indicatorTree: false,
|
||||
region: false,
|
||||
geoLocation: false,
|
||||
facilityType: false,
|
||||
@ -588,6 +706,8 @@ function App() {
|
||||
planningForm: false,
|
||||
});
|
||||
const [filterSearchErrorByKey, setFilterSearchErrorByKey] = useState<Record<FilterKey, string | null>>({
|
||||
templateLibrary: null,
|
||||
indicatorTree: null,
|
||||
region: null,
|
||||
geoLocation: null,
|
||||
facilityType: null,
|
||||
@ -595,6 +715,8 @@ function App() {
|
||||
planningForm: null,
|
||||
});
|
||||
const [appliedFilters, setAppliedFilters] = useState<Record<FilterKey, SelectedFilterNode[]>>({
|
||||
templateLibrary: getDefaultTemplateFilterNodes(),
|
||||
indicatorTree: [],
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
@ -607,6 +729,8 @@ function App() {
|
||||
const filterSearchComposingRef = useRef(false);
|
||||
const filterSearchTimerRef = useRef<number | null>(null);
|
||||
const filterSearchRequestSeqRef = useRef<Record<FilterKey, number>>({
|
||||
templateLibrary: 0,
|
||||
indicatorTree: 0,
|
||||
region: 0,
|
||||
geoLocation: 0,
|
||||
facilityType: 0,
|
||||
@ -629,7 +753,14 @@ function App() {
|
||||
const activeFilterTreeError = filterModalKey
|
||||
? trimmedFilterSearchValue ? filterSearchErrorByKey[filterModalKey] : filterTreeErrorByKey[filterModalKey]
|
||||
: null;
|
||||
const activeFilterCount = Object.values(appliedFilters).reduce((total, nodes) => total + nodes.length, 0);
|
||||
const selectedTemplateId = appliedFilters.templateLibrary[0]?.id || defaultTemplateFilterNode.id;
|
||||
const defaultIndicatorTreeNodes = useMemo(
|
||||
() => getDefaultIndicatorTreeFilterNodes(filterTreeByKey.indicatorTree),
|
||||
[filterTreeByKey.indicatorTree],
|
||||
);
|
||||
const activeFilterCount = Object.entries(appliedFilters).reduce((total, [key, nodes]) => (
|
||||
key === 'templateLibrary' && isDefaultTemplateSelection(nodes) ? total : total + nodes.length
|
||||
), 0);
|
||||
const chartEmptyText = selectedContentNodes.length === 0
|
||||
? '请选择右侧分类项'
|
||||
: activeFilterCount > 0
|
||||
@ -743,6 +874,36 @@ function App() {
|
||||
return flatRegionTree.length > 0 ? flatRegionTree : normalizeBackendTree(normalizeTreeRows(rows));
|
||||
};
|
||||
|
||||
const fetchTemplateLibraryTree = async (signal?: AbortSignal) => {
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTree?${buildQuery({ key: 'templateLibrary' })}`, {
|
||||
signal,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
const rows = pickArray(await response.json());
|
||||
const templateRows = normalizeFlatTemplateLibraryRows(rows);
|
||||
return templateRows.length > 0 ? templateRows : normalizeBackendTree(normalizeTreeRows(rows));
|
||||
};
|
||||
|
||||
const fetchIndicatorTree = async (signal?: AbortSignal) => {
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTree?${buildQuery({ key: 'indicatorTree', templateId: selectedTemplateId })}`, {
|
||||
signal,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
const rows = pickArray(await response.json());
|
||||
const indicatorRows = normalizeFlatIndicatorRows(rows);
|
||||
return indicatorRows.length > 0 ? indicatorRows : normalizeBackendTree(normalizeTreeRows(rows));
|
||||
};
|
||||
|
||||
const fetchBackendFilterTreeSearch = async (filterKey: FilterKey, keyword: string, signal?: AbortSignal) => {
|
||||
const response = await fetch(`${API_BASE_URL}/zw/getBuildingFunctionCostFilterTreeSearch?${buildQuery({
|
||||
key: filterKey,
|
||||
@ -791,10 +952,21 @@ function App() {
|
||||
const nodes = await fetchRegionFilterTree(signal);
|
||||
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes;
|
||||
}
|
||||
if (keyword?.trim()) {
|
||||
if (isTemplateFilterKey(filterKey)) {
|
||||
const nodes = await fetchTemplateLibraryTree(signal);
|
||||
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes;
|
||||
}
|
||||
if (filterKey === 'indicatorTree') {
|
||||
const nodes = await fetchIndicatorTree(signal);
|
||||
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes;
|
||||
}
|
||||
if (keyword?.trim() && isContentFilterKey(filterKey)) {
|
||||
return fetchBackendFilterTreeSearch(filterKey, keyword, signal);
|
||||
}
|
||||
return loadContentTreeWithDefaultExpansion(filterKey);
|
||||
if (isContentFilterKey(filterKey)) {
|
||||
return loadContentTreeWithDefaultExpansion(filterKey);
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
const toggleContentNode = (nodeId: string) => {
|
||||
@ -880,6 +1052,17 @@ function App() {
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const resetIndicatorTreeState = () => {
|
||||
filterTreeInitialLoadStartedRef.current.indicatorTree = false;
|
||||
filterSearchRequestSeqRef.current.indicatorTree += 1;
|
||||
setFilterTreeByKey((current) => ({ ...current, indicatorTree: [] }));
|
||||
setFilterTreeLoadingByKey((current) => ({ ...current, indicatorTree: false }));
|
||||
setFilterTreeErrorByKey((current) => ({ ...current, indicatorTree: null }));
|
||||
setFilterSearchTreeByKey((current) => ({ ...current, indicatorTree: [] }));
|
||||
setFilterSearchLoadingByKey((current) => ({ ...current, indicatorTree: false }));
|
||||
setFilterSearchErrorByKey((current) => ({ ...current, indicatorTree: null }));
|
||||
};
|
||||
|
||||
const ensureFilterTreeLoaded = (filterKey: FilterKey) => {
|
||||
if (filterTreeByKey[filterKey].length > 0 || filterTreeInitialLoadStartedRef.current[filterKey]) return;
|
||||
|
||||
@ -904,7 +1087,11 @@ function App() {
|
||||
|
||||
const openFilterModal = (filterKey: FilterKey) => {
|
||||
setFilterModalKey(filterKey);
|
||||
setDraftFilterNodes(appliedFilters[filterKey]);
|
||||
if (isIndicatorTreeFilterKey(filterKey) && appliedFilters[filterKey].length === 0 && defaultIndicatorTreeNodes.length > 0) {
|
||||
setDraftFilterNodes(defaultIndicatorTreeNodes);
|
||||
} else {
|
||||
setDraftFilterNodes(appliedFilters[filterKey]);
|
||||
}
|
||||
setFilterSearchValue('');
|
||||
lastFilterSearchRef.current = '';
|
||||
if (filterSearchTimerRef.current != null) {
|
||||
@ -950,6 +1137,19 @@ function App() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isTemplateFilterKey(filterKey) || filterKey === 'indicatorTree') {
|
||||
filterSearchTimerRef.current = window.setTimeout(() => {
|
||||
setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: true }));
|
||||
setFilterSearchErrorByKey((current) => ({ ...current, [filterKey]: null }));
|
||||
setFilterSearchTreeByKey((current) => ({
|
||||
...current,
|
||||
[filterKey]: filterTreeNodesByKeyword(filterTreeByKey[filterKey], keyword),
|
||||
}));
|
||||
setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: false }));
|
||||
}, 300);
|
||||
return;
|
||||
}
|
||||
|
||||
filterSearchTimerRef.current = window.setTimeout(() => {
|
||||
setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: true }));
|
||||
setFilterSearchErrorByKey((current) => ({ ...current, [filterKey]: null }));
|
||||
@ -1032,6 +1232,12 @@ function App() {
|
||||
setDraftFilterNodes((current) => {
|
||||
const selectionKey = getFilterSelectionKey(currentFilterKey, node.id);
|
||||
const exists = current.some((item) => getFilterSelectionKey(item.filterKey, item.id) === selectionKey);
|
||||
if (isSingleSelectFilterKey(currentFilterKey)) {
|
||||
if (exists) {
|
||||
return current;
|
||||
}
|
||||
return [{ id: node.id, filterKey: currentFilterKey, label: node.label }];
|
||||
}
|
||||
if (exists) {
|
||||
return current.filter((item) => getFilterSelectionKey(item.filterKey, item.id) !== selectionKey);
|
||||
}
|
||||
@ -1041,10 +1247,31 @@ function App() {
|
||||
|
||||
const applyFilterModal = () => {
|
||||
if (!filterModalKey) return;
|
||||
setAppliedFilters((current) => ({
|
||||
...current,
|
||||
[filterModalKey]: draftFilterNodes,
|
||||
}));
|
||||
let nextDraftNodes = draftFilterNodes;
|
||||
if (isTemplateFilterKey(filterModalKey) && nextDraftNodes.length === 0) {
|
||||
nextDraftNodes = getDefaultTemplateFilterNodes();
|
||||
}
|
||||
if (isIndicatorTreeFilterKey(filterModalKey)) {
|
||||
if (nextDraftNodes.length === 0) {
|
||||
nextDraftNodes = defaultIndicatorTreeNodes;
|
||||
}
|
||||
if (isDefaultIndicatorTreeSelection(nextDraftNodes, filterTreeByKey.indicatorTree)) {
|
||||
nextDraftNodes = [];
|
||||
}
|
||||
}
|
||||
setAppliedFilters((current) => {
|
||||
const nextFilters = {
|
||||
...current,
|
||||
[filterModalKey]: nextDraftNodes,
|
||||
};
|
||||
if (isTemplateFilterKey(filterModalKey)) {
|
||||
nextFilters.indicatorTree = [];
|
||||
}
|
||||
return nextFilters;
|
||||
});
|
||||
if (isTemplateFilterKey(filterModalKey)) {
|
||||
resetIndicatorTreeState();
|
||||
}
|
||||
setChartDataBySelection({});
|
||||
setLoadError(null);
|
||||
if (selectedContentNodes.length > 0) {
|
||||
@ -1056,11 +1283,27 @@ function App() {
|
||||
};
|
||||
|
||||
const clearFilter = (filterKey: FilterKey) => {
|
||||
if (appliedFilters[filterKey].length === 0) return;
|
||||
setAppliedFilters((current) => ({
|
||||
...current,
|
||||
[filterKey]: [],
|
||||
}));
|
||||
const nextNodes = isTemplateFilterKey(filterKey)
|
||||
? getDefaultTemplateFilterNodes()
|
||||
: isIndicatorTreeFilterKey(filterKey)
|
||||
? defaultIndicatorTreeNodes
|
||||
: [];
|
||||
if (appliedFilters[filterKey].length === nextNodes.length && appliedFilters[filterKey].every((node, index) => node.id === nextNodes[index]?.id)) {
|
||||
return;
|
||||
}
|
||||
setAppliedFilters((current) => {
|
||||
const nextFilters = {
|
||||
...current,
|
||||
[filterKey]: nextNodes,
|
||||
};
|
||||
if (isTemplateFilterKey(filterKey)) {
|
||||
nextFilters.indicatorTree = [];
|
||||
}
|
||||
return nextFilters;
|
||||
});
|
||||
if (isTemplateFilterKey(filterKey)) {
|
||||
resetIndicatorTreeState();
|
||||
}
|
||||
setChartDataBySelection({});
|
||||
setLoadError(null);
|
||||
if (selectedContentNodes.length > 0) {
|
||||
@ -1104,6 +1347,13 @@ function App() {
|
||||
});
|
||||
}, [activeContentKey, treeByContent]);
|
||||
|
||||
useEffect(() => {
|
||||
if (filterModalKey !== 'indicatorTree') return;
|
||||
if (draftFilterNodes.length > 0) return;
|
||||
if (defaultIndicatorTreeNodes.length === 0) return;
|
||||
setDraftFilterNodes(defaultIndicatorTreeNodes);
|
||||
}, [defaultIndicatorTreeNodes, draftFilterNodes.length, filterModalKey]);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
|
||||
@ -1136,6 +1386,7 @@ function App() {
|
||||
body: JSON.stringify({
|
||||
groupBy: groupKey,
|
||||
metric: requestMetricKey,
|
||||
templateId: selectedTemplateId,
|
||||
filters: appliedFilterPayload,
|
||||
nodes: selectedContentNodes.map((node) => ({
|
||||
key: getSelectionKey(node.contentKey, node.id),
|
||||
@ -1171,16 +1422,37 @@ function App() {
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes]);
|
||||
}, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
|
||||
|
||||
useEffect(() => {
|
||||
const frame = chartFrameRef.current;
|
||||
const fullscreenTarget = workspaceRef.current;
|
||||
if (!frame || !fullscreenTarget) return;
|
||||
|
||||
const getFullscreenButton = () => frame.querySelector<HTMLButtonElement>('.chart-fullscreen-button');
|
||||
const getTemplateButton = () => frame.querySelector<HTMLElement>('.ag-charts-myButton-template')?.closest<HTMLButtonElement>('.ag-charts-toolbar__button');
|
||||
const getIndicatorButton = () => frame.querySelector<HTMLElement>('.ag-charts-myButton-indicator')?.closest<HTMLButtonElement>('.ag-charts-toolbar__button');
|
||||
const getFullscreenButton = () => frame.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen')?.closest<HTMLButtonElement>('.ag-charts-toolbar__button');
|
||||
const getStatisticButton = () => frame.querySelector<HTMLElement>('.ag-charts-myButton-statistic')?.closest<HTMLButtonElement>('.ag-charts-toolbar__button');
|
||||
const setButtonAttribute = (button: HTMLButtonElement, name: string, value: string) => {
|
||||
if (button.getAttribute(name) !== value) {
|
||||
button.setAttribute(name, value);
|
||||
}
|
||||
};
|
||||
const syncToolbarButtons = () => {
|
||||
const templateButton = getTemplateButton();
|
||||
if (templateButton) {
|
||||
templateButton.classList.add('chart-template-button');
|
||||
setButtonAttribute(templateButton, 'aria-disabled', 'false');
|
||||
setButtonAttribute(templateButton, 'aria-expanded', String(filterModalKey === 'templateLibrary'));
|
||||
}
|
||||
|
||||
const indicatorButton = getIndicatorButton();
|
||||
if (indicatorButton) {
|
||||
indicatorButton.classList.add('chart-indicator-button');
|
||||
setButtonAttribute(indicatorButton, 'aria-disabled', 'false');
|
||||
setButtonAttribute(indicatorButton, 'aria-expanded', String(filterModalKey === 'indicatorTree'));
|
||||
}
|
||||
|
||||
const button = getFullscreenButton();
|
||||
if (button) {
|
||||
let icon = button.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen');
|
||||
@ -1190,7 +1462,10 @@ function App() {
|
||||
}
|
||||
|
||||
const isFullscreen = document.fullscreenElement === fullscreenTarget;
|
||||
button.classList.add('chart-fullscreen-button');
|
||||
button.classList.toggle('ag-charts-toolbar__button--active', isFullscreen);
|
||||
setButtonAttribute(button, 'aria-disabled', 'false');
|
||||
setButtonAttribute(button, 'aria-pressed', String(isFullscreen));
|
||||
icon?.classList.toggle('anticon-arrow-salt', !isFullscreen);
|
||||
icon?.classList.toggle('anticon-shrink', isFullscreen);
|
||||
}
|
||||
@ -1198,7 +1473,8 @@ function App() {
|
||||
const statisticButton = getStatisticButton();
|
||||
if (statisticButton) {
|
||||
statisticButton.classList.add('chart-statistic-button');
|
||||
statisticButton.setAttribute('aria-expanded', String(statisticMenuOpen));
|
||||
setButtonAttribute(statisticButton, 'aria-disabled', 'false');
|
||||
setButtonAttribute(statisticButton, 'aria-expanded', String(statisticMenuOpen));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1225,14 +1501,22 @@ function App() {
|
||||
const handleToolbarClick = (event: MouseEvent) => {
|
||||
const target = event.target as Element | null;
|
||||
const button = target?.closest<HTMLButtonElement>(
|
||||
'.chart-fullscreen-button, .chart-statistic-button',
|
||||
'.chart-template-button, .chart-indicator-button, .chart-fullscreen-button, .chart-statistic-button',
|
||||
);
|
||||
if (!button || !frame.contains(button)) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
if (button.classList.contains('chart-statistic-button')) {
|
||||
if (button.classList.contains('chart-template-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen(false);
|
||||
openFilterModal('templateLibrary');
|
||||
} else if (button.classList.contains('chart-indicator-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen(false);
|
||||
openFilterModal('indicatorTree');
|
||||
} else if (button.classList.contains('chart-statistic-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen((open) => !open);
|
||||
} else {
|
||||
@ -1245,14 +1529,22 @@ function App() {
|
||||
|
||||
const target = event.target as Element | null;
|
||||
const button = target?.closest<HTMLButtonElement>(
|
||||
'.chart-fullscreen-button, .chart-statistic-button',
|
||||
'.chart-template-button, .chart-indicator-button, .chart-fullscreen-button, .chart-statistic-button',
|
||||
);
|
||||
if (!button || !frame.contains(button)) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
if (button.classList.contains('chart-statistic-button')) {
|
||||
if (button.classList.contains('chart-template-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen(false);
|
||||
openFilterModal('templateLibrary');
|
||||
} else if (button.classList.contains('chart-indicator-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen(false);
|
||||
openFilterModal('indicatorTree');
|
||||
} else if (button.classList.contains('chart-statistic-button')) {
|
||||
setMetricMenuOpen(false);
|
||||
setStatisticMenuOpen((open) => !open);
|
||||
} else {
|
||||
@ -1265,6 +1557,11 @@ function App() {
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(syncToolbarButtons);
|
||||
const initialSyncTimers = [
|
||||
window.setTimeout(syncToolbarButtons, 0),
|
||||
window.setTimeout(syncToolbarButtons, 100),
|
||||
window.setTimeout(syncToolbarButtons, 500),
|
||||
];
|
||||
document.addEventListener('keydown', handleKeyDown, true);
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
@ -1275,6 +1572,7 @@ function App() {
|
||||
syncToolbarButtons();
|
||||
|
||||
return () => {
|
||||
initialSyncTimers.forEach((timer) => window.clearTimeout(timer));
|
||||
document.removeEventListener('keydown', handleKeyDown, true);
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.removeEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
@ -1283,7 +1581,7 @@ function App() {
|
||||
frame.removeEventListener('keydown', handleToolbarKeyDown, true);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [statisticMenuOpen]);
|
||||
}, [appliedFilters, filterModalKey, filterTreeByKey, statisticMenuOpen]);
|
||||
|
||||
const chartOptions = useMemo<AgCartesianChartOptions>(() => {
|
||||
const groupNames: string[] = [];
|
||||
@ -1296,11 +1594,12 @@ function App() {
|
||||
groupNames.push(datum.groupName);
|
||||
});
|
||||
});
|
||||
groupNames.sort(compareGroupNames);
|
||||
const visibleData = groupNames.map((groupName) => {
|
||||
const row: Record<string, string | number | null> = { groupName };
|
||||
selectedContentNodes.forEach((node, index) => {
|
||||
selectedContentNodes.forEach((node) => {
|
||||
const datum = chartDataBySelection[getSelectionKey(node.contentKey, node.id)]?.find((item) => item.groupName === groupName);
|
||||
row[getSeriesValueKey(index)] = datum?.[selectedValueKey] ?? null;
|
||||
row[getSeriesValueKey(node.contentKey, node.id)] = datum?.[selectedValueKey] ?? null;
|
||||
});
|
||||
return row;
|
||||
});
|
||||
@ -1363,6 +1662,16 @@ function App() {
|
||||
enabled: true,
|
||||
toolbar: {
|
||||
buttons: ([
|
||||
{
|
||||
value: 'callout',
|
||||
tooltip: '模板库',
|
||||
label: '<span class="ag-charts-myButton-template ag-charts-diy-button">库</span>',
|
||||
},
|
||||
{
|
||||
value: 'text',
|
||||
tooltip: '指标树形',
|
||||
label: '<span class="ag-charts-myButton-indicator ag-charts-diy-button">树</span>',
|
||||
},
|
||||
{
|
||||
value: 'note',
|
||||
tooltip: '切换统计指标',
|
||||
@ -1388,6 +1697,11 @@ function App() {
|
||||
value: 'fibonacci-menu',
|
||||
tooltip: 'Fibonacci Tool',
|
||||
},
|
||||
{
|
||||
value: 'comment',
|
||||
tooltip: '全屏(F11)',
|
||||
label: '<i class="anticon anticon-arrow-salt ag-charts-myButton-fullScreen ag-charts-diy-button"></i>',
|
||||
},
|
||||
{
|
||||
icon: 'delete',
|
||||
value: 'clear',
|
||||
@ -1396,13 +1710,14 @@ function App() {
|
||||
] as unknown as NonNullable<NonNullable<AgCartesianChartOptions['annotations']>['toolbar']>['buttons']),
|
||||
},
|
||||
},
|
||||
series: selectedContentNodes.map((node, index) => ({
|
||||
series: selectedContentNodes.map((node) => ({
|
||||
type: 'line',
|
||||
xKey: 'groupName',
|
||||
yKey: getSeriesValueKey(index),
|
||||
yKey: getSeriesValueKey(node.contentKey, node.id),
|
||||
yName: `${node.label} ${seriesValueLabel}`,
|
||||
stroke: node.color,
|
||||
strokeWidth: 2,
|
||||
connectMissingData: true,
|
||||
marker: {
|
||||
enabled: true,
|
||||
fill: node.color,
|
||||
@ -1492,7 +1807,7 @@ function App() {
|
||||
|
||||
<section className="workspace" aria-label="年度费用模板" ref={workspaceRef}>
|
||||
<div className="chart-filter-bar chart-filter-bar--workspace" aria-label="筛选条件">
|
||||
{filterOptions.map((option) => {
|
||||
{chartFilterOptions.map((option) => {
|
||||
const count = appliedFilters[option.key].length;
|
||||
const FilterIcon = option.icon;
|
||||
return (
|
||||
@ -1516,7 +1831,10 @@ function App() {
|
||||
type="button"
|
||||
title="清空全部筛选"
|
||||
onClick={() => {
|
||||
resetIndicatorTreeState();
|
||||
setAppliedFilters({
|
||||
templateLibrary: getDefaultTemplateFilterNodes(),
|
||||
indicatorTree: [],
|
||||
region: [],
|
||||
geoLocation: [],
|
||||
facilityType: [],
|
||||
@ -1593,9 +1911,6 @@ function App() {
|
||||
) : null}
|
||||
</div>
|
||||
{loading || loadError ? <div className="chart-status">{loading ? loadingHint || '加载中' : loadError}</div> : null}
|
||||
<button className="chart-fullscreen-button ag-charts-toolbar__button" type="button" title="全屏(F11)">
|
||||
<i className="anticon anticon-arrow-salt ag-charts-myButton-fullScreen ag-charts-diy-button" />
|
||||
</button>
|
||||
<AgCharts options={chartOptions} />
|
||||
{loading ? (
|
||||
<div className="chart-loading-mask" aria-live="polite" aria-busy="true">
|
||||
@ -1687,7 +2002,17 @@ function App() {
|
||||
)}
|
||||
</div>
|
||||
<footer className="filter-modal-actions">
|
||||
<button className="filter-modal-clear" type="button" onClick={() => setDraftFilterNodes([])}>
|
||||
<button
|
||||
className="filter-modal-clear"
|
||||
type="button"
|
||||
onClick={() => setDraftFilterNodes(
|
||||
isTemplateFilterKey(filterModalKey)
|
||||
? getDefaultTemplateFilterNodes()
|
||||
: isIndicatorTreeFilterKey(filterModalKey)
|
||||
? defaultIndicatorTreeNodes
|
||||
: [],
|
||||
)}
|
||||
>
|
||||
清空当前
|
||||
</button>
|
||||
<button className="filter-modal-cancel" type="button" onClick={closeFilterModal}>
|
||||
|
||||
@ -359,10 +359,25 @@ button {
|
||||
background: rgba(255, 252, 248, 0.94);
|
||||
}
|
||||
|
||||
.chart-frame .chart-template-button,
|
||||
.chart-frame .chart-indicator-button,
|
||||
.chart-frame .chart-statistic-button {
|
||||
color: #46413b;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-myButton-template,
|
||||
.chart-frame .ag-charts-myButton-indicator {
|
||||
display: block;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.chart-frame .chart-template-button[aria-expanded="true"],
|
||||
.chart-frame .chart-indicator-button[aria-expanded="true"],
|
||||
.chart-frame .chart-statistic-button[aria-expanded="true"] {
|
||||
color: #0078a8;
|
||||
border-color: rgba(0, 120, 168, 0.36);
|
||||
@ -456,29 +471,6 @@ button {
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.chart-frame .chart-fullscreen-button {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
top: 252px;
|
||||
z-index: 9;
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
min-width: 34px;
|
||||
min-height: 34px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(90, 82, 72, 0.22);
|
||||
border-radius: 3px;
|
||||
color: #46413b;
|
||||
background: rgba(255, 249, 241, 0.72);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button[title="Clear annotations"] {
|
||||
transform: translateY(42px);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-zoom-buttons {
|
||||
top: auto !important;
|
||||
right: 24px !important;
|
||||
|
||||
37
vite-dev.log
37
vite-dev.log
@ -194,3 +194,40 @@ Port 5173 is in use, trying another one...
|
||||
[2m15:44:06[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mhmr update [39m[2m/src/App.tsx[22m
|
||||
[2m16:32:19[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mhmr update [39m[2m/src/App.tsx[22m
|
||||
[2m17:15:32[22m [36m[1m[vite][22m[39m [90m[2m(client)[22m[39m [32mhmr update [39m[2m/src/App.tsx[22m
|
||||
| ||||