This commit is contained in:
wintsa 2026-05-12 15:56:50 +08:00
parent 7845ae8eb0
commit 53c1b6f523
22 changed files with 1088 additions and 57 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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&currenttime=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&currenttime=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&currenttime=1778560339458&__random__=1778560339458:0

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

View 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]: 加载中

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { AgCharts } from 'ag-charts-react'; import { AgCharts } from 'ag-charts-react';
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, LocateFixed, MapPinned } from 'lucide-react'; import { Building2, Construction, LayoutGrid, Library, LocateFixed, MapPinned, Waypoints } from 'lucide-react';
import { import {
AnnotationsModule, AnnotationsModule,
ContextMenuModule, ContextMenuModule,
@ -41,12 +41,15 @@ const contentOptions = [
] as const; ] as const;
const filterOptions = [ const filterOptions = [
{ key: 'templateLibrary', label: '模板库', icon: Library },
{ key: 'indicatorTree', label: '指标树形', icon: Waypoints },
{ 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: 'constructionStage', label: '建设阶段', icon: Construction }, { key: 'constructionStage', label: '建设阶段', icon: Construction },
{ key: 'planningForm', label: '规划形式', icon: LayoutGrid }, { key: 'planningForm', label: '规划形式', icon: LayoutGrid },
] as const; ] as const;
const chartFilterOptions = filterOptions.filter((option) => option.key !== 'templateLibrary' && option.key !== 'indicatorTree');
const browserTreeDefaults = { const browserTreeDefaults = {
treetype: '256', treetype: '256',
@ -96,6 +99,11 @@ const contentTreeConfigs = {
} as const; } as const;
const chartLineColors = ['#0078a8', '#d14d72', '#1f8f4d', '#d96f23', '#6b5cc8', '#0d7680', '#9a6b12', '#b24b38']; const chartLineColors = ['#0078a8', '#d14d72', '#1f8f4d', '#d96f23', '#6b5cc8', '#0d7680', '#9a6b12', '#b24b38'];
const defaultTemplateFilterNode = {
id: '3',
filterKey: 'templateLibrary',
label: '默认模板',
} as const;
// const mockGeoLocationPayload = { // const mockGeoLocationPayload = {
// checkStrictly: true, // 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 = { const regionFieldKeys = {
provinceId: ['provinceId', 'province_id', 'provinceid', 'sfid', 'sf_id', 'shengId', 'sheng_id', 'sheng'], provinceId: ['provinceId', 'province_id', 'provinceid', 'sfid', 'sf_id', 'shengId', 'sheng_id', 'sheng'],
provinceName: ['provinceName', 'province_name', 'province', 'sfmc', 'sf', 'shengName', 'sheng_name', 'shengmc'], provinceName: ['provinceName', 'province_name', 'province', 'sfmc', 'sf', 'shengName', 'sheng_name', 'shengmc'],
@ -384,12 +446,56 @@ function getFilterSelectionKey(filterKey: FilterKey, nodeId: string) {
return `${filterKey}:${nodeId}`; return `${filterKey}:${nodeId}`;
} }
function getSeriesValueKey(index: number) { function getSeriesValueKey(contentKey: ContentKey, nodeId: string) {
return `amount${index}`; 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 { 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[] { function filterTreeNodesByKeyword(nodes: TreeNode[], keyword: string): TreeNode[] {
@ -516,6 +622,8 @@ function App() {
planningForm: false, planningForm: false,
}); });
const filterTreeInitialLoadStartedRef = useRef<Record<FilterKey, boolean>>({ const filterTreeInitialLoadStartedRef = useRef<Record<FilterKey, boolean>>({
templateLibrary: false,
indicatorTree: false,
region: false, region: false,
geoLocation: false, geoLocation: false,
facilityType: false, facilityType: false,
@ -553,6 +661,8 @@ function App() {
const [loadError, setLoadError] = useState<string | null>(null); const [loadError, setLoadError] = useState<string | null>(null);
const [loadingHint, setLoadingHint] = useState(''); const [loadingHint, setLoadingHint] = useState('');
const [filterTreeByKey, setFilterTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({ const [filterTreeByKey, setFilterTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({
templateLibrary: [],
indicatorTree: [],
region: [], region: [],
geoLocation: [], geoLocation: [],
facilityType: [], facilityType: [],
@ -560,6 +670,8 @@ function App() {
planningForm: [], planningForm: [],
}); });
const [filterTreeLoadingByKey, setFilterTreeLoadingByKey] = useState<Record<FilterKey, boolean>>({ const [filterTreeLoadingByKey, setFilterTreeLoadingByKey] = useState<Record<FilterKey, boolean>>({
templateLibrary: false,
indicatorTree: false,
region: false, region: false,
geoLocation: false, geoLocation: false,
facilityType: false, facilityType: false,
@ -567,6 +679,8 @@ function App() {
planningForm: false, planningForm: false,
}); });
const [filterTreeErrorByKey, setFilterTreeErrorByKey] = useState<Record<FilterKey, string | null>>({ const [filterTreeErrorByKey, setFilterTreeErrorByKey] = useState<Record<FilterKey, string | null>>({
templateLibrary: null,
indicatorTree: null,
region: null, region: null,
geoLocation: null, geoLocation: null,
facilityType: null, facilityType: null,
@ -574,6 +688,8 @@ function App() {
planningForm: null, planningForm: null,
}); });
const [filterSearchTreeByKey, setFilterSearchTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({ const [filterSearchTreeByKey, setFilterSearchTreeByKey] = useState<Record<FilterKey, TreeNode[]>>({
templateLibrary: [],
indicatorTree: [],
region: [], region: [],
geoLocation: [], geoLocation: [],
facilityType: [], facilityType: [],
@ -581,6 +697,8 @@ function App() {
planningForm: [], planningForm: [],
}); });
const [filterSearchLoadingByKey, setFilterSearchLoadingByKey] = useState<Record<FilterKey, boolean>>({ const [filterSearchLoadingByKey, setFilterSearchLoadingByKey] = useState<Record<FilterKey, boolean>>({
templateLibrary: false,
indicatorTree: false,
region: false, region: false,
geoLocation: false, geoLocation: false,
facilityType: false, facilityType: false,
@ -588,6 +706,8 @@ function App() {
planningForm: false, planningForm: false,
}); });
const [filterSearchErrorByKey, setFilterSearchErrorByKey] = useState<Record<FilterKey, string | null>>({ const [filterSearchErrorByKey, setFilterSearchErrorByKey] = useState<Record<FilterKey, string | null>>({
templateLibrary: null,
indicatorTree: null,
region: null, region: null,
geoLocation: null, geoLocation: null,
facilityType: null, facilityType: null,
@ -595,6 +715,8 @@ function App() {
planningForm: null, planningForm: null,
}); });
const [appliedFilters, setAppliedFilters] = useState<Record<FilterKey, SelectedFilterNode[]>>({ const [appliedFilters, setAppliedFilters] = useState<Record<FilterKey, SelectedFilterNode[]>>({
templateLibrary: getDefaultTemplateFilterNodes(),
indicatorTree: [],
region: [], region: [],
geoLocation: [], geoLocation: [],
facilityType: [], facilityType: [],
@ -607,6 +729,8 @@ function App() {
const filterSearchComposingRef = useRef(false); const filterSearchComposingRef = useRef(false);
const filterSearchTimerRef = useRef<number | null>(null); const filterSearchTimerRef = useRef<number | null>(null);
const filterSearchRequestSeqRef = useRef<Record<FilterKey, number>>({ const filterSearchRequestSeqRef = useRef<Record<FilterKey, number>>({
templateLibrary: 0,
indicatorTree: 0,
region: 0, region: 0,
geoLocation: 0, geoLocation: 0,
facilityType: 0, facilityType: 0,
@ -629,7 +753,14 @@ function App() {
const activeFilterTreeError = filterModalKey const activeFilterTreeError = filterModalKey
? trimmedFilterSearchValue ? filterSearchErrorByKey[filterModalKey] : filterTreeErrorByKey[filterModalKey] ? trimmedFilterSearchValue ? filterSearchErrorByKey[filterModalKey] : filterTreeErrorByKey[filterModalKey]
: null; : 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 const chartEmptyText = selectedContentNodes.length === 0
? '请选择右侧分类项' ? '请选择右侧分类项'
: activeFilterCount > 0 : activeFilterCount > 0
@ -743,6 +874,36 @@ function App() {
return flatRegionTree.length > 0 ? flatRegionTree : normalizeBackendTree(normalizeTreeRows(rows)); 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 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}/zw/getBuildingFunctionCostFilterTreeSearch?${buildQuery({
key: filterKey, key: filterKey,
@ -791,10 +952,21 @@ function App() {
const nodes = await fetchRegionFilterTree(signal); const nodes = await fetchRegionFilterTree(signal);
return keyword?.trim() ? filterTreeNodesByKeyword(nodes, keyword) : nodes; 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 fetchBackendFilterTreeSearch(filterKey, keyword, signal);
} }
return loadContentTreeWithDefaultExpansion(filterKey); if (isContentFilterKey(filterKey)) {
return loadContentTreeWithDefaultExpansion(filterKey);
}
return [];
}; };
const toggleContentNode = (nodeId: string) => { const toggleContentNode = (nodeId: string) => {
@ -880,6 +1052,17 @@ function App() {
setLoading(false); 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) => { const ensureFilterTreeLoaded = (filterKey: FilterKey) => {
if (filterTreeByKey[filterKey].length > 0 || filterTreeInitialLoadStartedRef.current[filterKey]) return; if (filterTreeByKey[filterKey].length > 0 || filterTreeInitialLoadStartedRef.current[filterKey]) return;
@ -904,7 +1087,11 @@ function App() {
const openFilterModal = (filterKey: FilterKey) => { const openFilterModal = (filterKey: FilterKey) => {
setFilterModalKey(filterKey); setFilterModalKey(filterKey);
setDraftFilterNodes(appliedFilters[filterKey]); if (isIndicatorTreeFilterKey(filterKey) && appliedFilters[filterKey].length === 0 && defaultIndicatorTreeNodes.length > 0) {
setDraftFilterNodes(defaultIndicatorTreeNodes);
} else {
setDraftFilterNodes(appliedFilters[filterKey]);
}
setFilterSearchValue(''); setFilterSearchValue('');
lastFilterSearchRef.current = ''; lastFilterSearchRef.current = '';
if (filterSearchTimerRef.current != null) { if (filterSearchTimerRef.current != null) {
@ -950,6 +1137,19 @@ function App() {
return; 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(() => { filterSearchTimerRef.current = window.setTimeout(() => {
setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: true })); setFilterSearchLoadingByKey((current) => ({ ...current, [filterKey]: true }));
setFilterSearchErrorByKey((current) => ({ ...current, [filterKey]: null })); setFilterSearchErrorByKey((current) => ({ ...current, [filterKey]: null }));
@ -1032,6 +1232,12 @@ function App() {
setDraftFilterNodes((current) => { setDraftFilterNodes((current) => {
const selectionKey = getFilterSelectionKey(currentFilterKey, node.id); const selectionKey = getFilterSelectionKey(currentFilterKey, node.id);
const exists = current.some((item) => getFilterSelectionKey(item.filterKey, item.id) === selectionKey); 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) { if (exists) {
return current.filter((item) => getFilterSelectionKey(item.filterKey, item.id) !== selectionKey); return current.filter((item) => getFilterSelectionKey(item.filterKey, item.id) !== selectionKey);
} }
@ -1041,10 +1247,31 @@ function App() {
const applyFilterModal = () => { const applyFilterModal = () => {
if (!filterModalKey) return; if (!filterModalKey) return;
setAppliedFilters((current) => ({ let nextDraftNodes = draftFilterNodes;
...current, if (isTemplateFilterKey(filterModalKey) && nextDraftNodes.length === 0) {
[filterModalKey]: draftFilterNodes, 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({}); setChartDataBySelection({});
setLoadError(null); setLoadError(null);
if (selectedContentNodes.length > 0) { if (selectedContentNodes.length > 0) {
@ -1056,11 +1283,27 @@ function App() {
}; };
const clearFilter = (filterKey: FilterKey) => { const clearFilter = (filterKey: FilterKey) => {
if (appliedFilters[filterKey].length === 0) return; const nextNodes = isTemplateFilterKey(filterKey)
setAppliedFilters((current) => ({ ? getDefaultTemplateFilterNodes()
...current, : isIndicatorTreeFilterKey(filterKey)
[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({}); setChartDataBySelection({});
setLoadError(null); setLoadError(null);
if (selectedContentNodes.length > 0) { if (selectedContentNodes.length > 0) {
@ -1104,6 +1347,13 @@ function App() {
}); });
}, [activeContentKey, treeByContent]); }, [activeContentKey, treeByContent]);
useEffect(() => {
if (filterModalKey !== 'indicatorTree') return;
if (draftFilterNodes.length > 0) return;
if (defaultIndicatorTreeNodes.length === 0) return;
setDraftFilterNodes(defaultIndicatorTreeNodes);
}, [defaultIndicatorTreeNodes, draftFilterNodes.length, filterModalKey]);
useEffect(() => { useEffect(() => {
const controller = new AbortController(); const controller = new AbortController();
@ -1136,6 +1386,7 @@ function App() {
body: JSON.stringify({ body: JSON.stringify({
groupBy: groupKey, groupBy: groupKey,
metric: requestMetricKey, metric: requestMetricKey,
templateId: selectedTemplateId,
filters: appliedFilterPayload, filters: appliedFilterPayload,
nodes: selectedContentNodes.map((node) => ({ nodes: selectedContentNodes.map((node) => ({
key: getSelectionKey(node.contentKey, node.id), key: getSelectionKey(node.contentKey, node.id),
@ -1171,16 +1422,37 @@ function App() {
return () => { return () => {
controller.abort(); controller.abort();
}; };
}, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes]); }, [appliedFilterPayload, chartQueryVersion, groupKey, metricKey, requestMetricKey, selectedContentNodes, selectedTemplateId]);
useEffect(() => { useEffect(() => {
const frame = chartFrameRef.current; const frame = chartFrameRef.current;
const fullscreenTarget = workspaceRef.current; const fullscreenTarget = workspaceRef.current;
if (!frame || !fullscreenTarget) return; 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 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 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(); const button = getFullscreenButton();
if (button) { if (button) {
let icon = button.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen'); let icon = button.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen');
@ -1190,7 +1462,10 @@ function App() {
} }
const isFullscreen = document.fullscreenElement === fullscreenTarget; const isFullscreen = document.fullscreenElement === fullscreenTarget;
button.classList.add('chart-fullscreen-button');
button.classList.toggle('ag-charts-toolbar__button--active', isFullscreen); 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-arrow-salt', !isFullscreen);
icon?.classList.toggle('anticon-shrink', isFullscreen); icon?.classList.toggle('anticon-shrink', isFullscreen);
} }
@ -1198,7 +1473,8 @@ function App() {
const statisticButton = getStatisticButton(); const statisticButton = getStatisticButton();
if (statisticButton) { if (statisticButton) {
statisticButton.classList.add('chart-statistic-button'); 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 handleToolbarClick = (event: MouseEvent) => {
const target = event.target as Element | null; const target = event.target as Element | null;
const button = target?.closest<HTMLButtonElement>( 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; if (!button || !frame.contains(button)) return;
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
event.stopImmediatePropagation(); 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); setMetricMenuOpen(false);
setStatisticMenuOpen((open) => !open); setStatisticMenuOpen((open) => !open);
} else { } else {
@ -1245,14 +1529,22 @@ function App() {
const target = event.target as Element | null; const target = event.target as Element | null;
const button = target?.closest<HTMLButtonElement>( 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; if (!button || !frame.contains(button)) return;
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
event.stopImmediatePropagation(); 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); setMetricMenuOpen(false);
setStatisticMenuOpen((open) => !open); setStatisticMenuOpen((open) => !open);
} else { } else {
@ -1265,6 +1557,11 @@ function App() {
}; };
const observer = new MutationObserver(syncToolbarButtons); 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('keydown', handleKeyDown, true);
document.addEventListener('fullscreenchange', handleFullscreenChange); document.addEventListener('fullscreenchange', handleFullscreenChange);
document.addEventListener('contextmenu', suppressBrowserContextMenu); document.addEventListener('contextmenu', suppressBrowserContextMenu);
@ -1275,6 +1572,7 @@ function App() {
syncToolbarButtons(); syncToolbarButtons();
return () => { return () => {
initialSyncTimers.forEach((timer) => window.clearTimeout(timer));
document.removeEventListener('keydown', handleKeyDown, true); document.removeEventListener('keydown', handleKeyDown, true);
document.removeEventListener('fullscreenchange', handleFullscreenChange); document.removeEventListener('fullscreenchange', handleFullscreenChange);
document.removeEventListener('contextmenu', suppressBrowserContextMenu); document.removeEventListener('contextmenu', suppressBrowserContextMenu);
@ -1283,7 +1581,7 @@ function App() {
frame.removeEventListener('keydown', handleToolbarKeyDown, true); frame.removeEventListener('keydown', handleToolbarKeyDown, true);
observer.disconnect(); observer.disconnect();
}; };
}, [statisticMenuOpen]); }, [appliedFilters, filterModalKey, filterTreeByKey, statisticMenuOpen]);
const chartOptions = useMemo<AgCartesianChartOptions>(() => { const chartOptions = useMemo<AgCartesianChartOptions>(() => {
const groupNames: string[] = []; const groupNames: string[] = [];
@ -1296,11 +1594,12 @@ function App() {
groupNames.push(datum.groupName); groupNames.push(datum.groupName);
}); });
}); });
groupNames.sort(compareGroupNames);
const visibleData = 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, index) => { 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);
row[getSeriesValueKey(index)] = datum?.[selectedValueKey] ?? null; row[getSeriesValueKey(node.contentKey, node.id)] = datum?.[selectedValueKey] ?? null;
}); });
return row; return row;
}); });
@ -1363,6 +1662,16 @@ function App() {
enabled: true, enabled: true,
toolbar: { toolbar: {
buttons: ([ 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', value: 'note',
tooltip: '切换统计指标', tooltip: '切换统计指标',
@ -1388,6 +1697,11 @@ function App() {
value: 'fibonacci-menu', value: 'fibonacci-menu',
tooltip: 'Fibonacci Tool', 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', icon: 'delete',
value: 'clear', value: 'clear',
@ -1396,13 +1710,14 @@ function App() {
] as unknown as NonNullable<NonNullable<AgCartesianChartOptions['annotations']>['toolbar']>['buttons']), ] as unknown as NonNullable<NonNullable<AgCartesianChartOptions['annotations']>['toolbar']>['buttons']),
}, },
}, },
series: selectedContentNodes.map((node, index) => ({ series: selectedContentNodes.map((node) => ({
type: 'line', type: 'line',
xKey: 'groupName', xKey: 'groupName',
yKey: getSeriesValueKey(index), yKey: getSeriesValueKey(node.contentKey, node.id),
yName: `${node.label} ${seriesValueLabel}`, yName: `${node.label} ${seriesValueLabel}`,
stroke: node.color, stroke: node.color,
strokeWidth: 2, strokeWidth: 2,
connectMissingData: true,
marker: { marker: {
enabled: true, enabled: true,
fill: node.color, fill: node.color,
@ -1492,7 +1807,7 @@ function App() {
<section className="workspace" aria-label="年度费用模板" ref={workspaceRef}> <section className="workspace" aria-label="年度费用模板" ref={workspaceRef}>
<div className="chart-filter-bar chart-filter-bar--workspace" aria-label="筛选条件"> <div className="chart-filter-bar chart-filter-bar--workspace" aria-label="筛选条件">
{filterOptions.map((option) => { {chartFilterOptions.map((option) => {
const count = appliedFilters[option.key].length; const count = appliedFilters[option.key].length;
const FilterIcon = option.icon; const FilterIcon = option.icon;
return ( return (
@ -1516,7 +1831,10 @@ function App() {
type="button" type="button"
title="清空全部筛选" title="清空全部筛选"
onClick={() => { onClick={() => {
resetIndicatorTreeState();
setAppliedFilters({ setAppliedFilters({
templateLibrary: getDefaultTemplateFilterNodes(),
indicatorTree: [],
region: [], region: [],
geoLocation: [], geoLocation: [],
facilityType: [], facilityType: [],
@ -1593,9 +1911,6 @@ function App() {
) : null} ) : null}
</div> </div>
{loading || loadError ? <div className="chart-status">{loading ? loadingHint || '加载中' : loadError}</div> : null} {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} /> <AgCharts options={chartOptions} />
{loading ? ( {loading ? (
<div className="chart-loading-mask" aria-live="polite" aria-busy="true"> <div className="chart-loading-mask" aria-live="polite" aria-busy="true">
@ -1687,7 +2002,17 @@ function App() {
)} )}
</div> </div>
<footer className="filter-modal-actions"> <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>
<button className="filter-modal-cancel" type="button" onClick={closeFilterModal}> <button className="filter-modal-cancel" type="button" onClick={closeFilterModal}>

View File

@ -359,10 +359,25 @@ button {
background: rgba(255, 252, 248, 0.94); background: rgba(255, 252, 248, 0.94);
} }
.chart-frame .chart-template-button,
.chart-frame .chart-indicator-button,
.chart-frame .chart-statistic-button { .chart-frame .chart-statistic-button {
color: #46413b; 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"] { .chart-frame .chart-statistic-button[aria-expanded="true"] {
color: #0078a8; color: #0078a8;
border-color: rgba(0, 120, 168, 0.36); border-color: rgba(0, 120, 168, 0.36);
@ -456,29 +471,6 @@ button {
line-height: 18px; 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 { .chart-frame .ag-charts-zoom-buttons {
top: auto !important; top: auto !important;
right: 24px !important; right: 24px !important;

View File

@ -194,3 +194,40 @@ Port 5173 is in use, trying another one...
15:44:06 [vite] (client) hmr update /src/App.tsx 15:44:06 [vite] (client) hmr update /src/App.tsx
16:32:19 [vite] (client) hmr update /src/App.tsx 16:32:19 [vite] (client) hmr update /src/App.tsx
17:15:32 [vite] (client) hmr update /src/App.tsx 17:15:32 [vite] (client) hmr update /src/App.tsx
15:34:28 [vite] (client) hmr update /src/App.tsx
11:18:46 [vite] (client) hmr update /src/App.tsx
11:18:59 [vite] (client) hmr update /src/App.tsx
11:19:51 [vite] (client) hmr update /src/App.tsx
11:20:06 [vite] (client) hmr update /src/App.tsx
11:20:27 [vite] (client) hmr update /src/App.tsx
11:20:45 [vite] (client) hmr update /src/App.tsx
11:21:04 [vite] (client) hmr update /src/App.tsx
11:21:48 [vite] (client) hmr update /src/App.tsx
11:25:16 [vite] (client) hmr update /src/App.tsx
11:25:34 [vite] (client) hmr update /src/App.tsx
11:33:37 [vite] (client) hmr update /src/styles.css
11:38:01 [vite] (client) hmr update /src/App.tsx
11:38:14 [vite] (client) hmr update /src/App.tsx
11:38:28 [vite] (client) hmr update /src/App.tsx
11:38:42 [vite] (client) hmr update /src/styles.css
11:39:18 [vite] (client) hmr update /src/App.tsx
11:41:08 [vite] (client) hmr update /src/App.tsx
11:48:17 [vite] (client) hmr update /src/App.tsx
11:50:43 [vite] (client) hmr update /src/styles.css
12:07:07 [vite] (client) hmr update /src/App.tsx
12:07:24 [vite] (client) hmr update /src/App.tsx
12:07:45 [vite] (client) hmr update /src/App.tsx
12:08:39 [vite] (client) hmr update /src/App.tsx
12:08:54 [vite] (client) hmr update /src/App.tsx
12:09:23 [vite] (client) hmr update /src/App.tsx
12:09:54 [vite] (client) hmr update /src/App.tsx
12:10:36 [vite] (client) hmr update /src/App.tsx
12:11:14 [vite] (client) hmr update /src/App.tsx
12:12:29 [vite] (client) hmr update /src/App.tsx
12:13:01 [vite] (client) hmr update /src/App.tsx
12:14:05 [vite] (client) hmr update /src/styles.css
12:18:36 [vite] (client) hmr update /src/App.tsx
12:19:03 [vite] (client) hmr update /src/App.tsx
12:22:51 [vite] (client) hmr update /src/App.tsx
12:44:18 [vite] (client) hmr update /src/App.tsx
12:45:27 [vite] (client) hmr update /src/App.tsx