Merge remote-tracking branch 'origin/main' into main

# Conflicts:
#	config/routes.js
#	src/pages/nav_system_content/SystemContentList.js
#	src/pages/topnavbar/TopNavBar.js
main
jiangxucong 2 months ago
commit 6585574116

@ -30,6 +30,12 @@ export default [
name: 'basic',
component: './business_basic/basic',
},
// 消防检测报警
{
path: '/topnavbar00/business/fireWarning',
name: 'fireWarning',
component: './business_firewarning/FireWarning',
},
// 消防重点部位管理
{
path: '/topnavbar00/business/firekeynotearea',

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 953 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.3 MiB

@ -0,0 +1,4 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect y="7.5" width="16" height="2" fill="#3C7DFF"/>
<circle cx="8" cy="8.5" r="3.5" fill="white" stroke="#3C7DFF"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

@ -0,0 +1,6 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.915 5.78125H11.285C9.51866 5.78125 7.82466 6.48293 6.57567 7.73192C5.32668 8.98091 4.625 10.6749 4.625 12.4413V49.0712C4.625 49.9459 4.79727 50.8119 5.13196 51.6199C5.46666 52.428 5.95723 53.1621 6.57567 53.7806C7.19411 54.399 7.9283 54.8896 8.73633 55.2243C9.54436 55.559 10.4104 55.7313 11.285 55.7313H47.915C49.6813 55.7313 51.3753 55.0296 52.6243 53.7806C53.8733 52.5316 54.575 50.8376 54.575 49.0712V12.4413C54.575 10.6749 53.8733 8.98091 52.6243 7.73192C51.3753 6.48293 49.6813 5.78125 47.915 5.78125Z" fill="#0065F3"/>
<path opacity="0.4" d="M61.9541 19.6562H25.3241C23.5577 19.6562 21.8637 20.3579 20.6147 21.6069C19.3657 22.8559 18.6641 24.5499 18.6641 26.3163V62.9462C18.6641 64.7126 19.3657 66.4066 20.6147 67.6556C21.8637 68.9046 23.5577 69.6063 25.3241 69.6063H61.9541C63.7204 69.6063 65.4144 68.9046 66.6634 67.6556C67.9124 66.4066 68.6141 64.7126 68.6141 62.9462V26.3163C68.6141 24.5499 67.9124 22.8559 66.6634 21.6069C65.4144 20.3579 63.7204 19.6563 61.9541 19.6562Z" fill="#82B6FA"/>
<path opacity="0.4" d="M25.16 20.2344H61.79C63.403 20.2344 64.95 20.8751 66.0905 22.0157C67.2311 23.1563 67.8719 24.7032 67.8719 26.3163V62.9462C67.8719 64.5593 67.2311 66.1062 66.0905 67.2468C64.95 68.3874 63.403 69.0281 61.79 69.0281H25.16C23.547 69.0281 22 68.3874 20.8595 67.2468C19.7189 66.1062 19.0781 64.5593 19.0781 62.9462V26.3163C19.0781 24.7032 19.7189 23.1563 20.8595 22.0157C22 20.8751 23.547 20.2344 25.16 20.2344ZM69.0281 26.3163C69.0281 24.3966 68.2655 22.5555 66.9081 21.1981C65.5507 19.8407 63.7097 19.0781 61.79 19.0781H25.16C23.2403 19.0781 21.3993 19.8407 20.0419 21.1981C18.6845 22.5555 17.9219 24.3966 17.9219 26.3163V62.9462C17.9219 64.8659 18.6845 66.707 20.0419 68.0644C21.3993 69.4218 23.2403 70.1844 25.16 70.1844H61.79C63.7097 70.1844 65.5507 69.4218 66.9081 68.0644C68.2655 66.707 69.0281 64.8659 69.0281 62.9462V26.3163Z" fill="white"/>
<path d="M33.0297 37.7078C32.7391 37.7087 32.4591 37.5986 32.2469 37.4L30.3469 35.6159C30.2346 35.5141 30.1438 35.3909 30.0797 35.2535C30.0155 35.1162 29.9795 34.9675 29.9735 34.816C29.9676 34.6646 29.9919 34.5134 30.0451 34.3715C30.0982 34.2296 30.1792 34.0996 30.2832 33.9894C30.3871 33.8791 30.512 33.7906 30.6506 33.7292C30.7892 33.6678 30.9386 33.6346 31.0901 33.6316C31.2417 33.6286 31.3923 33.6558 31.5332 33.7117C31.674 33.7677 31.8024 33.8511 31.9106 33.9572L33.8106 35.7413C33.977 35.8978 34.0925 36.1007 34.1421 36.3236C34.1918 36.5466 34.1734 36.7793 34.0892 36.9916C34.0051 37.204 33.8591 37.3861 33.6702 37.5145C33.4812 37.6429 33.2581 37.7115 33.0297 37.7116V37.7078ZM54.1843 37.7078C53.9557 37.7081 53.7323 37.6397 53.5431 37.5115C53.3539 37.3833 53.2076 37.2011 53.1232 36.9887C53.0388 36.7763 53.0202 36.5434 53.0698 36.3203C53.1195 36.0972 53.235 35.8941 53.4015 35.7375L55.3015 33.9534C55.5226 33.7529 55.8136 33.6471 56.1119 33.6588C56.4102 33.6705 56.692 33.7987 56.8968 34.0159C57.1015 34.2331 57.2129 34.522 57.207 34.8204C57.2011 35.1189 57.0784 35.4031 56.8652 35.6121L54.9652 37.3962C54.7541 37.5957 54.4748 37.7072 54.1843 37.7078ZM43.607 34.3999C43.3047 34.3999 43.0147 34.2798 42.8009 34.066C42.5871 33.8522 42.467 33.5622 42.467 33.2599V30.37C42.467 30.0676 42.5871 29.7777 42.8009 29.5639C43.0147 29.3501 43.3047 29.23 43.607 29.23C43.9093 29.23 44.1993 29.3501 44.4131 29.5639C44.6269 29.7777 44.747 30.0676 44.747 30.37V33.2599C44.747 33.4096 44.7175 33.5578 44.6602 33.6961C44.6029 33.8345 44.519 33.9601 44.4131 34.066C44.3072 34.1718 44.1816 34.2558 44.0433 34.3131C43.9049 34.3704 43.7567 34.3999 43.607 34.3999ZM56.4757 57.3671H54.6384V46.1913C54.6384 43.3013 53.4904 40.5298 51.4469 38.4863C49.4034 36.4428 46.6318 35.2948 43.7419 35.2948C40.852 35.2948 38.0804 36.4428 36.0369 38.4863C33.9934 40.5298 32.8454 43.3013 32.8454 46.1913V57.3671H31.3824C31.0801 57.3671 30.7901 57.4872 30.5763 57.701C30.3625 57.9148 30.2424 58.2047 30.2424 58.5071C30.2424 58.8094 30.3625 59.0994 30.5763 59.3132C30.7901 59.527 31.0801 59.6471 31.3824 59.6471H56.4757C56.778 59.6471 57.068 59.527 57.2818 59.3132C57.4956 59.0994 57.6157 58.8094 57.6157 58.5071C57.6157 58.2047 57.4956 57.9148 57.2818 57.701C57.068 57.4872 56.778 57.3671 56.4757 57.3671ZM48.1366 48.9102L43.6906 53.6374C43.5888 53.7495 43.4658 53.8401 43.3286 53.9041C43.1914 53.9681 43.0428 54.0042 42.8915 54.0101C42.7403 54.0161 42.5893 53.9918 42.4475 53.9388C42.3057 53.8858 42.1759 53.8051 42.0656 53.7014C41.9554 53.5977 41.8668 53.4731 41.8053 53.3348C41.7437 53.1965 41.7102 53.0473 41.7069 52.896C41.7036 52.7446 41.7305 52.5941 41.7859 52.4533C41.8414 52.3124 41.9244 52.184 42.03 52.0756L44.6691 49.2674H40.3276C40.1013 49.2672 39.8803 49.1997 39.6925 49.0735C39.5047 48.9472 39.3588 48.768 39.2732 48.5585C39.1876 48.3491 39.1663 48.1189 39.212 47.8973C39.2576 47.6757 39.3682 47.4727 39.5296 47.3142L43.8673 43.062C44.0832 42.8503 44.3744 42.7331 44.6767 42.7362C44.9791 42.7392 45.2678 42.8622 45.4794 43.0781C45.6911 43.2941 45.8083 43.5852 45.8053 43.8875C45.8022 44.1899 45.6792 44.4786 45.4633 44.6903L43.1187 46.9874H47.2987C47.5215 46.9871 47.7396 47.0521 47.9259 47.1744C48.1121 47.2967 48.2585 47.4709 48.3468 47.6754C48.4352 47.88 48.4616 48.106 48.4229 48.3254C48.3842 48.5449 48.282 48.7482 48.129 48.9102H48.1366Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

@ -0,0 +1,6 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.915 5.78125H11.285C9.51866 5.78125 7.82466 6.48293 6.57567 7.73192C5.32668 8.98091 4.625 10.6749 4.625 12.4413V49.0712C4.625 49.9459 4.79727 50.8119 5.13196 51.6199C5.46666 52.428 5.95723 53.1621 6.57567 53.7806C7.19411 54.399 7.9283 54.8896 8.73633 55.2243C9.54436 55.559 10.4104 55.7313 11.285 55.7313H47.915C49.6813 55.7313 51.3753 55.0296 52.6243 53.7806C53.8733 52.5316 54.575 50.8376 54.575 49.0712V12.4413C54.575 10.6749 53.8733 8.98091 52.6243 7.73192C51.3753 6.48293 49.6813 5.78125 47.915 5.78125Z" fill="#0065F3"/>
<path opacity="0.4" d="M61.9541 19.6562H25.3241C23.5577 19.6562 21.8637 20.3579 20.6147 21.6069C19.3657 22.8559 18.6641 24.5499 18.6641 26.3163V62.9462C18.6641 64.7126 19.3657 66.4066 20.6147 67.6556C21.8637 68.9046 23.5577 69.6063 25.3241 69.6063H61.9541C63.7204 69.6063 65.4144 68.9046 66.6634 67.6556C67.9124 66.4066 68.6141 64.7126 68.6141 62.9462V26.3163C68.6141 24.5499 67.9124 22.8559 66.6634 21.6069C65.4144 20.3579 63.7204 19.6563 61.9541 19.6562Z" fill="#82B6FA"/>
<path d="M56.3273 61.7059H31.7016C31.1094 61.7036 30.5421 61.4671 30.1237 61.0479C29.7053 60.6287 29.4699 60.061 29.4688 59.4687V34.9587C29.4699 34.3669 29.7055 33.7996 30.124 33.3811C30.5425 32.9626 31.1098 32.727 31.7016 32.7259H32.3873C32.501 32.7259 32.61 32.771 32.6904 32.8514C32.7707 32.9318 32.8159 33.0408 32.8159 33.1544V36.0773C32.8181 36.6696 33.0547 37.2368 33.4739 37.6552C33.8931 38.0736 34.4608 38.3091 35.053 38.3102H52.9502C53.5417 38.3079 54.1083 38.072 54.5266 37.6537C54.9448 37.2354 55.1808 36.6688 55.183 36.0773V33.1544C55.183 33.0408 55.2282 32.9318 55.3086 32.8514C55.3889 32.771 55.4979 32.7259 55.6116 32.7259H56.3016C56.8931 32.7281 57.4597 32.9641 57.878 33.3824C58.2962 33.8006 58.5322 34.3672 58.5345 34.9587V59.4773C58.5379 59.7695 58.4833 60.0595 58.374 60.3305C58.2646 60.6015 58.1027 60.8481 57.8974 61.0561C57.6922 61.2642 57.4478 61.4295 57.1783 61.5424C56.9088 61.6554 56.6196 61.7139 56.3274 61.7145L56.3273 61.7059ZM51.3259 46.5558C51.0058 46.2443 50.5768 46.07 50.1302 46.07C49.6835 46.07 49.2545 46.2443 48.9345 46.5558L42.9345 52.2473L40.1402 49.5858C39.8201 49.2743 39.3911 49.1 38.9445 49.1C38.4978 49.1 38.0688 49.2743 37.7487 49.5858C37.5919 49.7292 37.4667 49.9036 37.381 50.0981C37.2953 50.2925 37.251 50.5027 37.251 50.7151C37.251 50.9276 37.2953 51.1378 37.381 51.3322C37.4667 51.5266 37.5919 51.7011 37.7487 51.8444L41.7344 55.6244C42.0573 55.9323 42.4862 56.104 42.9323 56.104C43.3784 56.104 43.8073 55.9323 44.1302 55.6244L51.3087 48.8144C51.4666 48.6722 51.5932 48.4987 51.6804 48.305C51.7676 48.1112 51.8134 47.9014 51.815 47.6889C51.8167 47.4764 51.774 47.2659 51.6898 47.0709C51.6056 46.8758 51.4816 46.7004 51.3259 46.5558ZM49.6116 36.0773H38.4301C37.8394 36.075 37.2734 35.8396 36.8553 35.4223C36.4372 35.005 36.2007 34.4395 36.1973 33.8487V32.953C36.1965 32.8846 36.212 32.8171 36.2425 32.756C36.2731 32.6948 36.3179 32.6419 36.3731 32.6016C40.5045 29.5802 40.723 29.4045 41.7602 28.4145C41.8407 28.3368 41.9483 28.2938 42.0602 28.2944H46.1573C46.2677 28.2949 46.3736 28.3379 46.4531 28.4145C47.4002 29.3273 47.563 29.5802 51.5616 32.5373C51.6448 32.5978 51.7126 32.6771 51.7595 32.7687C51.8064 32.8602 51.8311 32.9616 51.8316 33.0644V33.8487C51.835 34.1431 51.7797 34.4352 51.6687 34.7079C51.5578 34.9806 51.3935 35.2284 51.1855 35.4368C50.9776 35.6451 50.7301 35.8099 50.4576 35.9213C50.1851 36.0328 49.8931 36.0887 49.5988 36.0859L49.6116 36.0773ZM44.0402 30.5059C43.6865 30.5079 43.3382 30.5934 43.0238 30.7556C42.7094 30.9177 42.4378 31.1518 42.231 31.4387C42.0242 31.7257 41.8881 32.0575 41.8339 32.407C41.7796 32.7566 41.8087 33.114 41.9187 33.4502C42.0275 33.7789 42.2117 34.0776 42.4565 34.3224C42.7014 34.5672 43.0001 34.7514 43.3287 34.8602C43.6351 34.9607 43.9595 34.9937 44.2799 34.9569C44.6002 34.9201 44.9087 34.8144 45.1843 34.6471C45.4598 34.4797 45.6959 34.2546 45.8762 33.9873C46.0565 33.7201 46.1767 33.4169 46.2287 33.0987C46.2807 32.7805 46.2632 32.4548 46.1773 32.1441C46.0915 31.8333 45.9393 31.5448 45.7314 31.2984C45.5234 31.0521 45.2646 30.8537 44.9726 30.7168C44.6807 30.58 44.3626 30.5081 44.0402 30.5059Z" fill="white"/>
<path opacity="0.4" d="M25.16 20.2344H61.79C63.403 20.2344 64.95 20.8751 66.0905 22.0157C67.2311 23.1563 67.8719 24.7032 67.8719 26.3163V62.9462C67.8719 64.5593 67.2311 66.1062 66.0905 67.2468C64.95 68.3874 63.403 69.0281 61.79 69.0281H25.16C23.547 69.0281 22 68.3874 20.8595 67.2468C19.7189 66.1062 19.0781 64.5593 19.0781 62.9462V26.3163C19.0781 24.7032 19.7189 23.1563 20.8595 22.0157C22 20.8751 23.547 20.2344 25.16 20.2344ZM69.0281 26.3163C69.0281 24.3966 68.2655 22.5555 66.9081 21.1981C65.5507 19.8407 63.7097 19.0781 61.79 19.0781H25.16C23.2403 19.0781 21.3993 19.8407 20.0419 21.1981C18.6845 22.5555 17.9219 24.3966 17.9219 26.3163V62.9462C17.9219 64.8659 18.6845 66.707 20.0419 68.0644C21.3993 69.4218 23.2403 70.1844 25.16 70.1844H61.79C63.7097 70.1844 65.5507 69.4218 66.9081 68.0644C68.2655 66.707 69.0281 64.8659 69.0281 62.9462V26.3163Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

@ -0,0 +1,6 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.915 5.78125H11.285C9.51866 5.78125 7.82466 6.48293 6.57567 7.73192C5.32668 8.98091 4.625 10.6749 4.625 12.4413V49.0712C4.625 49.9459 4.79727 50.8119 5.13196 51.6199C5.46666 52.428 5.95723 53.1621 6.57567 53.7806C7.19411 54.399 7.9283 54.8896 8.73633 55.2243C9.54436 55.559 10.4104 55.7313 11.285 55.7313H47.915C49.6813 55.7313 51.3753 55.0296 52.6243 53.7806C53.8733 52.5316 54.575 50.8376 54.575 49.0712V12.4413C54.575 10.6749 53.8733 8.98091 52.6243 7.73192C51.3753 6.48293 49.6813 5.78125 47.915 5.78125Z" fill="#0065F3"/>
<path opacity="0.4" d="M61.9541 19.6562H25.3241C23.5577 19.6562 21.8637 20.3579 20.6147 21.6069C19.3657 22.8559 18.6641 24.5499 18.6641 26.3163V62.9462C18.6641 64.7126 19.3657 66.4066 20.6147 67.6556C21.8637 68.9046 23.5577 69.6063 25.3241 69.6063H61.9541C63.7204 69.6063 65.4144 68.9046 66.6634 67.6556C67.9124 66.4066 68.6141 64.7126 68.6141 62.9462V26.3163C68.6141 24.5499 67.9124 22.8559 66.6634 21.6069C65.4144 20.3579 63.7204 19.6563 61.9541 19.6562Z" fill="#82B6FA"/>
<path d="M58.1991 33.6166V47.0444C58.1991 53.7974 48.9717 60.9824 43.9891 60.9824C39.0083 60.9824 29.7773 53.7974 29.7773 47.0444V33.5366H31.1603C31.1603 33.5366 34.3902 33.5366 37.6218 32.2531C40.9423 30.9697 43.1572 29.5139 43.1572 29.5139L43.9891 29.002L44.8192 29.601C44.8192 29.601 47.0341 31.0533 50.3564 32.3367C53.5845 33.533 56.8161 33.6166 56.8161 33.6166H58.1991ZM45.1925 43.5195L50.2551 37.1362H42.5386L38.5533 47.0444H42.535L38.971 55.4346L49.6756 43.5177H45.1907V43.5195H45.1925Z" fill="white"/>
<path opacity="0.4" d="M25.16 20.2344H61.79C63.403 20.2344 64.95 20.8751 66.0905 22.0157C67.2311 23.1563 67.8719 24.7032 67.8719 26.3163V62.9462C67.8719 64.5593 67.2311 66.1062 66.0905 67.2468C64.95 68.3874 63.403 69.0281 61.79 69.0281H25.16C23.547 69.0281 22 68.3874 20.8595 67.2468C19.7189 66.1062 19.0781 64.5593 19.0781 62.9462V26.3163C19.0781 24.7032 19.7189 23.1563 20.8595 22.0157C22 20.8751 23.547 20.2344 25.16 20.2344ZM69.0281 26.3163C69.0281 24.3966 68.2655 22.5555 66.9081 21.1981C65.5507 19.8407 63.7097 19.0781 61.79 19.0781H25.16C23.2403 19.0781 21.3993 19.8407 20.0419 21.1981C18.6845 22.5555 17.9219 24.3966 17.9219 26.3163V62.9462C17.9219 64.8659 18.6845 66.707 20.0419 68.0644C21.3993 69.4218 23.2403 70.1844 25.16 70.1844H61.79C63.7097 70.1844 65.5507 69.4218 66.9081 68.0644C68.2655 66.707 69.0281 64.8659 69.0281 62.9462V26.3163Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.50181 1C4.80395 1 1.77592 3.86737 1.51953 7.5H15.4841C15.2277 3.86737 12.1997 1 8.50181 1ZM10.5018 8.5H6.50181C6.50181 9.60456 7.39725 10.5 8.50181 10.5C9.60637 10.5 10.5018 9.60456 10.5018 8.5ZM5.57083 10.7216L5.57069 10.7218C5.47942 10.6238 5.34938 10.5625 5.20494 10.5625C4.9288 10.5625 4.70494 10.7864 4.70494 11.0625C4.70494 11.1966 4.75781 11.3182 4.84373 11.408C5.7565 12.3873 7.05748 13 8.50181 13C9.94611 13 11.2471 12.3873 12.1599 11.4081C12.2458 11.3183 12.2987 11.1966 12.2987 11.0625C12.2987 10.7864 12.0748 10.5625 11.7987 10.5625C11.6542 10.5625 11.5242 10.6238 11.4329 10.7218L11.4328 10.7216C10.7024 11.5079 9.65964 12 8.50181 12C7.34398 12 6.30127 11.5079 5.57083 10.7216ZM13.2674 11.9219C13.1163 11.9219 12.981 11.989 12.8893 12.095L12.8881 12.0939C11.793 13.2667 10.233 14 8.50181 14C6.77058 14 5.21062 13.2667 4.1155 12.0939L4.11434 12.095C4.02266 11.989 3.88731 11.9219 3.73619 11.9219C3.46005 11.9219 3.23619 12.1457 3.23619 12.4219C3.23619 12.5654 3.29673 12.6947 3.39355 12.7858C4.67073 14.1485 6.4868 15 8.50181 15C10.5168 15 12.3329 14.1485 13.6101 12.7858C13.7069 12.6947 13.7674 12.5654 13.7674 12.4219C13.7674 12.1457 13.5436 11.9219 13.2674 11.9219Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -1,10 +1,11 @@
import React, { useState } from 'react';
import { Card, Row, Col, Statistic, Progress, Button, Space } from 'antd';
import styles from './basic.less';
import ResponsibilityImplementation from './module/ResponsibilityImplementation'; //责任落实
import OnlineMonitoring from './module/OnlineMonitoring'; //在线监测预警
import RiskAssessment from './module/RiskAssessment'; //风险管控
import EvaluationReport from './module/EvaluationReport'; //评估报告
import ResponsibilityImplementation from './components/ResponsibilityImplementation';
import OnlineMonitoring from './components/OnlineMonitoring';
import RiskAssessment from './components/RiskAssessment';
import EvaluationReport from './components/EvaluationReport';
import LicenseManagement from './components/LicenseManagement';
@ -20,6 +21,8 @@ const SafeMajorHazardList = () => {
switch (activeModule) {
case 'organization':
return <ResponsibilityImplementation />;
case 'license':
return <LicenseManagement />;
case 'equipment':
return <OnlineMonitoring />;
case 'firefighting':
@ -40,11 +43,11 @@ const SafeMajorHazardList = () => {
onClick={() => handleModuleClick("organization")}
>组织机构管理
</Button>
{/* <Button
className={`${styles.TopButtonItem} ${activeModule === "equipment" ? styles.active : ""}`}
onClick={() => handleModuleClick("equipment")}
>设备设施管理
</Button> */}
<Button
className={`${styles.TopButtonItem} ${activeModule === "license" ? styles.active : ""}`}
onClick={() => handleModuleClick("license")}
>资质证照管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "firefighting" ? styles.active : ""}`}
onClick={() => handleModuleClick("firefighting")}

@ -244,7 +244,7 @@ const EvaluationReport = () => {
width: 1
},
itemStyle: {
color: '#FFFFFF',
color: '#fff',
borderColor: '#1269FF',
borderWidth: 1
},

@ -0,0 +1,604 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Table, Tag, Space, Typography, Progress, Row, Col, Button, Input, Select } from 'antd';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './LicenseManagement.less';
import icon_echart from '@/assets/business_basic/icon_echart.svg';
const { Title } = Typography;
const { Search } = Input;
const { Option } = Select;
const LicenseManagement = () => {
const chartRef = useRef(null);
const [searchValue, setSearchValue] = useState('');
const [selectedType, setSelectedType] = useState('all');
// 图表数据
const chartData = [
{ name: '安全生产许可证', value: 35, itemStyle: { color: '#3C7DFF' } },
{ name: '安全评估报告', value: 25, itemStyle: { color: '#FF8800' } },
{ name: '安全三同时材料', value: 20, itemStyle: { color: '#FF3E48' } },
{ name: '施工资质证书', value: 15, itemStyle: { color: '#FFC403' } },
{ name: '应急预案', value: 10, itemStyle: { color: '#22C55E' } },
{ name: '其他', value: 5, itemStyle: { color: '#31BCFF' } }
];
// 初始化图表
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'horizontal',
bottom: 0,
left: 'center',
itemWidth: 14,
itemHeight: 4,
itemGap: 10,
textStyle: {
fontSize: 12,
color: '#333',
width: 100
},
formatter: function (name) {
return name;
},
data: (() => {
// 找到最长的名称长度
const maxLength = Math.max(...chartData.map(item => item.name.length));
// 将所有名称填充到相同长度
return chartData.map(item => {
const paddingLength = maxLength - item.name.length;
return item.name + ' '.repeat(paddingLength);
});
})()
},
series: [
{
name: '证件类型分布',
type: 'pie',
radius: ['20%', '65%'],
center: ['50%', '38%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 5,
// color:"red",/
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '16',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: (() => {
// 找到最长的名称长度
const maxLength = Math.max(...chartData.map(item => item.name.length));
// 将所有名称填充到相同长度
return chartData.map(item => ({
...item,
name: item.name + ' '.repeat(maxLength - item.name.length)
}));
})()
}
]
};
chart.setOption(option);
// 响应式处理
const handleResize = () => {
chart.resize();
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
chart.dispose();
};
}
}, []);
// 表格数据
const tableData = [
{
key: '1',
no: '01',
name: '安全生产许可证',
type: '资质证书',
id: 'HQ-XF-01-001',
authority: '应急管理部',
validUntil: '2025-09-10',
status: '已过期',
statusType: 'error'
},
{
key: '2',
no: '02',
name: '安全预评估报告',
type: '安全三同时',
id: 'HQ-XF-02-015',
authority: '第三方评估机构',
validUntil: '2025-09-10',
status: '有效',
statusType: 'warning'
},
{
key: '3',
no: '03',
name: '施工资质证书',
type: '资质证书',
id: 'HQ-XF-03-007',
authority: '3设计院',
validUntil: '2025-09-10',
status: '有效',
statusType: 'success'
},
{
key: '4',
no: '04',
name: '安全标准化证书',
type: '资质证书',
id: 'HQ-XF-03-007',
authority: '第三方评估机构',
validUntil: '2025-09-10',
status: '有效',
statusType: 'success'
},
{
key: '5',
no: '05',
name: '消防验收合格证',
type: '消防证书',
id: 'HQ-XF-05-012',
authority: '消防局',
validUntil: '2026-03-15',
status: '有效',
statusType: 'success'
},
{
key: '6',
no: '06',
name: '职业健康安全管理体系认证',
type: '管理体系认证',
id: 'HQ-XF-06-008',
authority: '认证机构',
validUntil: '2026-06-20',
status: '有效',
statusType: 'success'
},
{
key: '7',
no: '07',
name: '环境管理体系认证',
type: '管理体系认证',
id: 'HQ-XF-07-009',
authority: '认证机构',
validUntil: '2026-08-25',
status: '有效',
statusType: 'success'
},
{
key: '8',
no: '08',
name: '特种设备使用登记证',
type: '特种设备证书',
id: 'HQ-XF-08-011',
authority: '质量技术监督局',
validUntil: '2026-12-10',
status: '有效',
statusType: 'success'
},
{
key: '9',
no: '09',
name: '危险化学品经营许可证',
type: '经营许可证',
id: 'HQ-XF-09-013',
authority: '应急管理局',
validUntil: '2027-01-30',
status: '有效',
statusType: 'success'
},
{
key: '10',
no: '10',
name: '辐射安全许可证',
type: '辐射安全证书',
id: 'HQ-XF-10-014',
authority: '生态环境部',
validUntil: '2027-04-18',
status: '有效',
statusType: 'success'
}
];
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'no',
key: 'no',
width: 80,
},
{
title: '证照名称',
dataIndex: 'name',
key: 'name',
width: 150,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 120,
},
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 150,
},
{
title: '发证机关',
dataIndex: 'authority',
key: 'authority',
width: 150,
},
{
title: '有效期至',
dataIndex: 'validUntil',
key: 'validUntil',
width: 120,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 120,
render: (text, record) => {
const getStatusStyle = (status) => {
if (status === '有效') {
return {
color: '#44BB5F',
backgroundColor: '#D8F7DE',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
} else if (status === '即将到期') {
return {
color: '#FF8800',
backgroundColor: '#FFF3E9',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
} else if (status === '已过期') {
return {
color: '#FF3E48',
backgroundColor: '#FFE0E2',
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
display: 'inline-block'
};
}
return {};
};
return (
<span style={getStatusStyle(text)}>
{text}
</span>
);
}
},
{
title: '操作',
dataIndex: 'action',
key: 'action',
width: 120,
render: (text, record) => {
const handleEdit = (record) => {
console.log('编辑记录:', record);
};
const handleDelete = (record) => {
console.log('删除记录:', record);
};
return (
<div style={{
display: 'flex',
gap: '8px',
justifyContent: 'center',
alignItems: 'center'
}}>
<Button
onClick={() => handleEdit(record)}
style={{
color: '#2E4CD4',
backgroundColor: 'transparent',
// borderColor: '#E6E9FB',
fontSize: '12px',
height: '28px',
padding: '0 12px'
}}
>
更新
</Button>
<Button
onClick={() => handleDelete(record)}
style={{
color: '#2E4CD4',
backgroundColor: 'transparent',
// borderColor: '#E6E9FB',
fontSize: '12px',
height: '28px',
padding: '0 12px'
}}
>
查看
</Button>
</div>
);
}
},
];
return (
<div className={styles.licenseManagementContainer}>
<div className={styles.topSectionContainer}>
<div className={styles.firstBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证件类型分布</span>
</div>
<div className={styles.chartContainer}>
<div ref={chartRef} className={styles.chart}></div>
</div>
</div>
<div className={styles.secondBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证件状态概览</span>
</div>
<div className={styles.chartContainer}>
{/* 上半部分:进度条和百分比 */}
<div className={styles.progressSection}>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>有效证照</div>
<div className={styles.progressWrapper}>
<Progress
percent={50}
strokeColor="#3C7DFF"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>50%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>即将到期</div>
<div className={styles.progressWrapper}>
<Progress
percent={15}
strokeColor="#FFC403"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>15%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>已过期</div>
<div className={styles.progressWrapper}>
<Progress
percent={20}
strokeColor="#FF3E48"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>20%</span>
</div>
</div>
<div className={styles.progressItem}>
<div className={styles.progressLabel}>待审核材料</div>
<div className={styles.progressWrapper}>
<Progress
percent={15}
strokeColor="#FF8800"
trailColor="#F0F0F0"
showInfo={false}
className={styles.customProgress}
/>
<span className={styles.progressPercent}>15%</span>
</div>
</div>
</div>
{/* 下半部分:数字统计 */}
<div className={styles.statsSection}>
<Row gutter={[16, 16]}>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#3C7DFF' }}>42</div>
<div className={styles.statLabel}>总证照数</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FFC403' }}>8</div>
<div className={styles.statLabel}>即将过期</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FF3E48' }}>6</div>
<div className={styles.statLabel}>已过期</div>
</div>
</Col>
<Col span={6}>
<div className={styles.statItem}>
<div className={styles.statNumber} style={{ color: '#FF8800' }}>6</div>
<div className={styles.statLabel}>待审核材料</div>
</div>
</Col>
</Row>
</div>
</div>
</div>
<div className={styles.thirdBlock}>
<div className={styles.chartHeader}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>临期预警</span>
</div>
<div className={styles.chartContainer}>
{/* 透明块容器 */}
<div className={styles.transparentBlock}>
{/* 四个垂直分布的卡片 */}
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>安全生产许可证</div>
<div className={styles.licenseNumber}>编号: AQXK-2023-0582</div>
</div>
<div className={styles.expiryTag}>
<span className={styles.expiryText}>15天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>安全评估报告</div>
<div className={styles.licenseNumber}>编号: AQPG-2023-0125</div>
</div>
<div className={styles.expiryTag}>
<span className={styles.expiryText}>30天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>施工资质证书</div>
<div className={styles.licenseNumber}>编号: SGZZ-2023-0089</div>
</div>
<div className={styles.expiryTag} style={{ backgroundColor: '#FFE0E2' }}>
<span className={styles.expiryText} style={{ color: '#FF2526' }}>7天后到期</span>
</div>
</div>
<div className={styles.licenseCard}>
<div className={styles.cardContent}>
<div className={styles.licenseName}>应急预案</div>
<div className={styles.licenseNumber}>编号: YJYA-2023-0045</div>
</div>
<div className={styles.expiryTag} style={{ backgroundColor: '#FFE0E2' }}>
<span className={styles.expiryText} style={{ color: '#FF2526' }}>4天后到期</span>
</div>
</div>
</div>
</div>
</div>
</div>
{/* 证照列表区域 */}
<div className={styles.listCard}>
<div className={styles.chartHeader}>
<div className={styles.headerLeft}>
<div className={styles.colorBlock}></div>
<span className={styles.chartTitle}>证照列表</span>
</div>
<div className={styles.headerRight}>
<Search
placeholder="搜索证照名称或编号..."
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onSearch={(value) => console.log('搜索:', value)}
className={styles.searchInput}
/>
<Select
value={selectedType}
onChange={setSelectedType}
className={styles.typeSelector}
>
<Option value="all">全部类型</Option>
<Option value="safety">安全生产许可证</Option>
<Option value="assessment">安全评估报告</Option>
<Option value="construction">施工资质证书</Option>
<Option value="emergency">应急预案</Option>
<Option value="other">其他</Option>
</Select>
<Button
type="primary"
className={styles.addButton}
onClick={() => console.log('新增证照')}
>
新增证照
</Button>
</div>
</div>
<StandardTable
columns={columns}
data={{
list: tableData, // ======== 表格数据列表 ========
pagination: { // ======== 分页配置 ========
currentPage: 1, // ======== 当前页码 ========
pageSize: 5, // ======== 每页显示5条数据 ========
total: tableData.length, // ======== 总数据条数 ========
} // ======== 分页配置结束 ========
}} // ======== 数据对象结束 ========
selectedRows={[]} // ======== 选中的行数据,初始为空数组 ========
onSelectRow={() => { }} // ======== 行选择事件处理函数 ========
onChange={() => { }} // ======== 表格变化事件处理函数 ========
pagination={{
currentPage: 1,
pageSize: 5,
total: tableData.length,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
locale: {
jump_to: '前往',
page: '页',
items_per_page: '条/页',
}
}}
/>
</div>
</div>
);
};
export default LicenseManagement;

@ -0,0 +1,498 @@
.licenseManagementContainer {
height: 90vh;
.topSectionContainer {
padding: 0;
margin: 15px 0px 15px 5px;
height: 40%;
display: flex;
gap: 15px;
align-items: stretch;
.firstBlock {
width: 30%;
background-color: #fff;
display: flex;
flex-direction: column;
padding: 10px 16px;
border-radius: 2px;
.chartHeader {
display: flex;
align-items: center;
margin-bottom: 16px;
.colorBlock {
width: 2px;
height: 18px;
background-color: #2E4CD4;
margin-right: 8px;
border-radius: 1px;
}
.chartTitle {
font-size: 14px;
font-weight: 500;
color: #333333;
line-height: 18px;
}
}
.chartContainer {
flex: 1;
width: 100%;
position: relative;
.chart {
width: 100%;
height: 100%;
min-height: 200px;
}
// 进度条区域样式
.progressSection {
margin-bottom: 20px;
.progressItem {
margin-bottom: 16px;
.progressLabel {
font-size: 12px;
color: #666;
margin-bottom: 8px;
font-weight: 400;
}
.progressWrapper {
display: flex;
align-items: center;
gap: 12px;
.customProgress {
flex: 1;
:global(.ant-progress-bg) {
height: 8px !important;
border-radius: 4px;
}
:global(.ant-progress-outer) {
.ant-progress-inner {
background-color: #F0F0F0;
border-radius: 4px;
}
}
}
.progressPercent {
font-size: 12px;
color: #333;
font-weight: 500;
min-width: 30px;
text-align: right;
}
}
}
}
// 数字统计区域样式
.statsSection {
.statItem {
text-align: center;
padding: 8px;
.statNumber {
font-size: 24px;
font-weight: 600;
line-height: 1.2;
margin-bottom: 4px;
}
.statLabel {
font-size: 12px;
color: #666;
font-weight: 400;
}
}
}
}
}
.secondBlock {
width: 30%;
background-color: #fff;
display: flex;
flex-direction: column;
padding: 10px 16px;
border-radius: 2px;
.chartHeader {
display: flex;
align-items: center;
margin-bottom: 8px;
.colorBlock {
width: 2px;
height: 18px;
background-color: #2E4CD4;
margin-right: 8px;
border-radius: 1px;
}
.chartTitle {
font-size: 14px;
font-weight: 500;
color: #333333;
// line-height: 18px;
}
}
.chartContainer {
flex: 1;
width: 100%;
position: relative;
// 进度条区域样式
.progressSection {
// margin-bottom: 20px;
.progressItem {
// margin-bottom: 16px;
.progressLabel {
font-size: 10px;
color: #666;
// margin-bottom: 8px;
font-weight: 400;
}
.progressWrapper {
display: flex;
align-items: center;
gap: 5px;
.customProgress {
flex: 1;
:global(.ant-progress-bg) {
height: 8px !important;
border-radius: 4px;
}
:global(.ant-progress-outer) {
.ant-progress-inner {
background-color: #F0F0F0;
border-radius: 4px;
}
}
}
.progressPercent {
font-size: 12px;
color: #333;
font-weight: 500;
min-width: 30px;
text-align: right;
}
}
}
}
// 数字统计区域样式
.statsSection {
.statItem {
text-align: center;
padding: 0px 2px 2px 2px;
.statNumber {
font-size: 22px;
font-weight: 600;
line-height: 1.2;
margin-bottom: 4px;
}
.statLabel {
font-size: 12px;
color: #666;
font-weight: 400;
}
}
}
}
}
.thirdBlock {
flex: 1;
background-image: url('@/assets/business_basic/background_lqyj.svg');
background-color: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
display: flex;
flex-direction: column;
padding: 10px 16px;
border-radius: 2px;
.chartHeader {
display: flex;
align-items: center;
margin-bottom: 8px;
.colorBlock {
width: 2px;
height: 18px;
background-color: #2E4CD4;
margin-right: 8px;
border-radius: 1px;
}
.chartTitle {
font-size: 14px;
font-weight: 500;
color: #333333;
}
}
.chartContainer {
flex: 1;
width: 100%;
position: relative;
// 透明块容器样式
.transparentBlock {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
padding: 4px 8px;
.licenseCard {
width: 60%;
height: auto;
background-color: #FFF9F4;
border: 1px solid #FFD7BB;
border-radius: 2px;
padding: 5px 8px;
display: flex;
justify-content: space-between;
align-items: center;
// box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.cardContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.licenseName {
font-size: 12px;
font-weight: 500;
color: #333;
line-height: 1.2;
}
.licenseNumber {
font-size: 12px;
color: #666;
font-weight: 400;
}
}
.expiryTag {
width: 38%;
background-color: #FFEDDE;
border-radius: 2px;
padding: 5px 12px;
margin-left: 12px;
.expiryText {
font-size: 12px;
font-weight: 500;
display: flex;
align-items: center;
color: #D46B08;
}
}
}
}
}
}
}
.listCard {
padding: 0;
padding: 15px 5px 15px 20px;
flex: 1;
// display: flex;
gap: 15px;
background-color: #fff;
// align-items: stretch;
.chartHeader {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
.headerLeft {
display: flex;
align-items: center;
.colorBlock {
width: 2px;
height: 18px;
background-color: #2E4CD4;
margin-right: 8px;
border-radius: 1px;
}
.chartTitle {
font-size: 14px;
font-weight: 500;
color: #333333;
line-height: 18px;
}
}
.headerRight {
display: flex;
align-items: center;
gap: 12px;
.searchInput {
width: 280px;
.ant-input {
border-radius: 2px;
border: 1px solid #d9d9d9;
&:hover {
border-color: #40a9ff;
}
&:focus {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
}
.typeSelector {
width: 120px;
.ant-select-selector {
border-radius: 2px;
border: 1px solid #d9d9d9;
&:hover {
border-color: #40a9ff;
}
}
&.ant-select-focused .ant-select-selector {
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
}
.addButton {
border-radius: 4px;
background-color: #2E4CD4;
// border-color: #1890ff;
height: 32px;
padding: 4px 15px;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #2E4CD4;
// border-color: #40a9ff;
}
&:focus {
background-color: #2E4CD4;
// border-color: #40a9ff;
}
}
}
}
// StandardTable 组件样式
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-pagination-options-quick-jumper input) {
text-align: center !important;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
// 覆盖Ant Design默认样式
.licenseManagementContainer {
.ant-card {
box-shadow: none;
}
.ant-card-body {
padding: 20px;
}
.ant-table {
font-size: 14px;
}
.ant-tag {
border-radius: 4px;
font-size: 12px;
padding: 2px 8px;
}
.ant-btn-link {
padding: 0;
height: auto;
font-size: 14px;
}
.ant-input-search {
.ant-input {
border-radius: 6px;
}
}
.ant-select {
.ant-select-selector {
border-radius: 6px;
}
}
.ant-btn-primary {
border-radius: 6px;
}
}

@ -0,0 +1,48 @@
import React, { useState } from 'react';
import { Button } from 'antd';
import styles from './FireWarning.less';
import RealtimeMonitoring from './components/RealtimeMonitoring';
import DataAnalysisWarning from './components/DataAnalysisWarning';
const Firewarning = () => {
const [activeModule, setActiveModule] = useState('realtime');
const handleModuleClick = (module) => {
setActiveModule(module);
};
const renderModule = () => {
switch (activeModule) {
case 'realtime':
return <RealtimeMonitoring />;
case 'analysis':
return <DataAnalysisWarning />;
default:
return <RealtimeMonitoring />;
}
};
return (
<div className={styles.firewarningContainer}>
<div className={styles.firewarningTopButton}>
<Button
className={`${styles.firewarningTopButtonItem} ${activeModule === "realtime" ? styles.active : ""}`}
onClick={() => handleModuleClick("realtime")}
>
实时状态监测
</Button>
<Button
className={`${styles.firewarningTopButtonItem} ${activeModule === "analysis" ? styles.active : ""}`}
onClick={() => handleModuleClick("analysis")}
>
数据分析与预警
</Button>
</div>
<div className={styles.firewarningContent}>
{renderModule()}
</div>
</div>
);
};
export default Firewarning;

@ -0,0 +1,66 @@
.firewarningContainer {
background-color: transparent;
width: 100%;
height: 89vh;
overflow: hidden;
display: flex;
flex-direction: column;
.firewarningTopButton {
background-color: white;
width: 100%;
padding: 10px 30px;
display: flex;
gap: 24px;
margin-left: 6px;
.firewarningTopButtonItem {
background-color: transparent !important;
color: #333333 !important;
font-family: 'PingFang SC', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
line-height: 100% !important;
border-radius: 8px !important;
padding: 6px 10px !important;
height: auto !important;
border: none !important;
box-shadow: none !important;
position: relative !important;
&:hover {
color: #333333 !important;
border: none !important;
}
&:focus {
color: #2E4CD4 !important;
border: none !important;
}
&.active {
color: #2E4CD4 !important;
&::after {
content: '';
position: absolute;
bottom: -10px;
left: 0;
right: 0;
width: 100%;
height: 4px;
background-color: #2E4CD4;
border-radius: 0;
opacity: 1;
}
}
}
}
.firewarningContent {
// ======== 内容区域样式 ========
flex: 1; // ======== 占据剩余空间 ========
overflow-y: auto; // ======== 允许垂直滚动 ========
padding: 0; // ======== 无内边距 ========
}
}

@ -0,0 +1,922 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented, Progress, Input } from 'antd';
import { CheckCircleOutlined, ExportOutlined, HeartFilled, LineHeightOutlined, ExclamationCircleOutlined, SearchOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './DataAnalysisWarning.less';
import img1 from '@/assets/safe_majorHazard/online_monitoring/img1.png';
import img2 from '@/assets/safe_majorHazard/online_monitoring/img2.png';
import img3 from '@/assets/safe_majorHazard/online_monitoring/img3.png';
import map1 from '@/assets/safe_majorHazard/online_monitoring/map.png';
import risk1 from '@/assets/safe_majorHazard/online_monitoring/risk1.png';
import risk2 from '@/assets/safe_majorHazard/online_monitoring/risk2.png';
import risk3 from '@/assets/safe_majorHazard/online_monitoring/risk3.png';
import eqicon1 from '@/assets/business_basic/eqicon1.png';
import eqicon2 from '@/assets/business_basic/eqicon2.png';
import eqicon3 from '@/assets/business_basic/eqicon3.png';
import eqicon4 from '@/assets/business_basic/eqicon4.png';
const DataAnalysisWarning = () => {
const chartRef = useRef(null);
const pieChartRef = useRef(null);
const faultPieChartRef = useRef(null);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 8,
total: 0,
});
const [searchText, setSearchText] = useState('');
// 柱状图初始化
useEffect(() => {
if (pieChartRef.current) {
const barChart = echarts.init(pieChartRef.current);
const barOption = {
grid: {
left: '5%',
right: '5%',
bottom: '10%',
top: '20%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['灭火器', '消火栓', '报警器', '疏散灯', '排烟设备'],
axisLabel: {
fontSize: 12,
color: '#333',
interval: 0,
rotate: 0
},
axisLine: {
show: false
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 50,
interval: 10,
axisLabel: {
fontSize: 12,
color: '#666',
formatter: '{value}'
},
axisLine: {
show: false
},
axisTick: {
show: false
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [{
name: '使用次数',
type: 'bar',
barWidth: 27,
data: [35, 28, 42, 31, 38],
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#199BFB' },
{ offset: 1, color: '#1373FA' }
]
}
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#0D7AE8' },
{ offset: 1, color: '#0F5BC7' }
]
}
}
}
}],
legend: {
show: true,
top: '5%',
left: 'center',
itemWidth: 15,
itemHeight: 3,
textStyle: {
fontSize: 12,
color: '#333'
},
data: [{
name: '使用次数',
icon: 'rect',
itemStyle: {
color: '#4B69F1'
}
}]
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params) {
return `${params[0].name}<br/>使用次数: ${params[0].value}`;
}
}
};
barChart.setOption(barOption);
// 响应式调整
const handleBarResize = () => {
if (barChart && !barChart.isDisposed()) {
barChart.resize();
}
};
window.addEventListener('resize', handleBarResize);
return () => {
window.removeEventListener('resize', handleBarResize);
if (barChart && !barChart.isDisposed()) {
barChart.dispose();
}
};
}
}, []);
// 维护费用趋势折线图初始化
useEffect(() => {
if (faultPieChartRef.current) {
const faultPieChart = echarts.init(faultPieChartRef.current);
const faultPieOption = {
legend: {
show: true,
top: '5%',
left: 'center',
itemWidth: 20,
itemHeight: 8,
textStyle: {
color: '#333',
fontSize: 12
}
},
grid: {
left: '5%',
right: '5%',
bottom: '10%',
top: '20%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLine: {
lineStyle: {
color: '#E5E5E5'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0
}
},
yAxis: {
type: 'value',
min: 20000,
max: 30000,
interval: 2000,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
fontSize: 12,
formatter: '¥{value}'
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [{
name: '费用',
type: 'line',
data: [29000, 21000, 27500, 21900, 26000, 25000, 27000, 24000, 22300, 28000, 29000, 27000],
smooth: false,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#1269FF',
width: 1
},
itemStyle: {
color: '#fff',
borderColor: '#1269FF',
borderWidth: 1
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(18, 105, 255, 0.3)'
}, {
offset: 1,
color: 'rgba(18, 105, 255, 0.05)'
}]
}
}
}]
};
faultPieChart.setOption(faultPieOption);
// 响应式调整
const handleFaultPieResize = () => {
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.resize();
}
};
window.addEventListener('resize', handleFaultPieResize);
return () => {
window.removeEventListener('resize', handleFaultPieResize);
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.dispose();
}
};
}
}, []);
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
// 强制初始化时调整大小
setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 100);
const option = {
color: ['#3C7EFF', '#FF8800', '#FFC403', '#31BCFF'],
legend: {
orient: 'vertical',
right: '2%',
top: 'middle',
itemWidth: 14,
itemHeight: 5,
textStyle: {
fontSize: 10,
color: '#666'
}
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{d}%'
},
series: [
{
name: '设备类型占比',
type: 'pie',
radius: '70%',
center: ['40%', '55%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 1
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false
}
},
labelLine: {
show: false
},
data: [
{ value: 25, name: '灭火器' },
{ value: 30, name: '消防栓' },
{ value: 20, name: '报警器' },
{ value: 25, name: '烟雾探测器' }
]
}
]
};
chart.setOption(option);
// 响应式调整 - 使用多种方式监听容器尺寸变化
let resizeTimer = null;
const handleResize = () => {
// 防抖处理避免频繁调用resize
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 50); // 减少延迟时间
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题)
let resizeObserver = null;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
// 使用requestAnimationFrame确保在下一帧执行
requestAnimationFrame(() => {
handleResize();
});
}
});
resizeObserver.observe(chartRef.current);
}
// 额外监听父容器的尺寸变化
const parentContainer = chartRef.current?.parentElement;
let parentObserver = null;
if (parentContainer && window.ResizeObserver) {
parentObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
requestAnimationFrame(() => {
handleResize();
});
}
});
parentObserver.observe(parentContainer);
}
// 使用MutationObserver监听DOM结构变化菜单展开收起时
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'class' || mutation.attributeName === 'style')) {
// 延迟执行确保DOM更新完成
setTimeout(() => {
handleResize();
}, 200);
}
});
});
// 监听整个页面的class和style变化
mutationObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class', 'style'],
subtree: true
});
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
if (parentObserver) {
parentObserver.disconnect();
}
if (mutationObserver) {
mutationObserver.disconnect();
}
if (resizeTimer) {
clearTimeout(resizeTimer);
}
if (chart && !chart.isDisposed()) {
chart.dispose();
}
};
}
}, []);
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 60,
render: (text, record, index) => {
const page = pagination.current || 1;
const pageSize = pagination.pageSize || 8;
const number = (page - 1) * pageSize + index + 1;
return `0${number}`.slice(-2);
}
},
{
title: '设备编号',
dataIndex: 'deviceId',
key: 'deviceId',
width: 140,
},
{
title: '设备名称',
dataIndex: 'deviceName',
key: 'deviceName',
width: 110,
},
{
title: '类型',
dataIndex: 'modelSpec',
key: 'modelSpec',
width: 120,
},
{
title: '安装位置',
dataIndex: 'installLocation',
key: 'installLocation',
width: 100,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (text) => {
const statusMap = {
'报废': { color: '#FF3E48', bg: '#FFE0E2' },
'待维修': { color: '#FF8800', bg: '#FFF3E9' },
'已使用': { color: '#00AAFA', bg: '#DAF3FF' },
'正常': { color: '#44BB5F', bg: '#D8F7DE' }
};
const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' };
return (
<span style={{
color: status.color,
backgroundColor: status.bg,
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px'
}}>
{text}
</span>
);
}
},
{
title: '最后维护时间',
dataIndex: 'lastMaintenance',
key: 'lastMaintenance',
width: 150,
},
{
title: '操作',
key: 'action',
width: 140,
render: (_, record) => (
<div>
<Button type="link" size="small" style={{
padding: '2px 8px',
fontSize: 12,
marginRight: 8,
border: '1px solid #E6E9FB',
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
编辑
</Button>
<Button type="link" size="small" style={{
padding: '2px 8px',
fontSize: 12,
color: '#FF2526',
border: '1px solid #FFE0E2',
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
删除
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
deviceId: 'HQ-XF-01-001',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '1层大厅',
status: '报废',
lastMaintenance: '2025-09-10',
},
{
key: '2',
id: '002',
deviceId: 'HQ-XF-02-015',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '3层东区',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '3',
id: '003',
deviceId: 'HQ-XF-03-007',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '4',
id: '004',
deviceId: 'HQ-XF-03-008',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '5',
id: '005',
deviceId: 'HQ-XF-01-009',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '报废',
lastMaintenance: '2025-09-10',
},
{
key: '6',
id: '006',
deviceId: 'HQ-XF-01-010',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '7',
id: '007',
deviceId: 'HQ-XF-01-011',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '8',
id: '008',
deviceId: 'HQ-XF-01-012',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '9',
id: '009',
deviceId: 'HQ-XF-01-013',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '10',
id: '010',
deviceId: 'HQ-XF-01-014',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '11',
id: '011',
deviceId: 'HQ-XF-01-015',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '12',
id: '012',
deviceId: 'HQ-XF-01-016',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '13',
id: '013',
deviceId: 'HQ-XF-01-017',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '2层西区',
status: '报废',
lastMaintenance: '2024-08-15',
},
{
key: '14',
id: '014',
deviceId: 'HQ-XF-02-018',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '4层南区',
status: '报废',
lastMaintenance: '2024-07-20',
},
{
key: '15',
id: '015',
deviceId: 'HQ-XF-03-019',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下二层',
status: '报废',
lastMaintenance: '2024-06-10',
},
{
key: '16',
id: '016',
deviceId: 'HQ-XF-01-020',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '5层北区',
status: '报废',
lastMaintenance: '2024-05-05',
},
];
// 初始化数据
useEffect(() => {
setPagination(prev => ({ ...prev, total: mockData.length }));
}, []);
// 根据分页获取当前页数据
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const startIndex = (current - 1) * pageSize;
const endIndex = startIndex + pageSize;
return mockData.slice(startIndex, endIndex);
};
// 表格选择变化
const onSelectChange = (newSelectedRowKeys, newSelectedRows) => {
setSelectedRowKeys(newSelectedRowKeys);
setSelectedRows(newSelectedRows);
};
// 新增设备按钮点击事件
const handleAddDevice = () => {
console.log('新增设备');
// TODO: 实现新增设备逻辑
};
// 导出数据按钮点击事件
const handleExportData = () => {
console.log('导出数据');
// TODO: 实现导出数据逻辑
};
// 分页变化处理
const handleTableChange = (pagination) => {
setPagination(prev => ({
...prev,
current: pagination.current,
pageSize: pagination.pageSize,
}));
};
// 搜索处理
const handleSearchChange = (e) => {
setSearchText(e.target.value);
console.log('搜索:', e.target.value);
// TODO: 实现搜索逻辑,根据设备名称、编号等筛选数据
};
return (
<div className={styles.analysisContainer}>
{/* 第1个div - 高度39% */}
<div className={styles.analysisContainerMiddle}>
<div className={styles.analysisSectionContent}>
<div className={styles.analysisMiddleBlock1}>
<div className={styles.analysisBlock1Header}>
<div className={styles.analysisBlock1Title}>
<div className={styles.analysisTitleIcon}></div>
设备使用频率分析
</div>
</div>
{/* 设备状态饼图 */}
<div className={styles.analysisDeviceStatusChart} ref={pieChartRef}>
</div>
</div>
<div className={styles.analysisMiddleBlock12}>
<div className={styles.analysisBlock1Header}>
<div className={styles.analysisBlock1Title}>
<div className={styles.analysisTitleIcon}></div>
近一年维护费用趋势
</div>
</div>
{/* 维护费用趋势折线图 */}
<div className={styles.analysisDeviceStatusChart} ref={faultPieChartRef}>
</div>
</div>
<div className={styles.analysisMiddleBlock2}>
<div className={styles.analysisMiddleBlock2Title}>
<div className={styles.analysisTitleLeft}>
<div className={styles.analysisTitleIcon}></div>
<div>设备类型占比</div>
</div>
</div>
<div className={styles.analysisMiddleBlock2Chart} ref={chartRef}>
</div>
</div>
</div>
</div>
{/* 第2个div - 占满剩余位置 */}
<div className={styles.analysisContainerBottom}>
<div className={styles.analysisSectionContent}>
<div className={styles.analysisLeftBlock}>
<div className={styles.analysisMaintenanceStack}>
<div className={styles.analysisMaintenanceSection}>
<div className={styles.analysisMaintenanceTitle}>
<div className={styles.analysisTitleIcon}></div>
<div>近期维护提醒</div>
</div>
<div className={styles.analysisMaintenanceContent1}>
<div className={styles.analysisMaintenanceItem}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>SH-MHQ-023-C 干粉灭火器</div>
<div className={styles.analysisMaintenanceText2}>位置: 4楼办公区丨维护类型: 季度检查</div>
<div className={styles.analysisMaintenanceText3}>负责人: 张三</div>
</div>
<div className={styles.analysisMaintenanceRight}>
<div className={styles.analysisMaintenanceStatus}>3天后到期</div>
</div>
</div>
<div className={styles.analysisMaintenanceItem}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>SH-XHS-045-D 室内消火栓</div>
<div className={styles.analysisMaintenanceText2}>位置: 2楼东侧走廊丨维护类型: 水压测试</div>
<div className={styles.analysisMaintenanceText3}>负责人: 李四</div>
</div>
<div className={styles.analysisMaintenanceRight2}>
<div className={styles.analysisMaintenanceStatus}>8天后到期</div>
</div>
</div>
</div>
</div>
<div className={styles.analysisMaintenanceSection}>
<div className={styles.analysisMaintenanceTitle}>
<div className={styles.analysisTitleIcon}></div>
<div>维护任务进度</div>
</div>
<div className={styles.analysisMaintenanceContent2}>
{/* 进度条区域 */}
<div className={styles.analysisProgressSection}>
<div className={styles.analysisProgressLabel}>月度维护计划</div>
<Progress percent={75} status="active" />
<div className={styles.analysisProgressLabel}>季度维护计划</div>
<Progress percent={60} status="active" />
<div className={styles.analysisProgressLabel}>年度维护计划</div>
<Progress percent={85} status="active" />
{/* 警告提示框 */}
<div className={styles.analysisWarningBox}>
<ExclamationCircleOutlined className={styles.analysisWarningIcon} />
<span className={styles.analysisWarningText}>本月有5项维护任务即将到期</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div className={styles.analysisRightBlock}>
{/* 表格 */}
<div className={styles.analysisTableHeader}>
<div className={styles.analysisTableTitle}>
<div className={styles.analysisTitleIcon}></div>
<div>消防设施与器材列表</div>
</div>
</div>
{/* 操作按钮 */}
<div className={styles.analysisTableActions}>
<div className={styles.analysisLeftActions}>
<Input
placeholder="搜索设备名称、编号..."
onChange={handleSearchChange}
value={searchText}
style={{ width: 250, fontSize: 12 }}
allowClear
suffix={<SearchOutlined />}
/>
</div>
<div className={styles.analysisRightActions}>
<button className={styles.analysisActionButton} onClick={handleAddDevice}>
<span className={styles.analysisButtonIcon}>+</span>
<span>新增设备</span>
</button>
<button className={styles.analysisActionButton} onClick={handleExportData}>
<span className={styles.analysisButtonIcon}><ExportOutlined /></span>
<span>导出数据</span>
</button>
</div>
</div>
{/* 表格 */}
<div className={styles.analysisTableContainer}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: pagination
}}
loading={loading}
selectionType="checkbox"
onSelectRow={onSelectChange}
onChange={handleTableChange}
pagination={{
...pagination,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
}}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default DataAnalysisWarning;

@ -0,0 +1,558 @@
.analysisContainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第二个div - 高度35%
.analysisContainerMiddle {
// height: 400px;
min-height: 35%;
border-radius: 4px;
display: flex;
flex-direction: column;
.analysisSectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.analysisMiddleBlock1 {
width: 30%;
height: 100%;
background: #fff;
border: 2px solid #fff;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
z-index: 10;
min-height: 100%;
}
}
.analysisMiddleBlock12 {
flex: 1;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
z-index: 10;
}
}
.analysisMiddleBlock12 {
width: 45%;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
min-height: 100%;
z-index: 10;
}
}
.analysisMiddleBlock2 {
// flex: 1;
width: calc(100% - 75% - 15px);
height: 100%;
// background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%);
// border: 2px solid #fff;
background-color: #fff;
// border-radius: 4px;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.analysisMiddleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
.analysisTitleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisMiddleBlock2Chart {
width: 100%;
height: 100%;
}
}
}
}
// 第三个div - 占满剩余位置
.analysisContainerBottom {
display: flex;
flex-direction: column;
flex-shrink: 0;
.analysisSectionContent {
display: flex;
flex-direction: row;
flex: 1;
gap: 10px;
padding: 0;
.analysisLeftBlock {
width: 30%;
flex-shrink: 0;
height: 100%;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
.analysisLeftBlockTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 10px;
.analysisTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.analysisMaintenanceStack {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
.analysisMaintenanceSection {
width: 100%;
height: 50%;
background: #FFF;
border-radius: 4px;
display: flex;
flex-direction: column;
padding: 12px 14px;
.analysisMaintenanceTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 8px;
}
.analysisTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
.analysisMaintenanceContent {
flex: 1;
width: 100%;
}
.analysisMaintenanceContent1 {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
.analysisMaintenanceItem {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #F1F7FF;
border-radius: 4px;
padding: 16px 16px;
.analysisMaintenanceLeft {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.analysisMaintenanceText1 {
font-size: 14px;
font-weight: 500;
color: #333333;
font-family: PingFang SC;
}
.analysisMaintenanceText2 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
.analysisMaintenanceText3 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
}
.analysisMaintenanceRight {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.analysisMaintenanceStatus {
font-size: 12px;
color: #FF3E48;
font-weight: 500;
font-family: PingFang SC;
background-color: #FFE0E2;
padding: 4px 8px;
border-radius: 4px;
// border: 1px solid #FFE0E2;
}
}
.analysisMaintenanceRight2 {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.analysisMaintenanceStatus {
font-size: 12px;
color: #FF8800;
font-weight: 500;
font-family: PingFang SC;
background-color: #FFF3E9;
padding: 4px 8px;
border-radius: 4px;
// padding-right: 2px;
}
}
}
}
.analysisMaintenanceContent2 {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 15px;
padding: 8px 0;
.analysisWarningBox {
display: flex;
align-items: center;
gap: 8px;
background-color: #FFF3CD;
border: 1px solid #F4E3AE;
border-radius: 4px;
padding: 8px 12px;
// margin-bottom: 8px;
// margin-top: 10px;
.analysisWarningIcon {
color: #8C6C0B;
font-size: 14px;
}
.analysisWarningText {
color: #8C6C0B;
font-size: 12px;
font-family: PingFang SC;
font-weight: 400;
}
}
.analysisProgressSection {
width: 100%;
display: flex;
flex-direction: column;
// gap: 12px;
padding: 0px 12px 12px 12px;
.analysisProgressLabel {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
font-weight: 400;
}
// 自定义进度条样式
:global(.ant-progress) {
.ant-progress-bg {
background: linear-gradient(90deg, #2E4CD4 0%, #4B69F1 100%);
}
.ant-progress-text {
color: #2E4CD4;
font-weight: 500;
}
}
}
}
}
}
}
.analysisRightBlock {
width: calc(100% - 28% - 10px);
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.analysisTableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 15px 5px 15px;
.analysisTableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
.analysisTableActions {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-top: 5px;
padding: 0px 15px;
.analysisLeftActions {
display: flex;
align-items: center;
}
.analysisRightActions {
display: flex;
gap: 8px;
align-items: center;
}
.analysisActionButton {
display: flex;
align-items: center;
gap: 4px;
height: 28px;
border: 1px solid #DFE4F6;
border-radius: 4px;
color: #2E4CD4;
font-weight: 500;
font-size: 12px;
padding: 0px 8px;
background: transparent;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #f0f2ff;
border-color: #2E4CD4;
}
&:active {
background-color: #e6ebff;
}
.analysisButtonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
.analysisTableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
}
}

@ -0,0 +1,848 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented } from 'antd';
import { CheckCircleOutlined, ExportOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './RealtimeMonitoring.less';
// import './RealtimeMonitoring.less';
import eqicon1 from '@/assets/business_basic/eqicon1.png';
// import eqicon2 from '@/assets/business_basic/eqicon2.png';
// import eqicon3 from '@/assets/business_basic/eqicon3.png';
// import eqicon4 from '@/assets/business_basic/eqicon4.png';
import eqicon2 from '@/assets/business_basic/iconre1.svg';
import eqicon3 from '@/assets/business_basic/iconre2.svg';
import eqicon4 from '@/assets/business_basic/iconre3.svg';
// import eqicon4 from '@/assets/business_basic/iconre1.svg';
const RealtimeMonitoring = () => {
const chartRef = useRef(null);
const pieChartRef = useRef(null);
const faultPieChartRef = useRef(null);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 5,
total: 0,
});
// 饼图初始化
useEffect(() => {
if (pieChartRef.current) {
const pieChart = echarts.init(pieChartRef.current);
const pieOption = {
color: ['#4B69F1', '#FFD85A', '#A493FB', '#9AA5D5'],
legend: {
orient: 'vertical',
right: '10%',
top: 'center',
itemWidth: 10,
itemHeight: 3,
textStyle: {
fontSize: 12,
color: '#333'
}
},
series: [{
name: '设备状态',
type: 'pie',
radius: ['40%', '70%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '14',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 480, name: '正常运行' },
{ value: 289, name: '待维护' },
{ value: 200, name: '故障' },
{ value: 150, name: '离线' }
]
}]
};
pieChart.setOption(pieOption);
// 响应式调整
const handlePieResize = () => {
if (pieChart && !pieChart.isDisposed()) {
pieChart.resize();
}
};
window.addEventListener('resize', handlePieResize);
return () => {
window.removeEventListener('resize', handlePieResize);
if (pieChart && !pieChart.isDisposed()) {
pieChart.dispose();
}
};
}
}, []);
// 故障类型饼图初始化
useEffect(() => {
if (faultPieChartRef.current) {
const faultPieChart = echarts.init(faultPieChartRef.current);
const faultPieOption = {
color: ['#FF3E48', '#FF8800', '#FFC403'],
legend: {
orient: 'vertical',
right: '10%',
top: 'center',
itemWidth: 8,
itemHeight: 8,
textStyle: {
fontSize: 12,
color: '#333'
}
},
series: [{
name: '设备故障类型',
type: 'pie',
radius: '70%',
center: ['35%', '50%'],
avoidLabelOverlap: false,
label: {
show: true,
position: 'outside',
formatter: '{b}: {c}',
fontSize: 12
},
emphasis: {
label: {
show: true,
fontSize: '14',
fontWeight: 'bold'
}
},
labelLine: {
show: true
},
data: [
{ value: 120, name: '紧急' },
{ value: 80, name: '重要' },
{ value: 60, name: '一般' }
]
}]
};
faultPieChart.setOption(faultPieOption);
// 响应式调整
const handleFaultPieResize = () => {
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.resize();
}
};
window.addEventListener('resize', handleFaultPieResize);
return () => {
window.removeEventListener('resize', handleFaultPieResize);
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.dispose();
}
};
}
}, []);
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
// 强制初始化时调整大小
setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 100);
const option = {
color: ['#8979FF', '#3CC3DF'],
legend: {
// data: ['消防水泵1', '消防水泵2'],
top: "-3px",
// left: "center",
// itemGap: 40,
itemWidth: 20,
itemHeight: 8,
// icon: 'path://M902 472.7H747.9c-19.1-113.3-117.7-200-236.4-200s-217.3 86.7-236.4 200H119.7c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h155.5c19.1 113.3 117.7 200 236.4 200S728.9 666 748 552.7h154c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z m-390.5 200c-88.2 0-160-71.8-160-160s71.8-160 160-160 160 71.8 160 160-71.8 160-160 160z',
textStyle: {
fontSize: 10
}
},
grid: {
left: '2%',
right: '4%',
bottom: '2%',
top: '12%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00'],
axisLabel: {
fontSize: 10
}
},
yAxis: {
type: 'value',
min: 0,
max: 30,
axisLabel: {
formatter: '{value}',
fontSize: 10
}
},
series: [
{
name: '消防水泵1',
type: 'line',
smooth: false,
lineStyle: {
width: 2,
color: '#8979FF'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(137, 121, 255, 0.3)' },
{ offset: 1, color: 'rgba(137, 121, 255, 0.05)' }
]
}
},
symbol: 'circle',
symbolSize: 4,
itemStyle: {
color: '#fff',
borderColor: '#8979FF',
borderWidth: 1
},
data: [12, 15, 18, 14, 16, 20, 22, 19, 17, 21, 23, 25]
},
{
name: '消防水泵2',
type: 'line',
smooth: false,
lineStyle: {
width: 2,
color: '#3CC3DF'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(60, 195, 223, 0.3)' },
{ offset: 1, color: 'rgba(60, 195, 223, 0.05)' }
]
}
},
symbol: 'circle',
symbolSize: 4,
itemStyle: {
color: '#fff',
borderColor: '#3CC3DF',
borderWidth: 1
},
data: [8, 11, 14, 10, 13, 17, 19, 16, 14, 18, 20, 22]
}
]
};
chart.setOption(option);
// 响应式调整 - 使用多种方式监听容器尺寸变化
let resizeTimer = null;
const handleResize = () => {
// 防抖处理避免频繁调用resize
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 50); // 减少延迟时间
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题)
let resizeObserver = null;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
// 使用requestAnimationFrame确保在下一帧执行
requestAnimationFrame(() => {
handleResize();
});
}
});
resizeObserver.observe(chartRef.current);
}
// 额外监听父容器的尺寸变化
const parentContainer = chartRef.current?.parentElement;
let parentObserver = null;
if (parentContainer && window.ResizeObserver) {
parentObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
requestAnimationFrame(() => {
handleResize();
});
}
});
parentObserver.observe(parentContainer);
}
// 使用MutationObserver监听DOM结构变化菜单展开收起时
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'class' || mutation.attributeName === 'style')) {
// 延迟执行确保DOM更新完成
setTimeout(() => {
handleResize();
}, 200);
}
});
});
// 监听整个页面的class和style变化
mutationObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class', 'style'],
subtree: true
});
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
if (parentObserver) {
parentObserver.disconnect();
}
if (mutationObserver) {
mutationObserver.disconnect();
}
if (resizeTimer) {
clearTimeout(resizeTimer);
}
if (chart && !chart.isDisposed()) {
chart.dispose();
}
};
}
}, []);
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 60,
render: (text, record, index) => {
const page = pagination.current || 1;
const pageSize = pagination.pageSize || 5;
const number = (page - 1) * pageSize + index + 1;
return `0${number}`.slice(-2);
}
},
{
title: '设备编号',
dataIndex: 'deviceId',
key: 'deviceId',
width: 140,
},
{
title: '设备名称',
dataIndex: 'deviceName',
key: 'deviceName',
width: 110,
},
{
title: '型号规格',
dataIndex: 'modelSpec',
key: 'modelSpec',
width: 140,
},
{
title: '安装位置',
dataIndex: 'installLocation',
key: 'installLocation',
width: 200,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80,
render: (text) => {
const statusMap = {
'故障': { color: '#FF4D4F', bg: '#FFF2F0' },
'预警': { color: '#FAAD14', bg: '#FFF3E9' },
'正常': { color: '#44BB5F', bg: '#D8F7DE' }
};
const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' };
return (
<span style={{
color: status.color,
backgroundColor: status.bg,
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px'
}}>
{text}
</span>
);
}
},
{
title: '最后维护',
dataIndex: 'lastMaintenance',
key: 'lastMaintenance',
width: 150,
},
{
title: '操作',
key: 'action',
width: 140,
render: (_, record) => (
<div>
<Button type="link" size="small" style={{
padding: '2px 8px',
fontSize: 12,
marginRight: 8,
border: '1px solid #E6E9FB',
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
编辑
</Button>
<Button type="link" size="small" style={{
padding: '2px 8px',
fontSize: 12,
border: '1px solid #E6E9FB',
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
详情
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼1层大厅',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '2',
id: '002',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼3层 东区',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '3',
id: '003',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '4',
id: '004',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '5',
id: '005',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '6',
id: '006',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '7',
id: '007',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '8',
id: '008',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '9',
id: '009',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '10',
id: '010',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '11',
id: '011',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '12',
id: '012',
deviceId: 'HQ-XF-01-001',
deviceName: '消防水泵',
modelSpec: 'XBD5.0/30-125',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
];
// 初始化数据
useEffect(() => {
setPagination(prev => ({ ...prev, total: mockData.length }));
}, []);
// 根据分页获取当前页数据
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const startIndex = (current - 1) * pageSize;
const endIndex = startIndex + pageSize;
return mockData.slice(startIndex, endIndex);
};
// 表格选择变化
const onSelectChange = (newSelectedRowKeys, newSelectedRows) => {
setSelectedRowKeys(newSelectedRowKeys);
setSelectedRows(newSelectedRows);
};
// 新增设备按钮点击事件
const handleAddDevice = () => {
console.log('新增设备');
// TODO: 实现新增设备逻辑
};
// 导出数据按钮点击事件
const handleExportData = () => {
console.log('导出数据');
// TODO: 实现导出数据逻辑
};
// 分页变化处理
const handleTableChange = (pagination) => {
setPagination(prev => ({
...prev,
current: pagination.current,
pageSize: pagination.pageSize,
}));
};
return (
<div className={styles.realtimeContainer}>
{/* 第一个div - 高度20% */}
<div className={styles.realtimeContainerTop}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeBlocksContainer}>
{/* 块1 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>设备总数</div>
<div className={styles.realtimeBlockNumber}>1280</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon1} alt="设备总数" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>本月报警</div>
<div className={styles.realtimeBlockNumber}>32</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon2} alt="高风险设备" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>巡检任务</div>
<div className={styles.realtimeBlockNumber}>158</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon3} alt="今日预警次数" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>待处理隐患</div>
<div className={styles.realtimeBlockNumber}>19</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon4} alt="未处理预警" className={styles.realtimeBlockImage} />
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeContainerMiddle}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeMiddleBlock2}>
<div className={styles.realtimeMiddleBlock2Title}>
<div className={styles.realtimeTitleLeft}>
<div className={styles.realtimeTitleIcon}></div>
<div>设备运行参数</div>
</div>
</div>
<div className={styles.realtimeMiddleBlock2Chart} ref={chartRef}>
</div>
</div>
<div className={styles.realtimeMiddleBlock1}>
<div className={styles.realtimeBlock1Header}>
<div className={styles.realtimeBlock1Title}>
<div className={styles.realtimeTitleIcon}></div>
设备状态分布
</div>
</div>
{/* 设备状态饼图 */}
<div className={styles.realtimeDeviceStatusChart} ref={pieChartRef}>
</div>
</div>
<div className={styles.realtimeMiddleBlock12}>
<div className={styles.realtimeBlock1Header}>
<div className={styles.realtimeBlock1Title}>
<div className={styles.realtimeTitleIcon}></div>
设备故障类型分布
</div>
<Select
className={styles.realtimeCustomSelect}
style={{
width: 120,
display: 'flex',
alignItems: 'center'
}}
defaultValue="全部区域"
options={[
{ value: '全部区域', label: '全部区域' },
{ value: '部分区域', label: '部分区域' },
]}
/>
</div>
{/* 设备故障类型饼图 */}
<div className={styles.realtimeDeviceStatusChart} ref={faultPieChartRef}>
</div>
</div>
</div>
</div>
{/* 第三个div - 占满剩余位置 */}
<div className={styles.realtimeContainerBottom}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeLeftBlock}>
{/* 第一行块 - 蓝色方块加标题 */}
<div className={styles.realtimeLeftBlockTitle}>
<div className={styles.realtimeTitleIcon}></div>
<div>预警信息</div>
</div>
<div className={styles.realtimeDevelopmentContainer}>
<div className={styles.realtimeDevelopmentBlock1}>
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>灭火器压力不足</div>
<div className={styles.realtimeSubText}>2号楼3层 15分钟前</div>
</div>
<div className={styles.realtimeRightContent}>
<div className={styles.realtimeImportantTag}>重要</div>
</div>
</div>
<div className={styles.realtimeDevelopmentBlock1}>
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>烟雾探测器电池低电量</div>
<div className={styles.realtimeSubText}>1号楼5层 1小时前</div>
</div>
<div className={styles.realtimeRightContent}>
<div className={styles.realtimeImportantTag}>重要</div>
</div>
</div>
<div className={styles.realtimeDevelopmentBlock1}>
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>消防栓维护到期</div>
<div className={styles.realtimeSubText}>3号楼1层 2小时前</div>
</div>
<div className={styles.realtimeRightContent}>
<div className={styles.realtimeNormalTag}>一般</div>
</div>
</div>
<div className={styles.realtimeDevelopmentBlock1}>
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>应急照明故障</div>
<div className={styles.realtimeSubText}>地下停车场 3小时前</div>
</div>
<div className={styles.realtimeRightContent}>
<div className={styles.realtimeNormalTag}>一般</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeRightBlock}>
{/* 表格 */}
<div className={styles.realtimeTableHeader}>
<div className={styles.realtimeTableTitle}>
<div className={styles.realtimeTitleIcon}></div>
<div>消防设备台账</div>
</div>
<div className={styles.realtimeTableActions}>
<button className={styles.realtimeActionButton} onClick={handleAddDevice}>
<span className={styles.realtimeButtonIcon}>+</span>
<span>新增设备</span>
</button>
<button className={styles.realtimeActionButton} onClick={handleExportData}>
<span className={styles.realtimeButtonIcon}><ExportOutlined /></span>
<span>导出数据</span>
</button>
</div>
</div>
{/* 表格 */}
<div className={styles.realtimeTableContainer}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: pagination
}}
loading={loading}
selectionType="checkbox"
onSelectRow={onSelectChange}
onChange={handleTableChange}
pagination={{
...pagination,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
}}
// scroll={{ x: 1200 }}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default RealtimeMonitoring;

@ -0,0 +1,562 @@
.realtimeContainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第一个div - 高度20%
.realtimeContainerTop {
height: 16%;
// background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
.realtimeSectionContent {
height: 100%;
display: flex;
flex-direction: column;
// padding: 15px;
.realtimeBlocksContainer {
flex: 1;
display: flex;
gap: 10px;
height: 100%;
.realtimeBlockItem {
flex: 1;
height: 100%;
display: flex;
background: url('@/assets/business_basic/background_re.svg') no-repeat center center;
border-radius: 4px;
border: 2px solid #FFFFFF;
.realtimeBlockLeft {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 15px;
padding-left: 20px;
gap: 8px;
.realtimeBlockTitle {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 1.2;
}
.realtimeBlockNumber {
font-family: PingFang SC;
font-weight: 700;
font-size: 24px;
color: #333333;
line-height: 1.2;
}
.realtimeBlockChange {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #1269FF;
line-height: 1.2;
display: flex;
align-items: center;
gap: 4px;
.realtimeArrow {
font-size: 14px;
font-weight: bold;
}
.realtimeCheckIcon {
font-size: 16px;
color: #1269FF;
}
}
}
.realtimeBlockRight {
flex: 1;
height: 100%;
background-color: transparent;
border-radius: 0 4px 4px 0;
display: flex;
align-items: center;
justify-content: center;
.realtimeBlockImage {
// width: 80%;
height: 65%;
// height: 80%;
object-fit: contain;
margin-right: -5px;
}
}
}
}
}
}
// 第二个div - 高度39%
.realtimeContainerMiddle {
height: 33%;
border-radius: 4px;
display: flex;
flex-direction: column;
.realtimeSectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.realtimeMiddleBlock1 {
width: 25%;
height: 100%;
background: #fff;
border: 2px solid #fff;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.realtimeBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.realtimeBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
// .realtimeBlock1Segmented {
// padding: 0;
// margin: 0;
// border: 1px solid #E3E3E3;
// border-radius: 4px;
// height: 28px;
// :global(.ant-segmented) {
// padding: 0;
// margin: 0;
// height: 28px;
// }
// :global(.ant-segmented-item) {
// font-size: 12px;
// padding: 2px 8px;
// height: 26px;
// line-height: 26px;
// display: flex;
// align-items: center;
// justify-content: center;
// }
// :global(.ant-segmented-item-selected) {
// background-color: #1890ff;
// color: #fff;
// }
// }
}
.realtimeDeviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
}
.realtimeMiddleBlock12 {
// flex: 1;
width: 30%;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.realtimeBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.realtimeBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.realtimeBlock1Segmented {
padding: 0;
margin: 0;
border: 1px solid #E3E3E3;
border-radius: 4px;
height: 28px;
:global(.ant-segmented) {
padding: 0;
margin: 0;
height: 28px;
}
:global(.ant-segmented-item) {
font-size: 12px;
padding: 2px 8px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: center;
}
:global(.ant-segmented-item-selected) {
background-color: #1890ff;
color: #fff;
}
}
.realtimeCustomSelect {
:global(.ant-select-single:not(.ant-select-customize-input) .ant-select-selector) {
height: 26px !important;
display: flex !important;
align-items: center !important;
}
:global(.ant-select-selection-item) {
line-height: 24px !important;
// height: 24px !important;
display: flex !important;
align-items: center !important;
}
}
}
.realtimeDeviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
}
.realtimeMiddleBlock2 {
flex: 1;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.realtimeMiddleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 10px;
.realtimeTitleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.realtimeMiddleBlock2Chart {
width: 100%;
height: 100%;
// min-height: 200px;
}
}
}
}
// 第三个div - 高度不超过45%
.realtimeContainerBottom {
height: 45%; // 限制高度不超过45%
max-height: 45%; // 确保最大高度不超过45%
display: flex;
flex-direction: column;
.realtimeSectionContent {
display: flex;
flex-direction: row;
gap: 10px;
padding: 0;
.realtimeLeftBlock {
width: 28%;
flex-shrink: 0;
height: 100%;
background: #fff;
// background-size: cover;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
padding: 15px;
.realtimeLeftBlockTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 10px;
.realtimeTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.realtimeDevelopmentContainer {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
.realtimeDevelopmentBlock1 {
flex: 1;
background-color: #F1F7FF;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
.realtimeLeftContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
.realtimeMainText {
color: #333333;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 100%;
max-width: 500px;
}
.realtimeSubText {
color: #666666;
font-size: 12px;
font-weight: 400;
font-family: PingFang SC;
width: 100%;
max-width: 400px;
}
}
.realtimeRightContent {
flex: 0 0 auto;
display: flex;
justify-content: flex-end;
align-items: center;
padding-right: 10px;
min-width: 80px;
.realtimeImportantTag {
background-color: #FFE0E2;
color: #FF3E48;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 45px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
.realtimeNormalTag {
background-color: #DAF3FF;
color: #00AAFA;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 45px;
height: 25px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
}
}
}
}
.realtimeRightBlock {
width: calc(100% - 28% - 10px);
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.realtimeTableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 15px 5px 15px;
.realtimeTableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.realtimeTableActions {
display: flex;
gap: 8px;
margin-top: 5px;
.realtimeActionButton {
display: flex;
align-items: center;
gap: 4px;
height: 28px;
border: 1px solid #DFE4F6;
border-radius: 4px;
color: #2E4CD4;
font-weight: 500;
font-size: 12px;
padding: 0px 8px;
background: transparent;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #f0f2ff;
border-color: #2E4CD4;
}
&:active {
background-color: #e6ebff;
}
.realtimeButtonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
}
.realtimeTableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
}
}

@ -13,6 +13,7 @@ import menuTitle1 from '@/assets/img/智能管控平台-1.svg'
import fireHydrant from '@/assets/img/fireHydrant.svg'
import fireHydrant1 from '@/assets/img/fireHydrant1.svg'
import fireKeynoteArea from '@/assets/img/fire_keynote_area.svg'
import firewarning from '@/assets/img/icon_firewarning.svg'
import trouble from '@/assets/img/trouble.svg'
import book from '@/assets/img/book.svg'
import danger from '@/assets/img/danger.svg'
@ -101,10 +102,23 @@ const SystemContentList = (props) => {
opacity: selectedKey.includes('/topnavbar00/business/basic') ? 1 : 0.6
}}
/>,
// icon: <img src={icon1} alt="基础信息管理" style={{ width: '16px', height: '16px' }} />,
key: "/topnavbar00/business/basic",
"label": "基础信息管理"
},
{
path: '/topnavbar00/business/fireWarning',
icon: <img
src={firewarning}
alt="消防监测报警"
style={{
width: '16px',
height: '16px',
opacity: selectedKey.includes('/topnavbar00/business/fireWarning') ? 1 : 0.6
}}
/>,
key: "/topnavbar00/business/fireWarning",
"label": "消防监测报警"
},
{
path: '/topnavbar00/business/firekeynotearea',
icon: <img

@ -12,6 +12,10 @@ const menuItem = [
label: '基础信息管理',
key: '/topnavbar00/business/basic',
},
{
label: '消防监测报警',
key: '/topnavbar00/business/fireWarning',
},
{
label: '消防重点部位管理',
key: '/topnavbar00/business/firekeynotearea',

Loading…
Cancel
Save