diff --git a/jjb.config.js b/jjb.config.js index 4c694c0..f5a24ef 100644 --- a/jjb.config.js +++ b/jjb.config.js @@ -20,7 +20,7 @@ module.exports = { // 应用后端分支名称,部署上线需要 javaGitBranch: "", // 接口服务地址 - API_HOST: "http://192.168.198.8:30140", + API_HOST: "https://gbs-gateway.qhdsafety.com", }, production: { // 应用后端分支名称,部署上线需要 diff --git a/package.json b/package.json index 1f1c37e..529d94d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "@cqsjjb/jjb-react-admin-component": "latest", "ahooks": "^3.9.5", "animate.css": "^4.1.1", - "antd": "6.5.0", + "antd": "^6.5.0", "autofit.js": "^3.2.8", "cesium": "1.144.0", "dayjs": "^1.11.7", @@ -41,7 +41,7 @@ "react-transition-group": "^4.4.5", "sass": "^1.97.0", "sass-loader": "^16.0.6", - "zy-react-library": "^2.1.4" + "zy-react-library": "^2.1.12" }, "devDependencies": { "@antfu/eslint-config": "^5.4.1", diff --git a/src/assets/images/public/bigScreen/bg10.png b/src/assets/images/map_bi/bg10.png similarity index 100% rename from src/assets/images/public/bigScreen/bg10.png rename to src/assets/images/map_bi/bg10.png diff --git a/src/assets/images/public/bigScreen/bg11.png b/src/assets/images/map_bi/bg11.png similarity index 100% rename from src/assets/images/public/bigScreen/bg11.png rename to src/assets/images/map_bi/bg11.png diff --git a/src/assets/images/public/bigScreen/bg12.png b/src/assets/images/map_bi/bg12.png similarity index 100% rename from src/assets/images/public/bigScreen/bg12.png rename to src/assets/images/map_bi/bg12.png diff --git a/src/assets/images/public/bigScreen/bg13.png b/src/assets/images/map_bi/bg13.png similarity index 100% rename from src/assets/images/public/bigScreen/bg13.png rename to src/assets/images/map_bi/bg13.png diff --git a/src/assets/images/public/bigScreen/bg14.png b/src/assets/images/map_bi/bg14.png similarity index 100% rename from src/assets/images/public/bigScreen/bg14.png rename to src/assets/images/map_bi/bg14.png diff --git a/src/assets/images/public/bigScreen/bg9.png b/src/assets/images/map_bi/bg9.png similarity index 100% rename from src/assets/images/public/bigScreen/bg9.png rename to src/assets/images/map_bi/bg9.png diff --git a/src/assets/images/public/bigScreen/btnbg.png b/src/assets/images/map_bi/btnbg.png similarity index 100% rename from src/assets/images/public/bigScreen/btnbg.png rename to src/assets/images/map_bi/btnbg.png diff --git a/src/assets/images/public/bigScreen/ico21.png b/src/assets/images/map_bi/ico21.png similarity index 100% rename from src/assets/images/public/bigScreen/ico21.png rename to src/assets/images/map_bi/ico21.png diff --git a/src/assets/images/public/bigScreen/ico3.png b/src/assets/images/map_bi/ico3.png similarity index 100% rename from src/assets/images/public/bigScreen/ico3.png rename to src/assets/images/map_bi/ico3.png diff --git a/src/assets/images/public/bigScreen/ico6.png b/src/assets/images/map_bi/ico6.png similarity index 100% rename from src/assets/images/public/bigScreen/ico6.png rename to src/assets/images/map_bi/ico6.png diff --git a/src/assets/images/public/bigScreen/ico7.png b/src/assets/images/map_bi/ico7.png similarity index 100% rename from src/assets/images/public/bigScreen/ico7.png rename to src/assets/images/map_bi/ico7.png diff --git a/src/assets/images/public/bigScreen/ico8.png b/src/assets/images/map_bi/ico8.png similarity index 100% rename from src/assets/images/public/bigScreen/ico8.png rename to src/assets/images/map_bi/ico8.png diff --git a/src/assets/images/public/bigScreen/ico9.png b/src/assets/images/map_bi/ico9.png similarity index 100% rename from src/assets/images/public/bigScreen/ico9.png rename to src/assets/images/map_bi/ico9.png diff --git a/src/assets/images/public/bigScreen/img10.png b/src/assets/images/map_bi/img10.png similarity index 100% rename from src/assets/images/public/bigScreen/img10.png rename to src/assets/images/map_bi/img10.png diff --git a/src/assets/images/public/bigScreen/img11.png b/src/assets/images/map_bi/img11.png similarity index 100% rename from src/assets/images/public/bigScreen/img11.png rename to src/assets/images/map_bi/img11.png diff --git a/src/assets/images/public/bigScreen/leftbg.png b/src/assets/images/map_bi/leftbg.png similarity index 100% rename from src/assets/images/public/bigScreen/leftbg.png rename to src/assets/images/map_bi/leftbg.png diff --git a/src/assets/images/public/bigScreen/more1.png b/src/assets/images/map_bi/more1.png similarity index 100% rename from src/assets/images/public/bigScreen/more1.png rename to src/assets/images/map_bi/more1.png diff --git a/src/assets/images/public/bigScreen/smalltitle.png b/src/assets/images/map_bi/smalltitle.png similarity index 100% rename from src/assets/images/public/bigScreen/smalltitle.png rename to src/assets/images/map_bi/smalltitle.png diff --git a/src/assets/images/public/bigScreen/tablebg1.png b/src/assets/images/map_bi/tablebg1.png similarity index 100% rename from src/assets/images/public/bigScreen/tablebg1.png rename to src/assets/images/map_bi/tablebg1.png diff --git a/src/assets/images/public/bigScreen/tablebg2.png b/src/assets/images/map_bi/tablebg2.png similarity index 100% rename from src/assets/images/public/bigScreen/tablebg2.png rename to src/assets/images/map_bi/tablebg2.png diff --git a/src/assets/images/public/bigScreen/topbg.png b/src/assets/images/map_bi/topbg.png similarity index 100% rename from src/assets/images/public/bigScreen/topbg.png rename to src/assets/images/map_bi/topbg.png diff --git a/src/assets/images/public/bigScreen/w_ico1.png b/src/assets/images/map_bi/w_ico1.png similarity index 100% rename from src/assets/images/public/bigScreen/w_ico1.png rename to src/assets/images/map_bi/w_ico1.png diff --git a/src/assets/images/public/bigScreen/w_ico2.png b/src/assets/images/map_bi/w_ico2.png similarity index 100% rename from src/assets/images/public/bigScreen/w_ico2.png rename to src/assets/images/map_bi/w_ico2.png diff --git a/src/assets/images/public/bigScreen/back.png b/src/assets/images/public/bigScreen/back.png deleted file mode 100644 index 406138d..0000000 Binary files a/src/assets/images/public/bigScreen/back.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/backgroundimg.jpg b/src/assets/images/public/bigScreen/backgroundimg.jpg deleted file mode 100644 index 51427b0..0000000 Binary files a/src/assets/images/public/bigScreen/backgroundimg.jpg and /dev/null differ diff --git a/src/assets/images/public/bigScreen/bg1.png b/src/assets/images/public/bigScreen/bg1.png deleted file mode 100644 index 9dd6684..0000000 Binary files a/src/assets/images/public/bigScreen/bg1.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/bg2.png b/src/assets/images/public/bigScreen/bg2.png deleted file mode 100644 index dd4617b..0000000 Binary files a/src/assets/images/public/bigScreen/bg2.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/bg3.png b/src/assets/images/public/bigScreen/bg3.png deleted file mode 100644 index acbae77..0000000 Binary files a/src/assets/images/public/bigScreen/bg3.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/bg4.png b/src/assets/images/public/bigScreen/bg4.png deleted file mode 100644 index 00092e7..0000000 Binary files a/src/assets/images/public/bigScreen/bg4.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/bg7.png b/src/assets/images/public/bigScreen/bg7.png deleted file mode 100644 index 5e80c0e..0000000 Binary files a/src/assets/images/public/bigScreen/bg7.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/close.png b/src/assets/images/public/bigScreen/close.png deleted file mode 100644 index d64f369..0000000 Binary files a/src/assets/images/public/bigScreen/close.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/guang.png b/src/assets/images/public/bigScreen/guang.png deleted file mode 100644 index 5f927b7..0000000 Binary files a/src/assets/images/public/bigScreen/guang.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/ico1.png b/src/assets/images/public/bigScreen/ico1.png deleted file mode 100644 index 069f8fb..0000000 Binary files a/src/assets/images/public/bigScreen/ico1.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/ico2.png b/src/assets/images/public/bigScreen/ico2.png deleted file mode 100644 index d869cc4..0000000 Binary files a/src/assets/images/public/bigScreen/ico2.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/icobg1.png b/src/assets/images/public/bigScreen/icobg1.png deleted file mode 100644 index 137aaa3..0000000 Binary files a/src/assets/images/public/bigScreen/icobg1.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/icobg2.png b/src/assets/images/public/bigScreen/icobg2.png deleted file mode 100644 index 08178db..0000000 Binary files a/src/assets/images/public/bigScreen/icobg2.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img1.png b/src/assets/images/public/bigScreen/img1.png deleted file mode 100644 index 064a19a..0000000 Binary files a/src/assets/images/public/bigScreen/img1.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img12.png b/src/assets/images/public/bigScreen/img12.png deleted file mode 100644 index 2fc5155..0000000 Binary files a/src/assets/images/public/bigScreen/img12.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img13.png b/src/assets/images/public/bigScreen/img13.png deleted file mode 100644 index 80e4d0a..0000000 Binary files a/src/assets/images/public/bigScreen/img13.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img14.png b/src/assets/images/public/bigScreen/img14.png deleted file mode 100644 index 9a77586..0000000 Binary files a/src/assets/images/public/bigScreen/img14.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img15.png b/src/assets/images/public/bigScreen/img15.png deleted file mode 100644 index fd02174..0000000 Binary files a/src/assets/images/public/bigScreen/img15.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img16.png b/src/assets/images/public/bigScreen/img16.png deleted file mode 100644 index 7838cf3..0000000 Binary files a/src/assets/images/public/bigScreen/img16.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img17.png b/src/assets/images/public/bigScreen/img17.png deleted file mode 100644 index 16f8b9c..0000000 Binary files a/src/assets/images/public/bigScreen/img17.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img2.png b/src/assets/images/public/bigScreen/img2.png deleted file mode 100644 index ce5ed43..0000000 Binary files a/src/assets/images/public/bigScreen/img2.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img3.png b/src/assets/images/public/bigScreen/img3.png deleted file mode 100644 index 05fa5e7..0000000 Binary files a/src/assets/images/public/bigScreen/img3.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img4.png b/src/assets/images/public/bigScreen/img4.png deleted file mode 100644 index f1cc52a..0000000 Binary files a/src/assets/images/public/bigScreen/img4.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img5.png b/src/assets/images/public/bigScreen/img5.png deleted file mode 100644 index 2b3890c..0000000 Binary files a/src/assets/images/public/bigScreen/img5.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img6.png b/src/assets/images/public/bigScreen/img6.png deleted file mode 100644 index fc212db..0000000 Binary files a/src/assets/images/public/bigScreen/img6.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img7.png b/src/assets/images/public/bigScreen/img7.png deleted file mode 100644 index c68a0a4..0000000 Binary files a/src/assets/images/public/bigScreen/img7.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img8.png b/src/assets/images/public/bigScreen/img8.png deleted file mode 100644 index d9103b8..0000000 Binary files a/src/assets/images/public/bigScreen/img8.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/img9.png b/src/assets/images/public/bigScreen/img9.png deleted file mode 100644 index 80ec5ee..0000000 Binary files a/src/assets/images/public/bigScreen/img9.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/imgok.png b/src/assets/images/public/bigScreen/imgok.png deleted file mode 100644 index d43d764..0000000 Binary files a/src/assets/images/public/bigScreen/imgok.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/search.png b/src/assets/images/public/bigScreen/search.png deleted file mode 100644 index cad09cf..0000000 Binary files a/src/assets/images/public/bigScreen/search.png and /dev/null differ diff --git a/src/assets/images/public/bigScreen/title.png b/src/assets/images/public/bigScreen/title.png deleted file mode 100644 index 73f94c2..0000000 Binary files a/src/assets/images/public/bigScreen/title.png and /dev/null differ diff --git a/src/assets/images/public/index_perch_img.jpg b/src/assets/images/public/index_perch_img.jpg deleted file mode 100644 index 9eea107..0000000 Binary files a/src/assets/images/public/index_perch_img.jpg and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/bg1.png b/src/assets/images/public/prevention_imgs/bg1.png deleted file mode 100644 index e2a8690..0000000 Binary files a/src/assets/images/public/prevention_imgs/bg1.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/bg2.png b/src/assets/images/public/prevention_imgs/bg2.png deleted file mode 100644 index f6685a4..0000000 Binary files a/src/assets/images/public/prevention_imgs/bg2.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/bg3.png b/src/assets/images/public/prevention_imgs/bg3.png deleted file mode 100644 index 11aebac..0000000 Binary files a/src/assets/images/public/prevention_imgs/bg3.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/bg4.png b/src/assets/images/public/prevention_imgs/bg4.png deleted file mode 100644 index 068a6ba..0000000 Binary files a/src/assets/images/public/prevention_imgs/bg4.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/ico1.png b/src/assets/images/public/prevention_imgs/ico1.png deleted file mode 100644 index 581c5dd..0000000 Binary files a/src/assets/images/public/prevention_imgs/ico1.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/ico2.png b/src/assets/images/public/prevention_imgs/ico2.png deleted file mode 100644 index 83cd45d..0000000 Binary files a/src/assets/images/public/prevention_imgs/ico2.png and /dev/null differ diff --git a/src/assets/images/public/prevention_imgs/ico3.png b/src/assets/images/public/prevention_imgs/ico3.png deleted file mode 100644 index 6281168..0000000 Binary files a/src/assets/images/public/prevention_imgs/ico3.png and /dev/null differ diff --git a/src/assets/images/public/share/bg.jpg b/src/assets/images/public/share/bg.jpg deleted file mode 100644 index 552e622..0000000 Binary files a/src/assets/images/public/share/bg.jpg and /dev/null differ diff --git a/src/assets/images/public/share/ico1.png b/src/assets/images/public/share/ico1.png deleted file mode 100644 index 22489a2..0000000 Binary files a/src/assets/images/public/share/ico1.png and /dev/null differ diff --git a/src/assets/images/public/share/ico10.png b/src/assets/images/public/share/ico10.png deleted file mode 100644 index ac6f52c..0000000 Binary files a/src/assets/images/public/share/ico10.png and /dev/null differ diff --git a/src/assets/images/public/share/ico11.png b/src/assets/images/public/share/ico11.png deleted file mode 100644 index 7cf150b..0000000 Binary files a/src/assets/images/public/share/ico11.png and /dev/null differ diff --git a/src/assets/images/public/share/ico12.png b/src/assets/images/public/share/ico12.png deleted file mode 100644 index 5658334..0000000 Binary files a/src/assets/images/public/share/ico12.png and /dev/null differ diff --git a/src/assets/images/public/share/ico2.png b/src/assets/images/public/share/ico2.png deleted file mode 100644 index 187b250..0000000 Binary files a/src/assets/images/public/share/ico2.png and /dev/null differ diff --git a/src/assets/images/public/share/ico3.png b/src/assets/images/public/share/ico3.png deleted file mode 100644 index 2373101..0000000 Binary files a/src/assets/images/public/share/ico3.png and /dev/null differ diff --git a/src/assets/images/public/share/ico4.png b/src/assets/images/public/share/ico4.png deleted file mode 100644 index 6c9e60b..0000000 Binary files a/src/assets/images/public/share/ico4.png and /dev/null differ diff --git a/src/assets/images/public/share/ico5.png b/src/assets/images/public/share/ico5.png deleted file mode 100644 index 4a969a7..0000000 Binary files a/src/assets/images/public/share/ico5.png and /dev/null differ diff --git a/src/assets/images/public/share/ico6.png b/src/assets/images/public/share/ico6.png deleted file mode 100644 index 306f1fd..0000000 Binary files a/src/assets/images/public/share/ico6.png and /dev/null differ diff --git a/src/assets/images/public/share/ico7.png b/src/assets/images/public/share/ico7.png deleted file mode 100644 index e624451..0000000 Binary files a/src/assets/images/public/share/ico7.png and /dev/null differ diff --git a/src/assets/images/public/share/ico8.png b/src/assets/images/public/share/ico8.png deleted file mode 100644 index a4b699e..0000000 Binary files a/src/assets/images/public/share/ico8.png and /dev/null differ diff --git a/src/assets/images/public/share/ico9.png b/src/assets/images/public/share/ico9.png deleted file mode 100644 index 9356ed8..0000000 Binary files a/src/assets/images/public/share/ico9.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico1.png b/src/assets/images/public/share/icon/ico1.png deleted file mode 100644 index 22489a2..0000000 Binary files a/src/assets/images/public/share/icon/ico1.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico10.png b/src/assets/images/public/share/icon/ico10.png deleted file mode 100644 index ac6f52c..0000000 Binary files a/src/assets/images/public/share/icon/ico10.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico11.png b/src/assets/images/public/share/icon/ico11.png deleted file mode 100644 index 7cf150b..0000000 Binary files a/src/assets/images/public/share/icon/ico11.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico12.png b/src/assets/images/public/share/icon/ico12.png deleted file mode 100644 index 5658334..0000000 Binary files a/src/assets/images/public/share/icon/ico12.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico2.png b/src/assets/images/public/share/icon/ico2.png deleted file mode 100644 index 187b250..0000000 Binary files a/src/assets/images/public/share/icon/ico2.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico3.png b/src/assets/images/public/share/icon/ico3.png deleted file mode 100644 index 2373101..0000000 Binary files a/src/assets/images/public/share/icon/ico3.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico4.png b/src/assets/images/public/share/icon/ico4.png deleted file mode 100644 index 6c9e60b..0000000 Binary files a/src/assets/images/public/share/icon/ico4.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico5.png b/src/assets/images/public/share/icon/ico5.png deleted file mode 100644 index 4a969a7..0000000 Binary files a/src/assets/images/public/share/icon/ico5.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico6.png b/src/assets/images/public/share/icon/ico6.png deleted file mode 100644 index 306f1fd..0000000 Binary files a/src/assets/images/public/share/icon/ico6.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico7.png b/src/assets/images/public/share/icon/ico7.png deleted file mode 100644 index e624451..0000000 Binary files a/src/assets/images/public/share/icon/ico7.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico8.png b/src/assets/images/public/share/icon/ico8.png deleted file mode 100644 index a4b699e..0000000 Binary files a/src/assets/images/public/share/icon/ico8.png and /dev/null differ diff --git a/src/assets/images/public/share/icon/ico9.png b/src/assets/images/public/share/icon/ico9.png deleted file mode 100644 index 9356ed8..0000000 Binary files a/src/assets/images/public/share/icon/ico9.png and /dev/null differ diff --git a/src/assets/images/public/share/menubg.png b/src/assets/images/public/share/menubg.png deleted file mode 100644 index 95f480a..0000000 Binary files a/src/assets/images/public/share/menubg.png and /dev/null differ diff --git a/src/assets/images/public/share/titbg.png b/src/assets/images/public/share/titbg.png deleted file mode 100644 index 7798282..0000000 Binary files a/src/assets/images/public/share/titbg.png and /dev/null differ diff --git a/src/assets/images/public/share/title.png b/src/assets/images/public/share/title.png deleted file mode 100644 index 2d714a8..0000000 Binary files a/src/assets/images/public/share/title.png and /dev/null differ diff --git a/src/assets/images/public/share/yuan1.png b/src/assets/images/public/share/yuan1.png deleted file mode 100644 index 4433ec2..0000000 Binary files a/src/assets/images/public/share/yuan1.png and /dev/null differ diff --git a/src/assets/images/public/share/yuan2.png b/src/assets/images/public/share/yuan2.png deleted file mode 100644 index ad74791..0000000 Binary files a/src/assets/images/public/share/yuan2.png and /dev/null differ diff --git a/src/assets/images/public/share/yuan3.png b/src/assets/images/public/share/yuan3.png deleted file mode 100644 index f192893..0000000 Binary files a/src/assets/images/public/share/yuan3.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico1.png b/src/assets/images/public/stakeholder_imgs/ico1.png deleted file mode 100644 index f65b189..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico1.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico2.png b/src/assets/images/public/stakeholder_imgs/ico2.png deleted file mode 100644 index 7680cf6..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico2.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico3.png b/src/assets/images/public/stakeholder_imgs/ico3.png deleted file mode 100644 index 6c7d3d4..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico3.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico4.png b/src/assets/images/public/stakeholder_imgs/ico4.png deleted file mode 100644 index a9cbc8f..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico4.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico5.png b/src/assets/images/public/stakeholder_imgs/ico5.png deleted file mode 100644 index e0b45b8..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico5.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico6.png b/src/assets/images/public/stakeholder_imgs/ico6.png deleted file mode 100644 index 221321d..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico6.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico7.png b/src/assets/images/public/stakeholder_imgs/ico7.png deleted file mode 100644 index cc07c7b..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico7.png and /dev/null differ diff --git a/src/assets/images/public/stakeholder_imgs/ico_prev.png b/src/assets/images/public/stakeholder_imgs/ico_prev.png deleted file mode 100644 index cea6bf8..0000000 Binary files a/src/assets/images/public/stakeholder_imgs/ico_prev.png and /dev/null differ diff --git a/src/assets/images/public/tx.png b/src/assets/images/public/tx.png deleted file mode 100644 index 3f55e30..0000000 Binary files a/src/assets/images/public/tx.png and /dev/null differ diff --git a/src/assets/images/public/white/bg2.png b/src/assets/images/public/white/bg2.png deleted file mode 100644 index 7a3811d..0000000 Binary files a/src/assets/images/public/white/bg2.png and /dev/null differ diff --git a/src/assets/images/public/white/bg4.png b/src/assets/images/public/white/bg4.png deleted file mode 100644 index 62be96f..0000000 Binary files a/src/assets/images/public/white/bg4.png and /dev/null differ diff --git a/src/assets/images/public/white/img1.png b/src/assets/images/public/white/img1.png deleted file mode 100644 index 07491cb..0000000 Binary files a/src/assets/images/public/white/img1.png and /dev/null differ diff --git a/src/assets/images/public/white/img2.png b/src/assets/images/public/white/img2.png deleted file mode 100644 index 32587a9..0000000 Binary files a/src/assets/images/public/white/img2.png and /dev/null differ diff --git a/src/assets/images/public/white/mainbg.png b/src/assets/images/public/white/mainbg.png deleted file mode 100644 index 88f8ae5..0000000 Binary files a/src/assets/images/public/white/mainbg.png and /dev/null differ diff --git a/src/assets/images/public/white/more.png b/src/assets/images/public/white/more.png deleted file mode 100644 index 6636b57..0000000 Binary files a/src/assets/images/public/white/more.png and /dev/null differ diff --git a/src/assets/images/public/zgzs.png b/src/assets/images/public/zgzs.png deleted file mode 100644 index e5bcd90..0000000 Binary files a/src/assets/images/public/zgzs.png and /dev/null differ diff --git a/src/assets/images/public/white/back.png b/src/assets/images/statistics/back.png similarity index 100% rename from src/assets/images/public/white/back.png rename to src/assets/images/statistics/back.png diff --git a/src/assets/images/public/white/bg.png b/src/assets/images/statistics/bg.png similarity index 100% rename from src/assets/images/public/white/bg.png rename to src/assets/images/statistics/bg.png diff --git a/src/assets/images/public/white/bg1.png b/src/assets/images/statistics/bg1.png similarity index 100% rename from src/assets/images/public/white/bg1.png rename to src/assets/images/statistics/bg1.png diff --git a/src/assets/images/public/white/bg5.png b/src/assets/images/statistics/bg5.png similarity index 100% rename from src/assets/images/public/white/bg5.png rename to src/assets/images/statistics/bg5.png diff --git a/src/assets/images/public/white/bg6.png b/src/assets/images/statistics/bg6.png similarity index 100% rename from src/assets/images/public/white/bg6.png rename to src/assets/images/statistics/bg6.png diff --git a/src/assets/images/public/white/img3.png b/src/assets/images/statistics/img3.png similarity index 100% rename from src/assets/images/public/white/img3.png rename to src/assets/images/statistics/img3.png diff --git a/src/assets/images/public/white/img4.png b/src/assets/images/statistics/img4.png similarity index 100% rename from src/assets/images/public/white/img4.png rename to src/assets/images/statistics/img4.png diff --git a/src/assets/images/public/white/img5.png b/src/assets/images/statistics/img5.png similarity index 100% rename from src/assets/images/public/white/img5.png rename to src/assets/images/statistics/img5.png diff --git a/src/assets/images/public/white/img6.png b/src/assets/images/statistics/img6.png similarity index 100% rename from src/assets/images/public/white/img6.png rename to src/assets/images/statistics/img6.png diff --git a/src/assets/images/public/white/img7.png b/src/assets/images/statistics/img7.png similarity index 100% rename from src/assets/images/public/white/img7.png rename to src/assets/images/statistics/img7.png diff --git a/src/assets/images/public/white/title.png b/src/assets/images/statistics/title.png similarity index 100% rename from src/assets/images/public/white/title.png rename to src/assets/images/statistics/title.png diff --git a/src/assets/images/public/white/titlebg.png b/src/assets/images/statistics/titlebg.png similarity index 100% rename from src/assets/images/public/white/titlebg.png rename to src/assets/images/statistics/titlebg.png diff --git a/src/assets/images/public/white/titlebg1.png b/src/assets/images/statistics/titlebg1.png similarity index 100% rename from src/assets/images/public/white/titlebg1.png rename to src/assets/images/statistics/titlebg1.png diff --git a/src/assets/images/public/weather/1.png b/src/assets/images/weather/1.png similarity index 100% rename from src/assets/images/public/weather/1.png rename to src/assets/images/weather/1.png diff --git a/src/assets/images/public/weather/10.png b/src/assets/images/weather/10.png similarity index 100% rename from src/assets/images/public/weather/10.png rename to src/assets/images/weather/10.png diff --git a/src/assets/images/public/weather/11.png b/src/assets/images/weather/11.png similarity index 100% rename from src/assets/images/public/weather/11.png rename to src/assets/images/weather/11.png diff --git a/src/assets/images/public/weather/12.png b/src/assets/images/weather/12.png similarity index 100% rename from src/assets/images/public/weather/12.png rename to src/assets/images/weather/12.png diff --git a/src/assets/images/public/weather/13.png b/src/assets/images/weather/13.png similarity index 100% rename from src/assets/images/public/weather/13.png rename to src/assets/images/weather/13.png diff --git a/src/assets/images/public/weather/14.png b/src/assets/images/weather/14.png similarity index 100% rename from src/assets/images/public/weather/14.png rename to src/assets/images/weather/14.png diff --git a/src/assets/images/public/weather/15.png b/src/assets/images/weather/15.png similarity index 100% rename from src/assets/images/public/weather/15.png rename to src/assets/images/weather/15.png diff --git a/src/assets/images/public/weather/16.png b/src/assets/images/weather/16.png similarity index 100% rename from src/assets/images/public/weather/16.png rename to src/assets/images/weather/16.png diff --git a/src/assets/images/public/weather/2.png b/src/assets/images/weather/2.png similarity index 100% rename from src/assets/images/public/weather/2.png rename to src/assets/images/weather/2.png diff --git a/src/assets/images/public/weather/3.png b/src/assets/images/weather/3.png similarity index 100% rename from src/assets/images/public/weather/3.png rename to src/assets/images/weather/3.png diff --git a/src/assets/images/public/weather/4.png b/src/assets/images/weather/4.png similarity index 100% rename from src/assets/images/public/weather/4.png rename to src/assets/images/weather/4.png diff --git a/src/assets/images/public/weather/5.png b/src/assets/images/weather/5.png similarity index 100% rename from src/assets/images/public/weather/5.png rename to src/assets/images/weather/5.png diff --git a/src/assets/images/public/weather/6.png b/src/assets/images/weather/6.png similarity index 100% rename from src/assets/images/public/weather/6.png rename to src/assets/images/weather/6.png diff --git a/src/assets/images/public/weather/7.png b/src/assets/images/weather/7.png similarity index 100% rename from src/assets/images/public/weather/7.png rename to src/assets/images/weather/7.png diff --git a/src/assets/images/public/weather/8.png b/src/assets/images/weather/8.png similarity index 100% rename from src/assets/images/public/weather/8.png rename to src/assets/images/weather/8.png diff --git a/src/assets/images/public/weather/9.png b/src/assets/images/weather/9.png similarity index 100% rename from src/assets/images/public/weather/9.png rename to src/assets/images/weather/9.png diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/ManagementNav/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/ManagementNav/index.js index f8af6a4..7339293 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/ManagementNav/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/ManagementNav/index.js @@ -1,4 +1,4 @@ -import moreIcon from "~/assets/images/public/bigScreen/more1.png"; +import moreIcon from "~/assets/images/map_bi/more1.png"; import styles from "./index.module.less"; /** 顶部“管理九项”导航仅负责展示当前大屏菜单。 */ diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/RecordStatsPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/RecordStatsPanel/index.js index dd324cb..5b045bb 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/RecordStatsPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/RecordStatsPanel/index.js @@ -2,7 +2,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import background from "~/assets/images/public/bigScreen/bg13.png"; +import background from "~/assets/images/map_bi/bg13.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/VideoPatrolPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/VideoPatrolPanel/index.js index b638809..6709697 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Bottom/VideoPatrolPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Bottom/VideoPatrolPanel/index.js @@ -3,7 +3,7 @@ import { message, Spin } from "antd"; import { useEffect, useState } from "react"; import Video from "zy-react-library/components/Video"; import { randoms } from "zy-react-library/utils"; -import background from "~/assets/images/public/bigScreen/bg14.png"; +import background from "~/assets/images/map_bi/bg14.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Btn/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Btn/index.js index d47ddf4..201fc21 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Btn/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Btn/index.js @@ -1,4 +1,4 @@ -import img1 from "~/assets/images/public/bigScreen/btnbg.png"; +import img1 from "~/assets/images/map_bi/btnbg.png"; import styles from "./index.module.less"; function Btn(props) { @@ -8,7 +8,7 @@ function Btn(props) { className={styles.item} style={{ backgroundImage: `url(${img1})` }} onClick={() => { - props.history.push("./white/branchOffice"); + props.history.push("./statistics/branchOffice"); }} > 分公司大屏 @@ -17,7 +17,7 @@ function Btn(props) { className={styles.item} style={{ backgroundImage: `url(${img1})` }} onClick={() => { - props.history.push("./white/share"); + props.history.push("./statistics/supervision"); }} > 监管端大屏 diff --git a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/HazardSourcePanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/HazardSourcePanel/index.js index bce6701..0993fbc 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/HazardSourcePanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/HazardSourcePanel/index.js @@ -1,6 +1,6 @@ -import detailBackground from "~/assets/images/public/bigScreen/bg9.png"; -import summaryBackground from "~/assets/images/public/bigScreen/bg10.png"; -import warningIcon from "~/assets/images/public/bigScreen/ico3.png"; +import detailBackground from "~/assets/images/map_bi/bg9.png"; +import summaryBackground from "~/assets/images/map_bi/bg10.png"; +import warningIcon from "~/assets/images/map_bi/ico3.png"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/RectificationPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/RectificationPanel/index.js index 2284636..b172725 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/RectificationPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/RectificationPanel/index.js @@ -2,8 +2,8 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import tableHeaderBackground from "~/assets/images/public/bigScreen/tablebg1.png"; -import tableRowBackground from "~/assets/images/public/bigScreen/tablebg2.png"; +import tableHeaderBackground from "~/assets/images/map_bi/tablebg1.png"; +import tableRowBackground from "~/assets/images/map_bi/tablebg2.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/WeatherPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/WeatherPanel/index.js index 8bccb29..46d32a2 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/WeatherPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/WeatherPanel/index.js @@ -2,10 +2,10 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import tableHeaderBackground from "~/assets/images/public/bigScreen/tablebg1.png"; -import tableRowBackground from "~/assets/images/public/bigScreen/tablebg2.png"; -import temperatureIcon from "~/assets/images/public/bigScreen/w_ico1.png"; -import windIcon from "~/assets/images/public/bigScreen/w_ico2.png"; +import tableHeaderBackground from "~/assets/images/map_bi/tablebg1.png"; +import tableRowBackground from "~/assets/images/map_bi/tablebg2.png"; +import temperatureIcon from "~/assets/images/map_bi/w_ico1.png"; +import windIcon from "~/assets/images/map_bi/w_ico2.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import { getWeatherIcon } from "~/utils"; import Panel from "../../Panel"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/index.js index 02c8baa..a5057a2 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/LeftPanel/index.js @@ -1,4 +1,4 @@ -import rightbg from "~/assets/images/public/bigScreen/leftbg.png"; +import rightbg from "~/assets/images/map_bi/leftbg.png"; import HazardSourcePanel from "./HazardSourcePanel"; import styles from "./index.module.less"; import RectificationPanel from "./RectificationPanel"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Panel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Panel/index.js index 332918b..da8222a 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Panel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Panel/index.js @@ -1,4 +1,4 @@ -import titleBackground from "~/assets/images/public/bigScreen/smalltitle.png"; +import titleBackground from "~/assets/images/map_bi/smalltitle.png"; import styles from "./index.module.less"; /** diff --git a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/AlarmPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/AlarmPanel/index.js index 03fa038..f4a6097 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/AlarmPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/AlarmPanel/index.js @@ -2,8 +2,8 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import tableHeaderBackground from "~/assets/images/public/bigScreen/tablebg1.png"; -import tableRowBackground from "~/assets/images/public/bigScreen/tablebg2.png"; +import tableHeaderBackground from "~/assets/images/map_bi/tablebg1.png"; +import tableRowBackground from "~/assets/images/map_bi/tablebg2.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/GatePanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/GatePanel/index.js index 0316509..673ef6f 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/GatePanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/GatePanel/index.js @@ -1,8 +1,8 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; -import closedBackground from "~/assets/images/public/bigScreen/bg11.png"; -import gateBackground from "~/assets/images/public/bigScreen/bg12.png"; +import closedBackground from "~/assets/images/map_bi/bg11.png"; +import gateBackground from "~/assets/images/map_bi/bg12.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/PartnerPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/PartnerPanel/index.js index d0e31b2..160e2a7 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/PartnerPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/PartnerPanel/index.js @@ -2,8 +2,8 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import tableHeaderBackground from "~/assets/images/public/bigScreen/tablebg1.png"; -import tableRowBackground from "~/assets/images/public/bigScreen/tablebg2.png"; +import tableHeaderBackground from "~/assets/images/map_bi/tablebg1.png"; +import tableRowBackground from "~/assets/images/map_bi/tablebg2.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/WorkPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/WorkPanel/index.js index 36dcd61..8b7a12c 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/WorkPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/WorkPanel/index.js @@ -1,10 +1,10 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; -import threePersonIcon from "~/assets/images/public/bigScreen/ico6.png"; -import dangerIcon from "~/assets/images/public/bigScreen/ico7.png"; -import nightIcon from "~/assets/images/public/bigScreen/ico8.png"; -import newWorkIcon from "~/assets/images/public/bigScreen/ico9.png"; +import threePersonIcon from "~/assets/images/map_bi/ico6.png"; +import dangerIcon from "~/assets/images/map_bi/ico7.png"; +import nightIcon from "~/assets/images/map_bi/ico8.png"; +import newWorkIcon from "~/assets/images/map_bi/ico9.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "../../Panel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/index.js b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/index.js index 3bb4249..1c5d8a8 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/RightPanel/index.js @@ -1,4 +1,4 @@ -import rightbg from "~/assets/images/public/bigScreen/leftbg.png"; +import rightbg from "~/assets/images/map_bi/leftbg.png"; import AlarmPanel from "./AlarmPanel"; import GatePanel from "./GatePanel"; import styles from "./index.module.less"; diff --git a/src/pages/Container/Map/components/Content/IndexInfo/Search/index.js b/src/pages/Container/Map/components/Content/IndexInfo/Search/index.js index 5b70291..13a7f94 100644 --- a/src/pages/Container/Map/components/Content/IndexInfo/Search/index.js +++ b/src/pages/Container/Map/components/Content/IndexInfo/Search/index.js @@ -1,5 +1,5 @@ -import img2 from "~/assets/images/public/bigScreen/close.png"; -import img1 from "~/assets/images/public/bigScreen/topbg.png"; +import img2 from "~/assets/images/map_bi/close.png"; +import img1 from "~/assets/images/map_bi/topbg.png"; import styles from "./index.module.less"; function SearchScreen() { diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js index dc40272..bd5bf94 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexLeft/WeatherPreventionPanel/index.js @@ -2,8 +2,8 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useContext, useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import temperatureIcon from "~/assets/images/public/bigScreen/img10.png"; -import windIcon from "~/assets/images/public/bigScreen/img11.png"; +import temperatureIcon from "~/assets/images/map_bi/img10.png"; +import windIcon from "~/assets/images/map_bi/img11.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; import { Context } from "~/pages/Container/Map/js/context"; diff --git a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/VideoPatrolPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/VideoPatrolPanel/index.js index 47d7d2a..9386f87 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/IndexRight/VideoPatrolPanel/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/IndexRight/VideoPatrolPanel/index.js @@ -2,7 +2,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useContext, useEffect, useState } from "react"; import Video from "zy-react-library/components/Video"; -import videoBackground from "~/assets/images/public/bigScreen/bg14.png"; +import videoBackground from "~/assets/images/map_bi/bg14.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; import { Context } from "~/pages/Container/Map/js/context"; diff --git a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js index a77c860..268e343 100644 --- a/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js +++ b/src/pages/Container/Map/components/Content/branchOffice/QiXiang/WeatherPreventionPanel/index.js @@ -2,9 +2,9 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { Spin } from "antd"; import { useEffect, useState } from "react"; import SeamlessScroll from "zy-react-library/components/SeamlessScroll"; -import precipitationIcon from "~/assets/images/public/bigScreen/ico21.png"; -import temperatureIcon from "~/assets/images/public/bigScreen/img10.png"; -import windIcon from "~/assets/images/public/bigScreen/img11.png"; +import precipitationIcon from "~/assets/images/map_bi/ico21.png"; +import temperatureIcon from "~/assets/images/map_bi/img10.png"; +import windIcon from "~/assets/images/map_bi/img11.png"; import { NS_BI_STATISTICS } from "~/enumerate/namespace"; import Panel from "~/pages/Container/Map/components/Content/branchOffice/Panel"; import { getAlertColor, getWeatherIcon } from "~/utils"; diff --git a/src/pages/Container/Map/components/PeopleTrajectorySelect/index.module.less b/src/pages/Container/Map/components/PeopleTrajectorySelect/index.module.less index f34be52..04876e1 100644 --- a/src/pages/Container/Map/components/PeopleTrajectorySelect/index.module.less +++ b/src/pages/Container/Map/components/PeopleTrajectorySelect/index.module.less @@ -7,12 +7,6 @@ box-shadow: 0 0 16px rgba(36, 136, 255, 0.35); } - :global { - .@{ant-prefix}-form-item { - margin: 0; - } - } - &__form { display: flex; gap: 16px; diff --git a/src/pages/Container/Statistics/BranchOffice/components/ExpandedContent/index.js b/src/pages/Container/Statistics/BranchOffice/components/ExpandedContent/index.js new file mode 100644 index 0000000..b8aabae --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/ExpandedContent/index.js @@ -0,0 +1,24 @@ +import HiddenDangerExpandedPanel from "../LeftPanel/HiddenDangerPanel/ExpandedPanel"; +import SafetyInspectionExpandedPanel from "../LeftPanel/SafetyInspectionPanel/ExpandedPanel"; +import EmergencyManagementExpandedPanel from "../MiddlePanel/EmergencyManagementPanel/ExpandedPanel"; +import RiskStatisticsExpandedPanel from "../MiddlePanel/RiskStatisticsPanel/ExpandedPanel"; +import FireInspectionExpandedPanel from "../RightPanel/FireInspectionPanel/ExpandedPanel"; +import KeyWorkExpandedPanel from "../RightPanel/KeyWorkPanel/ExpandedPanel"; +import RelatedPartyExpandedPanel from "../RightPanel/RelatedPartyPanel/ExpandedPanel"; + +const expandedPanels = { + hidden: HiddenDangerExpandedPanel, + inspection: SafetyInspectionExpandedPanel, + emergency: EmergencyManagementExpandedPanel, + risk: RiskStatisticsExpandedPanel, + related: RelatedPartyExpandedPanel, + keyWork: KeyWorkExpandedPanel, + fire: FireInspectionExpandedPanel, +}; + +function ExpandedContent({ moduleKey }) { + const Component = expandedPanels[moduleKey]; + return Component ? : null; +} + +export default ExpandedContent; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js new file mode 100644 index 0000000..8871806 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为隐患治理台账接口结果。 +const mockRows = [ + { + id: 1, + code: "YH-202608-101", + name: "消防通道堆放物料", + type: "消防", + unit: "二公司", + owner: "王强", + status: "待整改", + date: "2026-08-14", + }, + { + id: 2, + code: "YH-202608-102", + name: "配电箱门未闭合", + type: "电气", + unit: "六公司", + owner: "李明", + status: "整改中", + date: "2026-08-13", + }, + { + id: 3, + code: "YH-202608-103", + name: "设备护栏松动", + type: "设备", + unit: "七公司", + owner: "赵宁", + status: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "YH-202608-104", + name: "高处作业防护不到位", + type: "作业", + unit: "九公司", + owner: "周鹏", + status: "已整改", + date: "2026-08-11", + }, + { + id: 5, + code: "YH-202608-105", + name: "消防通道堆放物料", + type: "消防", + unit: "二公司", + owner: "王强", + status: "待整改", + date: "2026-08-10", + }, + { + id: 6, + code: "YH-202608-106", + name: "配电箱门未闭合", + type: "电气", + unit: "六公司", + owner: "李明", + status: "整改中", + date: "2026-08-09", + }, + { + id: 7, + code: "YH-202608-107", + name: "设备护栏松动", + type: "设备", + unit: "七公司", + owner: "赵宁", + status: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "YH-202608-108", + name: "高处作业防护不到位", + type: "作业", + unit: "九公司", + owner: "周鹏", + status: "已整改", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 隐患治理台账独立维护查询、表格、分页和详情状态。 */ +function HiddenLedgerListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "待整改", name: "待整改" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }, { bianma: "已整改", name: "已整改" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( + {recordItem.status}, + }, + { title: "发现日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default HiddenLedgerListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js new file mode 100644 index 0000000..21795cf --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 隐患治理台账详情独立维护展示字段。 */ +function HiddenLedgerRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "隐患编号", value: record["code"] }, + { label: "隐患描述", value: record["name"] }, + { label: "隐患类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任人", value: record["owner"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "发现日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default HiddenLedgerRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js new file mode 100644 index 0000000..d8d783f --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为逾期隐患接口结果。 +const mockRows = [ + { + id: 1, + code: "YQ-202608-51", + name: "消防设施整改逾期", + unit: "二公司", + owner: "张伟", + overdueDays: 3, + date: "2026-08-14", + status: "已逾期", + }, + { + id: 2, + code: "YQ-202608-52", + name: "电气线路整改逾期", + unit: "六公司", + owner: "刘洋", + overdueDays: 7, + date: "2026-08-13", + status: "已逾期", + }, + { + id: 3, + code: "YQ-202608-53", + name: "防护栏整改逾期", + unit: "七公司", + owner: "陈磊", + overdueDays: 12, + date: "2026-08-12", + status: "整改中", + }, + { + id: 4, + code: "YQ-202608-54", + name: "警示标识整改逾期", + unit: "九公司", + owner: "孙浩", + overdueDays: 18, + date: "2026-08-11", + status: "待复核", + }, + { + id: 5, + code: "YQ-202608-55", + name: "消防设施整改逾期", + unit: "二公司", + owner: "张伟", + overdueDays: 3, + date: "2026-08-10", + status: "已逾期", + }, + { + id: 6, + code: "YQ-202608-56", + name: "电气线路整改逾期", + unit: "六公司", + owner: "刘洋", + overdueDays: 7, + date: "2026-08-09", + status: "已逾期", + }, + { + id: 7, + code: "YQ-202608-57", + name: "防护栏整改逾期", + unit: "七公司", + owner: "陈磊", + overdueDays: 12, + date: "2026-08-08", + status: "整改中", + }, + { + id: 8, + code: "YQ-202608-58", + name: "警示标识整改逾期", + unit: "九公司", + owner: "孙浩", + overdueDays: 18, + date: "2026-08-07", + status: "待复核", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 逾期隐患独立维护查询、表格、分页和详情状态。 */ +function OverdueHiddenListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已逾期", name: "已逾期" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待复核", + name: "待复核", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default OverdueHiddenListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js new file mode 100644 index 0000000..b860f6f --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 逾期隐患详情独立维护展示字段。 */ +function OverdueHiddenRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "隐患编号", value: record["code"] }, + { label: "隐患描述", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任人", value: record["owner"] }, + { label: "逾期天数", value: record["overdueDays"] }, + { label: "整改期限", value: record["date"] }, + { label: "状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default OverdueHiddenRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js new file mode 100644 index 0000000..09b2863 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js @@ -0,0 +1,347 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import HiddenLedgerListPanel from "./Entries/HiddenLedger/ListPanel"; +import OverdueHiddenListPanel from "./Entries/OverdueHidden/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "hidden-ledger": HiddenLedgerListPanel, + "hidden-overdue": OverdueHiddenListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "隐患治理数据(2026年)", + description: "统计隐患发现、整改、验收和类型分布。", + chartType: "line", + metrics: [ + { + name: "隐患总数", + value: 111361, + tone: "blue", + }, + { + name: "整改数", + value: 108375, + tone: "green", + }, + { + name: "未整改数", + value: 2986, + tone: "red", + }, + { + name: "整改率", + value: "97.32%", + tone: "green", + }, + ], + trend: [ + 8800, + 8900, + 9000, + 9100, + 9200, + 9300, + 9250, + 9350, + 9450, + 9550, + 9650, + 9811, + ], + analysisMetrics: [ + { name: "生产环境类", value: 68710 }, + { name: "生产作业类", value: 12919 }, + { name: "相关方管理类", value: 12361 }, + { name: "消防应急类", value: 10802 }, + { name: "安全管理类", value: 6569 }, + ], + rows: [ + { + id: 1, + port: "安全环保部", + total: 22640, + rectified: 21980, + unrectified: 660, + rectificationRate: "97.08%", + }, + { + id: 2, + port: "生产运行部", + total: 31520, + rectified: 30890, + unrectified: 630, + rectificationRate: "98.00%", + }, + { + id: 3, + port: "工程技术部", + total: 18760, + rectified: 18290, + unrectified: 470, + rectificationRate: "97.50%", + }, + ], + entries: [ + { + key: "hidden-ledger", + title: "隐患治理台账", + description: "隐患发现、整改、验收和归档明细", + }, + { + key: "hidden-overdue", + title: "逾期隐患", + description: "按责任单位和逾期天数跟踪", + }, + ], + columns: [ + { + title: "部门", + dataIndex: "port", + }, + { + title: "隐患总数", + dataIndex: "total", + }, + { + title: "整改数", + dataIndex: "rectified", + }, + { + title: "未整改", + dataIndex: "unrectified", + }, + { + title: "整改率", + dataIndex: "rectificationRate", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#f07f7f", "#ffd54d", "#45c8b2", "#65a9e8", "#aa83d6"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item" }, + title: { + text: formatValue(total), + subtext: "隐患总数", + left: "28.5%", + top: "46%", + textAlign: "center", + textVerticalAlign: "middle", + itemGap: 2, + textStyle: { color: "#2e75c6", fontSize: 22, fontWeight: 700 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${formatValue(values[index])} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [48, 76], + center: ["29%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 隐患排查治理放大首页独立维护周期、分析内容和业务入口切换。 */ +function HiddenDangerExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

全年12个月新增隐患趋势

+ 2026年 +
+ +
+
+
+

隐患类型占比

+ {scopeName} +
+ +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default HiddenDangerExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.js new file mode 100644 index 0000000..8943b2b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.js @@ -0,0 +1,227 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const periodData = { + 年度: { + title: "隐患治理数据(2026年)", + metrics: [111361, 108375, 2986, "97.32%"], + compare: [111361, 102450, "+8.70%"], + trendTitle: "近6个月新增隐患趋势", + trend: [9000, 9100, 9200, 9300, 9250, 9350], + }, + 季度: { + title: "隐患治理数据(当前季度)", + metrics: [27900, 27214, 686, "97.54%"], + compare: [27900, 26310, "+6.04%"], + trendTitle: "季度新增隐患趋势", + trend: [26700, 27300, 27900, 0], + }, + 月度: { + title: "隐患治理数据(2026年8月)", + metrics: [9350, 9120, 230, "97.54%"], + compare: [9350, 9250, "+1.08%"], + trendTitle: "当月新增隐患趋势", + trend: [286, 304, 297, 310, 305, 312, 306], + }, +}; +const metricNames = ["隐患总数", "隐患整改数", "隐患未整改数", "隐患整改率"]; +const typeRows = [ + { name: "生产环境类", value: 68710 }, + { name: "生产作业类", value: 12919 }, + { name: "相关方管理类", value: 12361 }, + { name: "消防应急类", value: 10802 }, + { name: "安全管理类", value: 6569 }, +]; +const departmentRows = [ + { id: 1, department: "安全环保部", total: 22640, rectified: 21980, pending: 660, rate: "97.08%" }, + { id: 2, department: "生产运行部", total: 31520, rectified: 30890, pending: 630, rate: "98.00%" }, + { id: 3, department: "工程技术部", total: 18760, rectified: 18290, pending: 470, rate: "97.50%" }, + { id: 4, department: "设备管理部", total: 22410, rectified: 21760, pending: 650, rate: "97.10%" }, + { id: 5, department: "综合管理部", total: 16031, rectified: 15455, pending: 576, rate: "96.40%" }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function HiddenChart({ type, data, period }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + if (type === "pie") { + const total = data.reduce((sum, item) => sum + item.value, 0); + chart.setOption({ + color: ["#65a9e8", "#45c8b2", "#aa83d6", "#f07f7f", "#ffd54d"], + tooltip: { trigger: "item" }, + title: { + text: formatValue(total), + subtext: "隐患总数", + left: "17%", + top: "41%", + textAlign: "center", + textStyle: { color: "#2e75c6", fontSize: 17, fontWeight: 700 }, + subtextStyle: { color: "#66809f", fontSize: 10 }, + }, + legend: [ + { + orient: "vertical", + left: "34%", + top: "30%", + itemWidth: 8, + itemHeight: 8, + itemGap: 8, + data: [typeRows[0].name, typeRows[2].name, typeRows[4].name], + textStyle: { color: "#4f697b", fontSize: 12 }, + formatter: name => `${name} ${Math.round(data.find(item => item.name === name).value / total * 100)}%`, + }, + { + orient: "vertical", + left: "70%", + top: "30%", + itemWidth: 8, + itemHeight: 8, + itemGap: 8, + data: [typeRows[1].name, typeRows[3].name], + textStyle: { color: "#4f697b", fontSize: 12 }, + formatter: name => `${name} ${Math.round(data.find(item => item.name === name).value / total * 100)}%`, + }, + ], + series: [{ + type: "pie", + radius: [42, 66], + center: ["18%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data, + }], + }); + } + else { + chart.setOption({ + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 10, right: 10, bottom: 22, left: 35 }, + xAxis: { + type: "category", + boundaryGap: false, + data: period === "月度" ? ["1日", "5日", "10日", "15日", "20日", "25日", "31日"] : period === "季度" ? ["一季度", "二季度", "三季度", "四季度"] : ["3月", "4月", "5月", "6月", "7月", "8月"], + axisLabel: { color: "#66809f", fontSize: 10 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.35)" } }, + }, + series: [{ + type: "line", + smooth: true, + symbolSize: 4, + data, + lineStyle: { width: 2 }, + areaStyle: { color: "rgba(24,190,212,.12)" }, + }], + }); + } + const resize = () => chart.resize(); + const resizeObserver = new ResizeObserver(resize); + resizeObserver.observe(chartRef.current); + window.addEventListener("resize", resize); + return () => { + resizeObserver.disconnect(); + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [data, period, type]); + + return
; +} + +function HiddenDangerPanel({ onExpand }) { + const [period, setPeriod] = useState("年度"); + const data = periodData[period]; + + return ( + } + > +
+
+ {metricNames.map((name, index) => ( +
+ {name} + {formatValue(data.metrics[index])} +
+ ))} +
+
+ 同比统计 +
+ 本期新增 + {formatValue(data.compare[0])} +
+
+ 同比基数 + {formatValue(data.compare[1])} +
+
+ 同比 + {data.compare[2]} +
+
+
+
+ {data.trendTitle} + {period} +
+ +
+
+
+ 隐患类型占比 + {period} +
+ +
+
+
+ 部门闭环情况 + {period} +
+
+ + 部门 + 隐患总数 + 整改数 + 未整改 + 整改率 + + )} + renderRow={row => ( + + {row.department} + {formatValue(row.total)} + {formatValue(row.rectified)} + {formatValue(row.pending)} + {row.rate} + + )} + /> +
+
+
+
+ ); +} + +export default HiddenDangerPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.module.less new file mode 100644 index 0000000..057fac9 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/HiddenDangerPanel/index.module.less @@ -0,0 +1,114 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: 70px 56px minmax(120px, 1fr) minmax(120px, 1fr) minmax(120px, 0.95fr); + gap: 7px; +} + +.metrics { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 6px; + + > div { + display: grid; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 3px; + color: #2e75c6; + font-size: 18px; + } + + > div:nth-child(3) strong { + color: #e8473f; + } + + > div:nth-child(4) strong { + color: #16845b; + } +} + +.compare { + display: grid; + grid-template-columns: 94px repeat(3, minmax(0, 1fr)); + align-items: center; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.32); + + > strong { + color: #3f5f83; + text-align: center; + } + + > div { + text-align: center; + } + + span { + display: block; + color: #66809f; + font-size: 11px; + } + + b { + display: block; + margin-top: 2px; + color: #2e75c6; + font-size: 16px; + } + + > div:last-child b { + color: #e8473f; + } +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 7px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); +} + +.subheading { + display: flex; + height: 20px; + align-items: center; + justify-content: space-between; + + strong { + color: #3f5f83; + font-size: 12px; + } + + span { + color: #66809f; + font-size: 12px; + } +} + +.chart, +.table { + width: 100%; + height: calc(100% - 20px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js new file mode 100644 index 0000000..78eec43 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为整改验收接口结果。 +const mockRows = [ + { + id: 1, + code: "YS-202608-31", + name: "月度综合检查", + unit: "二公司", + issueCount: 3, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-14", + }, + { + id: 2, + code: "YS-202608-32", + name: "设备专项检查", + unit: "六公司", + issueCount: 6, + status: "整改中", + acceptanceResult: "待验收", + date: "2026-08-13", + }, + { + id: 3, + code: "YS-202608-33", + name: "环保专项检查", + unit: "七公司", + issueCount: 2, + status: "待验收", + acceptanceResult: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "YS-202608-34", + name: "消防安全检查", + unit: "九公司", + issueCount: 1, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-11", + }, + { + id: 5, + code: "YS-202608-35", + name: "月度综合检查", + unit: "二公司", + issueCount: 3, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-10", + }, + { + id: 6, + code: "YS-202608-36", + name: "设备专项检查", + unit: "六公司", + issueCount: 6, + status: "整改中", + acceptanceResult: "待验收", + date: "2026-08-09", + }, + { + id: 7, + code: "YS-202608-37", + name: "环保专项检查", + unit: "七公司", + issueCount: 2, + status: "待验收", + acceptanceResult: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "YS-202608-38", + name: "消防安全检查", + unit: "九公司", + issueCount: 1, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 整改验收独立维护查询、表格、分页和详情状态。 */ +function InspectionAcceptanceListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已整改", name: "已整改" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "验收结果", dataIndex: "acceptanceResult" }, + { title: "验收日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionAcceptanceListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js new file mode 100644 index 0000000..f84538d --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 整改验收详情独立维护展示字段。 */ +function InspectionAcceptanceRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "整改编号", value: record["code"] }, + { label: "检查主题", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "问题数", value: record["issueCount"] }, + { label: "整改状态", value: record["status"], isStatus: true }, + { label: "验收结果", value: record["acceptanceResult"] }, + { label: "验收日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionAcceptanceRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js new file mode 100644 index 0000000..439b13b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为检查计划接口结果。 +const mockRows = [ + { + id: 1, + code: "JH-202608-31", + name: "8月综合检查计划", + unit: "二公司", + period: "月度", + status: "已完成", + completionDate: "2026-08-14", + date: "2026-08-14", + }, + { + id: 2, + code: "JH-202608-32", + name: "设备专项检查计划", + unit: "六公司", + period: "专项", + status: "执行中", + completionDate: "待完成", + date: "2026-08-13", + }, + { + id: 3, + code: "JH-202608-33", + name: "环保专项检查计划", + unit: "七公司", + period: "季度", + status: "未开始", + completionDate: "待完成", + date: "2026-08-12", + }, + { + id: 4, + code: "JH-202608-34", + name: "消防安全检查计划", + unit: "九公司", + period: "专项", + status: "已完成", + completionDate: "2026-08-11", + date: "2026-08-11", + }, + { + id: 5, + code: "JH-202608-35", + name: "8月综合检查计划", + unit: "二公司", + period: "月度", + status: "已完成", + completionDate: "2026-08-14", + date: "2026-08-10", + }, + { + id: 6, + code: "JH-202608-36", + name: "设备专项检查计划", + unit: "六公司", + period: "专项", + status: "执行中", + completionDate: "待完成", + date: "2026-08-09", + }, + { + id: 7, + code: "JH-202608-37", + name: "环保专项检查计划", + unit: "七公司", + period: "季度", + status: "未开始", + completionDate: "待完成", + date: "2026-08-08", + }, + { + id: 8, + code: "JH-202608-38", + name: "消防安全检查计划", + unit: "九公司", + period: "专项", + status: "已完成", + completionDate: "2026-08-11", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查计划独立维护查询、表格、分页和详情状态。 */ +function InspectionPlanListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "执行中", name: "执行中" }, { + bianma: "未开始", + name: "未开始", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "完成日期", dataIndex: "completionDate" }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionPlanListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js new file mode 100644 index 0000000..15571d8 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查计划详情独立维护展示字段。 */ +function InspectionPlanRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "计划编号", value: record["code"] }, + { label: "计划名称", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "计划周期", value: record["period"] }, + { label: "计划状态", value: record["status"], isStatus: true }, + { label: "完成日期", value: record["completionDate"] }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionPlanRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js new file mode 100644 index 0000000..c243c98 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为检查记录接口结果。 +const mockRows = [ + { + id: 1, + code: "JC-202608-31", + name: "月度综合检查", + unit: "二公司", + planType: "计划内", + issueCount: 3, + status: "已完成", + date: "2026-08-14", + }, + { + id: 2, + code: "JC-202608-32", + name: "设备专项检查", + unit: "六公司", + planType: "计划内", + issueCount: 6, + status: "整改中", + date: "2026-08-13", + }, + { + id: 3, + code: "JC-202608-33", + name: "环保专项检查", + unit: "七公司", + planType: "计划外", + issueCount: 2, + status: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "JC-202608-34", + name: "消防安全检查", + unit: "九公司", + planType: "计划内", + issueCount: 0, + status: "已完成", + date: "2026-08-11", + }, + { + id: 5, + code: "JC-202608-35", + name: "月度综合检查", + unit: "二公司", + planType: "计划内", + issueCount: 3, + status: "已完成", + date: "2026-08-10", + }, + { + id: 6, + code: "JC-202608-36", + name: "设备专项检查", + unit: "六公司", + planType: "计划内", + issueCount: 6, + status: "整改中", + date: "2026-08-09", + }, + { + id: 7, + code: "JC-202608-37", + name: "环保专项检查", + unit: "七公司", + planType: "计划外", + issueCount: 2, + status: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "JC-202608-38", + name: "消防安全检查", + unit: "九公司", + planType: "计划内", + issueCount: 0, + status: "已完成", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查记录独立维护查询、表格、分页和详情状态。 */ +function InspectionRecordListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "检查日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionRecordListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js new file mode 100644 index 0000000..8e744db --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查记录详情独立维护展示字段。 */ +function InspectionRecordRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "检查编号", value: record["code"] }, + { label: "检查主题", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "计划属性", value: record["planType"] }, + { label: "问题数", value: record["issueCount"] }, + { label: "检查状态", value: record["status"], isStatus: true }, + { label: "检查日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionRecordRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.js new file mode 100644 index 0000000..f6eda9b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.js @@ -0,0 +1,411 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import InspectionAcceptanceListPanel from "./Entries/InspectionAcceptance/ListPanel"; +import InspectionPlanListPanel from "./Entries/InspectionPlan/ListPanel"; +import InspectionRecordListPanel from "./Entries/InspectionRecord/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "inspection-plan": InspectionPlanListPanel, + "inspection-record": InspectionRecordListPanel, + "inspection-acceptance": InspectionAcceptanceListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "安全环保检查数据", + description: "统计计划执行情况、检查总数及计划内外检查。", + chartType: "pie", + metrics: [ + { + name: "计划总数", + value: 68, + tone: "blue", + }, + { + name: "已完成计划数", + value: 55, + tone: "green", + }, + { + name: "检查总数", + value: 118, + tone: "blue", + }, + { + name: "计划内检查数", + value: 91, + tone: "blue", + }, + { + name: "计划外检查数", + value: 27, + tone: "orange", + }, + ], + trend: [ + 44, + 48, + 52, + 49, + 58, + 61, + 65, + 68, + 72, + 74, + 79, + 82, + ], + rows: [ + { + id: 1, + unit: "安全环保部", + planTotal: 68, + completedPlans: 55, + inspectionTotal: 118, + planned: 91, + unplanned: 27, + }, + { + id: 2, + unit: "生产运行部", + planTotal: 72, + completedPlans: 64, + inspectionTotal: 126, + planned: 110, + unplanned: 16, + }, + { + id: 3, + unit: "工程技术部", + planTotal: 61, + completedPlans: 52, + inspectionTotal: 104, + planned: 88, + unplanned: 16, + }, + { + id: 4, + unit: "设备管理部", + planTotal: 58, + completedPlans: 49, + inspectionTotal: 96, + planned: 82, + unplanned: 14, + }, + ], + entries: [ + { + key: "inspection-plan", + title: "检查计划", + description: "查看计划总数、已完成计划数及执行状态", + }, + { + key: "inspection-record", + title: "检查记录", + description: "查看计划内、计划外检查记录及问题数", + }, + { + key: "inspection-acceptance", + title: "整改验收", + description: "查看检查问题整改状态与验收结果", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "计划总数", + dataIndex: "planTotal", + }, + { + title: "已完成计划数", + dataIndex: "completedPlans", + }, + { + title: "检查总数", + dataIndex: "inspectionTotal", + }, + { + title: "计划内检查数", + dataIndex: "planned", + }, + { + title: "计划外检查数", + dataIndex: "unplanned", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#45c8b2", "#ffb547"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item" }, + title: { + text: total.toLocaleString("zh-CN"), + subtext: "检查总数", + left: "28.5%", + top: "46%", + textAlign: "center", + textVerticalAlign: "middle", + itemGap: 2, + textStyle: { color: "#2e75c6", fontSize: 22 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${values[index].toLocaleString("zh-CN")} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [48, 76], + center: ["29%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 安全环保检查放大首页独立维护周期、分析内容和业务入口切换。 */ +function SafetyInspectionExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + const planTotal = Number(data.metrics[0].value); + const completedPlans = Number(data.metrics[1].value); + const inspectionTotal = Number(data.metrics[2].value); + const planned = Number(data.metrics[3].value); + const unplanned = Number(data.metrics[4].value); + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

计划执行情况

+ 2026年 +
+
+
+ 计划总数 + {formatValue(planTotal)} +
+
+ 已完成计划数 + {formatValue(completedPlans)} +
+
+ 完成率 + {`${(completedPlans / planTotal * 100).toFixed(2)}%`} +
+
+ +
+
+
+

检查执行情况

+ {scopeName} +
+
+
+ 检查总数 + {formatValue(inspectionTotal)} +
+
+ 计划内 / 计划外 + {`${(planned / inspectionTotal * 100).toFixed(2)}% / ${(unplanned / inspectionTotal * 100).toFixed(2)}%`} +
+
+ +
+
+
+
+

单位执行明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default SafetyInspectionExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..abae924 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/ExpandedPanel.module.less @@ -0,0 +1,91 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.analysisSummary { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 2px 0 8px; + + div { + display: flex; + min-height: 48px; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 3px; + color: #2e75c6; + font-size: 19px; + } +} + +.inspectionSummary { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.analysisSummary + .chart { + height: calc(100% - 82px); +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.js new file mode 100644 index 0000000..8360a86 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.js @@ -0,0 +1,34 @@ +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const mockGroups = [ + { title: "计划执行情况", values: [{ name: "计划总数", value: 326 }, { name: "已完成计划数", value: 281 }] }, + { + title: "检查执行情况", + values: [{ name: "检查总数", value: 568 }, { name: "计划内检查数", value: 492 }, { name: "计划外检查数", value: 76 }], + }, +]; + +function SafetyInspectionPanel({ onExpand }) { + return ( + +
+ {mockGroups.map(group => ( +
+
{group.title}
+
+ {group.values.map(item => ( +
+ {item.name} + {item.value} +
+ ))} +
+
+ ))} +
+
+ ); +} + +export default SafetyInspectionPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.module.less new file mode 100644 index 0000000..092fd38 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/SafetyInspectionPanel/index.module.less @@ -0,0 +1,56 @@ +.panel { + height: 100%; +} + +.overview { + display: grid; + align-content: start; + grid-template-columns: 1fr 1.45fr; + gap: 10px; + + section { + display: grid; + min-width: 0; + grid-template-rows: 24px 74px; + } +} + +.heading { + color: #3b576a; + font-size: 14px; + font-weight: 700; +} + +.cards { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + + &[data-count="3"] { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + > div { + display: grid; + place-content: center; + padding: 0 4px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 13px; + line-height: 18px; + white-space: nowrap; + } + + strong { + margin-top: 6px; + color: #2e75c6; + font-size: 24px; + } +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.js new file mode 100644 index 0000000..efddc8d --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.js @@ -0,0 +1,14 @@ +import HiddenDangerPanel from "./HiddenDangerPanel"; +import styles from "./index.module.less"; +import SafetyInspectionPanel from "./SafetyInspectionPanel"; + +function LeftPanel({ onExpand }) { + return ( +
+ onExpand("hidden")} /> + onExpand("inspection")} /> +
+ ); +} + +export default LeftPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.module.less new file mode 100644 index 0000000..8a5145e --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/LeftPanel/index.module.less @@ -0,0 +1,7 @@ +.column { + display: grid; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(0, 80.2fr) minmax(0, 19.8fr); + gap: 10px; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js new file mode 100644 index 0000000..3eeedb4 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急演练接口结果。 +const mockRows = [ + { + id: 1, + code: "YL-2026-61", + name: "消防应急演练", + type: "专项", + unit: "二公司", + participants: 86, + status: "已完成", + date: "2026-08-14", + }, + { + id: 2, + code: "YL-2026-62", + name: "防汛应急演练", + type: "综合", + unit: "六公司", + participants: 125, + status: "计划中", + date: "2026-08-13", + }, + { + id: 3, + code: "YL-2026-63", + name: "危化品泄漏演练", + type: "专项", + unit: "七公司", + participants: 64, + status: "已完成", + date: "2026-08-12", + }, + { + id: 4, + code: "YL-2026-64", + name: "人员疏散演练", + type: "现场处置", + unit: "九公司", + participants: 103, + status: "待评估", + date: "2026-08-11", + }, + { + id: 5, + code: "YL-2026-65", + name: "消防应急演练", + type: "专项", + unit: "二公司", + participants: 86, + status: "已完成", + date: "2026-08-10", + }, + { + id: 6, + code: "YL-2026-66", + name: "防汛应急演练", + type: "综合", + unit: "六公司", + participants: 125, + status: "计划中", + date: "2026-08-09", + }, + { + id: 7, + code: "YL-2026-67", + name: "危化品泄漏演练", + type: "专项", + unit: "七公司", + participants: 64, + status: "已完成", + date: "2026-08-08", + }, + { + id: 8, + code: "YL-2026-68", + name: "人员疏散演练", + type: "现场处置", + unit: "九公司", + participants: 103, + status: "待评估", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急演练独立维护查询、表格、分页和详情状态。 */ +function EmergencyDrillListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "计划中", name: "计划中" }, { + bianma: "待评估", + name: "待评估", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "演练日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyDrillListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js new file mode 100644 index 0000000..8bba050 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急演练详情独立维护展示字段。 */ +function EmergencyDrillRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "演练编号", value: record["code"] }, + { label: "演练名称", value: record["name"] }, + { label: "演练类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "参演人次", value: record["participants"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "演练日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyDrillRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js new file mode 100644 index 0000000..fa37387 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js @@ -0,0 +1,184 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急值班接口结果。 +const mockRows = [ + { + id: 1, + date: "2026-08-14", + unit: "二公司", + position: "值班经理", + name: "张三", + phone: "13800138001", + status: "值班中", + }, + { + id: 2, + date: "2026-08-13", + unit: "六公司", + position: "值班主管", + name: "李四", + phone: "13800138002", + status: "待值班", + }, + { + id: 3, + date: "2026-08-12", + unit: "七公司", + position: "安全总监", + name: "王五", + phone: "13800138003", + status: "已交接", + }, + { + id: 4, + date: "2026-08-11", + unit: "九公司", + position: "部门部长", + name: "赵六", + phone: "13800138004", + status: "待交接", + }, + { + id: 5, + date: "2026-08-10", + unit: "二公司", + position: "值班经理", + name: "张三", + phone: "13800138001", + status: "值班中", + }, + { + id: 6, + date: "2026-08-09", + unit: "六公司", + position: "值班主管", + name: "李四", + phone: "13800138002", + status: "待值班", + }, + { + id: 7, + date: "2026-08-08", + unit: "七公司", + position: "安全总监", + name: "王五", + phone: "13800138003", + status: "已交接", + }, + { + id: 8, + date: "2026-08-07", + unit: "九公司", + position: "部门部长", + name: "赵六", + phone: "13800138004", + status: "待交接", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急值班独立维护查询、表格、分页和详情状态。 */ +function EmergencyDutyListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "值班中", name: "值班中" }, { bianma: "待值班", name: "待值班" }, { + bianma: "已交接", + name: "已交接", + }, { bianma: "待交接", name: "待交接" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyDutyListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js new file mode 100644 index 0000000..20ba70b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js @@ -0,0 +1,39 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急值班详情独立维护展示字段。 */ +function EmergencyDutyRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "值班日期", value: record["date"] }, + { label: "责任单位", value: record["unit"] }, + { label: "值班职务", value: record["position"] }, + { label: "值班人员", value: record["name"] }, + { label: "联系电话", value: record["phone"] }, + { label: "交接状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyDutyRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js new file mode 100644 index 0000000..71d5376 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js @@ -0,0 +1,199 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急物资接口结果。 +const mockRows = [ + { + id: 1, + code: "WZ-2026-101", + name: "防汛沙袋", + type: "防汛", + quantity: 320, + location: "东区仓库", + unit: "二公司", + status: "完好", + date: "2026-08-14", + }, + { + id: 2, + code: "WZ-2026-102", + name: "应急照明灯", + type: "照明", + quantity: 48, + location: "西区仓库", + unit: "六公司", + status: "完好", + date: "2026-08-13", + }, + { + id: 3, + code: "WZ-2026-103", + name: "防毒面具", + type: "防护", + quantity: 76, + location: "应急物资库", + unit: "七公司", + status: "待补充", + date: "2026-08-12", + }, + { + id: 4, + code: "WZ-2026-104", + name: "消防水带", + type: "消防", + quantity: 105, + location: "消防器材库", + unit: "九公司", + status: "完好", + date: "2026-08-11", + }, + { + id: 5, + code: "WZ-2026-105", + name: "防汛沙袋", + type: "防汛", + quantity: 320, + location: "东区仓库", + unit: "二公司", + status: "完好", + date: "2026-08-10", + }, + { + id: 6, + code: "WZ-2026-106", + name: "应急照明灯", + type: "照明", + quantity: 48, + location: "西区仓库", + unit: "六公司", + status: "完好", + date: "2026-08-09", + }, + { + id: 7, + code: "WZ-2026-107", + name: "防毒面具", + type: "防护", + quantity: 76, + location: "应急物资库", + unit: "七公司", + status: "待补充", + date: "2026-08-08", + }, + { + id: 8, + code: "WZ-2026-108", + name: "消防水带", + type: "消防", + quantity: 105, + location: "消防器材库", + unit: "九公司", + status: "完好", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急物资独立维护查询、表格、分页和详情状态。 */ +function EmergencyMaterialListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "完好", name: "完好" }, { bianma: "待补充", name: "待补充" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyMaterialListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js new file mode 100644 index 0000000..9217311 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急物资详情独立维护展示字段。 */ +function EmergencyMaterialRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "物资编号", value: record["code"] }, + { label: "物资名称", value: record["name"] }, + { label: "物资类别", value: record["type"] }, + { label: "数量", value: record["quantity"] }, + { label: "存放位置", value: record["location"] }, + { label: "责任单位", value: record["unit"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyMaterialRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js new file mode 100644 index 0000000..f74e496 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js @@ -0,0 +1,190 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急预案接口结果。 +const mockRows = [ + { + id: 1, + code: "YA-2026-41", + name: "综合应急预案", + type: "综合", + unit: "二公司", + version: "V3.0", + status: "有效", + date: "2026-08-14", + }, + { + id: 2, + code: "YA-2026-42", + name: "消防专项预案", + type: "专项", + unit: "六公司", + version: "V2.1", + status: "有效", + date: "2026-08-13", + }, + { + id: 3, + code: "YA-2026-43", + name: "防汛专项预案", + type: "专项", + unit: "七公司", + version: "V1.8", + status: "待更新", + date: "2026-08-12", + }, + { + id: 4, + code: "YA-2026-44", + name: "现场处置方案", + type: "现场处置", + unit: "九公司", + version: "V4.0", + status: "有效", + date: "2026-08-11", + }, + { + id: 5, + code: "YA-2026-45", + name: "综合应急预案", + type: "综合", + unit: "二公司", + version: "V3.0", + status: "有效", + date: "2026-08-10", + }, + { + id: 6, + code: "YA-2026-46", + name: "消防专项预案", + type: "专项", + unit: "六公司", + version: "V2.1", + status: "有效", + date: "2026-08-09", + }, + { + id: 7, + code: "YA-2026-47", + name: "防汛专项预案", + type: "专项", + unit: "七公司", + version: "V1.8", + status: "待更新", + date: "2026-08-08", + }, + { + id: 8, + code: "YA-2026-48", + name: "现场处置方案", + type: "现场处置", + unit: "九公司", + version: "V4.0", + status: "有效", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急预案独立维护查询、表格、分页和详情状态。 */ +function EmergencyPlanListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "有效", name: "有效" }, { bianma: "待更新", name: "待更新" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "发布日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyPlanListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js new file mode 100644 index 0000000..b09ca98 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急预案详情独立维护展示字段。 */ +function EmergencyPlanRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "预案编号", value: record["code"] }, + { label: "预案名称", value: record["name"] }, + { label: "预案类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "版本", value: record["version"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "发布日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyPlanRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js new file mode 100644 index 0000000..b0082cb --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js @@ -0,0 +1,378 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import EmergencyDrillListPanel from "./Entries/EmergencyDrill/ListPanel"; +import EmergencyDutyListPanel from "./Entries/EmergencyDuty/ListPanel"; +import EmergencyMaterialListPanel from "./Entries/EmergencyMaterial/ListPanel"; +import EmergencyPlanListPanel from "./Entries/EmergencyPlan/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "emergency-plan": EmergencyPlanListPanel, + "emergency-drill": EmergencyDrillListPanel, + "emergency-material": EmergencyMaterialListPanel, + "emergency-duty": EmergencyDutyListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "应急管理", + description: "汇总综合预案、专项预案、现场处置方案、应急物资、年度演练和应急值班。", + chartType: "bar", + metrics: [ + { + name: "综合预案", + value: 3, + tone: "blue", + }, + { + name: "专项预案", + value: 4, + tone: "blue", + }, + { + name: "现场处置方案", + value: 6, + tone: "blue", + }, + { + name: "应急物资", + value: 1286, + tone: "blue", + }, + { + name: "年度总数", + value: 36, + tone: "blue", + }, + { + name: "已完成数", + value: 31, + tone: "green", + }, + { + name: "参演人次", + value: 1248, + tone: "blue", + }, + ], + trend: [ + 12, + 18, + 20, + 24, + 29, + 34, + 38, + 41, + 45, + 48, + 52, + 56, + ], + rows: [ + { + id: 1, + unit: "二公司", + name: "田旭东", + role: "公司经理", + mobile: "139-3635-6266", + phone: "3568888", + }, + { + id: 2, + unit: "安全环保部", + name: "赵爱勇", + role: "安全总监", + mobile: "133-3330-0099", + phone: "3465654", + }, + { + id: 3, + unit: "生产运行部", + name: "刘迪", + role: "技设部部长", + mobile: "138-3353-9115", + phone: "3568470", + }, + { + id: 4, + unit: "工程技术部", + name: "张仙魁", + role: "业务部部长", + mobile: "189-0333-8690", + phone: "6985412", + }, + { id: 5, unit: "设备管理部", name: "张文利", role: "设备部长", mobile: "139-3360-6855", phone: "3896512" }, + { id: 6, unit: "综合管理部", name: "李强", role: "值班经理", mobile: "138****6201", phone: "3568211" }, + { id: 7, unit: "港区调度中心", name: "王敏", role: "值班主管", mobile: "139****3120", phone: "3568320" }, + { id: 8, unit: "应急救援队", name: "陈磊", role: "救援队长", mobile: "137****4563", phone: "3568456" }, + { id: 9, unit: "五公司", name: "刘洋", role: "生产部长", mobile: "136****1289", phone: "3568128" }, + { id: 10, unit: "六公司", name: "赵静", role: "安监部部长", mobile: "135****9876", phone: "3568987" }, + { id: 11, unit: "八公司", name: "孙浩", role: "设备部部长", mobile: "186****5314", phone: "3568531" }, + { id: 12, unit: "十公司", name: "周倩", role: "综合部部长", mobile: "187****2468", phone: "3568246" }, + { id: 13, unit: "保税物流中心", name: "马超", role: "运营部部长", mobile: "188****9675", phone: "3568967" }, + ], + entries: [ + { + key: "emergency-plan", + title: "应急预案", + description: "应急预案台账及有效性", + }, + { + key: "emergency-drill", + title: "应急演练", + description: "年度演练计划、完成情况和参演人次", + }, + { + key: "emergency-material", + title: "应急物资", + description: "物资装备台账及完好情况", + }, + { + key: "emergency-duty", + title: "应急值班", + description: "值班排班、日志和交接记录", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "姓名", + dataIndex: "name", + }, + { + title: "职务", + dataIndex: "role", + }, + { + title: "手机", + dataIndex: "mobile", + }, + { + title: "固定电话", + dataIndex: "phone", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 应急管理放大首页独立维护周期、分析内容和业务入口切换。 */ +function EmergencyManagementExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

应急预案与应急物资

+ 2026年 +
+
+ {data.metrics.slice(0, 4).map((item, index) => ( + + ))} +
+
+
+
+

年度演练执行情况

+ {scopeName} +
+
+ {data.metrics.slice(4).map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+ +
+
+
+
+

应急值班明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default EmergencyManagementExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..706113f --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less @@ -0,0 +1,119 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.resourceGrid { + display: grid; + height: calc(100% - 24px); + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; + padding-top: 4px; + + button { + display: flex; + min-width: 0; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + color: #4b668d; + cursor: pointer; + } + + span { + font-size: 14px; + } + + strong { + color: #2e75c6; + font-size: 28px; + } +} + +.analysisSummary { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 2px 0 8px; + + div { + display: flex; + min-height: 48px; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 6px 0; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 1px; + color: #2e75c6; + font-size: 19px; + } +} + +.analysisSummary + .chart { + height: calc(100% - 82px); +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.js new file mode 100644 index 0000000..b1cb4f4 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.js @@ -0,0 +1,117 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import { getWeatherIcon } from "~/utils"; +import styles from "./index.module.less"; + +const planRows = [ + { name: "综合预案", value: 3 }, + { name: "专项预案", value: 4 }, + { name: "现场处置方案", value: 6 }, + { name: "应急物资", value: 1286 }, +]; +const drillRows = [ + { name: "年度总数", value: 36 }, + { name: "已完成数", value: 31 }, + { name: "参演人次", value: 1248 }, +]; +const dutyRows = [ + { id: 1, unit: "七公司", name: "刘迪", role: "技设部部长", mobile: "138-3353-9115", phone: "3568470" }, + { id: 2, unit: "九公司", name: "张仙魁", role: "业务部部长", mobile: "189-0333-8690", phone: "6985412" }, + { id: 3, unit: "综服中心", name: "张文利", role: "安监部部长", mobile: "139-3360-6855", phone: "3896512" }, + { id: 4, unit: "一公司", name: "李强", role: "值班经理", mobile: "138****6201", phone: "3568211" }, + { id: 5, unit: "二公司", name: "王敏", role: "值班主管", mobile: "139****3120", phone: "3568320" }, + { id: 6, unit: "四公司", name: "陈磊", role: "安全部长", mobile: "137****4563", phone: "3568456" }, + { id: 7, unit: "六公司", name: "赵磊", role: "值班经理", mobile: "136****1289", phone: "3568128" }, + { id: 8, unit: "杂货公司", name: "孙浩", role: "生产部长", mobile: "138****3578", phone: "3568357" }, + { id: 9, unit: "铁运公司", name: "周倩", role: "调度主管", mobile: "139****5412", phone: "3568541" }, + { id: 10, unit: "流机公司", name: "刘洋", role: "设备部长", mobile: "137****6855", phone: "3568685" }, + { id: 11, unit: "中理公司", name: "王强", role: "安全主管", mobile: "138****9115", phone: "3568911" }, + { id: 12, unit: "餐饮公司", name: "张伟", role: "综合主管", mobile: "139****8690", phone: "3568869" }, + { id: 13, unit: "港区调度中心", name: "田旭东", role: "调度主任", mobile: "139****6266", phone: "3568626" }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function EmergencyManagementPanel({ weather, onExpand }) { + const currentWeather = weather?.now || weather || {}; + + return ( + +
+
+
+
+ {planRows.map(item => ( + + ))} +
+
+ {drillRows.map(item => ( + + ))} +
+
+
+
{weather?.alerts?.[0]?.title || "当前暂无气象预警"}
+
+
+ + {currentWeather.text || "暂无"} +
+
+ 温度 + {currentWeather.temp ? `${currentWeather.temp}℃` : "--"} +
+
+ 风速 + {currentWeather.wind_class || "--"} +
+
+
+
+
+
+ 应急值班 + + 值班单位: + {dutyRows.length} + 家 + +
+
+ + 单位 + 姓名 + 职务 + 手机 + 固定电话 + + )} + renderRow={row => ( + + {row.unit} + {row.name} + {row.role} + {row.mobile} + {row.phone} + + )} + /> +
+
+
+
+ ); +} + +export default EmergencyManagementPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.module.less new file mode 100644 index 0000000..6a381d9 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/EmergencyManagementPanel/index.module.less @@ -0,0 +1,141 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: minmax(0, 0.85fr) minmax(0, 1.35fr); + gap: 10px; +} + +.left { + display: grid; + height: 128px; + min-width: 0; + min-height: 0; + align-self: center; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); + gap: 8px; +} + +.summary { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.planRow, +.drillRow { + display: grid; + min-height: 0; + gap: 7px; + + button { + display: grid; + min-width: 0; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + cursor: pointer; + text-align: center; + } + + span { + color: #4b668d; + font-size: 14px; + line-height: 18px; + } + + strong { + margin-top: 1px; + color: #4387d6; + font-size: 22px; + line-height: 24px; + } +} + +.planRow { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.drillRow { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.weather { + display: grid; + height: 100%; + min-width: 0; + align-content: center; + box-sizing: border-box; + padding: 8px 12px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); +} + +.weatherTitle { + overflow: hidden; + color: #ff6600; + font-size: 14px; + font-weight: 700; + text-overflow: ellipsis; + white-space: nowrap; +} + +.weatherMain { + display: grid; + grid-template-columns: 1.2fr repeat(2, minmax(0, 1fr)); + align-items: center; + margin-top: 6px; + + > div { + display: grid; + justify-items: center; + color: #2b4977; + font-size: 13px; + } + + strong { + color: #2e75c6; + font-size: 14px; + } +} + +.condition { + display: flex !important; + align-items: center; + justify-content: center; + gap: 5px; + + img { + width: 38px; + height: 38px; + object-fit: contain; + } +} + +.dutyArea { + display: grid; + min-width: 0; + min-height: 0; + grid-template-rows: 28px minmax(0, 1fr); +} + +.dutyTitle { + display: flex; + align-items: center; + justify-content: space-between; + color: #3f5f83; + font-size: 13px; +} + +.table { + min-width: 0; + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/ListPanel.js new file mode 100644 index 0000000..addbf99 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为风险辨识单元接口结果。 +const mockRows = [ + { + id: 1, + code: "BS-2026-201", + name: "装船机作业单元", + riskEvent: "机械伤害", + level: "重大风险", + unit: "二公司", + department: "生产部", + status: "已辨识", + date: "2026-08-14", + }, + { + id: 2, + code: "BS-2026-202", + name: "储罐装卸单元", + riskEvent: "火灾爆炸", + level: "较大风险", + unit: "六公司", + department: "设备部", + status: "待复核", + date: "2026-08-13", + }, + { + id: 3, + code: "BS-2026-203", + name: "皮带输送单元", + riskEvent: "物体打击", + level: "一般风险", + unit: "七公司", + department: "安全部", + status: "已辨识", + date: "2026-08-12", + }, + { + id: 4, + code: "BS-2026-204", + name: "车辆运输单元", + riskEvent: "车辆伤害", + level: "低风险", + unit: "九公司", + department: "业务部", + status: "已更新", + date: "2026-08-11", + }, + { + id: 5, + code: "BS-2026-205", + name: "装船机作业单元", + riskEvent: "机械伤害", + level: "重大风险", + unit: "二公司", + department: "生产部", + status: "已辨识", + date: "2026-08-10", + }, + { + id: 6, + code: "BS-2026-206", + name: "储罐装卸单元", + riskEvent: "火灾爆炸", + level: "较大风险", + unit: "六公司", + department: "设备部", + status: "待复核", + date: "2026-08-09", + }, + { + id: 7, + code: "BS-2026-207", + name: "皮带输送单元", + riskEvent: "物体打击", + level: "一般风险", + unit: "七公司", + department: "安全部", + status: "已辨识", + date: "2026-08-08", + }, + { + id: 8, + code: "BS-2026-208", + name: "车辆运输单元", + riskEvent: "车辆伤害", + level: "低风险", + unit: "九公司", + department: "业务部", + status: "已更新", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险辨识单元独立维护查询、表格、分页和详情状态。 */ +function RiskIdentifyListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已辨识", name: "已辨识" }, { bianma: "待复核", name: "待复核" }, { + bianma: "已更新", + name: "已更新", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default RiskIdentifyListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/RecordDetail.js new file mode 100644 index 0000000..c8b4ef1 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskIdentify/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险辨识单元详情独立维护展示字段。 */ +function RiskIdentifyRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "辨识单元编号", value: record["code"] }, + { label: "辨识单元", value: record["name"] }, + { label: "风险事件", value: record["riskEvent"] }, + { label: "风险等级", value: record["level"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任部门", value: record["department"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default RiskIdentifyRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/ListPanel.js new file mode 100644 index 0000000..d8a7521 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/ListPanel.js @@ -0,0 +1,310 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为风险点台账接口结果。 +const mockRows = [ + { + id: 1, + code: "FXD-2026-101", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "FXD-2026-102", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "FXD-2026-103", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-12", + }, + { + id: 4, + code: "FXD-2026-104", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-11", + }, + { + id: 5, + code: "FXD-2026-105", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "FXD-2026-106", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "FXD-2026-107", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-08", + }, + { + id: 8, + code: "FXD-2026-108", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-07", + }, + { + id: 9, + code: "FXD-2026-109", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-06", + }, + { + id: 10, + code: "FXD-2026-110", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-05", + }, + { + id: 11, + code: "FXD-2026-111", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-04", + }, + { + id: 12, + code: "FXD-2026-112", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-03", + }, + { + id: 13, + code: "FXD-2026-113", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-02", + }, + { + id: 14, + code: "FXD-2026-114", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-01", + }, + { + id: 15, + code: "FXD-2026-115", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-07-31", + }, + { + id: 16, + code: "FXD-2026-116", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-07-30", + }, + { + id: 17, + code: "FXD-2026-117", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-07-29", + }, + { + id: 18, + code: "FXD-2026-118", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-07-28", + }, + { + id: 19, + code: "FXD-2026-119", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-07-27", + }, + { + id: 20, + code: "FXD-2026-120", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-07-26", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险点台账独立维护查询、表格、分页和详情状态。 */ +function RiskPointListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "待复核", name: "待复核" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + height={500} + disabledResizer + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default RiskPointListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/RecordDetail.js new file mode 100644 index 0000000..9048ecf --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/Entries/RiskPoint/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险点台账详情独立维护展示字段。 */ +function RiskPointRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "风险点编号", value: record["code"] }, + { label: "风险点名称", value: record["name"] }, + { label: "风险等级", value: record["level"] }, + { label: "责任单位", value: record["unit"] }, + { label: "管控层级", value: record["controlLevel"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default RiskPointRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.js new file mode 100644 index 0000000..ac17b74 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.js @@ -0,0 +1,412 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import RiskIdentifyListPanel from "./Entries/RiskIdentify/ListPanel"; +import RiskPointListPanel from "./Entries/RiskPoint/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "risk-point": RiskPointListPanel, + "risk-identify": RiskIdentifyListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "风险分级统计", + description: "按重大、较大、一般、低风险统计风险点,并可下钻到辨识单元和风险点台账。", + chartType: "pie", + metrics: [ + { + name: "重大风险", + value: 10, + tone: "red", + }, + { + name: "较大风险", + value: 5, + tone: "orange", + }, + { + name: "一般风险", + value: 2, + tone: "yellow", + }, + { + name: "低风险", + value: 4, + tone: "blue", + }, + ], + trend: [ + 61, + 66, + 63, + 72, + 70, + 76, + 80, + 78, + 84, + 86, + 89, + 92, + ], + rows: [ + { + id: 1, + unit: "安全环保部", + total: 5, + major: 2, + large: 1, + general: 1, + low: 1, + }, + { + id: 2, + unit: "生产运行部", + total: 5, + major: 3, + large: 1, + general: 0, + low: 1, + }, + { + id: 3, + unit: "工程技术部", + total: 4, + major: 2, + large: 1, + general: 1, + low: 0, + }, + { + id: 4, + unit: "设备管理部", + total: 4, + major: 2, + large: 1, + general: 0, + low: 1, + }, + { + id: 5, + unit: "综合管理部", + total: 3, + major: 1, + large: 1, + general: 0, + low: 1, + }, + { + id: 6, + unit: "铁运公司", + total: 1546, + major: 0, + large: 140, + general: 102, + low: 1304, + }, + { + id: 7, + unit: "流机公司", + total: 2636, + major: 0, + large: 421, + general: 285, + low: 1930, + }, + { + id: 8, + unit: "综服中心", + total: 366, + major: 0, + large: 140, + general: 0, + low: 226, + }, + { + id: 9, + unit: "中理公司", + total: 130, + major: 0, + large: 0, + general: 61, + low: 69, + }, + { + id: 10, + unit: "餐饮公司", + total: 673, + major: 3, + large: 2, + general: 3, + low: 665, + }, + ], + entries: [ + { + key: "risk-point", + title: "风险点台账", + description: "风险点等级、责任部门和管控措施", + }, + { + key: "risk-identify", + title: "风险辨识单元", + description: "辨识单元、风险事件和管控层级", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "风险点", + dataIndex: "total", + }, + { + title: "重大", + dataIndex: "major", + }, + { + title: "较大", + dataIndex: "large", + }, + { + title: "一般", + dataIndex: "general", + }, + { + title: "低风险", + dataIndex: "low", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff1f10", "#ff7900", "#ffca09", "#4b9eff"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item", confine: true }, + title: { + text: formatValue(total), + subtext: "风险总数", + left: "20.5%", + top: "42%", + textAlign: "center", + textStyle: { color: "#305683", fontSize: 23, fontWeight: 700, lineHeight: 23 }, + subtextStyle: { color: "#6381a5", fontSize: 12, lineHeight: 16 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${formatValue(values[index])} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [54, 92], + center: ["21%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 风险分级统计放大首页独立维护周期、分析内容和业务入口切换。 */ +function RiskStatisticsExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

风险等级结构

+ 2026年 +
+ +
+
+
+

单位风险点数量

+ {scopeName} +
+ ({ name: item.unit, value: item.total }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default RiskStatisticsExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..8b566e8 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/ExpandedPanel.module.less @@ -0,0 +1,56 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: 380px minmax(0, 1fr); + gap: 12px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 12px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, rgba(217, 239, 253, 0.96), rgba(232, 246, 255, 0.92)); + box-shadow: inset 0 1px 3px rgba(49, 122, 202, 0.35); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.pieChart, +.barChart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.js new file mode 100644 index 0000000..6e62afe --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.js @@ -0,0 +1,119 @@ +import * as echarts from "echarts"; +import { useEffect, useRef } from "react"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const riskMetrics = [ + { name: "重大风险", value: 41 }, + { name: "较大风险", value: 8 }, + { name: "一般风险", value: 107 }, + { name: "低风险", value: 564 }, +]; +const unitRows = [ + { id: 1, unit: "六公司", total: 260, major: 6, greater: 0, general: 4, low: 250 }, + { id: 2, unit: "七公司", total: 47, major: 7, greater: 1, general: 23, low: 16 }, + { id: 3, unit: "九公司", total: 29, major: 5, greater: 1, general: 16, low: 7 }, + { id: 4, unit: "杂货公司", total: 78, major: 14, greater: 1, general: 42, low: 21 }, + { id: 5, unit: "铁运公司", total: 81, major: 0, greater: 1, general: 5, low: 75 }, + { id: 6, unit: "流机公司", total: 128, major: 0, greater: 3, general: 14, low: 111 }, + { id: 7, unit: "综服中心", total: 14, major: 0, greater: 1, general: 0, low: 13 }, + { id: 8, unit: "中理公司", total: 7, major: 0, greater: 0, general: 3, low: 4 }, + { id: 9, unit: "餐饮公司", total: 38, major: 0, greater: 0, general: 0, low: 38 }, +]; + +function RiskChart() { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + const total = riskMetrics.reduce((sum, item) => sum + item.value, 0); + chart.setOption({ + color: ["#ff1f10", "#ff7900", "#ffca09", "#4b9eff"], + tooltip: { trigger: "item" }, + title: { + text: String(total), + subtext: "风险总数", + left: "48%", + top: "42%", + textAlign: "center", + textStyle: { color: "#2e75c6", fontSize: 24, fontWeight: 700 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + series: [{ + type: "pie", + radius: [64, 90], + center: ["50%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: riskMetrics, + }], + }); + const resize = () => chart.resize(); + const resizeObserver = new ResizeObserver(resize); + resizeObserver.observe(chartRef.current); + window.addEventListener("resize", resize); + return () => { + resizeObserver.disconnect(); + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, []); + + return
; +} + +function RiskStatisticsPanel({ onExpand }) { + return ( + +
+
+
+
+ {riskMetrics.map(item => ( +
+ {item.name} + {item.value} +
+ ))} +
+
+
+
+ 单位风险分布 + 重大 / 较大 / 一般 / 低风险 +
+
+ + 单位 + 风险点 + 重大 + 较大 + 一般 + 低风险 + + )} + renderRow={row => ( + + {row.unit} + {row.total} + {row.major} + {row.greater} + {row.general} + {row.low} + + )} + /> +
+
+
+
+ ); +} + +export default RiskStatisticsPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.module.less new file mode 100644 index 0000000..1557561 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/RiskStatisticsPanel/index.module.less @@ -0,0 +1,100 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: minmax(150px, 1fr) minmax(0, 1.25fr); + gap: 10px; +} + +.overview { + display: grid; + min-width: 0; + min-height: 0; + grid-template-columns: minmax(0, 1fr) minmax(0, 1.75fr); + gap: 10px; +} + +.metrics { + display: grid; + min-width: 0; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + + > div { + display: grid; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 14px; + } + + strong { + margin-top: 5px; + color: #347fd3; + font-size: 22px; + } + + > div:nth-child(1) strong { + color: #ff2709; + } + + > div:nth-child(2) strong { + color: #ff7900; + } + + > div:nth-child(3) strong { + color: #e8a900; + } +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 8px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); +} + +.chart { + width: 100%; + height: 100%; +} + +.heading { + display: flex; + height: 20px; + align-items: center; + justify-content: space-between; + + strong { + color: #3f5f83; + font-size: 13px; + } + + span { + color: #66809f; + font-size: 14px; + } +} + +.layout > .surface { + padding: 4px 8px 8px; +} + +.table { + height: calc(100% - 20px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.js new file mode 100644 index 0000000..aa7335d --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.js @@ -0,0 +1,14 @@ +import EmergencyManagementPanel from "./EmergencyManagementPanel"; +import styles from "./index.module.less"; +import RiskStatisticsPanel from "./RiskStatisticsPanel"; + +function MiddlePanel({ weather, onExpand }) { + return ( +
+ onExpand("emergency")} /> + onExpand("risk")} /> +
+ ); +} + +export default MiddlePanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.module.less new file mode 100644 index 0000000..b4d245a --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/MiddlePanel/index.module.less @@ -0,0 +1,7 @@ +.column { + display: grid; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(0, 44.6fr) minmax(0, 55.4fr); + gap: 10px; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/ListPanel.js new file mode 100644 index 0000000..82c0065 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/ListPanel.js @@ -0,0 +1,190 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为消防点位与器材接口结果。 +const mockRows = [ + { + id: 1, + code: "XF-1001", + area: "东堆场", + name: "干粉灭火器", + qrCode: "QR-XF-301", + unit: "二公司", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "XF-1002", + area: "西堆场", + name: "消防栓", + qrCode: "QR-XF-302", + unit: "六公司", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "XF-1003", + area: "办公区", + name: "应急照明", + qrCode: "QR-XF-303", + unit: "七公司", + status: "待更换", + date: "2026-08-12", + }, + { + id: 4, + code: "XF-1004", + area: "仓储区", + name: "消防水带", + qrCode: "QR-XF-304", + unit: "九公司", + status: "正常", + date: "2026-08-11", + }, + { + id: 5, + code: "XF-1005", + area: "东堆场", + name: "干粉灭火器", + qrCode: "QR-XF-305", + unit: "二公司", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "XF-1006", + area: "西堆场", + name: "消防栓", + qrCode: "QR-XF-306", + unit: "六公司", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "XF-1007", + area: "办公区", + name: "应急照明", + qrCode: "QR-XF-307", + unit: "七公司", + status: "待更换", + date: "2026-08-08", + }, + { + id: 8, + code: "XF-1008", + area: "仓储区", + name: "消防水带", + qrCode: "QR-XF-308", + unit: "九公司", + status: "正常", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点位与器材独立维护查询、表格、分页和详情状态。 */ +function FireDeviceListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "待更换", name: "待更换" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default FireDeviceListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/RecordDetail.js new file mode 100644 index 0000000..339a697 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireDevice/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点位与器材详情独立维护展示字段。 */ +function FireDeviceRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "点位编号", value: record["code"] }, + { label: "区域", value: record["area"] }, + { label: "器材名称", value: record["name"] }, + { label: "二维码", value: record["qrCode"] }, + { label: "责任单位", value: record["unit"] }, + { label: "设备状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default FireDeviceRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/ListPanel.js new file mode 100644 index 0000000..4680d70 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为消防点检记录接口结果。 +const mockRows = [ + { + id: 1, + code: "DJ-202608-81", + pointCode: "XF-1001", + area: "东堆场", + name: "干粉灭火器", + unit: "二公司", + inspector: "王强", + status: "已检查", + date: "2026-08-14", + }, + { + id: 2, + code: "DJ-202608-82", + pointCode: "XF-1002", + area: "西堆场", + name: "消防栓", + unit: "六公司", + inspector: "李明", + status: "未检查", + date: "2026-08-13", + }, + { + id: 3, + code: "DJ-202608-83", + pointCode: "XF-1003", + area: "办公区", + name: "应急照明", + unit: "七公司", + inspector: "赵宁", + status: "异常", + date: "2026-08-12", + }, + { + id: 4, + code: "DJ-202608-84", + pointCode: "XF-1004", + area: "仓储区", + name: "消防水带", + unit: "九公司", + inspector: "周鹏", + status: "待复核", + date: "2026-08-11", + }, + { + id: 5, + code: "DJ-202608-85", + pointCode: "XF-1005", + area: "东堆场", + name: "干粉灭火器", + unit: "二公司", + inspector: "王强", + status: "已检查", + date: "2026-08-10", + }, + { + id: 6, + code: "DJ-202608-86", + pointCode: "XF-1006", + area: "西堆场", + name: "消防栓", + unit: "六公司", + inspector: "李明", + status: "未检查", + date: "2026-08-09", + }, + { + id: 7, + code: "DJ-202608-87", + pointCode: "XF-1007", + area: "办公区", + name: "应急照明", + unit: "七公司", + inspector: "赵宁", + status: "异常", + date: "2026-08-08", + }, + { + id: 8, + code: "DJ-202608-88", + pointCode: "XF-1008", + area: "仓储区", + name: "消防水带", + unit: "九公司", + inspector: "周鹏", + status: "待复核", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点检记录独立维护查询、表格、分页和详情状态。 */ +function FireRecordListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已检查", name: "已检查" }, { bianma: "未检查", name: "未检查" }, { + bianma: "异常", + name: "异常", + }, { bianma: "待复核", name: "待复核" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "检查日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default FireRecordListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/RecordDetail.js new file mode 100644 index 0000000..e930513 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/Entries/FireRecord/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点检记录详情独立维护展示字段。 */ +function FireRecordRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "点检编号", value: record["code"] }, + { label: "点位编号", value: record["pointCode"] }, + { label: "区域", value: record["area"] }, + { label: "器材名称", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "检查人", value: record["inspector"] }, + { label: "检查结果", value: record["status"], isStatus: true }, + { label: "检查日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default FireRecordRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.js new file mode 100644 index 0000000..0daa7df --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.js @@ -0,0 +1,335 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import FireDeviceListPanel from "./Entries/FireDevice/ListPanel"; +import FireRecordListPanel from "./Entries/FireRecord/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "fire-device": FireDeviceListPanel, + "fire-record": FireRecordListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "消防点检数据", + description: "统计区域、点位、器材总数,以及按月已检查和未检查数量。", + chartType: "bar", + metrics: [ + { + name: "区域总数", + value: 7, + tone: "blue", + }, + { + name: "点位总数", + value: 96, + tone: "blue", + }, + { + name: "器材总数", + value: 636, + tone: "blue", + }, + { + name: "本月检查率", + value: "89%", + tone: "green", + }, + ], + trend: [ + 72, + 74, + 79, + 81, + 84, + 86, + 88, + 90, + 91, + 92, + 91, + 93, + ], + analysisMetrics: [ + { name: "已检查", value: 85 }, + { name: "未检查", value: 11 }, + { name: "异常点位", value: 5 }, + { name: "待复核", value: 3 }, + ], + rows: [ + { + id: 1, + unit: "二公司", + totalPoints: 52, + checked: 48, + unchecked: 4, + abnormal: 1, + }, + { + id: 2, + unit: "六公司", + totalPoints: 60, + checked: 56, + unchecked: 4, + abnormal: 2, + }, + { + id: 3, + unit: "七公司", + totalPoints: 51, + checked: 47, + unchecked: 4, + abnormal: 1, + }, + { + id: 4, + unit: "九公司", + totalPoints: 47, + checked: 43, + unchecked: 4, + abnormal: 1, + }, + { + id: 5, + unit: "杂货公司", + totalPoints: 55, + checked: 50, + unchecked: 5, + abnormal: 2, + }, + ], + entries: [ + { + key: "fire-device", + title: "消防点位与器材", + description: "消防点位、设备和二维码档案", + }, + { + key: "fire-record", + title: "消防点检记录", + description: "按月查看已检查、未检查和异常记录", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "点位总数", + dataIndex: "totalPoints", + }, + { + title: "已检查", + dataIndex: "checked", + }, + { + title: "未检查", + dataIndex: "unchecked", + }, + { + title: "异常点位", + dataIndex: "abnormal", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 消防点检放大首页独立维护周期、分析内容和业务入口切换。 */ +function FireInspectionExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

月度点检率趋势

+ 2026年 +
+ +
+
+
+

点检结果数量

+ {scopeName} +
+ +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default FireInspectionExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.js new file mode 100644 index 0000000..2c05a76 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.js @@ -0,0 +1,44 @@ +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const metrics = [ + { name: "区域总数", value: 28 }, + { name: "点位总数", value: 426 }, + { name: "器材总数", value: "2,541" }, +]; +const checkRows = [ + { name: "已检查", value: 392, percent: 92 }, + { name: "未检查", value: 34, percent: 8, warning: true }, +]; + +function FireInspectionPanel({ onExpand }) { + return ( + +
+
+ {metrics.map(item => ( +
+ {item.name} + {item.value} +
+ ))} +
+
+
+ 消防月度检查 + 92% +
+ {checkRows.map(item => ( +
+ {item.name} +
+ {item.value} +
+ ))} +
+
+
+ ); +} + +export default FireInspectionPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.module.less new file mode 100644 index 0000000..b876d6f --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/FireInspectionPanel/index.module.less @@ -0,0 +1,89 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + grid-template-columns: 1fr 1.25fr; + gap: 8px; +} + +.metrics { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 5px; + + > div { + display: grid; + min-width: 0; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 4px; + color: #2e75c6; + font-size: 21px; + } +} + +.check { + padding: 8px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); +} + +.heading { + display: flex; + align-items: center; + justify-content: space-between; + color: #3f5f83; + font-size: 12px; +} + +.heading span { + font-size: 14px; +} + +.progress { + display: grid; + grid-template-columns: 48px minmax(0, 1fr) 28px; + align-items: center; + gap: 6px; + margin-top: 8px; + color: #66809f; + font-size: 11px; + + > div { + height: 7px; + overflow: hidden; + border-radius: 4px; + background: #c3dff2; + } + + i { + display: block; + height: 100%; + border-radius: 4px; + background: #3d8fe8; + } + + i[data-warning] { + background: #ff9a24; + } + + strong { + color: #315f94; + text-align: right; + } +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js new file mode 100644 index 0000000..d1436aa --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为重点作业台账接口结果。 +const mockRows = [ + { + id: 1, + code: "ZY-202608-51", + name: "卸车机检修作业", + type: "夜间作业", + department: "设备部", + unit: "二公司", + date: "2026-08-14", + status: "进行中", + }, + { + id: 2, + code: "ZY-202608-52", + name: "东港区吊装作业", + type: "三人及以上", + department: "生产部", + unit: "六公司", + date: "2026-08-13", + status: "待开工", + }, + { + id: 3, + code: "ZY-202608-53", + name: "消防系统改造", + type: "四新作业", + department: "工程部", + unit: "七公司", + date: "2026-08-12", + status: "已完成", + }, + { + id: 4, + code: "ZY-202608-54", + name: "皮带机检修作业", + type: "重点作业", + department: "业务部", + unit: "九公司", + date: "2026-08-11", + status: "已归档", + }, + { + id: 5, + code: "ZY-202608-55", + name: "卸车机检修作业", + type: "夜间作业", + department: "设备部", + unit: "二公司", + date: "2026-08-10", + status: "进行中", + }, + { + id: 6, + code: "ZY-202608-56", + name: "东港区吊装作业", + type: "三人及以上", + department: "生产部", + unit: "六公司", + date: "2026-08-09", + status: "待开工", + }, + { + id: 7, + code: "ZY-202608-57", + name: "消防系统改造", + type: "四新作业", + department: "工程部", + unit: "七公司", + date: "2026-08-08", + status: "已完成", + }, + { + id: 8, + code: "ZY-202608-58", + name: "皮带机检修作业", + type: "重点作业", + department: "业务部", + unit: "九公司", + date: "2026-08-07", + status: "已归档", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重点作业台账独立维护查询、表格、分页和详情状态。 */ +function KeyWorkListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "进行中", name: "进行中" }, { bianma: "待开工", name: "待开工" }, { + bianma: "已完成", + name: "已完成", + }, { bianma: "已归档", name: "已归档" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default KeyWorkListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js new file mode 100644 index 0000000..636de73 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重点作业台账详情独立维护展示字段。 */ +function KeyWorkListRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "作业编号", value: record["code"] }, + { label: "重点作业名称", value: record["name"] }, + { label: "作业属性", value: record["type"] }, + { label: "主管部门", value: record["department"] }, + { label: "责任单位", value: record["unit"] }, + { label: "作业时间", value: record["date"] }, + { label: "状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default KeyWorkListRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.js new file mode 100644 index 0000000..8aafd65 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.js @@ -0,0 +1,324 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import KeyWorkListPanel from "./Entries/KeyWorkList/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "key-work-list": KeyWorkListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "重点作业", + description: "统计当天重点作业数量,并展示重点作业名称、作业属性和主管部门。", + chartType: "bar", + metrics: [ + { + name: "重点作业总数", + value: 8, + tone: "blue", + }, + { + name: "夜间作业数", + value: 3, + tone: "green", + }, + { + name: "四新作业数", + value: 2, + tone: "blue", + }, + { + name: "三人及以上作业数", + value: 4, + tone: "orange", + }, + ], + trend: [ + 8, + 12, + 10, + 15, + 18, + 22, + 19, + 24, + 26, + 28, + 31, + 33, + ], + attributeMetrics: [ + { name: "夜间作业", value: 3 }, + { name: "四新作业", value: 2 }, + { name: "三人及以上作业", value: 4 }, + { name: "其他危险作业", value: 2 }, + ], + departmentMetrics: [ + { name: "设备管理部", value: 3 }, + { name: "生产运行部", value: 2 }, + { name: "工程技术部", value: 2 }, + { name: "安全环保部", value: 1 }, + ], + rows: [ + { + id: 1, + name: "东区门机钢丝绳更换", + type: "三人及以上作业", + department: "设备管理部", + }, + { + id: 2, + name: "码头输送机检修", + type: "夜间作业", + department: "生产运行部", + }, + { + id: 3, + name: "新能源装卸设备试验", + type: "四新作业", + department: "工程技术部", + }, + { + id: 4, + name: "堆场高杆灯维修", + type: "高处作业", + department: "设备管理部", + }, + { + id: 5, + name: "港池水下设施检查", + type: "危险作业", + department: "安全环保部", + }, + ], + entries: [ + { + key: "key-work-list", + title: "重点作业台账", + description: "重点作业名称、属性、主管部门和状态", + }, + ], + columns: [ + { + title: "重点作业名称", + dataIndex: "name", + }, + { + title: "重点作业属性", + dataIndex: "type", + }, + { + title: "主管部门", + dataIndex: "department", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 重点作业放大首页独立维护周期、分析内容和业务入口切换。 */ +function KeyWorkExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

重点作业属性分布

+ 2026年8月22日 +
+ +
+
+
+

主管部门重点作业数

+ {scopeName} +
+ +
+
+
+
+

当天重点作业列表

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default KeyWorkExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.js new file mode 100644 index 0000000..f788cef --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.js @@ -0,0 +1,62 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const metrics = [ + { name: "重点作业总数", value: 8 }, + { name: "夜间作业数", value: 3 }, + { name: "四新作业数", value: 2 }, + { name: "三人及以上作业数", value: 4 }, +]; +const rows = [ + { id: 1, name: "堆场消防管线改造作业", type: "动火作业", department: "工程技术部" }, + { id: 2, name: "5号泊位抓斗维护作业", type: "受限空间作业", department: "设备管理部" }, + { id: 3, name: "新能源装卸设备试验", type: "四新作业", department: "工程技术部" }, + { id: 4, name: "堆场高杆灯维修", type: "高处作业", department: "设备管理部" }, + { id: 5, name: "港池水下设施检查", type: "危险作业", department: "安全环保部" }, + { id: 6, name: "装船机夜间保养", type: "夜间作业", department: "生产运行部" }, +]; + +function KeyWorkPanel({ onExpand }) { + return ( + +
+
+ {metrics.map(item => ( +
+ {item.name} + {item.value} +
+ ))} +
+
+ 当天重点作业列表 + 2026年8月19日 +
+
+ + 重点作业名称 + 重点作业属性 + 主管部门 + + )} + renderRow={row => ( + + {row.name} + {row.type} + {row.department} + + )} + /> +
+
+
+ ); +} + +export default KeyWorkPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.module.less new file mode 100644 index 0000000..94e52b2 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/KeyWorkPanel/index.module.less @@ -0,0 +1,59 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: 60px 22px minmax(0, 1fr); + gap: 5px; +} + +.metrics { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 5px; + + > div { + display: grid; + min-width: 0; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 11px; + white-space: nowrap; + } + + strong { + margin-top: 3px; + color: #347fd3; + font-size: 19px; + } +} + +.heading { + display: flex; + align-items: center; + justify-content: space-between; + + strong { + color: #3f5f83; + font-size: 12px; + } + + span { + color: #66809f; + font-size: 14px; + } +} + +.table { + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js new file mode 100644 index 0000000..2ce4f59 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为当前作业项目接口结果。 +const mockRows = [ + { + id: 1, + code: "XM-2026-301", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + startDate: "2026-07-01", + status: "作业中", + date: "2026-08-14", + }, + { + id: 2, + code: "XM-2026-302", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + startDate: "2026-07-15", + status: "待开工", + date: "2026-08-13", + }, + { + id: 3, + code: "XM-2026-303", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + startDate: "2026-08-01", + status: "作业中", + date: "2026-08-12", + }, + { + id: 4, + code: "XM-2026-304", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + startDate: "2026-08-05", + status: "暂停", + date: "2026-08-11", + }, + { + id: 5, + code: "XM-2026-305", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + startDate: "2026-07-01", + status: "作业中", + date: "2026-08-10", + }, + { + id: 6, + code: "XM-2026-306", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + startDate: "2026-07-15", + status: "待开工", + date: "2026-08-09", + }, + { + id: 7, + code: "XM-2026-307", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + startDate: "2026-08-01", + status: "作业中", + date: "2026-08-08", + }, + { + id: 8, + code: "XM-2026-308", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + startDate: "2026-08-05", + status: "暂停", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 当前作业项目独立维护查询、表格、分页和详情状态。 */ +function CurrentProjectListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "作业中", name: "作业中" }, { bianma: "待开工", name: "待开工" }, { + bianma: "暂停", + name: "暂停", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default CurrentProjectListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js new file mode 100644 index 0000000..f1caf40 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 当前作业项目详情独立维护展示字段。 */ +function CurrentProjectRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "项目编号", value: record["code"] }, + { label: "项目名称", value: record["name"] }, + { label: "项目等级", value: record["level"] }, + { label: "属地单位", value: record["unit"] }, + { label: "相关方单位", value: record["contractor"] }, + { label: "开工日期", value: record["startDate"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default CurrentProjectRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js new file mode 100644 index 0000000..ac0648c --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为项目申请接口结果。 +const mockRows = [ + { + id: 1, + code: "SQ-2026-201", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + applicant: "王强", + status: "审批中", + date: "2026-08-14", + }, + { + id: 2, + code: "SQ-2026-202", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + applicant: "李明", + status: "待补充", + date: "2026-08-13", + }, + { + id: 3, + code: "SQ-2026-203", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + applicant: "赵宁", + status: "审核通过", + date: "2026-08-12", + }, + { + id: 4, + code: "SQ-2026-204", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + applicant: "周鹏", + status: "已驳回", + date: "2026-08-11", + }, + { + id: 5, + code: "SQ-2026-205", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + applicant: "王强", + status: "审批中", + date: "2026-08-10", + }, + { + id: 6, + code: "SQ-2026-206", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + applicant: "李明", + status: "待补充", + date: "2026-08-09", + }, + { + id: 7, + code: "SQ-2026-207", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + applicant: "赵宁", + status: "审核通过", + date: "2026-08-08", + }, + { + id: 8, + code: "SQ-2026-208", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + applicant: "周鹏", + status: "已驳回", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 项目申请独立维护查询、表格、分页和详情状态。 */ +function ProjectApplicationListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "审批中", name: "审批中" }, { bianma: "待补充", name: "待补充" }, { + bianma: "审核通过", + name: "审核通过", + }, { bianma: "已驳回", name: "已驳回" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "申请日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default ProjectApplicationListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js new file mode 100644 index 0000000..e194adb --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 项目申请详情独立维护展示字段。 */ +function ProjectApplicationRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "申请编号", value: record["code"] }, + { label: "项目名称", value: record["name"] }, + { label: "项目等级", value: record["level"] }, + { label: "属地单位", value: record["unit"] }, + { label: "相关方单位", value: record["contractor"] }, + { label: "申请人", value: record["applicant"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "申请日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default ProjectApplicationRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js new file mode 100644 index 0000000..7630832 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为资质备案接口结果。 +const mockRows = [ + { + id: 1, + code: "ZZ-2026-101", + name: "华建建设", + type: "施工资质", + certificateNo: "CERT-301", + unit: "二公司", + expiryDate: "2027-06-30", + status: "有效", + date: "2026-08-14", + }, + { + id: 2, + code: "ZZ-2026-102", + name: "河港机械", + type: "设备维保", + certificateNo: "CERT-302", + unit: "六公司", + expiryDate: "2026-12-31", + status: "有效", + date: "2026-08-13", + }, + { + id: 3, + code: "ZZ-2026-103", + name: "星程消防", + type: "消防资质", + certificateNo: "CERT-303", + unit: "七公司", + expiryDate: "2026-09-30", + status: "即将到期", + date: "2026-08-12", + }, + { + id: 4, + code: "ZZ-2026-104", + name: "蓝天环保", + type: "环保资质", + certificateNo: "CERT-304", + unit: "九公司", + expiryDate: "2027-03-31", + status: "待复审", + date: "2026-08-11", + }, + { + id: 5, + code: "ZZ-2026-105", + name: "华建建设", + type: "施工资质", + certificateNo: "CERT-305", + unit: "二公司", + expiryDate: "2027-06-30", + status: "有效", + date: "2026-08-10", + }, + { + id: 6, + code: "ZZ-2026-106", + name: "河港机械", + type: "设备维保", + certificateNo: "CERT-306", + unit: "六公司", + expiryDate: "2026-12-31", + status: "有效", + date: "2026-08-09", + }, + { + id: 7, + code: "ZZ-2026-107", + name: "星程消防", + type: "消防资质", + certificateNo: "CERT-307", + unit: "七公司", + expiryDate: "2026-09-30", + status: "即将到期", + date: "2026-08-08", + }, + { + id: 8, + code: "ZZ-2026-108", + name: "蓝天环保", + type: "环保资质", + certificateNo: "CERT-308", + unit: "九公司", + expiryDate: "2027-03-31", + status: "待复审", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 资质备案独立维护查询、表格、分页和详情状态。 */ +function QualificationListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "有效", name: "有效" }, { bianma: "即将到期", name: "即将到期" }, { + bianma: "待复审", + name: "待复审", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:二公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default QualificationListPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js new file mode 100644 index 0000000..2277c81 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 资质备案详情独立维护展示字段。 */ +function QualificationRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "备案编号", value: record["code"] }, + { label: "相关方单位", value: record["name"] }, + { label: "资质类型", value: record["type"] }, + { label: "证书编号", value: record["certificateNo"] }, + { label: "属地单位", value: record["unit"] }, + { label: "到期日期", value: record["expiryDate"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default QualificationRecordDetail; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js new file mode 100644 index 0000000..5edc915 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js @@ -0,0 +1,344 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import CurrentProjectListPanel from "./Entries/CurrentProject/ListPanel"; +import ProjectApplicationListPanel from "./Entries/ProjectApplication/ListPanel"; +import QualificationListPanel from "./Entries/Qualification/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "qualification": QualificationListPanel, + "project-application": ProjectApplicationListPanel, + "current-project": CurrentProjectListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "相关方管理数据", + description: "以项目为统计维度,查看项目数量、涉及相关方、人员及当前作业项目。", + chartType: "bar", + metrics: [ + { + name: "项目数", + value: 14, + tone: "blue", + }, + { + name: "相关方单位", + value: 21, + tone: "blue", + }, + { + name: "相关方人员", + value: 186, + tone: "blue", + }, + { + name: "当前作业项目", + value: 5, + tone: "green", + }, + ], + trend: [ + 9, + 12, + 14, + 16, + 15, + 18, + 20, + 22, + 24, + 25, + 27, + 29, + ], + rows: [ + { + id: 1, + type: "生产类", + firstUnits: 34, + firstPeople: 569, + secondUnits: 111, + secondPeople: 767, + }, + { + id: 2, + type: "设备类", + firstUnits: 65, + firstPeople: 452, + secondUnits: 161, + secondPeople: 4750, + }, + { + id: 3, + type: "工程类", + firstUnits: 51, + firstPeople: 1254, + secondUnits: 13, + secondPeople: 1420, + }, + { + id: 4, + type: "劳务类", + firstUnits: 24, + firstPeople: 25, + secondUnits: 35, + secondPeople: 2328, + }, + { + id: 5, + type: "卫生环保类", + firstUnits: 32, + firstPeople: 124, + secondUnits: 71, + secondPeople: 382, + }, + ], + entries: [ + { + key: "qualification", + title: "资质备案", + description: "相关方资质准入、复审和预警", + }, + { + key: "project-application", + title: "项目申请", + description: "项目备案、审批和审核记录", + }, + { + key: "current-project", + title: "当前作业项目", + description: "项目等级、属地单位和项目状态", + }, + ], + columns: [ + { + title: "类型", + dataIndex: "type", + }, + { + title: "一级单位", + dataIndex: "firstUnits", + }, + { + title: "一级人数", + dataIndex: "firstPeople", + }, + { + title: "二级单位", + dataIndex: "secondUnits", + }, + { + title: "二级人数", + dataIndex: "secondPeople", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 相关方单位统计放大首页独立维护周期、分析内容和业务入口切换。 */ +function RelatedPartyExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "二公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

一级相关方单位数

+ 2026年 +
+ ({ name: item.type, value: item.firstUnits }))} + trend={data.trend} + /> +
+
+
+

二级相关方单位数

+ {scopeName} +
+ ({ name: item.type, value: item.secondUnits }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default RelatedPartyExpandedPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.js new file mode 100644 index 0000000..e8088d3 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.js @@ -0,0 +1,67 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const metrics = [ + { name: "项目数", value: 74 }, + { name: "相关方单位", value: 771 }, + { name: "相关方人员", value: "22,488" }, + { name: "当前作业项目", value: 12 }, +]; +const rows = [ + { id: 1, type: "设备类", firstUnits: 65, firstPeople: 452, secondUnits: 161, secondPeople: 4750 }, + { id: 2, type: "工程类", firstUnits: 51, firstPeople: 1254, secondUnits: 13, secondPeople: 1420 }, + { id: 3, type: "劳务类", firstUnits: 24, firstPeople: 25, secondUnits: 35, secondPeople: 2328 }, + { id: 4, type: "卫生环保类", firstUnits: 32, firstPeople: 124, secondUnits: 71, secondPeople: 382 }, + { id: 5, type: "物资供应类", firstUnits: 12, firstPeople: 854, secondUnits: 22, secondPeople: 145 }, + { id: 6, type: "其他", firstUnits: 41, firstPeople: 123, secondUnits: 112, secondPeople: 1164 }, + { id: 7, type: "生产类", firstUnits: 34, firstPeople: 569, secondUnits: 111, secondPeople: 767 }, +]; + +function RelatedPartyPanel({ onExpand }) { + return ( + +
+
+ {metrics.map(item => ( +
+ {item.name} + {item.value} +
+ ))} +
+
+ 相关方分类统计 + 一级 / 二级 +
+
+ + 类型 + 一级单位 + 一级人数 + 二级单位 + 二级人数 + + )} + renderRow={row => ( + + {row.type} + {row.firstUnits} + {row.firstPeople} + {row.secondUnits} + {row.secondPeople} + + )} + /> +
+
+
+ ); +} + +export default RelatedPartyPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.module.less new file mode 100644 index 0000000..a6fb012 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/RelatedPartyPanel/index.module.less @@ -0,0 +1,57 @@ +.panel { + height: 100%; +} + +.layout { + display: grid; + height: 100%; + min-height: 0; + grid-template-rows: 62px 24px minmax(0, 1fr); + gap: 6px; +} + +.metrics { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 6px; + + > div { + display: grid; + place-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + text-align: center; + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 4px; + color: #2e75c6; + font-size: 21px; + } +} + +.heading { + display: flex; + align-items: center; + justify-content: space-between; + + strong { + color: #3f5f83; + font-size: 13px; + } + + span { + color: #66809f; + font-size: 14px; + } +} + +.table { + min-height: 0; +} diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.js b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.js new file mode 100644 index 0000000..f5bc04c --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.js @@ -0,0 +1,16 @@ +import FireInspectionPanel from "./FireInspectionPanel"; +import styles from "./index.module.less"; +import KeyWorkPanel from "./KeyWorkPanel"; +import RelatedPartyPanel from "./RelatedPartyPanel"; + +function RightPanel({ onExpand }) { + return ( +
+ onExpand("related")} /> + onExpand("keyWork")} /> + onExpand("fire")} /> +
+ ); +} + +export default RightPanel; diff --git a/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.module.less b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.module.less new file mode 100644 index 0000000..d7c0913 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/components/RightPanel/index.module.less @@ -0,0 +1,7 @@ +.column { + display: grid; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(0, 52.7fr) minmax(0, 27.3fr) minmax(0, 20fr); + gap: 10px; +} diff --git a/src/pages/Container/Statistics/BranchOffice/index.js b/src/pages/Container/Statistics/BranchOffice/index.js new file mode 100644 index 0000000..e20ddb1 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/index.js @@ -0,0 +1,75 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import autoFit from "autofit.js"; +import { useEffect, useState } from "react"; +import background from "~/assets/images/statistics/bg.png"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import ExpandedContent from "~/pages/Container/Statistics/BranchOffice/components/ExpandedContent"; +import LeftPanel from "~/pages/Container/Statistics/BranchOffice/components/LeftPanel"; +import MiddlePanel from "~/pages/Container/Statistics/BranchOffice/components/MiddlePanel"; +import RightPanel from "~/pages/Container/Statistics/BranchOffice/components/RightPanel"; +import ExpandedLayer from "~/pages/Container/Statistics/components/ExpandedLayer"; +import Header from "~/pages/Container/Statistics/components/Header"; +import styles from "./index.module.less"; + +const moduleTitles = { + hidden: "隐患治理数据(2026年)", + inspection: "安全环保检查数据", + emergency: "应急管理", + risk: "风险分级统计", + related: "相关方管理数据", + keyWork: "重点作业", + fire: "消防点检数据", +}; + +function BranchOffice(props) { + const [scope, setScope] = useState("all"); + const [expandedModule, setExpandedModule] = useState(""); + const [weather, setWeather] = useState({}); + + useEffect(() => { + autoFit.init({ dw: 1920, dh: 1080, el: ".statistics_branch_office", resize: true }); + return () => autoFit.off(); + }, []); + + useEffect(() => { + const loadWeather = async () => { + try { + const response = await props.getWeather(); + setWeather(response?.result || {}); + } + catch { + setWeather({}); + } + }; + loadWeather(); + }, []); + + return ( +
+
+
+ + + +
+ setExpandedModule("")} + > + + +
+ ); +} + +export default Connect([NS_BI_STATISTICS], true)(BranchOffice); diff --git a/src/pages/Container/Statistics/BranchOffice/index.module.less b/src/pages/Container/Statistics/BranchOffice/index.module.less new file mode 100644 index 0000000..4ea7e12 --- /dev/null +++ b/src/pages/Container/Statistics/BranchOffice/index.module.less @@ -0,0 +1,108 @@ +.dashboard { + position: relative; + width: 100%; + height: 100%; + min-width: 0; + min-height: 0; + overflow: hidden; + background-position: center; + background-repeat: no-repeat; + background-size: cover; + color: #3b445c; + + * { + box-sizing: border-box; + } + + :global { + .@{ant-prefix} { + &-segmented { + padding: 0; + overflow: hidden; + border: 1px solid #a3ccfa; + border-radius: 4px; + background: #d6ecff; + + &-group { + gap: 0; + } + + &-item { + min-width: 58px; + height: 28px; + border-left: 1px solid #a3ccfa; + border-radius: 0; + color: #3c648d; + + &:first-child { + border-left: 0; + } + + &-label { + min-height: 26px; + padding: 0 12px; + line-height: 26px; + } + + &-selected { + border-radius: 0; + background: #6291fa; + box-shadow: none; + color: #fff; + } + + &:focus-visible { + outline: 2px solid #2e75c6; + outline-offset: -2px; + } + } + + &-item:hover:not(&-item-selected):not(&-item-disabled) { + color: #3c648d !important; + } + + &-item-selected:hover { + color: #fff !important; + } + + &-thumb { + border-radius: 0; + background: #6291fa; + box-shadow: none; + } + + &-sm { + .@{ant-prefix} { + &-segmented-item { + display: flex; + min-width: 38px; + height: 24px; + align-items: center; + } + + &-segmented-item-label { + display: flex; + width: 100%; + height: auto; + min-height: 0; + align-items: center; + justify-content: center; + padding: 0 10px; + line-height: 1; + } + } + } + } + } + } +} + +.content { + display: grid; + height: calc(100% - 86px); + min-width: 0; + min-height: 0; + grid-template-columns: minmax(0, 25.5fr) minmax(0, 43.5fr) minmax(0, 31fr); + gap: 10px; + padding: 0 12px 12px; +} diff --git a/src/pages/Container/Statistics/Supervision/components/ExpandedContent/index.js b/src/pages/Container/Statistics/Supervision/components/ExpandedContent/index.js new file mode 100644 index 0000000..86921c6 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/ExpandedContent/index.js @@ -0,0 +1,44 @@ +import FireSafetyExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel"; +import HiddenDangerExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel"; +import MajorHazardExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel"; +import RiskControlExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel"; +import EmergencyManagementExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel"; +import SafetyEvaluationExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel"; +import SafetyInspectionExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel"; +import FixedColorExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel"; +import KeyWorkExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel"; +import PenaltyStatisticsExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel"; +import RelatedPartyExpandedPanel + from "~/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel"; + +const components = { + risk: RiskControlExpandedPanel, + majorHazard: MajorHazardExpandedPanel, + hidden: HiddenDangerExpandedPanel, + fire: FireSafetyExpandedPanel, + emergency: EmergencyManagementExpandedPanel, + safetyEvaluation: SafetyEvaluationExpandedPanel, + inspection: SafetyInspectionExpandedPanel, + fixedColor: FixedColorExpandedPanel, + keyWork: KeyWorkExpandedPanel, + related: RelatedPartyExpandedPanel, + penalty: PenaltyStatisticsExpandedPanel, +}; + +/** 类似 Map Content,仅根据模块标识分发对应的独立放大内容。 */ +function ExpandedContent({ moduleKey }) { + const Component = components[moduleKey]; + return Component ? : null; +} + +export default ExpandedContent; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/ListPanel.js new file mode 100644 index 0000000..2a47946 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/ListPanel.js @@ -0,0 +1,190 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为消防点位与器材接口结果。 +const mockRows = [ + { + id: 1, + code: "XF-1001", + area: "东堆场", + name: "干粉灭火器", + qrCode: "QR-XF-301", + unit: "二公司", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "XF-1002", + area: "西堆场", + name: "消防栓", + qrCode: "QR-XF-302", + unit: "六公司", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "XF-1003", + area: "办公区", + name: "应急照明", + qrCode: "QR-XF-303", + unit: "七公司", + status: "待更换", + date: "2026-08-12", + }, + { + id: 4, + code: "XF-1004", + area: "仓储区", + name: "消防水带", + qrCode: "QR-XF-304", + unit: "九公司", + status: "正常", + date: "2026-08-11", + }, + { + id: 5, + code: "XF-1005", + area: "东堆场", + name: "干粉灭火器", + qrCode: "QR-XF-305", + unit: "二公司", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "XF-1006", + area: "西堆场", + name: "消防栓", + qrCode: "QR-XF-306", + unit: "六公司", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "XF-1007", + area: "办公区", + name: "应急照明", + qrCode: "QR-XF-307", + unit: "七公司", + status: "待更换", + date: "2026-08-08", + }, + { + id: 8, + code: "XF-1008", + area: "仓储区", + name: "消防水带", + qrCode: "QR-XF-308", + unit: "九公司", + status: "正常", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点位与器材独立维护查询、表格、分页和详情状态。 */ +function FireDeviceListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "待更换", name: "待更换" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default FireDeviceListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/RecordDetail.js new file mode 100644 index 0000000..339a697 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireDevice/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点位与器材详情独立维护展示字段。 */ +function FireDeviceRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "点位编号", value: record["code"] }, + { label: "区域", value: record["area"] }, + { label: "器材名称", value: record["name"] }, + { label: "二维码", value: record["qrCode"] }, + { label: "责任单位", value: record["unit"] }, + { label: "设备状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default FireDeviceRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/ListPanel.js new file mode 100644 index 0000000..32038c6 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为消防点检记录接口结果。 +const mockRows = [ + { + id: 1, + code: "DJ-202608-81", + pointCode: "XF-1001", + area: "东堆场", + name: "干粉灭火器", + unit: "二公司", + inspector: "王强", + status: "已检查", + date: "2026-08-14", + }, + { + id: 2, + code: "DJ-202608-82", + pointCode: "XF-1002", + area: "西堆场", + name: "消防栓", + unit: "六公司", + inspector: "李明", + status: "未检查", + date: "2026-08-13", + }, + { + id: 3, + code: "DJ-202608-83", + pointCode: "XF-1003", + area: "办公区", + name: "应急照明", + unit: "七公司", + inspector: "赵宁", + status: "异常", + date: "2026-08-12", + }, + { + id: 4, + code: "DJ-202608-84", + pointCode: "XF-1004", + area: "仓储区", + name: "消防水带", + unit: "九公司", + inspector: "周鹏", + status: "待复核", + date: "2026-08-11", + }, + { + id: 5, + code: "DJ-202608-85", + pointCode: "XF-1005", + area: "东堆场", + name: "干粉灭火器", + unit: "二公司", + inspector: "王强", + status: "已检查", + date: "2026-08-10", + }, + { + id: 6, + code: "DJ-202608-86", + pointCode: "XF-1006", + area: "西堆场", + name: "消防栓", + unit: "六公司", + inspector: "李明", + status: "未检查", + date: "2026-08-09", + }, + { + id: 7, + code: "DJ-202608-87", + pointCode: "XF-1007", + area: "办公区", + name: "应急照明", + unit: "七公司", + inspector: "赵宁", + status: "异常", + date: "2026-08-08", + }, + { + id: 8, + code: "DJ-202608-88", + pointCode: "XF-1008", + area: "仓储区", + name: "消防水带", + unit: "九公司", + inspector: "周鹏", + status: "待复核", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点检记录独立维护查询、表格、分页和详情状态。 */ +function FireRecordListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已检查", name: "已检查" }, { bianma: "未检查", name: "未检查" }, { + bianma: "异常", + name: "异常", + }, { bianma: "待复核", name: "待复核" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "检查日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default FireRecordListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/RecordDetail.js new file mode 100644 index 0000000..e930513 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/Entries/FireRecord/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 消防点检记录详情独立维护展示字段。 */ +function FireRecordRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "点检编号", value: record["code"] }, + { label: "点位编号", value: record["pointCode"] }, + { label: "区域", value: record["area"] }, + { label: "器材名称", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "检查人", value: record["inspector"] }, + { label: "检查结果", value: record["status"], isStatus: true }, + { label: "检查日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default FireRecordRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.js new file mode 100644 index 0000000..3cb36b2 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.js @@ -0,0 +1,335 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import FireDeviceListPanel from "./Entries/FireDevice/ListPanel"; +import FireRecordListPanel from "./Entries/FireRecord/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "fire-device": FireDeviceListPanel, + "fire-record": FireRecordListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "消防点检数据", + description: "统计区域、点位、器材总数,以及按月已检查和未检查数量。", + chartType: "bar", + metrics: [ + { + name: "区域总数", + value: 28, + tone: "blue", + }, + { + name: "点位总数", + value: 426, + tone: "blue", + }, + { + name: "器材总数", + value: 2541, + tone: "blue", + }, + { + name: "本月检查率", + value: "92%", + tone: "green", + }, + ], + trend: [ + 72, + 74, + 79, + 81, + 84, + 86, + 88, + 90, + 91, + 92, + 91, + 93, + ], + analysisMetrics: [ + { name: "已检查", value: 392 }, + { name: "未检查", value: 34 }, + { name: "异常点位", value: 11 }, + { name: "待复核", value: 8 }, + ], + rows: [ + { + id: 1, + unit: "二公司", + totalPoints: 52, + checked: 48, + unchecked: 4, + abnormal: 1, + }, + { + id: 2, + unit: "六公司", + totalPoints: 60, + checked: 56, + unchecked: 4, + abnormal: 2, + }, + { + id: 3, + unit: "七公司", + totalPoints: 51, + checked: 47, + unchecked: 4, + abnormal: 1, + }, + { + id: 4, + unit: "九公司", + totalPoints: 47, + checked: 43, + unchecked: 4, + abnormal: 1, + }, + { + id: 5, + unit: "杂货公司", + totalPoints: 55, + checked: 50, + unchecked: 5, + abnormal: 2, + }, + ], + entries: [ + { + key: "fire-device", + title: "消防点位与器材", + description: "消防点位、设备和二维码档案", + }, + { + key: "fire-record", + title: "消防点检记录", + description: "按月查看已检查、未检查和异常记录", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "点位总数", + dataIndex: "totalPoints", + }, + { + title: "已检查", + dataIndex: "checked", + }, + { + title: "未检查", + dataIndex: "unchecked", + }, + { + title: "异常点位", + dataIndex: "abnormal", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 消防点检放大首页独立维护周期、分析内容和业务入口切换。 */ +function FireSafetyExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

月度点检率趋势

+ 2026年 +
+ +
+
+
+

点检结果数量

+ {scopeName} +
+ +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default FireSafetyExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.js new file mode 100644 index 0000000..9979f51 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.js @@ -0,0 +1,40 @@ +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护消防点检首屏模拟数据。 +const mockMetrics = [ + { name: "区域总数", value: 28 }, + { name: "点位总数", value: 426 }, + { name: "器材总数", value: 2541 }, +]; +const mockCheck = { checked: 392, unchecked: 34 }; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function FireSafetyPanel({ onExpand }) { + return ( + +
+ {mockMetrics.map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+
+ 已检查 +
+ {formatValue(mockCheck.checked)} +
+
+ 未检查 +
+ {formatValue(mockCheck.unchecked)} +
+
+
+
+ ); +} + +export default FireSafetyPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.module.less new file mode 100644 index 0000000..1a6e354 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel/index.module.less @@ -0,0 +1,56 @@ +.panel { + height: 100%; +} + +.overview { + display: grid; + height: 100%; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.stat, +.check { + min-width: 0; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(224, 241, 255, 0.9); + text-align: center; +} + +.stat { + display: grid; + place-content: center; + + span { + color: #4b668d; + font-size: 14px; + } +} + +.check { + display: grid; + grid-template-columns: repeat(2, 1fr); + align-content: center; + gap: 12px; + padding: 5px; + color: #51719c; + + div { + font-size: 14px; + } + + div:last-child strong { + color: #ff7900; + } +} + +.stat strong, +.check strong { + display: inline-block; + margin-top: 5px; + color: #2e75c6; + font-size: 22px; + line-height: 1; +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js new file mode 100644 index 0000000..b2192c2 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为隐患治理台账接口结果。 +const mockRows = [ + { + id: 1, + code: "YH-202608-101", + name: "消防通道堆放物料", + type: "消防", + unit: "二公司", + owner: "王强", + status: "待整改", + date: "2026-08-14", + }, + { + id: 2, + code: "YH-202608-102", + name: "配电箱门未闭合", + type: "电气", + unit: "六公司", + owner: "李明", + status: "整改中", + date: "2026-08-13", + }, + { + id: 3, + code: "YH-202608-103", + name: "设备护栏松动", + type: "设备", + unit: "七公司", + owner: "赵宁", + status: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "YH-202608-104", + name: "高处作业防护不到位", + type: "作业", + unit: "九公司", + owner: "周鹏", + status: "已整改", + date: "2026-08-11", + }, + { + id: 5, + code: "YH-202608-105", + name: "消防通道堆放物料", + type: "消防", + unit: "二公司", + owner: "王强", + status: "待整改", + date: "2026-08-10", + }, + { + id: 6, + code: "YH-202608-106", + name: "配电箱门未闭合", + type: "电气", + unit: "六公司", + owner: "李明", + status: "整改中", + date: "2026-08-09", + }, + { + id: 7, + code: "YH-202608-107", + name: "设备护栏松动", + type: "设备", + unit: "七公司", + owner: "赵宁", + status: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "YH-202608-108", + name: "高处作业防护不到位", + type: "作业", + unit: "九公司", + owner: "周鹏", + status: "已整改", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 隐患治理台账独立维护查询、表格、分页和详情状态。 */ +function HiddenLedgerListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "待整改", name: "待整改" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }, { bianma: "已整改", name: "已整改" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "发现日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default HiddenLedgerListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js new file mode 100644 index 0000000..21795cf --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/HiddenLedger/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 隐患治理台账详情独立维护展示字段。 */ +function HiddenLedgerRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "隐患编号", value: record["code"] }, + { label: "隐患描述", value: record["name"] }, + { label: "隐患类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任人", value: record["owner"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "发现日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default HiddenLedgerRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js new file mode 100644 index 0000000..011b1ed --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为逾期隐患接口结果。 +const mockRows = [ + { + id: 1, + code: "YQ-202608-51", + name: "消防设施整改逾期", + unit: "二公司", + owner: "张伟", + overdueDays: 3, + date: "2026-08-14", + status: "已逾期", + }, + { + id: 2, + code: "YQ-202608-52", + name: "电气线路整改逾期", + unit: "六公司", + owner: "刘洋", + overdueDays: 7, + date: "2026-08-13", + status: "已逾期", + }, + { + id: 3, + code: "YQ-202608-53", + name: "防护栏整改逾期", + unit: "七公司", + owner: "陈磊", + overdueDays: 12, + date: "2026-08-12", + status: "整改中", + }, + { + id: 4, + code: "YQ-202608-54", + name: "警示标识整改逾期", + unit: "九公司", + owner: "孙浩", + overdueDays: 18, + date: "2026-08-11", + status: "待复核", + }, + { + id: 5, + code: "YQ-202608-55", + name: "消防设施整改逾期", + unit: "二公司", + owner: "张伟", + overdueDays: 3, + date: "2026-08-10", + status: "已逾期", + }, + { + id: 6, + code: "YQ-202608-56", + name: "电气线路整改逾期", + unit: "六公司", + owner: "刘洋", + overdueDays: 7, + date: "2026-08-09", + status: "已逾期", + }, + { + id: 7, + code: "YQ-202608-57", + name: "防护栏整改逾期", + unit: "七公司", + owner: "陈磊", + overdueDays: 12, + date: "2026-08-08", + status: "整改中", + }, + { + id: 8, + code: "YQ-202608-58", + name: "警示标识整改逾期", + unit: "九公司", + owner: "孙浩", + overdueDays: 18, + date: "2026-08-07", + status: "待复核", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 逾期隐患独立维护查询、表格、分页和详情状态。 */ +function OverdueHiddenListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已逾期", name: "已逾期" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待复核", + name: "待复核", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default OverdueHiddenListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js new file mode 100644 index 0000000..b860f6f --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/Entries/OverdueHidden/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 逾期隐患详情独立维护展示字段。 */ +function OverdueHiddenRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "隐患编号", value: record["code"] }, + { label: "隐患描述", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任人", value: record["owner"] }, + { label: "逾期天数", value: record["overdueDays"] }, + { label: "整改期限", value: record["date"] }, + { label: "状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default OverdueHiddenRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js new file mode 100644 index 0000000..4c18f3a --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.js @@ -0,0 +1,347 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import HiddenLedgerListPanel from "./Entries/HiddenLedger/ListPanel"; +import OverdueHiddenListPanel from "./Entries/OverdueHidden/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "hidden-ledger": HiddenLedgerListPanel, + "hidden-overdue": OverdueHiddenListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "隐患排查治理情况", + description: "统计隐患发现、整改、验收和类型分布。", + chartType: "line", + metrics: [ + { + name: "隐患总数", + value: 1398, + tone: "blue", + }, + { + name: "整改数", + value: 1212, + tone: "green", + }, + { + name: "未整改数", + value: 186, + tone: "red", + }, + { + name: "整改率", + value: "87%", + tone: "green", + }, + ], + trend: [ + 8800, + 8900, + 9000, + 9100, + 9200, + 9300, + 9250, + 9350, + 9450, + 9550, + 9650, + 9811, + ], + analysisMetrics: [ + { name: "生产环境类", value: 68710 }, + { name: "生产作业类", value: 12919 }, + { name: "相关方管理类", value: 12361 }, + { name: "消防应急类", value: 10802 }, + { name: "安全管理类", value: 6569 }, + ], + rows: [ + { + id: 1, + port: "秦皇岛港", + total: 93374, + rectified: 90760, + unrectified: 2614, + rectificationRate: "96.85%", + }, + { + id: 2, + port: "曹妃甸港", + total: 15615, + rectified: 15255, + unrectified: 360, + rectificationRate: "97.00%", + }, + { + id: 3, + port: "沧州港区", + total: 2372, + rectified: 2360, + unrectified: 12, + rectificationRate: "98.77%", + }, + ], + entries: [ + { + key: "hidden-ledger", + title: "隐患治理台账", + description: "隐患发现、整改、验收和归档明细", + }, + { + key: "hidden-overdue", + title: "逾期隐患", + description: "按责任单位和逾期天数跟踪", + }, + ], + columns: [ + { + title: "港区", + dataIndex: "port", + }, + { + title: "隐患总数", + dataIndex: "total", + }, + { + title: "整改数", + dataIndex: "rectified", + }, + { + title: "未整改", + dataIndex: "unrectified", + }, + { + title: "整改率", + dataIndex: "rectificationRate", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#f07f7f", "#ffd54d", "#45c8b2", "#65a9e8", "#aa83d6"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item" }, + title: { + text: formatValue(total), + subtext: "隐患总数", + left: "28.5%", + top: "46%", + textAlign: "center", + textVerticalAlign: "middle", + itemGap: 2, + textStyle: { color: "#2e75c6", fontSize: 22, fontWeight: 700 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${formatValue(values[index])} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [48, 76], + center: ["29%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 隐患排查治理放大首页独立维护周期、分析内容和业务入口切换。 */ +function HiddenDangerExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

全年12个月新增隐患趋势

+ 2026年 +
+ +
+
+
+

隐患类型占比

+ {scopeName} +
+ +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default HiddenDangerExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.js new file mode 100644 index 0000000..451f870 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.js @@ -0,0 +1,32 @@ +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +const labels = ["隐患总数", "隐患整改数", "隐患未整改数", "隐患整改率"]; +const tones = ["blue", "green", "red", "yellow"]; +// 当前组件独立维护隐患首屏模拟数据。 +const mockStrips = [ + { name: "股份公司", values: ["1398", "1212", "186", "87%"] }, + { name: "分子公司", values: ["68788", "63974", "4814", "93%"] }, +]; + +function HiddenDangerPanel({ onExpand }) { + return ( + +
+ {mockStrips.map(strip => ( +
+
{strip.name}
+ {strip.values.map((value, index) => ( +
+ {labels[index]} + {value} +
+ ))} +
+ ))} +
+
+ ); +} + +export default HiddenDangerPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.module.less new file mode 100644 index 0000000..e2014cb --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel/index.module.less @@ -0,0 +1,63 @@ +.panel { + height: 100%; +} + +.list { + display: flex; + height: 100%; + flex-direction: column; + justify-content: center; + gap: 7px; +} + +.strip { + display: grid; + min-height: 68px; + grid-template-columns: 82px repeat(4, minmax(0, 1fr)); + align-items: center; + padding: 5px; + border: 1px solid #aacbed; + border-radius: 4px; + background: #cde9ff; +} + +.name { + display: grid; + height: 56px; + place-items: center; + border: 2px solid #c6e6fe; + background: linear-gradient(90deg, #e3f0ff, #c9e7f9); + color: #457fc0; + font-size: 17px; + font-weight: 700; +} + +.item { + min-width: 0; + text-align: center; + + span { + display: block; + color: #4b668d; + font-size: 14px; + } + + strong { + display: block; + margin-top: 4px; + color: #4b9eff; + font-size: 17px; + } + + &[data-tone="green"] strong { + color: #159447; + } + + &[data-tone="red"] strong { + color: #ff2709; + } + + &[data-tone="yellow"] strong { + color: #ffa801; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/ListPanel.js new file mode 100644 index 0000000..488ee38 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/ListPanel.js @@ -0,0 +1,199 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为重大危险源台账接口结果。 +const mockRows = [ + { + id: 1, + code: "WX-2026-11", + name: "东区一号危险源", + level: "二级", + tankCount: 3, + activeCount: 3, + unit: "二公司", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "WX-2026-12", + name: "东区二号危险源", + level: "三级", + tankCount: 4, + activeCount: 4, + unit: "六公司", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "WX-2026-13", + name: "西区一号危险源", + level: "三级", + tankCount: 6, + activeCount: 6, + unit: "七公司", + status: "正常", + date: "2026-08-12", + }, + { + id: 4, + code: "WX-2026-14", + name: "油品储存区", + level: "二级", + tankCount: 2, + activeCount: 2, + unit: "九公司", + status: "待复核", + date: "2026-08-11", + }, + { + id: 5, + code: "WX-2026-15", + name: "东区一号危险源", + level: "二级", + tankCount: 3, + activeCount: 3, + unit: "二公司", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "WX-2026-16", + name: "东区二号危险源", + level: "三级", + tankCount: 4, + activeCount: 4, + unit: "六公司", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "WX-2026-17", + name: "西区一号危险源", + level: "三级", + tankCount: 6, + activeCount: 6, + unit: "七公司", + status: "正常", + date: "2026-08-08", + }, + { + id: 8, + code: "WX-2026-18", + name: "油品储存区", + level: "二级", + tankCount: 2, + activeCount: 2, + unit: "九公司", + status: "待复核", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重大危险源台账独立维护查询、表格、分页和详情状态。 */ +function MajorHazardLedgerListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "待复核", name: "待复核" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default MajorHazardLedgerListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/RecordDetail.js new file mode 100644 index 0000000..723bda7 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardLedger/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重大危险源台账详情独立维护展示字段。 */ +function MajorHazardLedgerRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "危险源编号", value: record["code"] }, + { label: "危险源名称", value: record["name"] }, + { label: "级别", value: record["level"] }, + { label: "储罐数", value: record["tankCount"] }, + { label: "在用数", value: record["activeCount"] }, + { label: "责任单位", value: record["unit"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default MajorHazardLedgerRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/ListPanel.js new file mode 100644 index 0000000..e38d1bd --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为重大危险源报告接口结果。 +const mockRows = [ + { + id: 1, + code: "BG-2026-31", + name: "重大危险源年度报告", + year: "2026", + unit: "二公司", + reporter: "王强", + status: "已报送", + date: "2026-08-14", + }, + { + id: 2, + code: "BG-2026-32", + name: "储罐区专项报告", + year: "2026", + unit: "六公司", + reporter: "李明", + status: "待审核", + date: "2026-08-13", + }, + { + id: 3, + code: "BG-2026-33", + name: "安全评估报告", + year: "2025", + unit: "七公司", + reporter: "赵宁", + status: "已归档", + date: "2026-08-12", + }, + { + id: 4, + code: "BG-2026-34", + name: "备案信息更新报告", + year: "2026", + unit: "九公司", + reporter: "周鹏", + status: "待补充", + date: "2026-08-11", + }, + { + id: 5, + code: "BG-2026-35", + name: "重大危险源年度报告", + year: "2026", + unit: "二公司", + reporter: "王强", + status: "已报送", + date: "2026-08-10", + }, + { + id: 6, + code: "BG-2026-36", + name: "储罐区专项报告", + year: "2026", + unit: "六公司", + reporter: "李明", + status: "待审核", + date: "2026-08-09", + }, + { + id: 7, + code: "BG-2026-37", + name: "安全评估报告", + year: "2025", + unit: "七公司", + reporter: "赵宁", + status: "已归档", + date: "2026-08-08", + }, + { + id: 8, + code: "BG-2026-38", + name: "备案信息更新报告", + year: "2026", + unit: "九公司", + reporter: "周鹏", + status: "待补充", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重大危险源报告独立维护查询、表格、分页和详情状态。 */ +function MajorHazardReportListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已报送", name: "已报送" }, { bianma: "待审核", name: "待审核" }, { + bianma: "已归档", + name: "已归档", + }, { bianma: "待补充", name: "待补充" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "报送日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default MajorHazardReportListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/RecordDetail.js new file mode 100644 index 0000000..ba1d055 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/Entries/MajorHazardReport/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重大危险源报告详情独立维护展示字段。 */ +function MajorHazardReportRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "报告编号", value: record["code"] }, + { label: "报告名称", value: record["name"] }, + { label: "报告年度", value: record["year"] }, + { label: "报送单位", value: record["unit"] }, + { label: "填报人", value: record["reporter"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "报送日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default MajorHazardReportRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.js new file mode 100644 index 0000000..7385c0f --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.js @@ -0,0 +1,325 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import MajorHazardLedgerListPanel from "./Entries/MajorHazardLedger/ListPanel"; +import MajorHazardReportListPanel from "./Entries/MajorHazardReport/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "major-hazard-ledger": MajorHazardLedgerListPanel, + "major-hazard-report": MajorHazardReportListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "重大危险源安全情况", + description: "展示重大危险源等级、储罐状态和温度、压力、液位报警处置情况。", + chartType: "bar", + metrics: [ + { + name: "二级危险源", + value: 1, + tone: "blue", + }, + { + name: "三级危险源", + value: 2, + tone: "blue", + }, + { + name: "在用储罐", + value: 13, + tone: "blue", + }, + { + name: "待处置报警", + value: 0, + tone: "red", + }, + ], + trend: [ + 2, + 3, + 2, + 4, + 3, + 3, + 2, + 2, + 1, + 2, + 1, + 1, + ], + rows: [ + { + id: 1, + unit: "二公司", + hazardCount: 3, + completed: 3, + pending: 0, + completionRate: "100%", + }, + { + id: 2, + unit: "六公司", + hazardCount: 2, + completed: 2, + pending: 0, + completionRate: "100%", + }, + { + id: 3, + unit: "七公司", + hazardCount: 4, + completed: 4, + pending: 0, + completionRate: "100%", + }, + { + id: 4, + unit: "九公司", + hazardCount: 4, + completed: 4, + pending: 0, + completionRate: "100%", + }, + ], + entries: [ + { + key: "major-hazard-ledger", + title: "重大危险源台账", + description: "危险源级别、状态和储罐信息", + }, + { + key: "major-hazard-report", + title: "重大危险源报告", + description: "重大危险源填报与年度报告", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "重大危险源数", + dataIndex: "hazardCount", + }, + { + title: "已完成报告", + dataIndex: "completed", + }, + { + title: "待处理报告", + dataIndex: "pending", + }, + { + title: "完成率", + dataIndex: "completionRate", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 重大危险源放大首页独立维护周期、分析内容和业务入口切换。 */ +function MajorHazardExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

月度重大危险源趋势

+ 2026年 +
+ +
+
+
+

单位重大危险源数

+ {scopeName} +
+ ({ name: item.unit, value: item.hazardCount }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default MajorHazardExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.js new file mode 100644 index 0000000..1ef4b71 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.js @@ -0,0 +1,76 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护重大危险源首屏模拟数据。 +const mockRows = [ + { id: 1, level: "二级", name: "东区一号罐", status: "正常" }, + { id: 2, level: "二级", name: "东区二号罐", status: "正常" }, + { id: 3, level: "二级", name: "东区三号罐", status: "正常" }, + { id: 4, level: "三级", name: "西区一号罐", status: "正常" }, + { id: 5, level: "三级", name: "西区二号罐", status: "正常" }, + { id: 6, level: "三级", name: "西区三号罐", status: "正常" }, + { id: 7, level: "三级", name: "西区四号罐", status: "正常" }, + { id: 8, level: "三级", name: "西区五号罐", status: "正常" }, + { id: 9, level: "三级", name: "西区六号罐", status: "正常" }, + { id: 10, level: "三级", name: "西区七号罐", status: "正常" }, + { id: 11, level: "三级", name: "西区八号罐", status: "正常" }, + { id: 12, level: "三级", name: "西区九号罐", status: "正常" }, + { id: 13, level: "三级", name: "西区十号罐", status: "正常" }, +]; + +function MajorHazardPanel({ onExpand }) { + return ( + +
+
+ 二级重大危险源(1个) +

储罐数:3 在用:3 停用:0

+
+
+ 三级重大危险源(2个) +

储罐数:10 在用:10 停用:0

+
+
+
+ + 级别 + 名称 + 状态 + + )} + renderRow={record => ( + + {record.level} + {record.name} + {record.status} + + )} + /> +
+
+ + {" "} + 当前暂无报警 +
+
+ 温度待处置数/报警个数: + 0/0 +
+ 压力待处置数/报警个数: + 0/0 +
+ 液位待处置数/报警个数: + 0/0 +
+
+
+
+ ); +} + +export default MajorHazardPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.module.less new file mode 100644 index 0000000..5fcd725 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel/index.module.less @@ -0,0 +1,66 @@ +.panel { + height: 100%; +} + +.summary { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + + > div { + padding: 7px 8px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, rgba(217, 239, 253, 0.96), rgba(232, 246, 255, 0.92)); + box-shadow: inset 0 1px 3px rgba(49, 122, 202, 0.35); + text-align: center; + } + + strong { + color: #305683; + } + + p { + margin: 4px 0 0; + color: #51719c; + } +} + +.main { + display: grid; + height: calc(100% - 63px); + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; + margin-top: 6px; +} + +.alert { + min-width: 0; +} + +.alertChip { + overflow: hidden; + padding: 5px 10px; + border: 1px solid #fff; + border-radius: 50px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + color: #38688e; + font-weight: 700; + text-overflow: ellipsis; + white-space: nowrap; + + span { + color: #159447; + } +} + +.alertLines { + margin-top: 8px; + color: #305683; + line-height: 28px; + + strong { + color: #079eef; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/ListPanel.js new file mode 100644 index 0000000..ba3db14 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为风险辨识单元接口结果。 +const mockRows = [ + { + id: 1, + code: "BS-2026-201", + name: "装船机作业单元", + riskEvent: "机械伤害", + level: "重大风险", + unit: "二公司", + department: "生产部", + status: "已辨识", + date: "2026-08-14", + }, + { + id: 2, + code: "BS-2026-202", + name: "储罐装卸单元", + riskEvent: "火灾爆炸", + level: "较大风险", + unit: "六公司", + department: "设备部", + status: "待复核", + date: "2026-08-13", + }, + { + id: 3, + code: "BS-2026-203", + name: "皮带输送单元", + riskEvent: "物体打击", + level: "一般风险", + unit: "七公司", + department: "安全部", + status: "已辨识", + date: "2026-08-12", + }, + { + id: 4, + code: "BS-2026-204", + name: "车辆运输单元", + riskEvent: "车辆伤害", + level: "低风险", + unit: "九公司", + department: "业务部", + status: "已更新", + date: "2026-08-11", + }, + { + id: 5, + code: "BS-2026-205", + name: "装船机作业单元", + riskEvent: "机械伤害", + level: "重大风险", + unit: "二公司", + department: "生产部", + status: "已辨识", + date: "2026-08-10", + }, + { + id: 6, + code: "BS-2026-206", + name: "储罐装卸单元", + riskEvent: "火灾爆炸", + level: "较大风险", + unit: "六公司", + department: "设备部", + status: "待复核", + date: "2026-08-09", + }, + { + id: 7, + code: "BS-2026-207", + name: "皮带输送单元", + riskEvent: "物体打击", + level: "一般风险", + unit: "七公司", + department: "安全部", + status: "已辨识", + date: "2026-08-08", + }, + { + id: 8, + code: "BS-2026-208", + name: "车辆运输单元", + riskEvent: "车辆伤害", + level: "低风险", + unit: "九公司", + department: "业务部", + status: "已更新", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险辨识单元独立维护查询、表格、分页和详情状态。 */ +function RiskIdentifyListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已辨识", name: "已辨识" }, { bianma: "待复核", name: "待复核" }, { + bianma: "已更新", + name: "已更新", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default RiskIdentifyListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/RecordDetail.js new file mode 100644 index 0000000..c8b4ef1 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskIdentify/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险辨识单元详情独立维护展示字段。 */ +function RiskIdentifyRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "辨识单元编号", value: record["code"] }, + { label: "辨识单元", value: record["name"] }, + { label: "风险事件", value: record["riskEvent"] }, + { label: "风险等级", value: record["level"] }, + { label: "责任单位", value: record["unit"] }, + { label: "责任部门", value: record["department"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default RiskIdentifyRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/ListPanel.js new file mode 100644 index 0000000..e6cb07d --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/ListPanel.js @@ -0,0 +1,310 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为风险点台账接口结果。 +const mockRows = [ + { + id: 1, + code: "FXD-2026-101", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "FXD-2026-102", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "FXD-2026-103", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-12", + }, + { + id: 4, + code: "FXD-2026-104", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-11", + }, + { + id: 5, + code: "FXD-2026-105", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "FXD-2026-106", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "FXD-2026-107", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-08", + }, + { + id: 8, + code: "FXD-2026-108", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-07", + }, + { + id: 9, + code: "FXD-2026-109", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-06", + }, + { + id: 10, + code: "FXD-2026-110", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-05", + }, + { + id: 11, + code: "FXD-2026-111", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-08-04", + }, + { + id: 12, + code: "FXD-2026-112", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-08-03", + }, + { + id: 13, + code: "FXD-2026-113", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-08-02", + }, + { + id: 14, + code: "FXD-2026-114", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-08-01", + }, + { + id: 15, + code: "FXD-2026-115", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-07-31", + }, + { + id: 16, + code: "FXD-2026-116", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-07-30", + }, + { + id: 17, + code: "FXD-2026-117", + name: "装卸作业区", + level: "重大风险", + unit: "二公司", + controlLevel: "公司级", + status: "正常", + date: "2026-07-29", + }, + { + id: 18, + code: "FXD-2026-118", + name: "油品储罐区", + level: "较大风险", + unit: "六公司", + controlLevel: "部门级", + status: "正常", + date: "2026-07-28", + }, + { + id: 19, + code: "FXD-2026-119", + name: "设备检修区", + level: "一般风险", + unit: "七公司", + controlLevel: "班组级", + status: "待复核", + date: "2026-07-27", + }, + { + id: 20, + code: "FXD-2026-120", + name: "堆场作业区", + level: "低风险", + unit: "九公司", + controlLevel: "岗位级", + status: "正常", + date: "2026-07-26", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险点台账独立维护查询、表格、分页和详情状态。 */ +function RiskPointListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "待复核", name: "待复核" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + height={500} + disabledResizer + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default RiskPointListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/RecordDetail.js new file mode 100644 index 0000000..9048ecf --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/Entries/RiskPoint/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险点台账详情独立维护展示字段。 */ +function RiskPointRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "风险点编号", value: record["code"] }, + { label: "风险点名称", value: record["name"] }, + { label: "风险等级", value: record["level"] }, + { label: "责任单位", value: record["unit"] }, + { label: "管控层级", value: record["controlLevel"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default RiskPointRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.js new file mode 100644 index 0000000..cc22461 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.js @@ -0,0 +1,412 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import RiskIdentifyListPanel from "./Entries/RiskIdentify/ListPanel"; +import RiskPointListPanel from "./Entries/RiskPoint/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "risk-point": RiskPointListPanel, + "risk-identify": RiskIdentifyListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "风险辨识管控情况", + description: "按重大、较大、一般、低风险统计风险点,并可下钻到辨识单元和风险点台账。", + chartType: "pie", + metrics: [ + { + name: "重大风险", + value: 1467, + tone: "red", + }, + { + name: "较大风险", + value: 1123, + tone: "orange", + }, + { + name: "一般风险", + value: 2183, + tone: "yellow", + }, + { + name: "低风险", + value: 9809, + tone: "blue", + }, + ], + trend: [ + 61, + 66, + 63, + 72, + 70, + 76, + 80, + 78, + 84, + 86, + 89, + 92, + ], + rows: [ + { + id: 1, + unit: "二公司", + total: 826, + major: 322, + large: 0, + general: 0, + low: 504, + }, + { + id: 2, + unit: "六公司", + total: 4642, + major: 214, + large: 0, + general: 81, + low: 4347, + }, + { + id: 3, + unit: "七公司", + total: 1137, + major: 250, + large: 140, + general: 469, + low: 278, + }, + { + id: 4, + unit: "九公司", + total: 765, + major: 178, + large: 140, + general: 326, + low: 121, + }, + { + id: 5, + unit: "杂货公司", + total: 1861, + major: 500, + large: 140, + general: 856, + low: 365, + }, + { + id: 6, + unit: "铁运公司", + total: 1546, + major: 0, + large: 140, + general: 102, + low: 1304, + }, + { + id: 7, + unit: "流机公司", + total: 2636, + major: 0, + large: 421, + general: 285, + low: 1930, + }, + { + id: 8, + unit: "综服中心", + total: 366, + major: 0, + large: 140, + general: 0, + low: 226, + }, + { + id: 9, + unit: "中理公司", + total: 130, + major: 0, + large: 0, + general: 61, + low: 69, + }, + { + id: 10, + unit: "餐饮公司", + total: 673, + major: 3, + large: 2, + general: 3, + low: 665, + }, + ], + entries: [ + { + key: "risk-point", + title: "风险点台账", + description: "风险点等级、责任部门和管控措施", + }, + { + key: "risk-identify", + title: "风险辨识单元", + description: "辨识单元、风险事件和管控层级", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "风险点", + dataIndex: "total", + }, + { + title: "重大", + dataIndex: "major", + }, + { + title: "较大", + dataIndex: "large", + }, + { + title: "一般", + dataIndex: "general", + }, + { + title: "低风险", + dataIndex: "low", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff1f10", "#ff7900", "#ffca09", "#4b9eff"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item", confine: true }, + title: { + text: formatValue(total), + subtext: "风险总数", + left: "20.5%", + top: "42%", + textAlign: "center", + textStyle: { color: "#305683", fontSize: 23, fontWeight: 700, lineHeight: 23 }, + subtextStyle: { color: "#6381a5", fontSize: 12, lineHeight: 16 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${formatValue(values[index])} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [54, 92], + center: ["21%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 风险辨识管控放大首页独立维护周期、分析内容和业务入口切换。 */ +function RiskControlExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

风险等级结构

+ 2026年 +
+ +
+
+
+

单位风险点数量

+ {scopeName} +
+ ({ name: item.unit, value: item.total }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default RiskControlExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..8b566e8 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/ExpandedPanel.module.less @@ -0,0 +1,56 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: 380px minmax(0, 1fr); + gap: 12px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 12px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, rgba(217, 239, 253, 0.96), rgba(232, 246, 255, 0.92)); + box-shadow: inset 0 1px 3px rgba(49, 122, 202, 0.35); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.pieChart, +.barChart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.js new file mode 100644 index 0000000..1db622e --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.js @@ -0,0 +1,40 @@ +import riskValuesBackground from "~/assets/images/statistics/bg1.png"; +import riskImage from "~/assets/images/statistics/img7.png"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护首屏模拟数据,后续可直接替换为风险统计接口结果。 +const mockMetrics = [ + { name: "重大风险", value: 1467, tone: "red" }, + { name: "较大风险", value: 1123, tone: "orange" }, + { name: "一般风险", value: 2183, tone: "yellow" }, + { name: "低风险", value: 9809, tone: "blue" }, +]; +const mockTotal = 14582; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function RiskControlPanel({ onExpand }) { + return ( + +
+
+ 风险等级分布 +
+ {formatValue(mockTotal)} + 风险总数 +
+
+
+ {mockMetrics.map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+
+
+ ); +} + +export default RiskControlPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.module.less new file mode 100644 index 0000000..eaa1f30 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel/index.module.less @@ -0,0 +1,92 @@ +.panel { + height: 100%; + + > [data-panel-content] { + padding: 0; + } +} + +.overview { + display: flex; + width: 100%; + height: 100%; + align-items: center; + padding: 5px; + background: linear-gradient(90deg, #dae9fa 39%, #e2efff); +} + +.donut { + position: relative; + display: grid; + width: 119px; + height: 118px; + flex: 0 0 119px; + place-items: center; + + img { + width: 119px; + height: 118px; + } + + div { + position: absolute; + inset: 0; + display: grid; + align-content: center; + place-items: center; + color: #305683; + } + + strong { + font-size: 16px; + line-height: 1; + } + + span { + margin-top: 2px; + color: #6381a5; + font-size: 10px; + } +} + +.values { + display: grid; + width: calc(100% - 139px); + height: 100%; + grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-rows: repeat(2, minmax(0, 1fr)); + margin-left: 20px; + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.value { + display: grid; + place-content: center; + text-align: center; + + span { + color: #2b4977; + font-size: 14px; + font-weight: 700; + } + + strong { + margin-top: 3px; + color: #4b9eff; + font-size: 24px; + } + + &[data-tone="red"] strong { + color: #ff2709; + } + + &[data-tone="orange"] strong { + color: #ff7900; + } + + &[data-tone="yellow"] strong { + color: #ffa801; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.js b/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.js new file mode 100644 index 0000000..e250fe7 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.js @@ -0,0 +1,19 @@ +import FireSafetyPanel from "~/pages/Container/Statistics/Supervision/components/LeftPanel/FireSafetyPanel"; +import HiddenDangerPanel from "~/pages/Container/Statistics/Supervision/components/LeftPanel/HiddenDangerPanel"; +import MajorHazardPanel from "~/pages/Container/Statistics/Supervision/components/LeftPanel/MajorHazardPanel"; +import RiskControlPanel from "~/pages/Container/Statistics/Supervision/components/LeftPanel/RiskControlPanel"; +import styles from "./index.module.less"; + +/** 左栏仅负责按原型顺序组合四个监管统计模块。 */ +function LeftPanel({ onExpand }) { + return ( +
+ onExpand("risk")} /> + onExpand("majorHazard")} /> + onExpand("hidden")} /> + onExpand("fire")} /> +
+ ); +} + +export default LeftPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.module.less new file mode 100644 index 0000000..eee6696 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/LeftPanel/index.module.less @@ -0,0 +1,23 @@ +.panel { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + gap: 10px; + + > :nth-child(1) { + flex: 0 0 200px; + } + + > :nth-child(2) { + flex: 0 0 270px; + } + + > :nth-child(3) { + flex: 0 0 230px; + } + + > :nth-child(4) { + flex: 1; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js new file mode 100644 index 0000000..8386857 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急演练接口结果。 +const mockRows = [ + { + id: 1, + code: "YL-2026-61", + name: "消防应急演练", + type: "专项", + unit: "二公司", + participants: 86, + status: "已完成", + date: "2026-08-14", + }, + { + id: 2, + code: "YL-2026-62", + name: "防汛应急演练", + type: "综合", + unit: "六公司", + participants: 125, + status: "计划中", + date: "2026-08-13", + }, + { + id: 3, + code: "YL-2026-63", + name: "危化品泄漏演练", + type: "专项", + unit: "七公司", + participants: 64, + status: "已完成", + date: "2026-08-12", + }, + { + id: 4, + code: "YL-2026-64", + name: "人员疏散演练", + type: "现场处置", + unit: "九公司", + participants: 103, + status: "待评估", + date: "2026-08-11", + }, + { + id: 5, + code: "YL-2026-65", + name: "消防应急演练", + type: "专项", + unit: "二公司", + participants: 86, + status: "已完成", + date: "2026-08-10", + }, + { + id: 6, + code: "YL-2026-66", + name: "防汛应急演练", + type: "综合", + unit: "六公司", + participants: 125, + status: "计划中", + date: "2026-08-09", + }, + { + id: 7, + code: "YL-2026-67", + name: "危化品泄漏演练", + type: "专项", + unit: "七公司", + participants: 64, + status: "已完成", + date: "2026-08-08", + }, + { + id: 8, + code: "YL-2026-68", + name: "人员疏散演练", + type: "现场处置", + unit: "九公司", + participants: 103, + status: "待评估", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急演练独立维护查询、表格、分页和详情状态。 */ +function EmergencyDrillListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "计划中", name: "计划中" }, { + bianma: "待评估", + name: "待评估", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "演练日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyDrillListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js new file mode 100644 index 0000000..8bba050 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDrill/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急演练详情独立维护展示字段。 */ +function EmergencyDrillRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "演练编号", value: record["code"] }, + { label: "演练名称", value: record["name"] }, + { label: "演练类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "参演人次", value: record["participants"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "演练日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyDrillRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js new file mode 100644 index 0000000..8094afc --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/ListPanel.js @@ -0,0 +1,184 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急值班接口结果。 +const mockRows = [ + { + id: 1, + date: "2026-08-14", + unit: "二公司", + position: "值班经理", + name: "张三", + phone: "13800138001", + status: "值班中", + }, + { + id: 2, + date: "2026-08-13", + unit: "六公司", + position: "值班主管", + name: "李四", + phone: "13800138002", + status: "待值班", + }, + { + id: 3, + date: "2026-08-12", + unit: "七公司", + position: "安全总监", + name: "王五", + phone: "13800138003", + status: "已交接", + }, + { + id: 4, + date: "2026-08-11", + unit: "九公司", + position: "部门部长", + name: "赵六", + phone: "13800138004", + status: "待交接", + }, + { + id: 5, + date: "2026-08-10", + unit: "二公司", + position: "值班经理", + name: "张三", + phone: "13800138001", + status: "值班中", + }, + { + id: 6, + date: "2026-08-09", + unit: "六公司", + position: "值班主管", + name: "李四", + phone: "13800138002", + status: "待值班", + }, + { + id: 7, + date: "2026-08-08", + unit: "七公司", + position: "安全总监", + name: "王五", + phone: "13800138003", + status: "已交接", + }, + { + id: 8, + date: "2026-08-07", + unit: "九公司", + position: "部门部长", + name: "赵六", + phone: "13800138004", + status: "待交接", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急值班独立维护查询、表格、分页和详情状态。 */ +function EmergencyDutyListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "值班中", name: "值班中" }, { bianma: "待值班", name: "待值班" }, { + bianma: "已交接", + name: "已交接", + }, { bianma: "待交接", name: "待交接" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyDutyListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js new file mode 100644 index 0000000..20ba70b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyDuty/RecordDetail.js @@ -0,0 +1,39 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急值班详情独立维护展示字段。 */ +function EmergencyDutyRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "值班日期", value: record["date"] }, + { label: "责任单位", value: record["unit"] }, + { label: "值班职务", value: record["position"] }, + { label: "值班人员", value: record["name"] }, + { label: "联系电话", value: record["phone"] }, + { label: "交接状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyDutyRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js new file mode 100644 index 0000000..d1a6a46 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/ListPanel.js @@ -0,0 +1,199 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急物资接口结果。 +const mockRows = [ + { + id: 1, + code: "WZ-2026-101", + name: "防汛沙袋", + type: "防汛", + quantity: 320, + location: "东区仓库", + unit: "二公司", + status: "完好", + date: "2026-08-14", + }, + { + id: 2, + code: "WZ-2026-102", + name: "应急照明灯", + type: "照明", + quantity: 48, + location: "西区仓库", + unit: "六公司", + status: "完好", + date: "2026-08-13", + }, + { + id: 3, + code: "WZ-2026-103", + name: "防毒面具", + type: "防护", + quantity: 76, + location: "应急物资库", + unit: "七公司", + status: "待补充", + date: "2026-08-12", + }, + { + id: 4, + code: "WZ-2026-104", + name: "消防水带", + type: "消防", + quantity: 105, + location: "消防器材库", + unit: "九公司", + status: "完好", + date: "2026-08-11", + }, + { + id: 5, + code: "WZ-2026-105", + name: "防汛沙袋", + type: "防汛", + quantity: 320, + location: "东区仓库", + unit: "二公司", + status: "完好", + date: "2026-08-10", + }, + { + id: 6, + code: "WZ-2026-106", + name: "应急照明灯", + type: "照明", + quantity: 48, + location: "西区仓库", + unit: "六公司", + status: "完好", + date: "2026-08-09", + }, + { + id: 7, + code: "WZ-2026-107", + name: "防毒面具", + type: "防护", + quantity: 76, + location: "应急物资库", + unit: "七公司", + status: "待补充", + date: "2026-08-08", + }, + { + id: 8, + code: "WZ-2026-108", + name: "消防水带", + type: "消防", + quantity: 105, + location: "消防器材库", + unit: "九公司", + status: "完好", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急物资独立维护查询、表格、分页和详情状态。 */ +function EmergencyMaterialListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "完好", name: "完好" }, { bianma: "待补充", name: "待补充" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyMaterialListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js new file mode 100644 index 0000000..9217311 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyMaterial/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急物资详情独立维护展示字段。 */ +function EmergencyMaterialRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "物资编号", value: record["code"] }, + { label: "物资名称", value: record["name"] }, + { label: "物资类别", value: record["type"] }, + { label: "数量", value: record["quantity"] }, + { label: "存放位置", value: record["location"] }, + { label: "责任单位", value: record["unit"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyMaterialRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js new file mode 100644 index 0000000..cf10eb4 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/ListPanel.js @@ -0,0 +1,190 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为应急预案接口结果。 +const mockRows = [ + { + id: 1, + code: "YA-2026-41", + name: "综合应急预案", + type: "综合", + unit: "二公司", + version: "V3.0", + status: "有效", + date: "2026-08-14", + }, + { + id: 2, + code: "YA-2026-42", + name: "消防专项预案", + type: "专项", + unit: "六公司", + version: "V2.1", + status: "有效", + date: "2026-08-13", + }, + { + id: 3, + code: "YA-2026-43", + name: "防汛专项预案", + type: "专项", + unit: "七公司", + version: "V1.8", + status: "待更新", + date: "2026-08-12", + }, + { + id: 4, + code: "YA-2026-44", + name: "现场处置方案", + type: "现场处置", + unit: "九公司", + version: "V4.0", + status: "有效", + date: "2026-08-11", + }, + { + id: 5, + code: "YA-2026-45", + name: "综合应急预案", + type: "综合", + unit: "二公司", + version: "V3.0", + status: "有效", + date: "2026-08-10", + }, + { + id: 6, + code: "YA-2026-46", + name: "消防专项预案", + type: "专项", + unit: "六公司", + version: "V2.1", + status: "有效", + date: "2026-08-09", + }, + { + id: 7, + code: "YA-2026-47", + name: "防汛专项预案", + type: "专项", + unit: "七公司", + version: "V1.8", + status: "待更新", + date: "2026-08-08", + }, + { + id: 8, + code: "YA-2026-48", + name: "现场处置方案", + type: "现场处置", + unit: "九公司", + version: "V4.0", + status: "有效", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急预案独立维护查询、表格、分页和详情状态。 */ +function EmergencyPlanListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "有效", name: "有效" }, { bianma: "待更新", name: "待更新" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "发布日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default EmergencyPlanListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js new file mode 100644 index 0000000..b09ca98 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/Entries/EmergencyPlan/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 应急预案详情独立维护展示字段。 */ +function EmergencyPlanRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "预案编号", value: record["code"] }, + { label: "预案名称", value: record["name"] }, + { label: "预案类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "版本", value: record["version"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "发布日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default EmergencyPlanRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js new file mode 100644 index 0000000..2beb7e2 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.js @@ -0,0 +1,378 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import EmergencyDrillListPanel from "./Entries/EmergencyDrill/ListPanel"; +import EmergencyDutyListPanel from "./Entries/EmergencyDuty/ListPanel"; +import EmergencyMaterialListPanel from "./Entries/EmergencyMaterial/ListPanel"; +import EmergencyPlanListPanel from "./Entries/EmergencyPlan/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "emergency-plan": EmergencyPlanListPanel, + "emergency-drill": EmergencyDrillListPanel, + "emergency-material": EmergencyMaterialListPanel, + "emergency-duty": EmergencyDutyListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "应急管理", + description: "汇总综合预案、专项预案、现场处置方案、应急物资、年度演练和应急值班。", + chartType: "bar", + metrics: [ + { + name: "综合预案", + value: 3, + tone: "blue", + }, + { + name: "专项预案", + value: 4, + tone: "blue", + }, + { + name: "现场处置方案", + value: 6, + tone: "blue", + }, + { + name: "应急物资", + value: 1286, + tone: "blue", + }, + { + name: "年度总数", + value: 36, + tone: "blue", + }, + { + name: "已完成数", + value: 31, + tone: "green", + }, + { + name: "参演人次", + value: 1248, + tone: "blue", + }, + ], + trend: [ + 12, + 18, + 20, + 24, + 29, + 34, + 38, + 41, + 45, + 48, + 52, + 56, + ], + rows: [ + { + id: 1, + unit: "二公司", + name: "田旭东", + role: "公司经理", + mobile: "139-3635-6266", + phone: "3568888", + }, + { + id: 2, + unit: "杂货公司", + name: "赵爱勇", + role: "安全总监", + mobile: "133-3330-0099", + phone: "3465654", + }, + { + id: 3, + unit: "七公司", + name: "刘迪", + role: "技设部部长", + mobile: "138-3353-9115", + phone: "3568470", + }, + { + id: 4, + unit: "九公司", + name: "张仙魁", + role: "业务部部长", + mobile: "189-0333-8690", + phone: "6985412", + }, + { id: 5, unit: "综服中心", name: "张文利", role: "安监部部长", mobile: "139-3360-6855", phone: "3896512" }, + { id: 6, unit: "一公司", name: "李强", role: "值班经理", mobile: "138****6201", phone: "3568211" }, + { id: 7, unit: "三公司", name: "王敏", role: "值班主管", mobile: "139****3120", phone: "3568320" }, + { id: 8, unit: "四公司", name: "陈磊", role: "安全部长", mobile: "137****4563", phone: "3568456" }, + { id: 9, unit: "五公司", name: "刘洋", role: "生产部长", mobile: "136****1289", phone: "3568128" }, + { id: 10, unit: "六公司", name: "赵静", role: "安监部部长", mobile: "135****9876", phone: "3568987" }, + { id: 11, unit: "八公司", name: "孙浩", role: "设备部部长", mobile: "186****5314", phone: "3568531" }, + { id: 12, unit: "十公司", name: "周倩", role: "综合部部长", mobile: "187****2468", phone: "3568246" }, + { id: 13, unit: "保税物流中心", name: "马超", role: "运营部部长", mobile: "188****9675", phone: "3568967" }, + ], + entries: [ + { + key: "emergency-plan", + title: "应急预案", + description: "应急预案台账及有效性", + }, + { + key: "emergency-drill", + title: "应急演练", + description: "年度演练计划、完成情况和参演人次", + }, + { + key: "emergency-material", + title: "应急物资", + description: "物资装备台账及完好情况", + }, + { + key: "emergency-duty", + title: "应急值班", + description: "值班排班、日志和交接记录", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "姓名", + dataIndex: "name", + }, + { + title: "职务", + dataIndex: "role", + }, + { + title: "手机", + dataIndex: "mobile", + }, + { + title: "固定电话", + dataIndex: "phone", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 应急管理放大首页独立维护周期、分析内容和业务入口切换。 */ +function EmergencyManagementExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

应急预案与应急物资

+ 2026年 +
+
+ {data.metrics.slice(0, 4).map((item, index) => ( + + ))} +
+
+
+
+

年度演练执行情况

+ {scopeName} +
+
+ {data.metrics.slice(4).map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+ +
+
+
+
+

应急值班明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default EmergencyManagementExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..706113f --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/ExpandedPanel.module.less @@ -0,0 +1,119 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.resourceGrid { + display: grid; + height: calc(100% - 24px); + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 8px; + padding-top: 4px; + + button { + display: flex; + min-width: 0; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + color: #4b668d; + cursor: pointer; + } + + span { + font-size: 14px; + } + + strong { + color: #2e75c6; + font-size: 28px; + } +} + +.analysisSummary { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 2px 0 8px; + + div { + display: flex; + min-height: 48px; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 6px 0; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 1px; + color: #2e75c6; + font-size: 19px; + } +} + +.analysisSummary + .chart { + height: calc(100% - 82px); +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.js new file mode 100644 index 0000000..92417c3 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.js @@ -0,0 +1,120 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import { getWeatherIcon } from "~/utils"; +import styles from "./index.module.less"; + +// 当前组件独立维护应急管理首屏模拟数据,天气仍使用真实接口结果。 +const mockPlans = [ + { name: "综合预案", value: 3 }, + { name: "专项预案", value: 4 }, + { name: "现场处置方案", value: 6 }, + { name: "应急物资", value: 1286 }, +]; +const mockDrills = [ + { name: "年度总数", value: 36 }, + { name: "已完成数", value: 31 }, + { name: "参演人次", value: 1248 }, +]; +const mockDutyRows = [ + { id: 1, unit: "二公司", name: "田旭东", role: "公司经理", mobile: "139-3635-6266", phone: "3568888" }, + { id: 2, unit: "杂货公司", name: "赵爱勇", role: "安全总监", mobile: "133-3330-0099", phone: "3465654" }, + { id: 3, unit: "七公司", name: "刘迪", role: "技设部部长", mobile: "138-3353-9115", phone: "3568470" }, + { id: 4, unit: "九公司", name: "张仙魁", role: "业务部部长", mobile: "189-0333-8690", phone: "6985412" }, + { id: 5, unit: "综服中心", name: "张文利", role: "安监部部长", mobile: "139-3360-6855", phone: "3896512" }, + { id: 6, unit: "一公司", name: "李强", role: "值班经理", mobile: "138****6201", phone: "3568211" }, + { id: 7, unit: "三公司", name: "王敏", role: "值班主管", mobile: "139****3120", phone: "3568320" }, + { id: 8, unit: "四公司", name: "陈磊", role: "安全部长", mobile: "137****4563", phone: "3568456" }, + { id: 9, unit: "五公司", name: "刘洋", role: "生产部长", mobile: "136****1289", phone: "3568128" }, + { id: 10, unit: "六公司", name: "赵静", role: "安监部部长", mobile: "135****9876", phone: "3568987" }, + { id: 11, unit: "八公司", name: "孙浩", role: "设备部部长", mobile: "186****5314", phone: "3568531" }, + { id: 12, unit: "十公司", name: "周倩", role: "综合部部长", mobile: "187****2468", phone: "3568246" }, + { id: 13, unit: "保税物流中心", name: "马超", role: "运营部部长", mobile: "188****9675", phone: "3568967" }, + { id: 14, unit: "港口医院", name: "郑伟", role: "应急办主任", mobile: "186****3721", phone: "3568372" }, + { id: 15, unit: "通信公司", name: "高峰", role: "运行部部长", mobile: "185****4186", phone: "3568418" }, + { id: 16, unit: "建设公司", name: "吴涛", role: "项目部经理", mobile: "183****6052", phone: "3568605" }, + { id: 17, unit: "物流公司", name: "韩雪", role: "调度中心主任", mobile: "182****7314", phone: "3568731" }, + { id: 18, unit: "信息中心", name: "宋杰", role: "技术部部长", mobile: "181****2947", phone: "3568294" }, + { id: 19, unit: "培训中心", name: "梁晨", role: "安全培训主管", mobile: "180****8463", phone: "3568846" }, + { id: 20, unit: "保卫中心", name: "许强", role: "消防保卫部长", mobile: "189****5127", phone: "3568512" }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function EmergencyManagementPanel({ weather, onExpand }) { + const currentWeather = weather?.now || weather || {}; + + return ( + +
+
+
+ {mockPlans.map(item => ( + + ))} +
+
+ {mockDrills.map(item => ( + + ))} +
+
+
+
{weather?.alerts?.[0]?.title || "当前暂无气象预警"}
+
+
+ + {currentWeather.text || "暂无"} +
+
+ 温度 + {currentWeather.temp ? `${currentWeather.temp}℃` : "--"} +
+
+ 风速 + {currentWeather.wind_class || "--"} +
+
+
+
+
+ 应急值班 + + 当日值班单位: + {mockDutyRows.length} + 家 + +
+
+ + 单位 + 姓名 + 职务 + 手机 + 固定电话 + + )} + renderRow={record => ( + + {record.unit} + {record.name} + {record.role} + {record.mobile} + {record.phone} + + )} + /> +
+
+ ); +} + +export default EmergencyManagementPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.module.less new file mode 100644 index 0000000..ca784b4 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel/index.module.less @@ -0,0 +1,133 @@ +.panel { + height: 100%; +} + +.top { + display: grid; + height: 142px; + grid-template-columns: minmax(0, 1fr) 285px; + gap: 10px; +} + +.summary { + display: grid; + min-width: 0; + grid-template-rows: repeat(2, minmax(0, 1fr)); + gap: 6px; +} + +.planRow, +.drillRow { + display: grid; + min-height: 0; + gap: 6px; +} + +.planRow { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.drillRow { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.card { + display: grid; + min-width: 0; + place-items: center; + align-content: center; + gap: 3px; + padding: 4px 5px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, rgba(217, 239, 253, 0.96), rgba(232, 246, 255, 0.92)); + box-shadow: inset 0 1px 3px rgba(49, 122, 202, 0.35); + cursor: pointer; + text-align: center; + + span { + width: 100%; + overflow: hidden; + color: #4b668d; + font-size: 14px; + text-overflow: ellipsis; + white-space: nowrap; + } + + strong { + color: #4387d6; + font-size: 22px; + } +} + +.weather { + display: flex; + min-width: 0; + flex-direction: column; + justify-content: center; + padding: 6px; + border: 1px solid #fff; + border-radius: 2px; + background: linear-gradient(180deg, rgba(217, 239, 253, 0.96), rgba(232, 246, 255, 0.92)); + box-shadow: inset 1px 1.732px 1px rgba(124, 163, 189, 0.3); +} + +.weatherTitle { + overflow: hidden; + color: #ff6600; + font-size: 14px; + font-weight: 700; + line-height: 18px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.weatherMain { + display: flex; + justify-content: space-between; + margin-top: 5px; + color: #2b4977; + font-size: 14px; + font-weight: 700; + + > div { + display: grid; + width: 30%; + align-content: center; + justify-items: center; + text-align: center; + } + + strong { + display: block; + color: #2e75c6; + font-size: 14px; + } +} + +.condition { + grid-template-columns: 40px auto; + align-items: center; + justify-content: center; + gap: 4px; + + img { + width: 40px; + height: 40px; + } +} + +.duty { + display: flex; + height: 24px; + align-items: center; + gap: 22px; + margin: 8px 0 5px; + color: #3b445c; + font-size: 13px; +} + +.table { + height: calc(100% - 179px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/ListPanel.js new file mode 100644 index 0000000..cbda124 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为安全考评录入接口结果。 +const mockRows = [ + { + id: 1, + code: "KP-2026-31", + unit: "二公司", + type: "月度考评", + baseScore: 100, + deduction: 2.5, + reviewer: "王强", + status: "已提交", + date: "2026-08-14", + }, + { + id: 2, + code: "KP-2026-32", + unit: "六公司", + type: "季度考评", + baseScore: 100, + deduction: 4, + reviewer: "李明", + status: "待审核", + date: "2026-08-13", + }, + { + id: 3, + code: "KP-2026-33", + unit: "七公司", + type: "专项考评", + baseScore: 100, + deduction: 1.5, + reviewer: "赵宁", + status: "已通过", + date: "2026-08-12", + }, + { + id: 4, + code: "KP-2026-34", + unit: "九公司", + type: "年度考评", + baseScore: 100, + deduction: 6, + reviewer: "周鹏", + status: "待修改", + date: "2026-08-11", + }, + { + id: 5, + code: "KP-2026-35", + unit: "二公司", + type: "月度考评", + baseScore: 100, + deduction: 2.5, + reviewer: "王强", + status: "已提交", + date: "2026-08-10", + }, + { + id: 6, + code: "KP-2026-36", + unit: "六公司", + type: "季度考评", + baseScore: 100, + deduction: 4, + reviewer: "李明", + status: "待审核", + date: "2026-08-09", + }, + { + id: 7, + code: "KP-2026-37", + unit: "七公司", + type: "专项考评", + baseScore: 100, + deduction: 1.5, + reviewer: "赵宁", + status: "已通过", + date: "2026-08-08", + }, + { + id: 8, + code: "KP-2026-38", + unit: "九公司", + type: "年度考评", + baseScore: 100, + deduction: 6, + reviewer: "周鹏", + status: "待修改", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 安全考评录入独立维护查询、表格、分页和详情状态。 */ +function SafetyScoreInputListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已提交", name: "已提交" }, { bianma: "待审核", name: "待审核" }, { + bianma: "已通过", + name: "已通过", + }, { bianma: "待修改", name: "待修改" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "考评日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default SafetyScoreInputListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/RecordDetail.js new file mode 100644 index 0000000..cba3af3 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreInput/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 安全考评录入详情独立维护展示字段。 */ +function SafetyScoreInputRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "考评编号", value: record["code"] }, + { label: "被考评单位", value: record["unit"] }, + { label: "考评类型", value: record["type"] }, + { label: "基础分", value: record["baseScore"] }, + { label: "扣分", value: record["deduction"] }, + { label: "考评人", value: record["reviewer"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "考评日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default SafetyScoreInputRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/ListPanel.js new file mode 100644 index 0000000..b972da9 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/ListPanel.js @@ -0,0 +1,190 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为安全考评排名接口结果。 +const mockRows = [ + { + id: 1, + rank: 1, + unit: "二公司", + score: 99.6, + change: "↑1", + period: "年度", + status: "已发布", + date: "2026-08-14", + }, + { + id: 2, + rank: 2, + unit: "六公司", + score: 98.8, + change: "持平", + period: "季度", + status: "已发布", + date: "2026-08-13", + }, + { + id: 3, + rank: 3, + unit: "七公司", + score: 98.2, + change: "↓1", + period: "月度", + status: "待发布", + date: "2026-08-12", + }, + { + id: 4, + rank: 4, + unit: "九公司", + score: 97.5, + change: "↑2", + period: "年度", + status: "已发布", + date: "2026-08-11", + }, + { + id: 5, + rank: 5, + unit: "二公司", + score: 99.6, + change: "↑1", + period: "年度", + status: "已发布", + date: "2026-08-10", + }, + { + id: 6, + rank: 6, + unit: "六公司", + score: 98.8, + change: "持平", + period: "季度", + status: "已发布", + date: "2026-08-09", + }, + { + id: 7, + rank: 7, + unit: "七公司", + score: 98.2, + change: "↓1", + period: "月度", + status: "待发布", + date: "2026-08-08", + }, + { + id: 8, + rank: 8, + unit: "九公司", + score: 97.5, + change: "↑2", + period: "年度", + status: "已发布", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 安全考评排名独立维护查询、表格、分页和详情状态。 */ +function SafetyScoreRankingListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已发布", name: "已发布" }, { bianma: "待发布", name: "待发布" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "发布日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default SafetyScoreRankingListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/RecordDetail.js new file mode 100644 index 0000000..e99c2d7 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/Entries/SafetyScoreRanking/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 安全考评排名详情独立维护展示字段。 */ +function SafetyScoreRankingRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "排名", value: record["rank"] }, + { label: "单位", value: record["unit"] }, + { label: "综合得分", value: record["score"] }, + { label: "同比变化", value: record["change"] }, + { label: "考评周期", value: record["period"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "发布日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default SafetyScoreRankingRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.js new file mode 100644 index 0000000..7833a69 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.js @@ -0,0 +1,325 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import SafetyScoreInputListPanel from "./Entries/SafetyScoreInput/ListPanel"; +import SafetyScoreRankingListPanel from "./Entries/SafetyScoreRanking/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "safety-score-input": SafetyScoreInputListPanel, + "safety-score-ranking": SafetyScoreRankingListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "分子公司安全考评", + description: "展示年度、季度评分及分公司排名。", + chartType: "line", + metrics: [ + { + name: "参评单位", + value: 9, + tone: "blue", + }, + { + name: "平均得分", + value: 96.8, + tone: "blue", + }, + { + name: "最高得分", + value: 100, + tone: "green", + }, + { + name: "待录入单位", + value: 1, + tone: "orange", + }, + ], + trend: [ + 88, + 90, + 91, + 92, + 91, + 93, + 94, + 95, + 95, + 96, + 96, + 97, + ], + rows: [ + { + id: 1, + unit: "二公司", + evaluationCount: 12, + completed: 12, + pending: 0, + completionRate: "100%", + }, + { + id: 2, + unit: "六公司", + evaluationCount: 12, + completed: 11, + pending: 1, + completionRate: "91.7%", + }, + { + id: 3, + unit: "七公司", + evaluationCount: 12, + completed: 12, + pending: 0, + completionRate: "100%", + }, + { + id: 4, + unit: "九公司", + evaluationCount: 12, + completed: 11, + pending: 1, + completionRate: "91.7%", + }, + ], + entries: [ + { + key: "safety-score-input", + title: "安全考评录入", + description: "手工维护分公司或部门考评得分", + }, + { + key: "safety-score-ranking", + title: "安全考评排名", + description: "按单位、领域和人员查看排名", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "考评次数", + dataIndex: "evaluationCount", + }, + { + title: "已完成", + dataIndex: "completed", + }, + { + title: "待处理", + dataIndex: "pending", + }, + { + title: "完成率", + dataIndex: "completionRate", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 分子公司安全考评放大首页独立维护周期、分析内容和业务入口切换。 */ +function SafetyEvaluationExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

月度安全考评趋势

+ 2026年 +
+ +
+
+
+

单位安全考评得分

+ {scopeName} +
+ ({ name: item.unit, value: item.completionRate }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default SafetyEvaluationExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.js new file mode 100644 index 0000000..c4630cc --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.js @@ -0,0 +1,86 @@ +import { Segmented } from "antd"; +import { useState } from "react"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护安全考评首屏模拟数据。 +const mockRows = [ + { + id: 1, + unit: "新益公司", + safety: "2", + technology: "0", + engineering: "0", + business: "0", + accident: "0", + score: "99.6", + }, + { id: 2, unit: "二公司", safety: "3", technology: "1", engineering: "0", business: "0", accident: "0", score: "98.0" }, + { + id: 3, + unit: "六公司", + safety: "9", + technology: "0.5", + engineering: "0", + business: "0", + accident: "0", + score: "97.5", + }, + { id: 4, unit: "七公司", safety: "7", technology: "1", engineering: "0", business: "0", accident: "0", score: "98.0" }, + { id: 5, unit: "九公司", safety: "8", technology: "1", engineering: "0", business: "0", accident: "0", score: "98.2" }, + { + id: 6, + unit: "杂货公司", + safety: "2", + technology: "0", + engineering: "0", + business: "0", + accident: "0", + score: "99.2", + }, + { id: 7, unit: "综服中心", safety: "0", technology: "0", engineering: "0", business: "0", accident: "0", score: "100" }, +]; + +function SafetyEvaluationPanel({ onExpand }) { + const [period, setPeriod] = useState("年度"); + + return ( + } + onExpand={onExpand} + className={styles.panel} + > + + 单位 + 安监部扣分 + 技设部扣分 + 工程部扣分 + 业务部扣分 + 事故考核扣分 + 得分 + + )} + renderRow={record => ( + + {record.unit} + {record.safety} + {record.technology} + {record.engineering} + {record.business} + {record.accident} + {record.score} + + )} + scrollLimit={7} + /> + + ); +} + +export default SafetyEvaluationPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.module.less new file mode 100644 index 0000000..4e9adb3 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel/index.module.less @@ -0,0 +1,8 @@ +.panel { + height: 100%; +} + +.period { + margin-right: auto; + margin-left: 8px; +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js new file mode 100644 index 0000000..bd3e470 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为整改验收接口结果。 +const mockRows = [ + { + id: 1, + code: "YS-202608-31", + name: "月度综合检查", + unit: "二公司", + issueCount: 3, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-14", + }, + { + id: 2, + code: "YS-202608-32", + name: "设备专项检查", + unit: "六公司", + issueCount: 6, + status: "整改中", + acceptanceResult: "待验收", + date: "2026-08-13", + }, + { + id: 3, + code: "YS-202608-33", + name: "环保专项检查", + unit: "七公司", + issueCount: 2, + status: "待验收", + acceptanceResult: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "YS-202608-34", + name: "消防安全检查", + unit: "九公司", + issueCount: 1, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-11", + }, + { + id: 5, + code: "YS-202608-35", + name: "月度综合检查", + unit: "二公司", + issueCount: 3, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-10", + }, + { + id: 6, + code: "YS-202608-36", + name: "设备专项检查", + unit: "六公司", + issueCount: 6, + status: "整改中", + acceptanceResult: "待验收", + date: "2026-08-09", + }, + { + id: 7, + code: "YS-202608-37", + name: "环保专项检查", + unit: "七公司", + issueCount: 2, + status: "待验收", + acceptanceResult: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "YS-202608-38", + name: "消防安全检查", + unit: "九公司", + issueCount: 1, + status: "已整改", + acceptanceResult: "通过", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 整改验收独立维护查询、表格、分页和详情状态。 */ +function InspectionAcceptanceListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已整改", name: "已整改" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "验收结果", dataIndex: "acceptanceResult" }, + { title: "验收日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionAcceptanceListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js new file mode 100644 index 0000000..f84538d --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionAcceptance/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 整改验收详情独立维护展示字段。 */ +function InspectionAcceptanceRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "整改编号", value: record["code"] }, + { label: "检查主题", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "问题数", value: record["issueCount"] }, + { label: "整改状态", value: record["status"], isStatus: true }, + { label: "验收结果", value: record["acceptanceResult"] }, + { label: "验收日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionAcceptanceRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js new file mode 100644 index 0000000..8f67721 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为检查计划接口结果。 +const mockRows = [ + { + id: 1, + code: "JH-202608-31", + name: "8月综合检查计划", + unit: "二公司", + period: "月度", + status: "已完成", + completionDate: "2026-08-14", + date: "2026-08-14", + }, + { + id: 2, + code: "JH-202608-32", + name: "设备专项检查计划", + unit: "六公司", + period: "专项", + status: "执行中", + completionDate: "待完成", + date: "2026-08-13", + }, + { + id: 3, + code: "JH-202608-33", + name: "环保专项检查计划", + unit: "七公司", + period: "季度", + status: "未开始", + completionDate: "待完成", + date: "2026-08-12", + }, + { + id: 4, + code: "JH-202608-34", + name: "消防安全检查计划", + unit: "九公司", + period: "专项", + status: "已完成", + completionDate: "2026-08-11", + date: "2026-08-11", + }, + { + id: 5, + code: "JH-202608-35", + name: "8月综合检查计划", + unit: "二公司", + period: "月度", + status: "已完成", + completionDate: "2026-08-14", + date: "2026-08-10", + }, + { + id: 6, + code: "JH-202608-36", + name: "设备专项检查计划", + unit: "六公司", + period: "专项", + status: "执行中", + completionDate: "待完成", + date: "2026-08-09", + }, + { + id: 7, + code: "JH-202608-37", + name: "环保专项检查计划", + unit: "七公司", + period: "季度", + status: "未开始", + completionDate: "待完成", + date: "2026-08-08", + }, + { + id: 8, + code: "JH-202608-38", + name: "消防安全检查计划", + unit: "九公司", + period: "专项", + status: "已完成", + completionDate: "2026-08-11", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查计划独立维护查询、表格、分页和详情状态。 */ +function InspectionPlanListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "执行中", name: "执行中" }, { + bianma: "未开始", + name: "未开始", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "完成日期", dataIndex: "completionDate" }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionPlanListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js new file mode 100644 index 0000000..15571d8 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionPlan/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查计划详情独立维护展示字段。 */ +function InspectionPlanRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "计划编号", value: record["code"] }, + { label: "计划名称", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "计划周期", value: record["period"] }, + { label: "计划状态", value: record["status"], isStatus: true }, + { label: "完成日期", value: record["completionDate"] }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionPlanRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js new file mode 100644 index 0000000..d942ce0 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为检查记录接口结果。 +const mockRows = [ + { + id: 1, + code: "JC-202608-31", + name: "月度综合检查", + unit: "二公司", + planType: "计划内", + issueCount: 3, + status: "已完成", + date: "2026-08-14", + }, + { + id: 2, + code: "JC-202608-32", + name: "设备专项检查", + unit: "六公司", + planType: "计划内", + issueCount: 6, + status: "整改中", + date: "2026-08-13", + }, + { + id: 3, + code: "JC-202608-33", + name: "环保专项检查", + unit: "七公司", + planType: "计划外", + issueCount: 2, + status: "待验收", + date: "2026-08-12", + }, + { + id: 4, + code: "JC-202608-34", + name: "消防安全检查", + unit: "九公司", + planType: "计划内", + issueCount: 0, + status: "已完成", + date: "2026-08-11", + }, + { + id: 5, + code: "JC-202608-35", + name: "月度综合检查", + unit: "二公司", + planType: "计划内", + issueCount: 3, + status: "已完成", + date: "2026-08-10", + }, + { + id: 6, + code: "JC-202608-36", + name: "设备专项检查", + unit: "六公司", + planType: "计划内", + issueCount: 6, + status: "整改中", + date: "2026-08-09", + }, + { + id: 7, + code: "JC-202608-37", + name: "环保专项检查", + unit: "七公司", + planType: "计划外", + issueCount: 2, + status: "待验收", + date: "2026-08-08", + }, + { + id: 8, + code: "JC-202608-38", + name: "消防安全检查", + unit: "九公司", + planType: "计划内", + issueCount: 0, + status: "已完成", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查记录独立维护查询、表格、分页和详情状态。 */ +function InspectionRecordListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已完成", name: "已完成" }, { bianma: "整改中", name: "整改中" }, { + bianma: "待验收", + name: "待验收", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "检查日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default InspectionRecordListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js new file mode 100644 index 0000000..8e744db --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/Entries/InspectionRecord/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 检查记录详情独立维护展示字段。 */ +function InspectionRecordRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "检查编号", value: record["code"] }, + { label: "检查主题", value: record["name"] }, + { label: "责任单位", value: record["unit"] }, + { label: "计划属性", value: record["planType"] }, + { label: "问题数", value: record["issueCount"] }, + { label: "检查状态", value: record["status"], isStatus: true }, + { label: "检查日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default InspectionRecordRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.js new file mode 100644 index 0000000..45d26a8 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.js @@ -0,0 +1,411 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import InspectionAcceptanceListPanel from "./Entries/InspectionAcceptance/ListPanel"; +import InspectionPlanListPanel from "./Entries/InspectionPlan/ListPanel"; +import InspectionRecordListPanel from "./Entries/InspectionRecord/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "inspection-plan": InspectionPlanListPanel, + "inspection-record": InspectionRecordListPanel, + "inspection-acceptance": InspectionAcceptanceListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "安全环保检查数据", + description: "统计计划执行情况、检查总数及计划内外检查。", + chartType: "pie", + metrics: [ + { + name: "计划总数", + value: 326, + tone: "blue", + }, + { + name: "已完成计划数", + value: 281, + tone: "green", + }, + { + name: "检查总数", + value: 568, + tone: "blue", + }, + { + name: "计划内检查数", + value: 492, + tone: "blue", + }, + { + name: "计划外检查数", + value: 76, + tone: "orange", + }, + ], + trend: [ + 44, + 48, + 52, + 49, + 58, + 61, + 65, + 68, + 72, + 74, + 79, + 82, + ], + rows: [ + { + id: 1, + unit: "二公司", + planTotal: 68, + completedPlans: 55, + inspectionTotal: 118, + planned: 91, + unplanned: 27, + }, + { + id: 2, + unit: "六公司", + planTotal: 72, + completedPlans: 64, + inspectionTotal: 126, + planned: 110, + unplanned: 16, + }, + { + id: 3, + unit: "七公司", + planTotal: 61, + completedPlans: 52, + inspectionTotal: 104, + planned: 88, + unplanned: 16, + }, + { + id: 4, + unit: "九公司", + planTotal: 58, + completedPlans: 49, + inspectionTotal: 96, + planned: 82, + unplanned: 14, + }, + ], + entries: [ + { + key: "inspection-plan", + title: "检查计划", + description: "查看计划总数、已完成计划数及执行状态", + }, + { + key: "inspection-record", + title: "检查记录", + description: "查看计划内、计划外检查记录及问题数", + }, + { + key: "inspection-acceptance", + title: "整改验收", + description: "查看检查问题整改状态与验收结果", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "计划总数", + dataIndex: "planTotal", + }, + { + title: "已完成计划数", + dataIndex: "completedPlans", + }, + { + title: "检查总数", + dataIndex: "inspectionTotal", + }, + { + title: "计划内检查数", + dataIndex: "planned", + }, + { + title: "计划外检查数", + dataIndex: "unplanned", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#45c8b2", "#ffb547"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item" }, + title: { + text: total.toLocaleString("zh-CN"), + subtext: "检查总数", + left: "28.5%", + top: "46%", + textAlign: "center", + textVerticalAlign: "middle", + itemGap: 2, + textStyle: { color: "#2e75c6", fontSize: 22 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${values[index].toLocaleString("zh-CN")} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [48, 76], + center: ["29%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 安全环保检查放大首页独立维护周期、分析内容和业务入口切换。 */ +function SafetyInspectionExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + const planTotal = Number(data.metrics[0].value); + const completedPlans = Number(data.metrics[1].value); + const inspectionTotal = Number(data.metrics[2].value); + const planned = Number(data.metrics[3].value); + const unplanned = Number(data.metrics[4].value); + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

计划执行情况

+ 2026年 +
+
+
+ 计划总数 + {formatValue(planTotal)} +
+
+ 已完成计划数 + {formatValue(completedPlans)} +
+
+ 完成率 + {`${(completedPlans / planTotal * 100).toFixed(2)}%`} +
+
+ +
+
+
+

检查执行情况

+ {scopeName} +
+
+
+ 检查总数 + {formatValue(inspectionTotal)} +
+
+ 计划内 / 计划外 + {`${(planned / inspectionTotal * 100).toFixed(2)}% / ${(unplanned / inspectionTotal * 100).toFixed(2)}%`} +
+
+ +
+
+
+
+

单位执行明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default SafetyInspectionExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..abae924 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/ExpandedPanel.module.less @@ -0,0 +1,91 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.analysisSummary { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + margin: 2px 0 8px; + + div { + display: flex; + min-height: 48px; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(217, 239, 253, 0.72); + } + + span { + color: #4b668d; + font-size: 12px; + } + + strong { + margin-top: 3px; + color: #2e75c6; + font-size: 19px; + } +} + +.inspectionSummary { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.analysisSummary + .chart { + height: calc(100% - 82px); +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.js new file mode 100644 index 0000000..8578bca --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.js @@ -0,0 +1,39 @@ +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护安全环保检查首屏模拟数据。 +const mockGroups = [ + { title: "计划执行情况", values: [{ name: "计划总数", value: 326 }, { name: "已完成计划数", value: 281 }] }, + { + title: "检查执行情况", + values: [{ name: "检查总数", value: 568 }, { name: "计划内检查数", value: 492 }, { name: "计划外检查数", value: 76 }], + }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function SafetyInspectionPanel({ onExpand }) { + return ( + +
+ {mockGroups.map((group, groupIndex) => ( +
+
{group.title}
+
+ {group.values.map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+
+ ))} +
+
+ ); +} + +export default SafetyInspectionPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.module.less new file mode 100644 index 0000000..0620218 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel/index.module.less @@ -0,0 +1,58 @@ +.panel { + height: 100%; +} + +.overview { + display: grid; + height: 100%; + grid-template-columns: 1fr 1.45fr; + gap: 10px; +} + +.group { + display: grid; + min-width: 0; + grid-template-rows: auto 1fr; + gap: 6px; +} + +.heading { + color: #3b576a; + font-size: 14px; + font-weight: 700; + line-height: 18px; +} + +.cards { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.checkGroup .cards { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.card { + display: grid; + min-width: 0; + place-content: center; + padding: 8px 4px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(224, 241, 255, 0.9); + text-align: center; + + span { + color: #4b668d; + font-size: 14px; + white-space: nowrap; + } + + strong { + margin-top: 4px; + color: #2e75c6; + font-size: 22px; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.js b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.js new file mode 100644 index 0000000..cd63626 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.js @@ -0,0 +1,20 @@ +import EmergencyManagementPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/EmergencyManagementPanel"; +import SafetyEvaluationPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyEvaluationPanel"; +import SafetyInspectionPanel + from "~/pages/Container/Statistics/Supervision/components/MiddlePanel/SafetyInspectionPanel"; +import styles from "./index.module.less"; + +/** 中栏仅负责按原型顺序组合三个监管统计模块。 */ +function MiddlePanel({ weather, onExpand }) { + return ( +
+ onExpand("emergency")} /> + onExpand("safetyEvaluation")} /> + onExpand("inspection")} /> +
+ ); +} + +export default MiddlePanel; diff --git a/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.module.less new file mode 100644 index 0000000..22b0943 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/MiddlePanel/index.module.less @@ -0,0 +1,19 @@ +.panel { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + gap: 10px; + + > :nth-child(1) { + flex: 0 0 410px; + } + + > :nth-child(2) { + flex: 0 0 320px; + } + + > :nth-child(3) { + flex: 1; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/ListPanel.js new file mode 100644 index 0000000..6d76168 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/ListPanel.js @@ -0,0 +1,199 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为封闭区域接口结果。 +const mockRows = [ + { + id: 1, + code: "FB-2026-21", + name: "东港封闭区", + color: "红", + personCount: 38, + entranceCount: 2, + unit: "二公司", + status: "正常", + date: "2026-08-14", + }, + { + id: 2, + code: "FB-2026-22", + name: "西港封闭区", + color: "橙", + personCount: 52, + entranceCount: 3, + unit: "六公司", + status: "正常", + date: "2026-08-13", + }, + { + id: 3, + code: "FB-2026-23", + name: "仓储封闭区", + color: "黄", + personCount: 27, + entranceCount: 1, + unit: "七公司", + status: "人数预警", + date: "2026-08-12", + }, + { + id: 4, + code: "FB-2026-24", + name: "罐区封闭区", + color: "蓝", + personCount: 44, + entranceCount: 2, + unit: "九公司", + status: "正常", + date: "2026-08-11", + }, + { + id: 5, + code: "FB-2026-25", + name: "东港封闭区", + color: "红", + personCount: 38, + entranceCount: 2, + unit: "二公司", + status: "正常", + date: "2026-08-10", + }, + { + id: 6, + code: "FB-2026-26", + name: "西港封闭区", + color: "橙", + personCount: 52, + entranceCount: 3, + unit: "六公司", + status: "正常", + date: "2026-08-09", + }, + { + id: 7, + code: "FB-2026-27", + name: "仓储封闭区", + color: "黄", + personCount: 27, + entranceCount: 1, + unit: "七公司", + status: "人数预警", + date: "2026-08-08", + }, + { + id: 8, + code: "FB-2026-28", + name: "罐区封闭区", + color: "蓝", + personCount: 44, + entranceCount: 2, + unit: "九公司", + status: "正常", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 封闭区域独立维护查询、表格、分页和详情状态。 */ +function ClosedAreaListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "正常", name: "正常" }, { bianma: "人数预警", name: "人数预警" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default ClosedAreaListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/RecordDetail.js new file mode 100644 index 0000000..a3ba7b3 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/ClosedArea/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 封闭区域详情独立维护展示字段。 */ +function ClosedAreaRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "区域编号", value: record["code"] }, + { label: "区域名称", value: record["name"] }, + { label: "区域颜色", value: record["color"] }, + { label: "当前人数", value: record["personCount"] }, + { label: "入口数量", value: record["entranceCount"] }, + { label: "责任单位", value: record["unit"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default ClosedAreaRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/ListPanel.js new file mode 100644 index 0000000..6473bfa --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/ListPanel.js @@ -0,0 +1,199 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为风险四色图接口结果。 +const mockRows = [ + { + id: 1, + unit: "二公司", + redCount: 12, + orangeCount: 18, + yellowCount: 36, + blueCount: 86, + total: 152, + status: "已更新", + date: "2026-08-14", + }, + { + id: 2, + unit: "六公司", + redCount: 6, + orangeCount: 12, + yellowCount: 28, + blueCount: 72, + total: 118, + status: "已更新", + date: "2026-08-13", + }, + { + id: 3, + unit: "七公司", + redCount: 8, + orangeCount: 15, + yellowCount: 31, + blueCount: 94, + total: 148, + status: "待更新", + date: "2026-08-12", + }, + { + id: 4, + unit: "九公司", + redCount: 5, + orangeCount: 9, + yellowCount: 22, + blueCount: 65, + total: 101, + status: "已更新", + date: "2026-08-11", + }, + { + id: 5, + unit: "二公司", + redCount: 12, + orangeCount: 18, + yellowCount: 36, + blueCount: 86, + total: 152, + status: "已更新", + date: "2026-08-10", + }, + { + id: 6, + unit: "六公司", + redCount: 6, + orangeCount: 12, + yellowCount: 28, + blueCount: 72, + total: 118, + status: "已更新", + date: "2026-08-09", + }, + { + id: 7, + unit: "七公司", + redCount: 8, + orangeCount: 15, + yellowCount: 31, + blueCount: 94, + total: 148, + status: "待更新", + date: "2026-08-08", + }, + { + id: 8, + unit: "九公司", + redCount: 5, + orangeCount: 9, + yellowCount: 22, + blueCount: 65, + total: 101, + status: "已更新", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险四色图独立维护查询、表格、分页和详情状态。 */ +function FourColorChartListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "已更新", name: "已更新" }, { bianma: "待更新", name: "待更新" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default FourColorChartListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/RecordDetail.js new file mode 100644 index 0000000..d728d55 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/Entries/FourColorChart/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 风险四色图详情独立维护展示字段。 */ +function FourColorChartRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "企业名称", value: record["unit"] }, + { label: "红色风险点", value: record["redCount"] }, + { label: "橙色风险点", value: record["orangeCount"] }, + { label: "黄色风险点", value: record["yellowCount"] }, + { label: "蓝色风险点", value: record["blueCount"] }, + { label: "风险点总数", value: record["total"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default FourColorChartRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.js new file mode 100644 index 0000000..d78bbee --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.js @@ -0,0 +1,356 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import ClosedAreaListPanel from "./Entries/ClosedArea/ListPanel"; +import FourColorChartListPanel from "./Entries/FourColorChart/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "four-color-chart": FourColorChartListPanel, + "closed-area": ClosedAreaListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "四色四固定", + description: "展示封闭区域、区域人数及四色风险分布。", + chartType: "pie", + metrics: [ + { + name: "封闭区域公司数", + value: 9, + tone: "blue", + }, + { + name: "封闭区域数", + value: 27, + tone: "blue", + }, + { + name: "当前人数", + value: 270, + tone: "blue", + }, + ], + trend: [ + 19, + 21, + 22, + 24, + 25, + 27, + 27, + 28, + 29, + 31, + 30, + 32, + ], + analysisMetrics: [ + { name: "红色区域", value: 3 }, + { name: "橙色区域", value: 4 }, + { name: "黄色区域", value: 6 }, + { name: "蓝色区域", value: 14 }, + ], + rows: [ + { + id: 1, + unit: "二公司", + red: 5, + orange: 3, + yellow: 6, + blue: 8, + }, + { + id: 2, + unit: "六公司", + red: 4, + orange: 4, + yellow: 7, + blue: 9, + }, + { + id: 3, + unit: "七公司", + red: 3, + orange: 5, + yellow: 5, + blue: 8, + }, + { + id: 4, + unit: "九公司", + red: 4, + orange: 3, + yellow: 6, + blue: 7, + }, + ], + entries: [ + { + key: "four-color-chart", + title: "风险四色图", + description: "企业风险四色分布和动态调整", + }, + { + key: "closed-area", + title: "封闭区域", + description: "封闭区域、口门和人员统计", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "红色区域", + dataIndex: "red", + }, + { + title: "橙色区域", + dataIndex: "orange", + }, + { + title: "黄色区域", + dataIndex: "yellow", + }, + { + title: "蓝色区域", + dataIndex: "blue", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff1f10", "#ff7900", "#ffca09", "#4b9eff"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + const values = metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0); + const total = values.reduce((sum, value) => sum + value, 0); + return { + color: chartColors, + tooltip: { trigger: "item" }, + title: { + text: formatValue(total), + subtext: "封闭区域总数", + left: "28.5%", + top: "46%", + textAlign: "center", + textVerticalAlign: "middle", + itemGap: 2, + textStyle: { color: "#2e75c6", fontSize: 22, fontWeight: 700 }, + subtextStyle: { color: "#66809f", fontSize: 11 }, + }, + legend: { + orient: "horizontal", + left: "43%", + right: "2%", + top: "middle", + itemWidth: 8, + itemHeight: 8, + itemGap: 24, + textStyle: { color: "#4f697b", fontSize: 14, lineHeight: 18 }, + formatter: (name) => { + const index = metrics.findIndex(item => item.name === name); + const ratio = total ? (values[index] / total * 100).toFixed(2) : "0.00"; + return `${name} ${formatValue(values[index])} · ${ratio}%`; + }, + }, + series: [{ + type: "pie", + startAngle: 90, + endAngle: -270, + radius: [48, 76], + center: ["29%", "50%"], + label: { show: false }, + emphasis: { scale: false }, + data: metrics.map((item, index) => ({ name: item.name, value: values[index] })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 四色四固定放大首页独立维护周期、分析内容和业务入口切换。 */ +function FixedColorExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

四色区域结构

+ 2026年 +
+ +
+
+
+

单位封闭区域数

+ {scopeName} +
+ ({ + name: item.unit, + value: item.red + item.orange + item.yellow + item.blue, + }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default FixedColorExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.js new file mode 100644 index 0000000..75db17e --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.js @@ -0,0 +1,31 @@ +import areaIcon from "~/assets/images/statistics/img3.png"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护四色四固定首屏模拟数据。 +const mockMetrics = [ + { name: "封闭区域公司数", value: 9 }, + { name: "封闭区域数", value: 27 }, + { name: "当前人数", value: 270 }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function FixedColorPanel({ onExpand }) { + return ( + +
+ {mockMetrics.map(item => ( +
+ + + {item.name} + {formatValue(item.value)} + +
+ ))} +
+
+ ); +} + +export default FixedColorPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.module.less new file mode 100644 index 0000000..e174abb --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel/index.module.less @@ -0,0 +1,42 @@ +.panel { + height: 100%; +} + +.stats { + display: grid; + height: 100%; + grid-template-columns: repeat(3, minmax(0, 1fr)); + align-items: center; + gap: 10px; +} + +.stat { + display: grid; + min-width: 0; + grid-template-columns: 40px minmax(0, 1fr); + align-items: center; + justify-content: center; + gap: 7px; + color: #3d5882; + text-align: center; + + img { + width: 33px; + height: 40px; + } + + span { + min-width: 0; + overflow: hidden; + font-size: 14px; + text-overflow: ellipsis; + white-space: nowrap; + } + + strong { + display: block; + margin-top: 3px; + color: #347fd3; + font-size: 22px; + } +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/ListPanel.js new file mode 100644 index 0000000..ef5a6c5 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为危险作业管理接口结果。 +const mockRows = [ + { + id: 1, + code: "WXY-202608-71", + name: "动火作业", + type: "一级动火", + unit: "二公司", + applicant: "王强", + approver: "张伟", + status: "作业中", + date: "2026-08-14", + }, + { + id: 2, + code: "WXY-202608-72", + name: "受限空间作业", + type: "受限空间", + unit: "六公司", + applicant: "李明", + approver: "刘洋", + status: "待审批", + date: "2026-08-13", + }, + { + id: 3, + code: "WXY-202608-73", + name: "高处作业", + type: "二级高处", + unit: "七公司", + applicant: "赵宁", + approver: "陈磊", + status: "已归档", + date: "2026-08-12", + }, + { + id: 4, + code: "WXY-202608-74", + name: "吊装作业", + type: "大型吊装", + unit: "九公司", + applicant: "周鹏", + approver: "孙浩", + status: "待开工", + date: "2026-08-11", + }, + { + id: 5, + code: "WXY-202608-75", + name: "动火作业", + type: "一级动火", + unit: "二公司", + applicant: "王强", + approver: "张伟", + status: "作业中", + date: "2026-08-10", + }, + { + id: 6, + code: "WXY-202608-76", + name: "受限空间作业", + type: "受限空间", + unit: "六公司", + applicant: "李明", + approver: "刘洋", + status: "待审批", + date: "2026-08-09", + }, + { + id: 7, + code: "WXY-202608-77", + name: "高处作业", + type: "二级高处", + unit: "七公司", + applicant: "赵宁", + approver: "陈磊", + status: "已归档", + date: "2026-08-08", + }, + { + id: 8, + code: "WXY-202608-78", + name: "吊装作业", + type: "大型吊装", + unit: "九公司", + applicant: "周鹏", + approver: "孙浩", + status: "待开工", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 危险作业管理独立维护查询、表格、分页和详情状态。 */ +function DangerousWorkListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "作业中", name: "作业中" }, { bianma: "待审批", name: "待审批" }, { + bianma: "已归档", + name: "已归档", + }, { bianma: "待开工", name: "待开工" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "作业日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default DangerousWorkListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/RecordDetail.js new file mode 100644 index 0000000..062e6ba --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/DangerousWork/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 危险作业管理详情独立维护展示字段。 */ +function DangerousWorkRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "作业票编号", value: record["code"] }, + { label: "危险作业名称", value: record["name"] }, + { label: "作业类型", value: record["type"] }, + { label: "责任单位", value: record["unit"] }, + { label: "申请人", value: record["applicant"] }, + { label: "审批人", value: record["approver"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "作业日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default DangerousWorkRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js new file mode 100644 index 0000000..e4ef970 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/ListPanel.js @@ -0,0 +1,193 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为重点作业台账接口结果。 +const mockRows = [ + { + id: 1, + code: "ZY-202608-51", + name: "卸车机检修作业", + type: "夜间作业", + department: "设备部", + unit: "二公司", + date: "2026-08-14", + status: "进行中", + }, + { + id: 2, + code: "ZY-202608-52", + name: "东港区吊装作业", + type: "三人及以上", + department: "生产部", + unit: "六公司", + date: "2026-08-13", + status: "待开工", + }, + { + id: 3, + code: "ZY-202608-53", + name: "消防系统改造", + type: "四新作业", + department: "工程部", + unit: "七公司", + date: "2026-08-12", + status: "已完成", + }, + { + id: 4, + code: "ZY-202608-54", + name: "皮带机检修作业", + type: "重点作业", + department: "业务部", + unit: "九公司", + date: "2026-08-11", + status: "已归档", + }, + { + id: 5, + code: "ZY-202608-55", + name: "卸车机检修作业", + type: "夜间作业", + department: "设备部", + unit: "二公司", + date: "2026-08-10", + status: "进行中", + }, + { + id: 6, + code: "ZY-202608-56", + name: "东港区吊装作业", + type: "三人及以上", + department: "生产部", + unit: "六公司", + date: "2026-08-09", + status: "待开工", + }, + { + id: 7, + code: "ZY-202608-57", + name: "消防系统改造", + type: "四新作业", + department: "工程部", + unit: "七公司", + date: "2026-08-08", + status: "已完成", + }, + { + id: 8, + code: "ZY-202608-58", + name: "皮带机检修作业", + type: "重点作业", + department: "业务部", + unit: "九公司", + date: "2026-08-07", + status: "已归档", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重点作业台账独立维护查询、表格、分页和详情状态。 */ +function KeyWorkListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "进行中", name: "进行中" }, { bianma: "待开工", name: "待开工" }, { + bianma: "已完成", + name: "已完成", + }, { bianma: "已归档", name: "已归档" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default KeyWorkListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js new file mode 100644 index 0000000..636de73 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/Entries/KeyWorkList/RecordDetail.js @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 重点作业台账详情独立维护展示字段。 */ +function KeyWorkListRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "作业编号", value: record["code"] }, + { label: "重点作业名称", value: record["name"] }, + { label: "作业属性", value: record["type"] }, + { label: "主管部门", value: record["department"] }, + { label: "责任单位", value: record["unit"] }, + { label: "作业时间", value: record["date"] }, + { label: "状态", value: record["status"], isStatus: true }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default KeyWorkListRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.js new file mode 100644 index 0000000..42a4a2b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.js @@ -0,0 +1,319 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import DangerousWorkListPanel from "./Entries/DangerousWork/ListPanel"; +import KeyWorkListPanel from "./Entries/KeyWorkList/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "key-work-list": KeyWorkListPanel, + "dangerous-work": DangerousWorkListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "重点作业", + description: "统计当天重点作业数量,并展示重点作业名称、作业属性和主管部门。", + chartType: "bar", + metrics: [ + { + name: "作业总数", + value: 220, + tone: "blue", + }, + { + name: "进行中", + value: 164, + tone: "green", + }, + { + name: "已归档", + value: 56, + tone: "blue", + }, + { + name: "夜间作业", + value: 96, + tone: "orange", + }, + ], + trend: [ + 8, + 12, + 10, + 15, + 18, + 22, + 19, + 24, + 26, + 28, + 31, + 33, + ], + rows: [ + { + id: 1, + unit: "二公司", + keyWorkStarts: 63, + dangerousWorkStarts: 58, + }, + { + id: 2, + unit: "六公司", + keyWorkStarts: 27, + dangerousWorkStarts: 46, + }, + { + id: 3, + unit: "七公司", + keyWorkStarts: 144, + dangerousWorkStarts: 114, + }, + { + id: 4, + unit: "九公司", + keyWorkStarts: 69, + dangerousWorkStarts: 37, + }, + { + id: 5, + unit: "杂货公司", + keyWorkStarts: 111, + dangerousWorkStarts: 3, + }, + ], + entries: [ + { + key: "key-work-list", + title: "重点作业台账", + description: "重点作业名称、属性、主管部门和状态", + }, + { + key: "dangerous-work", + title: "危险作业管理", + description: "危险作业审批和作业状态", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "重点作业开工数", + dataIndex: "keyWorkStarts", + }, + { + title: "危险作业开工数", + dataIndex: "dangerousWorkStarts", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 重点作业放大首页独立维护周期、分析内容和业务入口切换。 */ +function KeyWorkExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

重点作业开工数

+ 2026年 +
+ ({ name: item.unit, value: item.keyWorkStarts }))} + trend={data.trend} + /> +
+
+
+

危险作业开工数

+ {scopeName} +
+ ({ name: item.unit, value: item.dangerousWorkStarts }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default KeyWorkExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.js new file mode 100644 index 0000000..f3c26c4 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.js @@ -0,0 +1,58 @@ +import keyWorkBackground from "~/assets/images/statistics/bg5.png"; +import dangerWorkBackground from "~/assets/images/statistics/bg6.png"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护重点作业首屏模拟数据。 +const mockGroups = [ + { + title: "重点作业\n情况统计", + values: [ + { name: "作业总数", value: 450 }, + { name: "夜间作业", value: 96 }, + { name: "四新作业", value: 42 }, + { name: "三人及以上", value: 118 }, + ], + }, + { + title: "危险作业\n情况统计", + values: [ + { name: "作业总数", value: 450 }, + { name: "进行中数", value: 164 }, + { name: "已归档数", value: 286 }, + ], + orange: true, + }, +]; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function KeyWorkPanel({ onExpand }) { + return ( + +
+ {mockGroups.map(group => ( +
+
{group.title}
+
+ {group.values.map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+
+ ))} +
+
+ ); +} + +export default KeyWorkPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.module.less new file mode 100644 index 0000000..c8c2aa8 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel/index.module.less @@ -0,0 +1,55 @@ +.panel { + height: 100%; +} + +.list { + display: flex; + height: 100%; + flex-direction: column; + gap: 5px; +} + +.strip { + display: grid; + min-height: 0; + flex: 1; + grid-template-columns: 90px minmax(0, 1fr); + align-items: center; + padding: 4px 8px; + border-radius: 4px; + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.title { + color: #3b576a; + font-weight: 700; + text-align: center; + white-space: pre-line; +} + +.values { + display: grid; + gap: 4px; + text-align: center; + + span { + display: block; + padding-top: 4px; + color: #3b576a; + font-size: 12px; + font-weight: 700; + } + + strong { + display: block; + padding-bottom: 4px; + color: #347fd3; + font-size: 16px; + } +} + +.orange .values strong { + color: #ff9000; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/ListPanel.js new file mode 100644 index 0000000..98435e5 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为处罚管理接口结果。 +const mockRows = [ + { + id: 1, + code: "CF-2026-81", + name: "华建建设", + type: "现场违章", + count: 1, + amount: 3000, + unit: "二公司", + status: "待反馈", + date: "2026-08-14", + }, + { + id: 2, + code: "CF-2026-82", + name: "河港机械", + type: "资质过期", + count: 2, + amount: 4200, + unit: "六公司", + status: "已处理", + date: "2026-08-13", + }, + { + id: 3, + code: "CF-2026-83", + name: "星程消防", + type: "安全措施不足", + count: 1, + amount: 5400, + unit: "七公司", + status: "已处理", + date: "2026-08-12", + }, + { + id: 4, + code: "CF-2026-84", + name: "蓝天环保", + type: "进度逾期", + count: 3, + amount: 6600, + unit: "九公司", + status: "待整改", + date: "2026-08-11", + }, + { + id: 5, + code: "CF-2026-85", + name: "华建建设", + type: "现场违章", + count: 1, + amount: 3000, + unit: "二公司", + status: "待反馈", + date: "2026-08-10", + }, + { + id: 6, + code: "CF-2026-86", + name: "河港机械", + type: "资质过期", + count: 2, + amount: 4200, + unit: "六公司", + status: "已处理", + date: "2026-08-09", + }, + { + id: 7, + code: "CF-2026-87", + name: "星程消防", + type: "安全措施不足", + count: 1, + amount: 5400, + unit: "七公司", + status: "已处理", + date: "2026-08-08", + }, + { + id: 8, + code: "CF-2026-88", + name: "蓝天环保", + type: "进度逾期", + count: 3, + amount: 6600, + unit: "九公司", + status: "待整改", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 处罚管理独立维护查询、表格、分页和详情状态。 */ +function PunishmentListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "待反馈", name: "待反馈" }, { bianma: "已处理", name: "已处理" }, { + bianma: "待整改", + name: "待整改", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "处罚日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default PunishmentListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/RecordDetail.js new file mode 100644 index 0000000..2fc636c --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/Entries/PunishmentList/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 处罚管理详情独立维护展示字段。 */ +function PunishmentListRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "处罚编号", value: record["code"] }, + { label: "相关方单位", value: record["name"] }, + { label: "处罚项目", value: record["type"] }, + { label: "处罚次数", value: record["count"] }, + { label: "处罚金额", value: record["amount"] }, + { label: "责任单位", value: record["unit"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "处罚日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default PunishmentListRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.js new file mode 100644 index 0000000..2cd5f17 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.js @@ -0,0 +1,318 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import PunishmentListPanel from "./Entries/PunishmentList/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "punishment-list": PunishmentListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "相关方处罚统计表", + description: "按相关方单位统计处罚项目、处罚次数和处罚金额。", + chartType: "line", + metrics: [ + { + name: "处罚单位", + value: 3, + tone: "blue", + }, + { + name: "处罚项目", + value: 5, + tone: "blue", + }, + { + name: "处罚次数", + value: 8, + tone: "orange", + }, + { + name: "处罚金额", + value: 35000, + tone: "red", + }, + ], + trend: [ + 1, + 2, + 1, + 3, + 2, + 4, + 2, + 3, + 4, + 5, + 4, + 5, + ], + rows: [ + { + id: 1, + unit: "华建建设", + penaltyCount: 3, + processed: 3, + pending: 0, + processingRate: "100%", + }, + { + id: 2, + unit: "河港机械", + penaltyCount: 2, + processed: 1, + pending: 1, + processingRate: "50%", + }, + { + id: 3, + unit: "星程消防", + penaltyCount: 2, + processed: 2, + pending: 0, + processingRate: "100%", + }, + { + id: 4, + unit: "蓝天环保", + penaltyCount: 1, + processed: 1, + pending: 0, + processingRate: "100%", + }, + ], + entries: [ + { + key: "punishment-list", + title: "处罚管理", + description: "相关方处罚、处理和反馈记录", + }, + ], + columns: [ + { + title: "单位", + dataIndex: "unit", + }, + { + title: "处罚次数", + dataIndex: "penaltyCount", + }, + { + title: "已处理", + dataIndex: "processed", + }, + { + title: "待反馈", + dataIndex: "pending", + }, + { + title: "处理率", + dataIndex: "processingRate", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 相关方处罚统计放大首页独立维护周期、分析内容和业务入口切换。 */ +function PenaltyStatisticsExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

月度处罚次数趋势

+ 2026年 +
+ +
+
+
+

单位处罚次数

+ {scopeName} +
+ ({ name: item.unit, value: item.penaltyCount }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default PenaltyStatisticsExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.js new file mode 100644 index 0000000..6a43308 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.js @@ -0,0 +1,51 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护相关方处罚首屏模拟数据。 +const mockRows = [ + { id: 1, unit: "方宇物业", projectCount: 2, penaltyCount: 3, amount: 1500 }, + { id: 2, unit: "河港机械", projectCount: 1, penaltyCount: 2, amount: 2000 }, + { id: 3, unit: "星程消防", projectCount: 0, penaltyCount: 0, amount: 0 }, + { id: 4, unit: "蓝天环保", projectCount: 1, penaltyCount: 1, amount: 8000 }, +]; +const mockSummary = { projectCount: 4, penaltyCount: 6, amount: 11500 }; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function PenaltyStatisticsPanel({ onExpand }) { + return ( + + + 相关方单位名称 + 被处罚项目数 + 处罚次数 + 处罚金额 + + )} + renderRow={record => ( + + {record.unit} + {formatValue(record.projectCount)} + {formatValue(record.penaltyCount)} + {formatValue(record.amount)} + + )} + summary={( + + 合计 + {formatValue(mockSummary.projectCount)} + {formatValue(mockSummary.penaltyCount)} + {formatValue(mockSummary.amount)} + + )} + scrollLimit={4} + /> + + ); +} + +export default PenaltyStatisticsPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.module.less new file mode 100644 index 0000000..89e2575 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel/index.module.less @@ -0,0 +1,3 @@ +.panel { + height: 100%; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js new file mode 100644 index 0000000..bf4d7ad --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为当前作业项目接口结果。 +const mockRows = [ + { + id: 1, + code: "XM-2026-301", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + startDate: "2026-07-01", + status: "作业中", + date: "2026-08-14", + }, + { + id: 2, + code: "XM-2026-302", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + startDate: "2026-07-15", + status: "待开工", + date: "2026-08-13", + }, + { + id: 3, + code: "XM-2026-303", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + startDate: "2026-08-01", + status: "作业中", + date: "2026-08-12", + }, + { + id: 4, + code: "XM-2026-304", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + startDate: "2026-08-05", + status: "暂停", + date: "2026-08-11", + }, + { + id: 5, + code: "XM-2026-305", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + startDate: "2026-07-01", + status: "作业中", + date: "2026-08-10", + }, + { + id: 6, + code: "XM-2026-306", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + startDate: "2026-07-15", + status: "待开工", + date: "2026-08-09", + }, + { + id: 7, + code: "XM-2026-307", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + startDate: "2026-08-01", + status: "作业中", + date: "2026-08-08", + }, + { + id: 8, + code: "XM-2026-308", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + startDate: "2026-08-05", + status: "暂停", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 当前作业项目独立维护查询、表格、分页和详情状态。 */ +function CurrentProjectListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "作业中", name: "作业中" }, { bianma: "待开工", name: "待开工" }, { + bianma: "暂停", + name: "暂停", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default CurrentProjectListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js new file mode 100644 index 0000000..f1caf40 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/CurrentProject/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 当前作业项目详情独立维护展示字段。 */ +function CurrentProjectRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "项目编号", value: record["code"] }, + { label: "项目名称", value: record["name"] }, + { label: "项目等级", value: record["level"] }, + { label: "属地单位", value: record["unit"] }, + { label: "相关方单位", value: record["contractor"] }, + { label: "开工日期", value: record["startDate"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default CurrentProjectRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js new file mode 100644 index 0000000..2d39591 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为项目申请接口结果。 +const mockRows = [ + { + id: 1, + code: "SQ-2026-201", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + applicant: "王强", + status: "审批中", + date: "2026-08-14", + }, + { + id: 2, + code: "SQ-2026-202", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + applicant: "李明", + status: "待补充", + date: "2026-08-13", + }, + { + id: 3, + code: "SQ-2026-203", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + applicant: "赵宁", + status: "审核通过", + date: "2026-08-12", + }, + { + id: 4, + code: "SQ-2026-204", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + applicant: "周鹏", + status: "已驳回", + date: "2026-08-11", + }, + { + id: 5, + code: "SQ-2026-205", + name: "卸车流程改造", + level: "一级", + unit: "二公司", + contractor: "华建建设", + applicant: "王强", + status: "审批中", + date: "2026-08-10", + }, + { + id: 6, + code: "SQ-2026-206", + name: "东港区设备检修", + level: "二级", + unit: "六公司", + contractor: "河港机械", + applicant: "李明", + status: "待补充", + date: "2026-08-09", + }, + { + id: 7, + code: "SQ-2026-207", + name: "堆场消防升级", + level: "一级", + unit: "七公司", + contractor: "星程消防", + applicant: "赵宁", + status: "审核通过", + date: "2026-08-08", + }, + { + id: 8, + code: "SQ-2026-208", + name: "仓库照明改造", + level: "二级", + unit: "九公司", + contractor: "蓝天环保", + applicant: "周鹏", + status: "已驳回", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 项目申请独立维护查询、表格、分页和详情状态。 */ +function ProjectApplicationListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "审批中", name: "审批中" }, { bianma: "待补充", name: "待补充" }, { + bianma: "审核通过", + name: "审核通过", + }, { bianma: "已驳回", name: "已驳回" }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "申请日期", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default ProjectApplicationListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js new file mode 100644 index 0000000..e194adb --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/ProjectApplication/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 项目申请详情独立维护展示字段。 */ +function ProjectApplicationRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "申请编号", value: record["code"] }, + { label: "项目名称", value: record["name"] }, + { label: "项目等级", value: record["level"] }, + { label: "属地单位", value: record["unit"] }, + { label: "相关方单位", value: record["contractor"] }, + { label: "申请人", value: record["applicant"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "申请日期", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default ProjectApplicationRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js new file mode 100644 index 0000000..4185e90 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/ListPanel.js @@ -0,0 +1,202 @@ +import { Tag } from "antd"; +import { useState } from "react"; +import Search from "zy-react-library/components/Search"; +import Table from "zy-react-library/components/Table"; +import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender"; +import ListLayout from "~/pages/Container/Statistics/components/ListLayout"; +import RecordDetail from "./RecordDetail"; + +// 本入口独立维护模拟数据,后续可直接替换为资质备案接口结果。 +const mockRows = [ + { + id: 1, + code: "ZZ-2026-101", + name: "华建建设", + type: "施工资质", + certificateNo: "CERT-301", + unit: "二公司", + expiryDate: "2027-06-30", + status: "有效", + date: "2026-08-14", + }, + { + id: 2, + code: "ZZ-2026-102", + name: "河港机械", + type: "设备维保", + certificateNo: "CERT-302", + unit: "六公司", + expiryDate: "2026-12-31", + status: "有效", + date: "2026-08-13", + }, + { + id: 3, + code: "ZZ-2026-103", + name: "星程消防", + type: "消防资质", + certificateNo: "CERT-303", + unit: "七公司", + expiryDate: "2026-09-30", + status: "即将到期", + date: "2026-08-12", + }, + { + id: 4, + code: "ZZ-2026-104", + name: "蓝天环保", + type: "环保资质", + certificateNo: "CERT-304", + unit: "九公司", + expiryDate: "2027-03-31", + status: "待复审", + date: "2026-08-11", + }, + { + id: 5, + code: "ZZ-2026-105", + name: "华建建设", + type: "施工资质", + certificateNo: "CERT-305", + unit: "二公司", + expiryDate: "2027-06-30", + status: "有效", + date: "2026-08-10", + }, + { + id: 6, + code: "ZZ-2026-106", + name: "河港机械", + type: "设备维保", + certificateNo: "CERT-306", + unit: "六公司", + expiryDate: "2026-12-31", + status: "有效", + date: "2026-08-09", + }, + { + id: 7, + code: "ZZ-2026-107", + name: "星程消防", + type: "消防资质", + certificateNo: "CERT-307", + unit: "七公司", + expiryDate: "2026-09-30", + status: "即将到期", + date: "2026-08-08", + }, + { + id: 8, + code: "ZZ-2026-108", + name: "蓝天环保", + type: "环保资质", + certificateNo: "CERT-308", + unit: "九公司", + expiryDate: "2027-03-31", + status: "待复审", + date: "2026-08-07", + }, +]; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 资质备案独立维护查询、表格、分页和详情状态。 */ +function QualificationListPanel({ onBack }) { + const [form] = Search.useForm(); + const [filters, setFilters] = useState({}); + const [page, setPage] = useState(1); + const [record, setRecord] = useState(null); + const filteredRows = mockRows.filter((item) => { + const keywordMatched = !filters.keyword + || Object.values(item).some(value => String(value).includes(filters.keyword)); + const [startDateValue, endDateValue] = filters.dateRange || []; + const startDate = startDateValue?.format?.("YYYY-MM-DD") || startDateValue; + const endDate = endDateValue?.format?.("YYYY-MM-DD") || endDateValue; + return keywordMatched + && (!filters.status || item.status === filters.status) + && (!filters.unit || item.unit === filters.unit) + && (!startDate || item.date >= startDate) + && (!endDate || item.date <= endDate); + }); + + return ( + <> + { + setFilters(values); + setPage(1); + }} + options={[ + { name: "keyword", label: "关键词", render: FORM_ITEM_RENDER_ENUM.INPUT }, + { + name: "status", + label: "状态", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "有效", name: "有效" }, { bianma: "即将到期", name: "即将到期" }, { + bianma: "待复审", + name: "待复审", + }], + }, + { + name: "unit", + label: "责任单位", + render: FORM_ITEM_RENDER_ENUM.SELECT, + items: [{ bianma: "二公司", name: "二公司" }, { bianma: "六公司", name: "六公司" }, { + bianma: "七公司", + name: "七公司", + }, { bianma: "九公司", name: "九公司" }], + }, + { name: "dateRange", label: "日期范围", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE }, + ]} + /> + )} + tableContent={( +
{recordItem.status}, + }, + { title: "更新时间", dataIndex: "date" }, + ]} + dataSource={filteredRows} + options={false} + disabledResizer + height={500} + pagination={{ + current: page, + pageSize: 20, + showSizeChanger: false, + showTotal: total => `共 ${total} 条`, + onChange: setPage, + }} + onRow={recordItem => ({ onClick: () => setRecord(recordItem) })} + /> + )} + footerContent="当前统计范围:股份公司" + /> + {record && setRecord(null)} />} + + ); +} + +export default QualificationListPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js new file mode 100644 index 0000000..2277c81 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/Entries/Qualification/RecordDetail.js @@ -0,0 +1,41 @@ +import { Tag } from "antd"; +import DetailDialog from "~/pages/Container/Statistics/components/DetailDialog"; + +function statusColor(status) { + if (/完成|正常|有效|归档|通过|已处理|已检查|已整改|已辨识|已更新|已报送|已发布|已提交|已交接|完好/.test(status)) + return "success"; + if (/异常|逾期|报警|驳回|暂停/.test(status)) + return "error"; + return "warning"; +} + +/** 资质备案详情独立维护展示字段。 */ +function QualificationRecordDetail({ record, onClose }) { + const detailItems = [ + { label: "备案编号", value: record["code"] }, + { label: "相关方单位", value: record["name"] }, + { label: "资质类型", value: record["type"] }, + { label: "证书编号", value: record["certificateNo"] }, + { label: "属地单位", value: record["unit"] }, + { label: "到期日期", value: record["expiryDate"] }, + { label: "状态", value: record["status"], isStatus: true }, + { label: "更新时间", value: record["date"] }, + ]; + + return ( + + {detailItems.map(item => ( +
+ {item.label} + + {item.isStatus + ? {item.value} + : item.value} + +
+ ))} +
+ ); +} + +export default QualificationRecordDetail; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js new file mode 100644 index 0000000..56b433a --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.js @@ -0,0 +1,344 @@ +import { Segmented } from "antd"; +import * as echarts from "echarts"; +import { useEffect, useRef, useState } from "react"; +import ExpandedLayout from "~/pages/Container/Statistics/components/ExpandedLayout"; +import ListDialog from "~/pages/Container/Statistics/components/ListDialog"; +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import CurrentProjectListPanel from "./Entries/CurrentProject/ListPanel"; +import ProjectApplicationListPanel from "./Entries/ProjectApplication/ListPanel"; +import QualificationListPanel from "./Entries/Qualification/ListPanel"; +import styles from "./ExpandedPanel.module.less"; + +const entryComponents = { + "qualification": QualificationListPanel, + "project-application": ProjectApplicationListPanel, + "current-project": CurrentProjectListPanel, +}; + +// 当前放大组件独立维护模拟统计数据,后续可直接替换为本模块接口结果。 +const mockData = { + title: "相关方单位情况统计", + description: "以项目为统计维度,查看项目数量、涉及相关方、人员及当前作业项目。", + chartType: "bar", + metrics: [ + { + name: "项目数", + value: 74, + tone: "blue", + }, + { + name: "相关方单位", + value: 94, + tone: "blue", + }, + { + name: "相关方人员", + value: 658, + tone: "blue", + }, + { + name: "当前作业项目", + value: 12, + tone: "green", + }, + ], + trend: [ + 9, + 12, + 14, + 16, + 15, + 18, + 20, + 22, + 24, + 25, + 27, + 29, + ], + rows: [ + { + id: 1, + type: "生产类", + firstUnits: 34, + firstPeople: 569, + secondUnits: 111, + secondPeople: 767, + }, + { + id: 2, + type: "设备类", + firstUnits: 65, + firstPeople: 452, + secondUnits: 161, + secondPeople: 4750, + }, + { + id: 3, + type: "工程类", + firstUnits: 51, + firstPeople: 1254, + secondUnits: 13, + secondPeople: 1420, + }, + { + id: 4, + type: "劳务类", + firstUnits: 24, + firstPeople: 25, + secondUnits: 35, + secondPeople: 2328, + }, + { + id: 5, + type: "卫生环保类", + firstUnits: 32, + firstPeople: 124, + secondUnits: 71, + secondPeople: 382, + }, + ], + entries: [ + { + key: "qualification", + title: "资质备案", + description: "相关方资质准入、复审和预警", + }, + { + key: "project-application", + title: "项目申请", + description: "项目备案、审批和审核记录", + }, + { + key: "current-project", + title: "当前作业项目", + description: "项目等级、属地单位和项目状态", + }, + ], + columns: [ + { + title: "类型", + dataIndex: "type", + }, + { + title: "一级单位", + dataIndex: "firstUnits", + }, + { + title: "一级人数", + dataIndex: "firstPeople", + }, + { + title: "二级单位", + dataIndex: "secondUnits", + }, + { + title: "二级人数", + dataIndex: "secondPeople", + }, + ], +}; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +const chartColors = ["#ff4338", "#ff8a23", "#f6c445", "#3d8fe8", "#38bca7"]; + +function getChartOption(type, metrics, trend) { + if (type === "pie") { + return { + color: chartColors, + tooltip: { trigger: "item" }, + legend: { bottom: 0, textStyle: { color: "#4f697b", fontSize: 11 } }, + series: [{ + type: "pie", + radius: ["46%", "70%"], + center: ["50%", "43%"], + label: { show: false }, + data: metrics.map(item => ({ + name: item.name, + value: Number.parseFloat(String(item.value).replaceAll(",", "")) || 0, + })), + }], + }; + } + if (type === "bar") { + return { + color: ["#3d8fe8"], + tooltip: { trigger: "axis" }, + grid: { top: 8, right: 72, bottom: 8, left: 96 }, + xAxis: { type: "value", show: false }, + yAxis: [ + { + type: "category", + inverse: true, + data: metrics.map(item => item.name), + axisLabel: { color: "#66809f", fontSize: 11, fontWeight: 700 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + { + type: "category", + inverse: true, + position: "right", + data: metrics.map(item => formatValue(item.value)), + axisLabel: { color: "#3b445c", fontSize: 11, margin: 10 }, + axisLine: { show: false }, + axisTick: { show: false }, + }, + ], + series: [{ + type: "bar", + barMaxWidth: 14, + showBackground: true, + backgroundStyle: { color: "#c3dff2", borderRadius: 3 }, + data: metrics.map(item => Number.parseFloat(String(item.value).replaceAll(",", "")) || 0), + label: { show: false }, + itemStyle: { + borderRadius: 3, + color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [{ offset: 0, color: "#58a8f4" }, { + offset: 1, + color: "#18bed4", + }]), + }, + }], + }; + } + return { + color: ["#18bed4"], + tooltip: { trigger: "axis" }, + grid: { top: 12, right: 12, bottom: 22, left: 38 }, + xAxis: { + type: "category", + boundaryGap: false, + data: trend.map((_, index) => `${index + 1}月`), + axisLabel: { color: "#66809f", fontSize: 10, interval: 0 }, + axisLine: { lineStyle: { color: "#a8cce9" } }, + }, + yAxis: { + type: "value", + axisLabel: { color: "#66809f", fontSize: 10 }, + splitLine: { lineStyle: { color: "rgba(168,204,233,.38)" } }, + }, + series: [{ + type: "line", + symbolSize: 5, + data: trend, + lineStyle: { width: 3, color: "#18bed4" }, + itemStyle: { color: "#18bed4" }, + }], + }; +} + +/** 当前业务文件独立维护图表配置和实例,便于后续按真实接口调整。 */ +function DashboardChart({ type = "line", metrics, trend }) { + const chartRef = useRef(null); + + useEffect(() => { + const chart = echarts.init(chartRef.current); + chart.setOption(getChartOption(type, metrics, trend)); + const resize = () => chart.resize(); + window.addEventListener("resize", resize); + return () => { + window.removeEventListener("resize", resize); + chart.dispose(); + }; + }, [type, metrics, trend]); + + return
; +} + +/** 相关方单位统计放大首页独立维护周期、分析内容和业务入口切换。 */ +function RelatedPartyExpandedPanel() { + const [period, setPeriod] = useState("年度"); + const [entry, setEntry] = useState(null); + const data = mockData; + const scopeName = "股份公司"; + const EntryListPanel = entry ? entryComponents[entry.key] : null; + + return ( + <> + } + metrics={data.metrics} + mainContent={( +
+
+
+
+

一级相关方单位数

+ 2026年 +
+ ({ name: item.type, value: item.firstUnits }))} + trend={data.trend} + /> +
+
+
+

二级相关方单位数

+ {scopeName} +
+ ({ name: item.type, value: item.secondUnits }))} + trend={data.trend} + /> +
+
+
+
+

统计明细

+ + {scopeName} + {" "} + · + {" "} + {period} + +
+
+ + {data.columns.map(column => {column.title})} + + )} + renderRow={row => ( + + {data.columns.map(column => ( + + {formatValue(row[column.dataIndex])} + + ))} + + )} + /> +
+
+
+ )} + businessEntries={data.entries.map(entryItem => ({ ...entryItem, onClick: () => setEntry(entryItem) }))} + statistics={[ + { label: "统计范围", value: scopeName }, + { label: "统计周期", value: period === "月份" ? "月度" : period }, + { label: "指标数量", value: data.metrics.length }, + { label: "业务入口", value: data.entries.length }, + ]} + /> + {entry && EntryListPanel && ( + setEntry(null)}> + setEntry(null)} /> + + )} + + ); +} + +export default RelatedPartyExpandedPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less new file mode 100644 index 0000000..33db14b --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/ExpandedPanel.module.less @@ -0,0 +1,54 @@ +.content { + display: grid; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: minmax(260px, 1fr) minmax(220px, 0.8fr); + gap: 10px; +} + +.analysis { + display: grid; + min-height: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.surface { + min-width: 0; + min-height: 0; + overflow: hidden; + padding: 10px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); + + h3 { + height: 24px; + margin: 0; + color: #3f5f83; + font-size: 14px; + } +} + +.tableTitle { + display: flex; + align-items: center; + justify-content: space-between; + + span { + color: #66809f; + font-size: 12px; + } +} + +.overviewTable { + height: calc(100% - 24px); + min-height: 0; +} + +.chart { + width: 100%; + height: calc(100% - 24px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.js new file mode 100644 index 0000000..05d2e92 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.js @@ -0,0 +1,97 @@ +import OverviewTable, { OverviewTableRow } from "~/pages/Container/Statistics/components/OverviewTable"; +import Panel from "~/pages/Container/Statistics/components/Panel"; +import styles from "./index.module.less"; + +// 当前组件独立维护相关方单位首屏模拟数据。 +const mockFirstRows = [ + { id: 1, type: "生产类", unitCount: 34, peopleCount: 569 }, + { id: 2, type: "设备类", unitCount: 65, peopleCount: 452 }, + { id: 3, type: "工程类", unitCount: 51, peopleCount: 1254 }, + { id: 4, type: "劳务类", unitCount: 24, peopleCount: 25 }, + { id: 5, type: "卫生环保类", unitCount: 32, peopleCount: 124 }, + { id: 6, type: "物资供应类", unitCount: 12, peopleCount: 854 }, + { id: 7, type: "其他", unitCount: 41, peopleCount: 123 }, +]; +const mockSecondRows = [ + { id: 1, type: "生产类", unitCount: 111, peopleCount: 767 }, + { id: 2, type: "设备类", unitCount: 161, peopleCount: 4750 }, + { id: 3, type: "工程类", unitCount: 13, peopleCount: 1420 }, + { id: 4, type: "劳务类", unitCount: 35, peopleCount: 2328 }, + { id: 5, type: "卫生环保类", unitCount: 71, peopleCount: 382 }, + { id: 6, type: "物资供应类", unitCount: 22, peopleCount: 145 }, + { id: 7, type: "其他", unitCount: 112, peopleCount: 1164 }, +]; +const mockFirstSummary = { type: "统计", unitCount: 259, peopleCount: 3401 }; +const mockSecondSummary = { type: "合计", unitCount: 525, peopleCount: 10956 }; +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +function RelatedPartyPanel({ onExpand }) { + return ( + +
+
+
一级相关方
+
+ + 类型 + 单位数 + 人数 + + )} + renderRow={record => ( + + {record.type} + {formatValue(record.unitCount)} + {formatValue(record.peopleCount)} + + )} + summary={( + + {mockFirstSummary.type} + {formatValue(mockFirstSummary.unitCount)} + {formatValue(mockFirstSummary.peopleCount)} + + )} + /> +
+
+
+
二级相关方
+
+ + 类型 + 单位数 + 人数 + + )} + renderRow={record => ( + + {record.type} + {formatValue(record.unitCount)} + {formatValue(record.peopleCount)} + + )} + summary={( + + {mockSecondSummary.type} + {formatValue(mockSecondSummary.unitCount)} + {formatValue(mockSecondSummary.peopleCount)} + + )} + /> +
+
+
+
+ ); +} + +export default RelatedPartyPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.module.less new file mode 100644 index 0000000..0c379fc --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel/index.module.less @@ -0,0 +1,31 @@ +.panel { + height: 100%; +} + +.tables { + display: grid; + height: 100%; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.table { + min-width: 0; + min-height: 0; +} + +.tableTitle { + height: 31px; + border: 1px solid #fff; + border-radius: 2px 2px 0 0; + background: #bcdffd; + color: #305683; + font-weight: 700; + line-height: 30px; + text-align: center; +} + +.tableBody { + height: calc(100% - 31px); + min-height: 0; +} diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/index.js b/src/pages/Container/Statistics/Supervision/components/RightPanel/index.js new file mode 100644 index 0000000..3898fbc --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/index.js @@ -0,0 +1,20 @@ +import FixedColorPanel from "~/pages/Container/Statistics/Supervision/components/RightPanel/FixedColorPanel"; +import KeyWorkPanel from "~/pages/Container/Statistics/Supervision/components/RightPanel/KeyWorkPanel"; +import PenaltyStatisticsPanel + from "~/pages/Container/Statistics/Supervision/components/RightPanel/PenaltyStatisticsPanel"; +import RelatedPartyPanel from "~/pages/Container/Statistics/Supervision/components/RightPanel/RelatedPartyPanel"; +import styles from "./index.module.less"; + +/** 右栏仅负责按原型顺序组合四个监管统计模块。 */ +function RightPanel({ onExpand }) { + return ( +
+ onExpand("fixedColor")} /> + onExpand("keyWork")} /> + onExpand("related")} /> + onExpand("penalty")} /> +
+ ); +} + +export default RightPanel; diff --git a/src/pages/Container/Statistics/Supervision/components/RightPanel/index.module.less b/src/pages/Container/Statistics/Supervision/components/RightPanel/index.module.less new file mode 100644 index 0000000..49e54a3 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/components/RightPanel/index.module.less @@ -0,0 +1,23 @@ +.panel { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + gap: 10px; + + > :nth-child(1) { + flex: 0 0 130px; + } + + > :nth-child(2) { + flex: 0 0 200px; + } + + > :nth-child(3) { + flex: 0 0 390px; + } + + > :nth-child(4) { + flex: 1; + } +} diff --git a/src/pages/Container/Statistics/Supervision/index.js b/src/pages/Container/Statistics/Supervision/index.js new file mode 100644 index 0000000..4f21d94 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/index.js @@ -0,0 +1,79 @@ +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import autoFit from "autofit.js"; +import { useEffect, useState } from "react"; +import bg from "~/assets/images/statistics/bg.png"; +import { NS_BI_STATISTICS } from "~/enumerate/namespace"; +import ExpandedLayer from "~/pages/Container/Statistics/components/ExpandedLayer"; +import Header from "~/pages/Container/Statistics/components/Header"; +import ExpandedContent from "~/pages/Container/Statistics/Supervision/components/ExpandedContent"; +import LeftPanel from "~/pages/Container/Statistics/Supervision/components/LeftPanel"; +import MiddlePanel from "~/pages/Container/Statistics/Supervision/components/MiddlePanel"; +import RightPanel from "~/pages/Container/Statistics/Supervision/components/RightPanel"; +import styles from "./index.module.less"; + +const moduleTitles = { + risk: "风险辨识管控情况", + majorHazard: "重大危险源安全情况", + hidden: "隐患排查治理情况", + fire: "消防点检数据", + emergency: "应急管理", + safetyEvaluation: "分子公司安全考评", + inspection: "安全环保检查数据", + fixedColor: "四色四固定", + keyWork: "重点作业", + related: "相关方单位情况统计", + penalty: "相关方处罚统计表", +}; + +function Supervision(props) { + const [scope, setScope] = useState("all"); + const [expandedModule, setExpandedModule] = useState(""); + const [weather, setWeather] = useState({}); + + useEffect(() => { + autoFit.init({ dw: 1920, dh: 1080, el: ".statistics_supervision_office", resize: true }); + return () => autoFit.off(); + }, []); + + useEffect(() => { + const loadWeather = async () => { + try { + const response = await props.getWeather(); + setWeather(response?.result || {}); + } + catch { + setWeather({}); + } + }; + loadWeather(); + }, []); + + return ( +
+
+
+ + + +
+ setExpandedModule("")} + > + + +
+ ); +} + +export default Connect([NS_BI_STATISTICS], true)(Supervision); diff --git a/src/pages/Container/Statistics/Supervision/index.module.less b/src/pages/Container/Statistics/Supervision/index.module.less new file mode 100644 index 0000000..6288525 --- /dev/null +++ b/src/pages/Container/Statistics/Supervision/index.module.less @@ -0,0 +1,81 @@ +.dashboard { + position: relative; + width: 100vw; + height: 100vh; + overflow: hidden; + color: #305683; + background-repeat: no-repeat; + background-size: 100% 100%; + + * { + box-sizing: border-box; + } + + :global { + .@{ant-prefix} { + &-segmented { + padding: 0; + overflow: hidden; + border: 1px solid #a3ccfa; + border-radius: 4px; + background: #d6ecff; + + &-group { + gap: 0; + } + + &-item { + min-width: 58px; + height: 28px; + border-left: 1px solid #a3ccfa; + border-radius: 0; + color: #3c648d; + + &:first-child { + border-left: 0; + } + + &-label { + min-height: 26px; + padding: 0 12px; + line-height: 26px; + } + + &-selected { + border-radius: 0; + background: #6291fa; + box-shadow: none; + color: #fff; + } + + &:focus-visible { + outline: 2px solid #2e75c6; + outline-offset: -2px; + } + } + + &-item:hover:not(&-item-selected):not(&-item-disabled) { + color: #3c648d !important; + } + + &-item-selected:hover { + color: #fff !important; + } + + &-thumb { + border-radius: 0; + background: #6291fa; + box-shadow: none; + } + } + } + } +} + +.content { + display: grid; + height: calc(100% - 86px); + grid-template-columns: 500px minmax(0, 1fr) 530px; + gap: 12px; + padding: 8px 10px 14px; +} diff --git a/src/pages/Container/Statistics/components/DetailDialog/index.js b/src/pages/Container/Statistics/components/DetailDialog/index.js new file mode 100644 index 0000000..b382632 --- /dev/null +++ b/src/pages/Container/Statistics/components/DetailDialog/index.js @@ -0,0 +1,31 @@ +import { CloseOutlined } from "@ant-design/icons"; +import { useEffect } from "react"; +import styles from "./index.module.less"; + +/** 列表内记录详情公共外框,字段和业务内容由模块自己的详情组件传入。 */ +function DetailDialog({ title, children, onClose }) { + useEffect(() => { + const closeOnEscape = (event) => { + if (event.key === "Escape") + onClose(); + }; + document.addEventListener("keydown", closeOnEscape); + return () => document.removeEventListener("keydown", closeOnEscape); + }, [onClose]); + + return ( +
+
+
+

{title}

+ +
+
{children}
+
+
+ ); +} + +export default DetailDialog; diff --git a/src/pages/Container/Statistics/components/DetailDialog/index.module.less b/src/pages/Container/Statistics/components/DetailDialog/index.module.less new file mode 100644 index 0000000..30954b4 --- /dev/null +++ b/src/pages/Container/Statistics/components/DetailDialog/index.module.less @@ -0,0 +1,76 @@ +.mask { + position: absolute; + z-index: 20; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(40, 76, 112, 0.34); +} + +.dialog { + display: grid; + width: min(720px, calc(100% - 80px)); + max-height: calc(100% - 80px); + grid-template-rows: 52px minmax(0, 1fr); + overflow: hidden; + border: 1px solid #fff; + border-radius: 6px; + background: #e8f4fd; + box-shadow: 0 15px 50px rgba(28, 65, 99, 0.25); +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 18px; + border-bottom: 1px solid rgba(89, 139, 187, 0.28); + background: transparent; + + h2 { + margin: 0; + color: #305683; + font-size: 19px; + } + + button { + display: grid; + width: 36px; + height: 36px; + place-items: center; + border: 0; + background: transparent; + color: #305683; + cursor: pointer; + font-size: 20px; + } +} + +.content { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + min-height: 0; + overflow: auto; + padding: 18px; + + > div { + padding: 12px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.52); + } + + > div > span { + display: block; + color: #66809f; + font-size: 12px; + } + + > div > strong { + display: block; + margin-top: 5px; + color: #3b445c; + } +} diff --git a/src/pages/Container/Statistics/components/ExpandedLayer/index.js b/src/pages/Container/Statistics/components/ExpandedLayer/index.js new file mode 100644 index 0000000..579a783 --- /dev/null +++ b/src/pages/Container/Statistics/components/ExpandedLayer/index.js @@ -0,0 +1,36 @@ +import { CloseOutlined } from "@ant-design/icons"; +import { useEffect } from "react"; +import styles from "./index.module.less"; + +/** 当前页放大层,仅承载业务模块内容,不改变路由。 */ +function ExpandedLayer({ open, title, children, onClose }) { + useEffect(() => { + if (!open) + return undefined; + const closeOnEscape = (event) => { + if (event.key === "Escape" && !document.querySelector("[data-layer-level=\"list\"]")) + onClose(); + }; + document.addEventListener("keydown", closeOnEscape); + return () => document.removeEventListener("keydown", closeOnEscape); + }, [open, onClose]); + + if (!open) + return null; + + return ( +
+
+
+

{title}

+ +
+
{children}
+
+
+ ); +} + +export default ExpandedLayer; diff --git a/src/pages/Container/Statistics/components/ExpandedLayer/index.module.less b/src/pages/Container/Statistics/components/ExpandedLayer/index.module.less new file mode 100644 index 0000000..956feef --- /dev/null +++ b/src/pages/Container/Statistics/components/ExpandedLayer/index.module.less @@ -0,0 +1,58 @@ +.mask { + position: absolute; + z-index: 100; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 34px; + background: rgba(40, 76, 112, 0.38); +} + +.layer { + position: relative; + display: grid; + width: 100%; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-rows: 58px minmax(0, 1fr); + overflow: hidden; + border: 1px solid #fff; + border-radius: 6px; + background: #e8f4fd; + box-shadow: 0 15px 50px rgba(28, 65, 99, 0.3); +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + border-bottom: 1px solid #c2ddf2; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + + h1 { + margin: 0; + color: #305683; + font-size: 24px; + } + + button { + display: grid; + width: 38px; + height: 38px; + place-items: center; + border: 0; + background: transparent; + color: #305683; + cursor: pointer; + font-size: 22px; + } +} + +.body { + min-width: 0; + min-height: 0; + overflow: hidden; +} diff --git a/src/pages/Container/Statistics/components/ExpandedLayout/index.js b/src/pages/Container/Statistics/components/ExpandedLayout/index.js new file mode 100644 index 0000000..fb89268 --- /dev/null +++ b/src/pages/Container/Statistics/components/ExpandedLayout/index.js @@ -0,0 +1,69 @@ +import { RightOutlined } from "@ant-design/icons"; +import styles from "./index.module.less"; + +const formatValue = value => typeof value === "number" ? value.toLocaleString("zh-CN") : value; + +/** 放大首页公共左右布局,仅负责展示各业务模块传入的内容。 */ +function ExpandedLayout({ + title, + subtitle, + headerExtra, + metrics, + mainContent, + businessEntries, + statistics, +}) { + return ( +
+
+
+
+

{title}

+

{subtitle}

+
+ {headerExtra} +
+
+ {metrics.map(item => ( +
+ {item.name} + {formatValue(item.value)} +
+ ))} +
+
{mainContent}
+
+ +
+ ); +} + +export default ExpandedLayout; diff --git a/src/pages/Container/Statistics/components/ExpandedLayout/index.module.less b/src/pages/Container/Statistics/components/ExpandedLayout/index.module.less new file mode 100644 index 0000000..177400d --- /dev/null +++ b/src/pages/Container/Statistics/components/ExpandedLayout/index.module.less @@ -0,0 +1,247 @@ +.page { + display: grid; + width: 100%; + height: 100%; + min-width: 0; + min-height: 0; + grid-template-columns: minmax(0, 1fr) 400px; + gap: 16px; + padding: 16px 18px 18px; +} + +.main, +.side { + min-width: 0; + min-height: 0; + overflow: hidden; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(224, 237, 253, 0.94); + box-shadow: 0 3px 12px rgba(44, 91, 162, 0.12); +} + +.main { + display: grid; + grid-template-rows: 72px 118px minmax(0, 1fr); + gap: 12px; + padding: 16px; + + &[data-metric-count="7"] { + grid-template-rows: 72px 172px minmax(0, 1fr); + } +} + +.heading { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 8px; + border-bottom: 1px solid rgba(89, 139, 187, 0.28); + + h2, + p { + margin: 0; + } + + h2 { + color: #305683; + font-size: 27px; + } + + p { + margin-top: 5px; + color: #66809f; + font-size: 15px; + line-height: 1.4; + } +} + +.metrics { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 12px; + + &[data-count="7"] { + grid-template-columns: repeat(12, minmax(0, 1fr)); + + .metric:nth-child(-n+4) { + grid-column: span 3; + } + + .metric:nth-child(n+5) { + grid-column: span 4; + } + } +} + +.metric { + display: flex; + min-width: 0; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + + span { + color: #4b668d; + font-size: 15px; + } + + strong { + margin-top: 7px; + color: #2e75c6; + font-size: 29px; + font-weight: 600; + } + + &[data-tone="red"] strong { + color: #e8473f; + } + + &[data-tone="orange"] strong { + color: #ef8b23; + } + + &[data-tone="yellow"] strong { + color: #c99500; + } + + &[data-tone="green"] strong { + color: #16845b; + } +} + +.mainContent { + min-width: 0; + min-height: 0; + overflow: hidden; +} + +.side { + display: grid; + grid-template-rows: 72px auto auto; + align-content: start; + gap: 12px; + padding: 16px; +} + +.sideHeading { + display: flex; + align-items: center; + border-bottom: 1px solid rgba(89, 139, 187, 0.28); + color: #305683; + font-size: 20px; + font-weight: 700; +} + +.entries { + display: flex; + min-height: 0; + flex-direction: column; + gap: 10px; + overflow: auto; + + button { + display: grid; + min-height: 80px; + grid-template-columns: 48px minmax(0, 1fr) 24px; + align-items: center; + gap: 10px; + padding: 12px; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + color: #315f94; + cursor: pointer; + text-align: left; + } +} + +.entryIndex { + display: grid; + width: 42px; + height: 42px; + place-items: center; + border-radius: 4px; + background: #6aa8e9; + color: #fff; + font-weight: 700; +} + +.entryText { + min-width: 0; + + strong, + small { + display: block; + } + + strong { + color: #305683; + font-size: 16px; + } + + small { + margin-top: 4px; + color: #66809f; + font-size: 13px; + line-height: 1.4; + } +} + +.context { + padding: 12px; + border: 1px solid #fff; + border-radius: 4px; + background: rgba(255, 255, 255, 0.3); +} + +.contextHeading { + display: flex; + height: 28px; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; + color: #3f5f83; + + span { + font-size: 14px; + font-weight: 700; + } + + small { + color: #66809f; + font-size: 12px; + } +} + +.contextGrid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + + > div { + display: flex; + min-height: 70px; + flex-direction: column; + align-items: center; + justify-content: center; + border: 1px solid #fff; + border-radius: 4px; + background: linear-gradient(180deg, #d9effd, #e8f6ff); + box-shadow: inset 0 1px 3px rgba(49, 122, 202, 0.2); + } + + span { + color: #66809f; + font-size: 13px; + } + + strong { + margin-top: 3px; + color: #2e75c6; + font-size: 20px; + } +} diff --git a/src/pages/Container/Statistics/components/Header/index.js b/src/pages/Container/Statistics/components/Header/index.js new file mode 100644 index 0000000..2054514 --- /dev/null +++ b/src/pages/Container/Statistics/components/Header/index.js @@ -0,0 +1,57 @@ +import { Select, Spin } from "antd"; +import back from "~/assets/images/statistics/back.png"; +import title from "~/assets/images/statistics/title.png"; +import bg from "~/assets/images/statistics/titlebg.png"; +import styles from "./index.module.less"; + +const scopeOptions = [ + { value: "all", label: "股份公司数据统计" }, + { value: "company-2", label: "二公司" }, + { value: "company-6", label: "六公司" }, + { value: "company-7", label: "七公司" }, + { value: "company-9", label: "九公司" }, +]; + +function Header(props) { + const weather = props.weather.now || props.weather; + const weekday = new Date().toLocaleDateString("zh-CN", { weekday: "long" }); + const weatherText = weather.text + ? `${weather.temp || "--"}℃ ${weather.text} ${weather.wind_class || ""} ${weekday}` + : "暂无天气信息"; + + return ( +
+
+ +
已安全生产 365 天
+
+ + {weatherText} + +
+
+
+ +
+