From 63773b1e24c3f459f5f8691b91459bfdd696b9e0 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 3 Sep 2025 14:09:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E4=B8=BB=E9=A1=B5?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E3=80=81=E6=B6=88=E6=81=AF=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/icon/access_authorization.png | Bin 0 -> 886 bytes assets/icon/access_management.png | Bin 0 -> 738 bytes assets/icon/announcement.png | Bin 0 -> 563 bytes assets/icon/approval_record.png | Bin 0 -> 764 bytes assets/icon/attendance_setting.png | Bin 0 -> 1044 bytes assets/icon/attendance_statistics.png | Bin 0 -> 1151 bytes assets/icon/bar/home.png | Bin 0 -> 2747 bytes assets/icon/bar/home_selected.png | Bin 0 -> 2162 bytes assets/icon/bar/mine.png | Bin 0 -> 2951 bytes assets/icon/bar/mine_selected.png | Bin 0 -> 2323 bytes assets/icon/bar/notification.png | Bin 0 -> 3771 bytes assets/icon/bar/notification_selected.png | Bin 0 -> 3176 bytes assets/icon/broadcast.png | Bin 0 -> 619 bytes assets/icon/business_trip.png | Bin 0 -> 1916 bytes assets/icon/call_relationship.png | Bin 0 -> 833 bytes assets/icon/call_reminder.png | Bin 0 -> 1050 bytes assets/icon/device_management.png | Bin 0 -> 502 bytes assets/icon/go_out.png | Bin 0 -> 1762 bytes assets/icon/icon_one_key_door_key.png | Bin 462 -> 431 bytes assets/icon/icon_table_menu.png | Bin 0 -> 662 bytes assets/icon/info_publish.png | Bin 0 -> 1598 bytes assets/icon/initiate_approval.png | Bin 0 -> 857 bytes assets/icon/intelligent_analysis.png | Bin 0 -> 949 bytes assets/icon/intelligent_inspection.png | Bin 0 -> 1417 bytes assets/icon/intercom_device.png | Bin 0 -> 894 bytes assets/icon/leave_request.png | Bin 0 -> 2309 bytes assets/icon/make_up_card.png | Bin 0 -> 2009 bytes assets/icon/mobile_checkin.png | Bin 0 -> 986 bytes assets/icon/my_attendance.png | Bin 0 -> 480 bytes assets/icon/my_visitor.png | Bin 0 -> 887 bytes assets/icon/one_click_open_door.png | Bin 0 -> 712 bytes assets/icon/password_open_door.png | Bin 0 -> 728 bytes assets/icon/person_capture.png | Bin 0 -> 1162 bytes assets/icon/personnel_management.png | Bin 0 -> 840 bytes assets/icon/team_qrcode.png | Bin 0 -> 1098 bytes assets/icon/traffic_record.png | Bin 0 -> 887 bytes assets/icon/vehicle_management.png | Bin 0 -> 972 bytes assets/icon/video_center.png | Bin 0 -> 707 bytes assets/icon/video_search.png | Bin 0 -> 616 bytes assets/icon/visitor_invitation.png | Bin 0 -> 903 bytes assets/icon/visitor_management.png | Bin 0 -> 949 bytes lib/common/constant/app_images.dart | 57 +++ lib/views/home/home_controller.dart | 68 ++- lib/views/home/home_view.dart | 147 ++++--- .../widget/home_carousel_area_widget.dart | 7 +- .../home_function_list_area_widget.dart | 191 +++++++-- lib/views/main/main_controller.dart | 71 ++- lib/views/main/main_view.dart | 5 + .../main/widget/main_left_drawer_widget.dart | 280 ++++++++++++ lib/views/messages/messages_controller.dart | 76 ++++ lib/views/messages/messages_model.dart | 78 ++++ lib/views/messages/messages_view.dart | 404 +++++++++++++++++- pubspec.yaml | 1 + 53 files changed, 1240 insertions(+), 145 deletions(-) create mode 100644 assets/icon/access_authorization.png create mode 100644 assets/icon/access_management.png create mode 100644 assets/icon/announcement.png create mode 100644 assets/icon/approval_record.png create mode 100644 assets/icon/attendance_setting.png create mode 100644 assets/icon/attendance_statistics.png create mode 100644 assets/icon/bar/home.png create mode 100644 assets/icon/bar/home_selected.png create mode 100644 assets/icon/bar/mine.png create mode 100644 assets/icon/bar/mine_selected.png create mode 100644 assets/icon/bar/notification.png create mode 100644 assets/icon/bar/notification_selected.png create mode 100644 assets/icon/broadcast.png create mode 100644 assets/icon/business_trip.png create mode 100644 assets/icon/call_relationship.png create mode 100644 assets/icon/call_reminder.png create mode 100644 assets/icon/device_management.png create mode 100644 assets/icon/go_out.png create mode 100644 assets/icon/icon_table_menu.png create mode 100644 assets/icon/info_publish.png create mode 100644 assets/icon/initiate_approval.png create mode 100644 assets/icon/intelligent_analysis.png create mode 100644 assets/icon/intelligent_inspection.png create mode 100644 assets/icon/intercom_device.png create mode 100644 assets/icon/leave_request.png create mode 100644 assets/icon/make_up_card.png create mode 100644 assets/icon/mobile_checkin.png create mode 100644 assets/icon/my_attendance.png create mode 100644 assets/icon/my_visitor.png create mode 100644 assets/icon/one_click_open_door.png create mode 100644 assets/icon/password_open_door.png create mode 100644 assets/icon/person_capture.png create mode 100644 assets/icon/personnel_management.png create mode 100644 assets/icon/team_qrcode.png create mode 100644 assets/icon/traffic_record.png create mode 100644 assets/icon/vehicle_management.png create mode 100644 assets/icon/video_center.png create mode 100644 assets/icon/video_search.png create mode 100644 assets/icon/visitor_invitation.png create mode 100644 assets/icon/visitor_management.png create mode 100644 lib/views/main/widget/main_left_drawer_widget.dart create mode 100644 lib/views/messages/messages_model.dart diff --git a/assets/icon/access_authorization.png b/assets/icon/access_authorization.png new file mode 100644 index 0000000000000000000000000000000000000000..0c9e417b89393c652be9ae1b5748fc34818522f2 GIT binary patch literal 886 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 Lpsky%?hyV16BTt& literal 0 HcmV?d00001 diff --git a/assets/icon/bar/home.png b/assets/icon/bar/home.png new file mode 100644 index 0000000000000000000000000000000000000000..9edc026a29366368bb14bfd080823163e0ae04bf GIT binary patch literal 2747 zcmV;s3PkmZP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91PM`w-1ONa40RR91P5=M^0Jud2M*si{B}qg~8hh7gYn#YbL{(FGi6BC;;sT`=50wBVjl!*z3X-^Qh^Q5j5>oY{QYny1 zF8BrB5Eu#t6WPwL_qeLkAaM!Mx^i=a*xubUd~@tMo-^mNvuC~QxSS)|GxPud{PWLu z=JLy$iWpmR$Yq~PIO8|CGWr&O}-A8aaEAKqsn~^oE zz`KC+(p08o0dd}?tVw28%dE;aCha!2F!gvBR|sJr5d5h6av2XeB9p)%Wu*2@Pfu+U zbP)=ETWji$si~=d3c4>8f<8tBf#*R}`V{)zKm}5*)YCqEiaY-4G#~8S}cTxtZDZ33Yr@(F>~R= zjVrkl$qWo^e+jNR-L__={CG;pU=vP`>jGbYMxUx{oII~bF{0G60cwFc4ST;I3n$yV?CgWO0 zsd1)MwkOdD!(^+{UCXTE`GW>+*?}B()m+O0)S$cQUHn-q9u)vIuv-O)kkD&Sj#A@7uTU znus?pWb<4%UU#cnMx=47aI@3zP2+g)(?PPozu#Tg>&VM8u0J9ZtHpD9L%pyjb&VB_wMcNzYG78)ng@=PZ?}~xkCOT(+Q)% z_>8O&YtX}3gPxLUavW{(bLxN*VEJUp@qfDC8+>J>C}vs_iy`p zg`5-|(>qFK=9$z{=vl0^9u5%qkRpx4qT0fux=YHzaLTm}r(ClVO)Hwsn%}e;IUx&U zQT-qm)xVKxay-yD=lF=(e~<+YN)8r_g)=fOPF$@pHhun%O`i`3>GBz;9#|mr1Tud# zD05tz+El{I@VV);vvckt8{acsZNqql_~ECsw@S(Hcp9CikB92xGY;d@ULjO0PFxi6 zMuaoP!UIuyea2zmLTf!5rF6A;KI2wP@oMNvjEjq6f?R@}i~zR4e78(wzKHMLyVtXo zD(E_4T1QBg;d{o|0&}*uRGymt>)$SsjM?+BRhT-f@~Wldb_ma|ceO%Ngt(4qHCdS- z3pvb-D34!$>H7#FWs_0bK^fV^C~K#TU15fCg-~{JV|#mHe0=<>TwpEnSjLG0mgnAv z&A)M9tFW)3;rcQTj-PvCBHb~1Oja~}WZ)&0;K_EdOK_EdOLEycCK;)#~>?#fo4W(vg-WbCE;FsZOWZ|vw z_00^7Jk!~^^K95nvu4}L%Ug5cvf2J^jLe_Fe&wS&0N{}5B}&?#C>F^> z>+HPewA?LmDgKi-tDFMR_o#VR()22hf{u4|Y(AxXsJ4=~_E^J!!@k|^3k&5FHe+9i zG7FVG55NvBf4CGS>hMgn|IV^K|11_?>C!a=XPV~LicL>q7W`aOfL=Ge~OfA5R)I|E|%XrI8tlt z*9I=r)6+#t_Bj}GOGsU@S504NZ%cn0lyT_LDRjun@L*0WwW_Kj9u=D zx~M%e&jGCO95}H5dG!*$khvK{{^^=++dot;dpM@zyUR~wEc|6PyS0GB1&a$PWQVMb zrcy&VBzi@rt+#l0cVBnKE)~3X(+jx5v(roSrvf*DRWTJb&L0~5%+FQ1^_HHVo|wkO zrK~{M-8%Kq0!~NK|C>w#T+BC~0G9w46M>l7k`|W$7ZZV)*^(9)6S%sr-qczqc29;G zv!B9FMk`hv_>~mKeWbiS5WZBFaDL;upLQWxI2i5q?);Hze7#u4+x6KaD-HLA0g`vT8qGPvREZya;u>okI2^m__3@@B930d@5XI4Ql2%--G|WU+z| z1DU6lE&EUWSL%L2Y3rM9ZFV`JjGKm3Dy375E$*^OI)D|dY z)=TKLFv6Y(VBgXOY`xSiu`@v+K_EdOL0}CK_#Zv+Q>>DtRfGTl002ovPDHLkV1oYf BDe(XR literal 0 HcmV?d00001 diff --git a/assets/icon/bar/home_selected.png b/assets/icon/bar/home_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..befc618ebf6e74d481b7a43ab2098fbbeefdd02c GIT binary patch literal 2162 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 literal 0 HcmV?d00001 diff --git a/assets/icon/bar/mine_selected.png b/assets/icon/bar/mine_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..d9e05bd459e72fc731b70dc242171c06e0467494 GIT binary patch literal 2323 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 literal 0 HcmV?d00001 diff --git a/assets/icon/bar/notification_selected.png b/assets/icon/bar/notification_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..fa11338e884f47cb5749b62be74cfbb8b894ce91 GIT binary patch literal 3176 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 DPdz-n literal 0 HcmV?d00001 diff --git a/assets/icon/business_trip.png b/assets/icon/business_trip.png new file mode 100644 index 0000000000000000000000000000000000000000..83c6b6cc7bde8549b2042f316f13a8490061a846 GIT binary patch literal 1916 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 literal 0 HcmV?d00001 diff --git a/assets/icon/device_management.png b/assets/icon/device_management.png new file mode 100644 index 0000000000000000000000000000000000000000..cd57bbc7f0e3ec81d0a12c2c90613fa67cbb5a52 GIT binary patch literal 502 zcmeAS@N?(olHy`uVBq!ia0vp^Q6S903?%u>HX4990X`wFK)U_Qf8W+0zOCPZq+c5d z1-AVTX#W+^{=NP4zrqv$fg(V*Z|AR$FaP{ne+2gbZ~yYQ{qx_B&;Q!L`~wOCxj^ke z#fC-SEXu#THGH?J`o8*v={BJ05+y-?!3+)`UtinRX>Y7>fNh$C#s29$OJ;m}v865P z|LiLa42;H}E{-7)hu>WD+2f5@6eyLbNgysx%k->yrVJ5H20C{CHwWAR+> z;xwi>?v*QJd?igPCrvhO(pa~Art*qMhUILLVv839ci(fku4C1x7g@p(yCEZHiq+}F zb*%n(T=hTYE4qrm6!j~a8u!%Y{iWOX_Fvyd*Z*su@V}HHR${-i)QejOYHdH9>e=7> zr`v8{yTo0Kx0%6(=T`3y{~o_PwU5bwhuLvyZ?}V=(Cz>K(}JQTOe8`XeYJIWKW*7n z-?FP64F5aceSJCLq1lpmKLcMZ-uKk7R6q9C3*80p1lp1U%1<~MudsI6IqPceF469i zGry9Bm^qw&>M-<_op~m4{#*aEI{%LJ&l$&=`YeFd|2IcD4sa;E{2$8W_jfkSjakoY lP48bWGdVw<#qq&IR@0z4-RJfhmIH&B!PC{xWt~$(69Arf^^gDn literal 0 HcmV?d00001 diff --git a/assets/icon/go_out.png b/assets/icon/go_out.png new file mode 100644 index 0000000000000000000000000000000000000000..7b4e8f942706bfdd9ed498d929fa3dca1df10bd4 GIT binary patch literal 1762 zcmYLJ2~-nj7mWpRVQr733aM2@5M@yzLJ?4Qv6LX9R44?jWibhg1r#*$3z`H`idql` z7nXovApxwi8V}e~DQgS_5_WwEt<}nYnY{`_8%V-8bia*}*~nh8xW` zqEINqfFnL3$TwX3uGc}(IJJH+3bkfDI55-~NvU!eFNEEPKp2L-dGL=*Fjc1I(IPED zj_kVC2uH&Ctp*`pBcNT6CPoA!ctfGBBXML53Z8}#LR|*&VmML=JF-F8?^7cRG#Q9I0b?4POoN+- zLuX;?k_M^&8`MSkgg~2J`~V;1XxxT0h{!({kRny~_f$*TF$7xpFKEkZ$`VYGX)DM` z(L$}?eHRqp&cQBkHR12!s0BD=20Aed>TvR9^Sg{J%C?WnFl-r>4A}i-Wn7;yZ)zBX* zXh072EkjvPRVQw)NI~tQ{@{VFY)uJEol>%TCV%DJD^e-)gTs5jtocGy(xhfIQ3kKQkjLNdo2f+ROd$*{m$v>RNM}!^= z_Bv>@1FgSq@uOtC?^%6ydCpB*d~A^aSG#p^PcNWQx{(1so}u5qmK3cX=N!iTZfd$o z_t+*=U7t`llSQAOkAGzKzo@J@T#5-fT3WIDjg50=W~Opdc(rvhxxPzkMTfZll!Umu z{cUMHa_^OAHl^2RO6YC+)|_1NE_?Su6P#F1c#m^2zdUZ#nuSR?mDG{gny!eBDCR5> zuZZ;O9%70DxbySzfnmO{D1QeWE))Ar(n08QGEv8X<5r$#zSB#;Idb^OW=9}*bUcPtl|x>)57E8 zb?%7em*Tfs63{dNo0xq5_B8^#d5pg1n@B-RETcT4GdJ48&h^G)sao&CKMvlYX=RXmP&_gK>x|uJg2RzTsm8jj$n}a4aK)$Y)kXq|-t!71mgX72FR)v-HuA`NQqicbe~H2bGiESG&;c# zU}54*do#6bIzVUYZ;hRu?cp7k<@_SJbj;8F6?04Y*szimm~*pX`O`y_U2}=a6tq&V z+8q0Hiui$76yEXICg%6-KgGtH)V2c7uz{UawAraHV`f;g_5mw4J_gU+@sop1C}($T zcq1Yh{1W(z!(MiYwBWO153QYHy$A>`lMoa-R#L)-w2azHCa<3{yBeO!qBpV^ zV>}gC?6yawu^3%bS&nCIJ@B8ObmytAzT8S$>rO6sc!?F{hA-}J>F;p!nd2jH%f4Z+ z@tu;3;f2&A6!c}=e(#Vb9gA*R-epaF!BE~?%p*Po%zg` zMVV5j#A2czVFo0kW%{L;OW6`p550+1b(;LJK%NoBthhgiB_7>X>ayTa{9~DT(#1Xl Qxyw)izCk_=ujtIb0JdWND*ylh literal 0 HcmV?d00001 diff --git a/assets/icon/icon_one_key_door_key.png b/assets/icon/icon_one_key_door_key.png index 0488296f60bacac370b4ee7d3fac683036d9abea..03948c3f5992a0920902cef21440e1041485427a 100644 GIT binary patch delta 405 zcmV;G0c!rv1Fr*+B!5LvOjJbx003j><7MgPYw_-6=i_AO<7DUKW9H*z=;Uhc?PTcW zW9Q>!=i_DPz=i+1L<74OJW$5E)>FHQ42!KpkCR!myn2M976&9uyFtY0Ru@yK~zY`<&;aVgMTm#MeY0w4WCkY-v3gc z6d__r<9Jk6?U`9Rimz@0aAA$k#T6trqy%n)gtUWoXQhMs#+ap8m0XrwRU*clrDOC@ zS=xDOB(IU^t0k{0Q*$k!^i+ON>X1v=L5T@;A(*T4pcg!VW078hmN|hcglr*I!6J{4 z%aDjkQjn#PI)9`FT}~}#-{{26^u4*!i48_R`a2}h1V delta 436 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 X1Ix9;%{5&;00000NkvXXu0mjf!_efH literal 0 HcmV?d00001 diff --git a/assets/icon/intelligent_inspection.png b/assets/icon/intelligent_inspection.png new file mode 100644 index 0000000000000000000000000000000000000000..2db41e439e332ec25f11f566bba695698afa8721 GIT binary patch literal 1417 zcmYLJc{r3?93I=f%_7TKih3%c9)@z8Nug3h5)Y+_l+ty}R#H=AWSyC2$;4Q)Wt%MF zl5A17#+b)8Nl30W;z~1&vCWK(?|#!i_j{i6p7*@x{k`XR&L1EBoQs1hOb-TuKvW&i z*tvo8Rd8J1q6Ff6X{jRwqJTQ*e9<0UvR?qL`9SM0EMQ3ocmRbcppxckJRf;K8i0D3@$lMjGf+yamS zV1oRtUz}3_&`Ap{=2}+L5-4uU{x^UOEC&&c;ugzwFiKtsHVLNvij}K?yh^en?;QNV z*ddOb^w!Ifoeyvd!HdY-4!!*yRAM84Fv+4Y+?;rAv-hKz!(Qco1tvcM9c6%MQI?c2 z&#MLm{Eg8ODJy3&iopLmAsuc9z6?szXvP%y;)%&>xPlG(pqQukOckURJ!RD%HX0*Sis-!r4d&BGOM4vM# zgF#+K;@$e}l;|+Kl|i8^1ftaHXm|3W|G41uM;DT<`frPl2vqcyG#w_z&A>rVje;&I z3RyhKIM7QH0-E8yMOFIgV}st&cw-JC0&8graMiw*JxNJ1DXluPG8f*Mhm>G(u@09) zufey`);D3bNN9TJblH2uo_8mr-}Ax^KAG*=+A{Z zSa6`z!woGRw{E{;-@-TgRw%Eh&BVed5=Boq&Fpb)O1;OyxWf-}+z)zd^lvrx`k8#Z zlE{vK&S)LLTO+eu?@t7{_q`pBte~@&Lwj!a8n#v4EaUa}6F%r$qaSe`$rOx4CzHhb zonAQ@T4FS}mQ{Oi+)MPZ#gLL-SFz(yi-02u+Kq5#!|K)vE!{g!2RxxOrVO(kEg1~7 zDmwPt=Vb35=$4&;yFext#p)($;5s)V=`ipYD* zU#;mb#hq^-c%?9u%{rVF*%W-~Wd%jdMrgdZ60Qy0CkohaK5=~Nf*hi{_d6wLl};r zsaT<>7SuhnP#uafE-0*|~JrVa7JEF-2fOi!uzy!q*$w*#p!afxWO z87&kHRlJ@XEE~m*Jo5AnX+~>In&y&y)mB?F#ZH)7r?9 zXirVA7LLr}sF|O&$kyiAaqo&e8AfF?9v&h?&Mrxc{w-{}7M-cdeSUXbm0)LXFEY-; zpY?gZj~IsRvEI|;+Za*JwhoJ!dEa1AuG@3wJEse8>n9T84de0D<-WK+TGNPyWuov3 z&Pqr~8W}q={Lj(h38QPX#>brb>WAlMwQTcK>6fR4&iX2v-B;L&4NsZ5QbvyP9=<6f zV(b?8M*O%j%Nw#*k5OcrhFFy9F67%9!w!iXhin~|71NrOZAvnrF*!aa=H=m<+lr<2 zQm6Zh!T^=;ruoNm+Xy8cdi@+;D$HcLrsj_ljQzV3e(NE4@I>hbyT!XT086mRBsSA&Ik5h_Wb0Dy1=TNvWybR+U)Dz*@FKFh@-uW K-K$gAiT?qQ?hD)i literal 0 HcmV?d00001 diff --git a/assets/icon/intercom_device.png b/assets/icon/intercom_device.png new file mode 100644 index 0000000000000000000000000000000000000000..60cced343e208637e47f9fc410d44c2a68c82ddf GIT binary patch literal 894 zcmV-^1A+XBP)?c ztNDMY`Skey@b&)F;QeRB`lG=5o4Wc@qWMXb_(_uZHhK2I)BF+(wetV~02g#pPE!EC z5XHKXQ9JY5T=VYO*1(~fLnZX0J#h?HC;$KgP)S5VR9M69*y(nnKoEvuP@=fSElCXx ziXh^?MPru9Hury1lNpV5Fp5q4`Oo(P9r%StZDQT&EMbDWeote`s`IH2l(Mc&i za|F(&Nx5`3PQId(x;i{$gHFsy9gwf-1E3-rE zI9iX&DFPNffO#aHkI=VB`gZ@Q@x#s`>0E?1k+d11=|F>C)0x^yZ5i4diLV?FPkT-A zzow0*2P3iR8BO=Zwk4Q$nRY@f?F(*&r5(Ywu=H4PeJtHzxj|TZwUsABFg^4)@|a}| z%@cVgIPLMI(KJog^rMZZXJXv~PV=-|czW=(ru`Y5rj}UecyO8)f?o$@}_{uc56#H^)0L|wl(iA~c-`)4v5Sqe2Kj|ihCTczH(mfPSgGN+#!{f9`lFcT9 z8`-okQ}r&>pWesIlvBTfg%8uQ`pbX5*NhnRC(TQ zOQAN;Ic9(Tp+bM~{yJvs-cg}DdPY0m&C6TBhg#-n2X8MonoNaX3KgX2k_ugtqN^%& zRf?{u&^1k}a&oz>$yF~Vm5X&vvb$nJ`A(CrUQRTPx|XC`wN#Kg&!uWjyL3sVzXmo1 UcSEUS7ytkO07*qoM6N<$f`_i!rT_o{ literal 0 HcmV?d00001 diff --git a/assets/icon/leave_request.png b/assets/icon/leave_request.png new file mode 100644 index 0000000000000000000000000000000000000000..3c29cc274582739f9461c5e8e7cc29d8e213e392 GIT binary patch literal 2309 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 IM6N<$g6owE)c^nh literal 0 HcmV?d00001 diff --git a/assets/icon/my_attendance.png b/assets/icon/my_attendance.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2d5ceb4bd90811987b00598d9589bb89195965 GIT binary patch literal 480 zcmV<60U!Q}P)`E z+#Z>kIIpSSxv~s0%x{LQOq|w_wON@swI8dqGI8b)QZlOkFKT1EhM4*50}g>1YC3C< z#V$Ao9B15td?YSaGmRm$WiD4U+rZ8&qh{FKkd&ItPVHDq&2G0eZ&>`)!ayuz@C-ex zA6wazkinDea3x$QHPda>4bI_IYNpw!37>a)4XupCGqh2&D5}1mT2X)t+-p^SfgTD!^Sr-B&?+0reDCPXg*^Sp7Wr)<^I9+&3hAuh0KAs#?|8P!kJ6@cS=D z1Eqx+9aJr1SSX0ZFi>kr;joMof(%xOLW~t)CC?D++G1!tESU>&CQ^;(^RC7fC7o~O W%`_~I1s>`E0000IP)mhWJi}`AvoR zO@;SQhxtv0_)mxVO@{bVkN;zw|4)YaQH%dgh4+W^{`vX&!1nv5_58i|`_cFN-T3+_ z*S>}T001p?QchC<1iuiokUi(!MJ)I8*~Pl1kX%ta6Y$Kw496taqM=z4(VmusPP=?@ z83|_=Ved0$00077Nkl8r%=zfmJXBfL-8m`gsV^W*b<^%)zoGTM;)gONxg9yk~(%7 zlA5LrOKmeO^_ZclEj0Crp{WBjHBHde9z#>xcxnqzy~I;HcxwN*SMA`{dWqK|7G7W5 zc+=>iogoQDJNW{Fc3zK&9+pafVBL*lqKmE2c{n*FE!qPkJ-IO~(93-8Mo$SR)f8B> ze!jMWI%~fQETHRj-!e%s>i-4iJ4?&i)2%y4H+CzGkM3(a1|k-;k%QE%eKjNdU^ z5Cz`%$F(bqQ0|J9Ja7pQpx8^HOUs;dDRZ-Z{0`vxZ$9#Gcx-XgQ(MTpl|!t#!TekE zYRy`BY1a9YU)1XTr`7DoWp8l7HOsPV-7W7E+G=J`q+nKAbM{4j-7~5Bq>^bOF31aY z&TB;7Ba>gAsMQ%f7wU6eZFCRpR@rzQQggF&^8HtQ6~gMUq5cb~pNp@yuzK5QJhfzM z$kafw_mH}bHPfd0gx_*IUgavQ^$vIi03#y~)$IB9>rDA!i;FBlhy44Yh@uhoM29}cmv0r1B3BjwZg&+Q{YJ};;bAM_gE{K zJ*%jlSFJetU&XzC(+a_!%U}Y%=uY>6>w{ZaeQ<2c2gf!v$-qEIJ^@M&Od{)<@Javx N002ovPDHLkV1lvj##8_R literal 0 HcmV?d00001 diff --git a/assets/icon/one_click_open_door.png b/assets/icon/one_click_open_door.png new file mode 100644 index 0000000000000000000000000000000000000000..5198fd5bfd56dc0d964532fee8af3f43c9bce590 GIT binary patch literal 712 zcmV;(0yq7MP)^{oTXW&i*GVe+a*+m3*$`}ODg@Jif}64Y<_{rX(soI2Tu zG1!3+(QVK6;>Yye!SmU)@zIg$ymREKD%X7n(P;Ag_kHHDXyT(%-jzS)ktN)JBGq^s z*mm3a>8tO{rtZktaA-~d000(rQchCN zaME&w&ieUni!o-|d~`cP!{a=IeP;N4b{=}czFMNw4*Sjsu<055$-|~6zx1r4EBaZq zx!!tvY&y(cP3-`i4l@=TADs?6($VZpN3TlKyUqG-lT4fC=()?%D@EU2Mn%6fGWwN{ zKG4xiI(kb-!-9^!S!6WqEHWB=Gfg(j(%>B!^7j-rag*s&c^b|}lDq^28s-VMwd14P zS+*IM*AC}ZAVWsZeEO&3$Oxgj{yqR`qUeUKQ&_tI0000< literal 0 HcmV?d00001 diff --git a/assets/icon/password_open_door.png b/assets/icon/password_open_door.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ef96dafd1a962d2270df3737ee73af63eeb48d GIT binary patch literal 728 zcmV;}0w?{6P)|6%f~^ZfV7^xeJk*Jk3O8q{5!G)?>Y6OxgeBa5RL8Vj0000ObW%=J0Pevb5Cr-0-Pz8%qL7Ga zT~Ryp^46G^>y+Fg0005&NklxBX74`k!ZxNFflL3ZK zFUY3@KffYi9kzv!U4)os$lq?i5Ulb`U;0(giKigZDk%m)nNa?eabI z*a2MWeAf-RtIoGV=q@5#*xib)zHDlBso3oNjNj_fxBRptbp9=FYLAIU3p`gGW49Ztj*I-Nb0Jx5!i z)5q;!N74$N-hUq3lUC^T?(^7=veFE??uInuHP$?QO&OUwgWja;$dA40}7#s zaM$eaFhuAHz=JD4044|>0D$4i1^@zqZZ<>-M@}LDC9tND+J^C)QyHcLO+ng&vCr9$ zMw`K_yRrJocX%}u1&tspHnrgxvPPn)Z~*V3_-PW_-)o6^#u2<5grcxu35=G3C@C0# zb%Q{Z7LLHOQ7EdKDX>V*1f{8}4NnENU@}y_IS`un)Nm3AAE93l*)R7+;eEjX0000< KMNUMnLSTa3Dr?pN literal 0 HcmV?d00001 diff --git a/assets/icon/person_capture.png b/assets/icon/person_capture.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf74cfbb1335ecc5c8105e3cc52a1bd94a92746 GIT binary patch literal 1162 zcmV;51aN_pyI}m)t^3H7`@x0|fuQ;T z001>~QchC6b9gcP&z=1NEu~DP;mC_AZ2oT|7SYT z)|0lx?k4CuUhv(ym+N0YX-Ek+iJrdAok3Y!vHfCsM0IE%byxMjo zdhAX!p;;Sn3JJ)#A>&ANdjAn*A%PU}v8|ZKqdSm*B^6zQBut5D6B02bq76tylZfs? zB1MVlEF_YWh}I#Ido+6NpLUEU-g?MhPXT#%dWp0~*80*v=OgMBN=HO|ts5j@L}{hY zdzt5nM07U%;bUIw!u#4F;uN9QIPo1~sp8QVB%=!_7a*aO{8Hl^Ta*g$W6$v%k|~Hs zry!Bm&+TiCNT@*qB~vC^smhkdl>v2*M942mQ<04}kqubQ5@`hs`?kn4n%Kc4FN+=? z^6udaHG+^nOR9y#qjmHeBhSLF1M<4uvx9sG9earKl*8YMry?)lvOq}gh7RmQqwN8` z$wi0#0lTQmM6)aNpdXn&u;176(IYp+#%=|Sf*c@%7Y@cO{sE5XPF6iAx}epn;H=gQ z2wALG4nVbCHl(5zz1Xq5g7ew@ZL|27yLsCVp25{@bsC09w5i`&z&Nw5`D(e|Y}U)w zeD{|F0h7C4vS?Lr1LN0@@JlmRLq((22ItoAM57(fd9@AaN7voyw9w*ihdEN()l#PH z-ne(=dvYtIwH@G^Jh97Xz~gTC!ZsPdHY?5dx=VSCM5_aqjMaAFD~lIOD=O#v@0P)9 z&*T1&cMjz%uMoq*O8Z_bd9vrhErZvd$N%3$%H;9Oks73BEx|AsnWbI+$jX cqoe=tFSPpEy@yM4S^xk507*qoM6N<$g7%kSHvj+t literal 0 HcmV?d00001 diff --git a/assets/icon/personnel_management.png b/assets/icon/personnel_management.png new file mode 100644 index 0000000000000000000000000000000000000000..a2447211945dda1946c29b4e3a63aed4b7b6db2b GIT binary patch literal 840 zcmV-O1GoH%P)$P)OK9poALbi?ECY`^xiz$h!fIp z-1zFu_29ts*_Z3TX5yhJ)_e}pY~%Uv)Ar=H@zSjC%u(KyNZgP0{rH0CvT@|8G}wbN z*MaW)_22mGrtZi}7{QAG000nlQchC<8~pS0^4Z9}wwr5ZPHq`oSxE%|00KNoL_t(o z!|mDGa-u*KfZ+i}BdF=)fGrV_#eIopo0<22vYARvv3R;e)1Jyj!guTG-xe&aYJap^ zdWQW@r$4-T2D`tTyt}*CoRRyt>i@diX0YKTH`JPu>le%VS~GH;Vp&IPMs8A~GV^8A zhBN4O8Dl?7mtmL6s_JEo-GuHL>w(*wmrqyQbt4*7D9bTn2V^+8A;Lkgq&!^~W zD9t#P^nei>RMI`lbgz=`Ql`6=G$YlfzfbzI3IKw`db8aTr$Zld%jb|XeTFiBlhLIg zp(;+P(k@(E&S=s*ACQP9?E-dU?`EzCC7wHLmRRR@?d+SfZDw92Ayr${>vHIA{7+IR_7ElOY86&wM_;Nq8oW^ZL-c*0Qny0xB zxi{K$R!XOY>HTF>xsB_WS5A-)w||ZSAb#?k-j+VELGBoF?R1#H$C6Mp9Uh=g9BQR` z0`*p0Cmn%`NGE*)6(^l^3@T!sG^o)@KY@ycPWok`Qr(Pm-us{_zw1kq+>_^t=hYXi_`V)Cj3&}IbCW(3b=V)Lp4&}9VBW@GiN z1JP#@-DL#OW(3h@1kq(;^sNZbXkzuT1JGpy(Pd)vs{+tvV)Lp5&}Cust7QMG1JGq+ z@~Q;TWnuHGV)LtH^{iv`s$})5X!oxJ&t+lqs=)bdssI20J#?PR7Iui8$=? zL~JXj_?CiB2N`3X&&Vex`0m)gwXBVaVN?#&dUQk}8tI>K;?c0<5&GWLxO+%%Ga%`) zq4od(0{=-wK~z}7?3LS&f-n$3QK3{=5Q6u+@h)n7-emay&sryi5W3R=_i@8{qT}(T z0UE59mX^QMJ==ZI@lLy-ss+1~2ReQG+%=cKJTMR?rIkb`%lk5wBQRr&!V)&4MTR{C zM_6bftC9Qedcfi<&F3wTy5DEu*(hxTJZB2z4Nl2wCrtj&Eo?Q2@x zAbr4v*t@R#O!0C^r-!&opKon1@bb_g-NW)LLzXi zw*MgQW(tQ-JGtim`}8wYZ29yy*Sd}pd*sR1s7b7N=)L4lK=lK6%|{$Kq(XOkh(U!LnhN*UGp#c<`~_|tLQF7 zb>JEH=fjt_TYl|tyE=Pia61UaFtAPjqQlN5Hj#*KPcEuOVmdGBrdmOzu`JM|poB^* ziDm~FM$ZJA8@mLWG7E!iwI|W6#lfDh3edbU!)Vq{vS{7}V>EBwF?!re&P-ytEtw%&H2yI%<#x|UX?50sI8zLamXe>I` z`i!Q+ID|eQJ3_Z(ry@j+?$^?H#YaO0p9-DCsnUsxYZdyCYVE0z8AL5wLpmDw@R2?)Q1G$Z_(+<4H4GhO1|%IIQ2lMv$07g$i-H04oOx^EY}~ Q?f?J)07*qoM6N<$g27G@BLDyZ literal 0 HcmV?d00001 diff --git a/assets/icon/traffic_record.png b/assets/icon/traffic_record.png new file mode 100644 index 0000000000000000000000000000000000000000..2e76d85216bb6e264a0169b2820e2842f32814c3 GIT binary patch literal 887 zcmV--1Bm>IP)xw`b0ybs0?JkKVMToa z000|wQchCus$>8F0z64XK~z}7?bT;< zn?MkN;RDpGRb6gRGGK%nxXH3C*Vz95zbSY~!bG!z)0K>p=>4jhXPDKpkV8D>+Gl3D zNI5E&&9ion8}6EUx>I?x%LVtVRG?}kIL0yzKRnR87czG3c9$UaRRZ<(FPu0+;_^5RayR3#TBXm&>T|{WA zsQ-YT+E2a~dUVAYo9UouV-|Ixp7w7T(?dU8vbgIZ*6m1`zOz|+_ProIWSLC_>Bnr^ z7Nn;fPrZM$=Gc(E-0S3R)OSN(bX~sjB>emC^@08;>2NZhYNB5lV^MjPgrJCLE6ryAN4);aQ|kVy&-$K*MI(PoTc56b1B+? zvbSHpxwd;9D5cMnKApi@9UTmQ?+u}SA=%lG9T%ZPiDyol144Tu^kB1hj9!WSgPXGR zjL}OK^cpbQRX{r~N*6qN`U9YV(50m`9RNTHp$+gPX=f4920}MrAxXbNR0E+~04pi_ z8+_G5XbS*5K{}DaBIvNe4Ml0^HI@y6)q%+N#OPoFaUFtI0oZZgpE2xwz>g{fuLoZ( zyd6)fvv%QUJ+h@{{QHs@48JK^jQBza%Pr=N2vBhyL%W^G|XR2`uQolJQ2|B&WOnBuFgf>WX=uIT~DX}yp z2!`}}zDLkBTQp{_LuhDIm{bFIV8aZJO|ZQI`Mlwa(2wyqn7 zdGj;^jBw@w0gkp(#^C|AEJ{6s>QF)8sPLI!%q=jhhJwL^a?IxJLv5g-&|z!|q3(j( zQE-I6%g2c10P|Ks;zGl`RNaP37ppd*VTS!aR9(SxhafD?C73}|&;-!Jm#C*uXW`Y> zpO8YXhmBVoxXmt5tp~7$CK0t?EE@F@6Rk`as&pFl0h+p)&^s$M>{MtgI?0+)<0({V z&^F08vji~d)Gh&p?7h;Sd=T3F01N_j`u(Y#6CI$oXvW?-u1dZzVRFpCBlHeNxz)6e zXO$!vGs%-Bjk?u5>t^a{r|ulwW4J}!OPZt*^}aoanid>`K-0qW_Cbp2#^SCbQs=u= zwR-jrwLBeHDekr9STI#Jc~_dJPp((VJtD=5)Wg4^&JS(j`>Q)y7u1S)tD37y%@nEE zi&f;GmN?V$%T*W&q|yZG_+`On?? zQIz;jjQ2W!_u=XJPMG<^)cCE&_=KzYOqTd(m-um>_w4ZbvS!1$rQ_>QsodZhS4 ziuhrY_eYTTR*UyHdiFMX_G_8RKF1yTS20ia1lK~z}7?bruy!Y~woVX_IkgDxk|fDm>I-L&^VS3$%y zRdAyC(R}z1|jqW^rU&0n>`jxrYQE=T4f0Zm#-m=g@ukL=%>u(0*As8ZR#|oZqf9bePa#3Dp}<-zTo? zI)CcHR~;=s-F+GQfziU(dc0rnS-Q<=(I+HIoitvDt%kdzd=@1Iix%yMy%50-lLFYQY~6;g7ZOhjH)` pBL3hLe_V?{WQLE%WipuzqaPC!BJETh8#n*}002ovPDHLkV1g8AaN7U? literal 0 HcmV?d00001 diff --git a/assets/icon/video_search.png b/assets/icon/video_search.png new file mode 100644 index 0000000000000000000000000000000000000000..8f6ffb337cf2cbc2430d2a8c7add055b268f5680 GIT binary patch literal 616 zcmV-u0+;=XP)c{p|AoF>>~Qr};E=_v7sSz|{O;lK1lV{(7YO zJ9+nL!undP`AdZNN`v>%|9B+;000DR^7`|1VrG<7PTGl)%f*?4GyOIYVsI zG_!wx0rM;l{XmGo594gkF;`yX03c4}t%iOyK@F(U&$d0~c z86XlvPe!X76h>t zljYfBDN<|M#EUWgO4^%2Bgtl9Z$px9L-q#P(hbnwdQUCUdRa~XdRFRugLKSub5;90 z${+R6o%c_9w;c1ts;7LZ7iQbD%U^X(+QO3d-9?)-(A<=|)HT|?QOe)dZx&nj5Vu_X z*d>2_M7!V*u4othks0lB#sO{IFh1L|D9Y{ZuksC^B(SST$DW!10000*qcA|#k#eSTv0nF5fBgu?Q2eN<*1^eM=FD1P&5;}e11^?00MtWL_t(o!|j;s zcA_v8Mor{?u|>RVy>~hV_5B}jW}tEs3i(W|Yt3@8|Aj-g$8ZutdL13cSPtio1)?Uf zepb+DAgWK%x9AO`+E`hI1){n*)Y$=1T{v~-AgW6yppHJdnynI_*E+){_mdSv;>c}r z!KNE2SM%oaj_B^7KHnD9Qb(i4q+PmUQeP#G z)KW*?rnFZXVb#~RU)inERf9Sa;(c0?4QgNP9n0^Lm?9|ZH?N|EKNsJAbNX2r zK1fHg@JN19A=E?dn z`CKg6@nCN^Kc`+sAAzKZqvemN`FrIh0(+@ayEgkCWwuLJ)owaasqN-IwrJk`Hk#-y z=`g|Uu-=2z+eLMCn(n=DFbKVUI)!RHAlvHN$jb%15m3X&=v;FizvMMu#tJoNbN%6A z-oWq7HkBe_#KOlnGT2qFIuzIUI(djS_vFT|pPjrL`iS-s$ zVhzLwuL8=OKi~yMhs>}>@M~9fG!D{6n+0R=7RMaCF*65mC(Xf|U~}-+{eGtDuKeKG drPE%Al0VB_Y~zbuiO>K5002ovPDHLkV1it|&#V9d literal 0 HcmV?d00001 diff --git a/assets/icon/visitor_management.png b/assets/icon/visitor_management.png new file mode 100644 index 0000000000000000000000000000000000000000..1349f0b81b72b087a7cee276f53341418c1ec866 GIT binary patch literal 949 zcmV;m14{gfP)_*i1+92{iMPAbItohE!un^n`c;+qyzBjcmnu3VngIeTzKQqMsc4 z1$n4NZyoxE@~}KQ?#IK3lx>Kk*TYdCs1zE<(c|I47$a!(EyNLf2#2@?4RLJqaN3V# zXozErhtu6ih=w?}c-U{9^C>jM@w(B&0?qjp8prXu(Zd6n^OOL_-|E zH+r}Ob3PLdas21=FgAQN#PLs~htna<`8+hl@o#euM=rW=A`8)C?X~L^I{hcFjPV*&^u#PuR(RDK~I-W zr63f3a`a#l$s{N1(Zpz~s9F~FeUtgV=T@^npKuGJSv{v0dzR2%!Kt<2r8c(#nzPC{ zQ*pY{9#tXYacnrFv9Nofl-`e}4b+mTYw>tG3VuB@O6K<`Y7mMZFskv2S2G$$q0jB7 z7=J@((eP2FM{`w%l1n!FFcf{rMlV9q3pTnM1U}YP#MZbB+G)|LahjGSI6aLzbqdlO XTBc4Ldc!*$00000NkvXXu0mjf+H2-R literal 0 HcmV?d00001 diff --git a/lib/common/constant/app_images.dart b/lib/common/constant/app_images.dart index c464f2d..2a9e24e 100644 --- a/lib/common/constant/app_images.dart +++ b/lib/common/constant/app_images.dart @@ -3,4 +3,61 @@ class AppImages{ static const String iconOneKeyDoor = 'assets/icon/icon_one_key_door.png'; static const String iconOneKeyDoorKey = 'assets/icon/icon_one_key_door_key.png'; static const String bgOneKeyDoor = 'assets/images/bg_one_key_door.png'; + static const String mockImage = 'assets/images/mockImage.jpg'; + + // 视频类图标 + static const String iconVideoCenter = 'assets/icon/video_center.png'; + static const String iconIntelligentInspection = 'assets/icon/intelligent_inspection.png'; + static const String iconVideoSearch = 'assets/icon/video_search.png'; + static const String iconIntelligentAnalysis = 'assets/icon/intelligent_analysis.png'; + static const String iconPersonCapture = 'assets/icon/person_capture.png'; + static const String iconVehicleManagement = 'assets/icon/vehicle_management.png'; + + // 人员通行类图标 + static const String iconAccessManagement = 'assets/icon/access_management.png'; + static const String iconAccessAuthorization = 'assets/icon/access_authorization.png'; + static const String iconOneClickOpenDoor = 'assets/icon/one_click_open_door.png'; + static const String iconPasswordOpenDoor = 'assets/icon/password_open_door.png'; + static const String iconTrafficRecord = 'assets/icon/traffic_record.png'; + static const String iconVisitorManagement = 'assets/icon/visitor_management.png'; + static const String iconMyVisitor = 'assets/icon/my_visitor.png'; + static const String iconVisitorInvitation = 'assets/icon/visitor_invitation.png'; + + // 考勤类图标 + static const String iconAttendanceSetting = 'assets/icon/attendance_setting.png'; + static const String iconAttendanceStatistics = 'assets/icon/attendance_statistics.png'; + static const String iconMyAttendance = 'assets/icon/my_attendance.png'; + static const String iconMobileCheckin = 'assets/icon/mobile_checkin.png'; + static const String iconLeaveRequest = 'assets/icon/leave_request.png'; + static const String iconMakeUpCard = 'assets/icon/make_up_card.png'; + static const String iconBusinessTrip = 'assets/icon/business_trip.png'; + static const String iconGoOut = 'assets/icon/go_out.png'; + + // 审批类图标 + static const String iconInitiateApproval = 'assets/icon/initiate_approval.png'; + static const String iconApprovalRecord = 'assets/icon/approval_record.png'; + + // 可视对讲类图标 + static const String iconIntercomDevice = 'assets/icon/intercom_device.png'; + static const String iconCallRelationship = 'assets/icon/call_relationship.png'; + static const String iconCallReminder = 'assets/icon/call_reminder.png'; + + // 信息发布类图标 + static const String iconBroadcast = 'assets/icon/broadcast.png'; + static const String iconAnnouncement = 'assets/icon/announcement.png'; + static const String iconInfoPublish = 'assets/icon/info_publish.png'; + + // 基础应用类图标 + static const String iconPersonnelManagement = 'assets/icon/personnel_management.png'; + static const String iconTeamQrcode = 'assets/icon/team_qrcode.png'; + static const String iconDeviceManagement = 'assets/icon/device_management.png'; + static const String iconTableMenu = 'assets/icon/icon_table_menu.png'; + + // 底部导航栏图标 + static const String iconHome = 'assets/icon/bar/home.png'; + static const String iconHomeSelected = 'assets/icon/bar/home_selected.png'; + static const String iconNotification = 'assets/icon/bar/notification.png'; + static const String iconNotificationSelected = 'assets/icon/bar/notification_selected.png'; + static const String iconMine = 'assets/icon/bar/mine.png'; + static const String iconMineSelected = 'assets/icon/bar/mine_selected.png'; } \ No newline at end of file diff --git a/lib/views/home/home_controller.dart b/lib/views/home/home_controller.dart index 69c0622..a5b270a 100644 --- a/lib/views/home/home_controller.dart +++ b/lib/views/home/home_controller.dart @@ -4,43 +4,89 @@ import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:starwork_flutter/base/app_permission.dart'; import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/views/main/main_controller.dart'; class HomeController extends BaseController { + + final mainController = Get.find(); + final isOpenNotificationPermission = false.obs; + // 页面加载状态 + final isLoading = true.obs; + var carouselCurrentIndex = 0.obs; - + // 渐变颜色列表 final List gradientColors = [ - const Color(0xFFBFCBEF), // #bfcbef - const Color(0xFFECBE9B), // 原来的颜色 - const Color(0xFFD5F3D5), // 清新绿色 - const Color(0xFFFFB6C1), // 温柔粉色 + const Color(0xFFBFCBEF), + const Color(0xFFECBE9B), ]; - + // 当前渐变颜色 var currentGradientColor = const Color(0xFFBFCBEF).obs; @override void onInit() async { super.onInit(); - isOpenNotificationPermission.value = await AppPermission.checkPermission( - permission: Permission.notification, - ); + + // 模拟初始化加载 + await _initializeData(); // 监听轮播图切换,更新渐变颜色 carouselCurrentIndex.listen((index) { updateGradientColor(index); }); } - + + // 初始化数据 + Future _initializeData() async { + // 模拟加载延迟(2秒) + await Future.delayed(const Duration(seconds: 2)); + + // 检查通知权限 + isOpenNotificationPermission.value = await AppPermission.checkPermission( + permission: Permission.notification, + ); + + // 加载完成,隐藏骨架屏 + isLoading.value = false; + } + // 根据轮播图索引更新渐变颜色 void updateGradientColor(int index) { if (index < gradientColors.length) { currentGradientColor.value = gradientColors[index]; } else { // 如果索引超出颜色数量,使用模运算轮环 - currentGradientColor.value = gradientColors[index % gradientColors.length]; + currentGradientColor.value = + gradientColors[index % gradientColors.length]; } } + + // 首页刷新方法 + Future refreshHome() async { + // 显示加载状态(可选) + // isLoading.value = true; + + // 模拟网络请求延迟 + await Future.delayed(const Duration(seconds: 1)); + + // 这里可以添加实际的刷新逻辑,比如: + // 1. 重新获取轮播图数据 + // 2. 刷新统计数据 + // 3. 更新功能列表 + // 4. 刷新考勤图表数据 + // 5. 更新门禁列表 + + // 重新检查通知权限 + isOpenNotificationPermission.value = await AppPermission.checkPermission( + permission: Permission.notification, + ); + + // 隐藏加载状态 + // isLoading.value = false; + + print('首页数据刷新完成'); + } } diff --git a/lib/views/home/home_view.dart b/lib/views/home/home_view.dart index e7e120b..d013fa2 100644 --- a/lib/views/home/home_view.dart +++ b/lib/views/home/home_view.dart @@ -1,4 +1,3 @@ -import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -18,9 +17,6 @@ import 'home_controller.dart'; class HomeView extends GetView { const HomeView({super.key}); - static final GlobalKey _scaffoldKey = - GlobalKey(); - @override Widget build(BuildContext context) { return Obx( @@ -36,68 +32,26 @@ class HomeView extends GetView { stops: const [0.1, 1.0], // 第一个颜色到10%,然后第二个颜色开始直到100% ), ), - child: Scaffold( - key: _scaffoldKey, - backgroundColor: Colors.transparent, - body: SafeArea( - child: Container( - width: 1.sw, - padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h), - child: Column( - children: [ - // 固定的上半部分 - _buildPageHead(context), - SizedBox( - height: 10.h, - ), - _buildSystemNotificationPermissionRow(), + child: SafeArea( + child: Container( + width: 1.sw, + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h), + child: Column( + children: [ + // 固定的上半部分 + _buildPageHead(context), + SizedBox( + height: 10.h, + ), + _buildSystemNotificationPermissionRow(), - // 可滚动的下半部分 - Expanded( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - HomeCarouselAreaWidget( - carouselCurrentIndex: - controller.carouselCurrentIndex, - ), - HomeTeamNoticeRowWidget(), - HomeStatisticsRowWidget( - personCount: 12, - deviceCount: 1, - ), - SizedBox(height: 10.h), - HomeFunctionListAreaWidget(), - SizedBox( - height: 10.h, - ), - HomeOnButtonDoorOpeningWidget( - doorList: const [ - '主门门禁', - '车库门禁', - '后门门禁', - '单元门门禁', - ], - ), - SizedBox( - height: 10.h, - ), - HomeAttendanceChartAreaWidget(), - SizedBox( - height: 10.h, - ), - ], - ), - ), - ), - ], - ), + // 可滚动的下半部分(带下拉刷新) + Expanded( + child: _buildRefreshableContent(), + ), + ], ), ), - drawer: HomeLeftDrawerWidget( - teamList: ['家庭群组', '测试团队1', '测试团队2'], - ), ), ), ); @@ -106,7 +60,8 @@ class HomeView extends GetView { _buildPageHead(BuildContext context) { return GestureDetector( onTap: () { - _scaffoldKey.currentState?.openDrawer(); + // 使用MainController的专用方法 + controller.mainController.openDrawer(); }, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -199,4 +154,68 @@ class HomeView extends GetView { ), ); } + + // 带下拉刷新的内容区域 + Widget _buildRefreshableContent() { + return RefreshIndicator( + onRefresh: _onRefresh, + // 基础样式配置 + color: const Color(0xFF4A90E2), // 刷新指示器颜色 + backgroundColor: Colors.white, // 背景颜色 + + // 控制下拉触发距离的关键属性 + displacement: 60.0, // 刷新指示器距离顶部的距离 + edgeOffset: 0.0, // 边缘偏移量 + + // 控制下拉幅度的关键属性 + triggerMode: RefreshIndicatorTriggerMode.onEdge, // 触发模式 + + // 描边宽度 + strokeWidth: 2.5, // 刷新指示器的描边宽度 + + // 语义标签(用于无障碍功能) + semanticsLabel: '下拉刷新首页内容', + semanticsValue: '刷新中...', + + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics( + // 添加弹性滚动效果 + parent: BouncingScrollPhysics(), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + HomeCarouselAreaWidget( + carouselCurrentIndex: controller.carouselCurrentIndex, + ), + HomeTeamNoticeRowWidget(), + HomeStatisticsRowWidget( + personCount: 12, + deviceCount: 1, + ), + SizedBox(height: 10.h), + HomeFunctionListAreaWidget(), + SizedBox(height: 10.h), + HomeOnButtonDoorOpeningWidget( + doorList: const [ + '主门门禁', + '车库门禁', + '后门门禁', + '单元门门禁', + ], + ), + SizedBox(height: 10.h), + HomeAttendanceChartAreaWidget(), + SizedBox(height: 10.h), + ], + ), + ), + ); + } + + // 处理下拉刷新 + Future _onRefresh() async { + // 调用controller的刷新方法 + await controller.refreshHome(); + } } diff --git a/lib/views/home/widget/home_carousel_area_widget.dart b/lib/views/home/widget/home_carousel_area_widget.dart index 41511f4..fe25149 100644 --- a/lib/views/home/widget/home_carousel_area_widget.dart +++ b/lib/views/home/widget/home_carousel_area_widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; class HomeCarouselAreaWidget extends StatefulWidget { const HomeCarouselAreaWidget({ @@ -18,9 +19,9 @@ class HomeCarouselAreaWidget extends StatefulWidget { class _HomeCarouselAreaWidgetState extends State { final List imgList = [ - 'assets/images/mockImage.jpg', - 'assets/images/mockImage.jpg', - 'assets/images/mockImage.jpg', + AppImages.mockImage, + AppImages.mockImage, + AppImages.mockImage, ]; @override diff --git a/lib/views/home/widget/home_function_list_area_widget.dart b/lib/views/home/widget/home_function_list_area_widget.dart index 79dea13..c529d9e 100644 --- a/lib/views/home/widget/home_function_list_area_widget.dart +++ b/lib/views/home/widget/home_function_list_area_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; class HomeFunctionListAreaWidget extends StatefulWidget { HomeFunctionListAreaWidget({super.key}); @@ -15,18 +16,81 @@ class _HomeFunctionListAreaWidgetState extends State with SingleTickerProviderStateMixin { late TabController _tabController; + // 图片缓存Map + final Map _imageCache = {}; + + // 标记是否已经预加载 + bool _isPreloaded = false; + @override void initState() { super.initState(); _tabController = TabController(length: 6, vsync: this); } + @override + void didChangeDependencies() { + super.didChangeDependencies(); + // 在这里预加载图片,确保MediaQuery可用 + if (!_isPreloaded) { + _preloadImages(); + _isPreloaded = true; + } + } + @override void dispose() { _tabController.dispose(); super.dispose(); } + // 预加载所有图片资源 + void _preloadImages() { + final List imagePaths = [ + AppImages.iconAccessManagement, + AppImages.iconAccessAuthorization, + AppImages.iconOneClickOpenDoor, + AppImages.iconPasswordOpenDoor, + AppImages.iconTrafficRecord, + AppImages.iconVisitorManagement, + AppImages.iconMyVisitor, + AppImages.iconVisitorInvitation, + AppImages.iconAttendanceSetting, + AppImages.iconAttendanceStatistics, + AppImages.iconMyAttendance, + AppImages.iconMobileCheckin, + AppImages.iconLeaveRequest, + AppImages.iconMakeUpCard, + AppImages.iconBusinessTrip, + AppImages.iconGoOut, + AppImages.iconInitiateApproval, + AppImages.iconApprovalRecord, + AppImages.iconIntercomDevice, + AppImages.iconCallRelationship, + AppImages.iconCallReminder, + AppImages.iconBroadcast, + AppImages.iconAnnouncement, + AppImages.iconInfoPublish, + AppImages.iconPersonnelManagement, + AppImages.iconTeamQrcode, + AppImages.iconDeviceManagement, + AppImages.iconVideoCenter, + AppImages.iconIntelligentInspection, + AppImages.iconVideoSearch, + AppImages.iconIntelligentAnalysis, + AppImages.iconPersonCapture, + ]; + + for (String path in imagePaths) { + _imageCache[path] = AssetImage(path); + // 预缓存图片,使用异步方式避免阻塞 + precacheImage(_imageCache[path]!, context).catchError((error) { + // 处理预加载失败的情况 + debugPrint('预加载图片失败: $path, 错误: $error'); + }); + } + } + @override Widget build(BuildContext context) { return Container( @@ -59,7 +123,7 @@ class _HomeFunctionListAreaWidgetState extends State return Row( children: [ - Expanded( + Flexible( child: TabBar( controller: _tabController, isScrollable: true, @@ -97,10 +161,22 @@ class _HomeFunctionListAreaWidgetState extends State // 处理菜单点击事件 print('菜单被点击'); }, - child: Icon( - Icons.menu, - size: 18.sp, - color: Colors.grey[600], + child: Image( + image: AssetImage(AppImages.iconTableMenu), + width: 20.w, + height: 20.w, + fit: BoxFit.contain, + gaplessPlayback: true, + // 防止闪烁 + filterQuality: FilterQuality.medium, + // 优化过滤质量 + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.image_not_supported, + size: 26.sp, + color: Colors.grey, + ); + }, ), ), ), @@ -121,27 +197,28 @@ class _HomeFunctionListAreaWidgetState extends State // 每个分类对应的功能列表 final Map>> functionData = { '人员通行': [ - {'icon': Icons.door_front_door, 'title': '门禁管理'}, - {'icon': Icons.people, 'title': '门禁授权'}, - {'icon': Icons.access_time, 'title': '一键开门'}, - {'icon': Icons.card_membership, 'title': '密码开门'}, - {'icon': Icons.face, 'title': '我的访客'}, - {'icon': Icons.fingerprint, 'title': '访客统计'}, - {'icon': Icons.security, 'title': '访客邀约'}, + {'icon': AppImages.iconAccessManagement, 'title': '门禁管理'}, + {'icon': AppImages.iconAccessAuthorization, 'title': '门禁授权'}, + {'icon': AppImages.iconOneClickOpenDoor, 'title': '一键开门'}, + {'icon': AppImages.iconPasswordOpenDoor, 'title': '密码开门'}, + {'icon': AppImages.iconTrafficRecord, 'title': '通行记录'}, + {'icon': AppImages.iconVisitorManagement, 'title': '访客管理'}, + {'icon': AppImages.iconMyVisitor, 'title': '我的访客'}, + {'icon': AppImages.iconVisitorInvitation, 'title': '访客邀约'}, ], '考勤': [ - {'icon': Icons.schedule, 'title': '考勤设置'}, - {'icon': Icons.assignment, 'title': '审批记录'}, - {'icon': Icons.broadcast_on_personal, 'title': '广播'}, - {'icon': Icons.announcement, 'title': '信息发布'}, - {'icon': Icons.bar_chart, 'title': '统计报表'}, - {'icon': Icons.calendar_today, 'title': '排班管理'}, - {'icon': Icons.access_alarm, 'title': '打卡记录'}, - {'icon': Icons.person_add, 'title': '添加常用'}, + {'icon': AppImages.iconAttendanceSetting, 'title': '考勤设置'}, + {'icon': AppImages.iconAttendanceStatistics, 'title': '考勤统计'}, + {'icon': AppImages.iconMyAttendance, 'title': '我的考勤'}, + {'icon': AppImages.iconMobileCheckin, 'title': '手机打卡'}, + {'icon': AppImages.iconLeaveRequest, 'title': '请假'}, + {'icon': AppImages.iconMakeUpCard, 'title': '补卡'}, + {'icon': AppImages.iconBusinessTrip, 'title': '出差'}, + {'icon': AppImages.iconGoOut, 'title': '外出'}, ], '审批': [ - {'icon': Icons.approval, 'title': '待审批'}, - {'icon': Icons.check_circle, 'title': '已审批'}, + {'icon': AppImages.iconInitiateApproval, 'title': '发起审批'}, + {'icon': AppImages.iconApprovalRecord, 'title': '审批记录'}, {'icon': Icons.pending, 'title': '审批中'}, {'icon': Icons.history, 'title': '审批历史'}, {'icon': Icons.rule, 'title': '审批规则'}, @@ -150,34 +227,34 @@ class _HomeFunctionListAreaWidgetState extends State {'icon': Icons.analytics, 'title': '审批统计'}, ], '可视对讲': [ - {'icon': Icons.call, 'title': '对讲设备'}, - {'icon': Icons.call_received, 'title': '呼叫记录'}, + {'icon': AppImages.iconIntercomDevice, 'title': '对讲设备'}, + {'icon': AppImages.iconCallRelationship, 'title': '呼叫关系'}, + {'icon': AppImages.iconCallReminder, 'title': '呼叫提醒'}, {'icon': Icons.video_call, 'title': '视频通话'}, {'icon': Icons.volume_up, 'title': '广播通知'}, {'icon': Icons.settings_phone, 'title': '设备设置'}, {'icon': Icons.group_add, 'title': '群组管理'}, - {'icon': Icons.record_voice_over, 'title': '语音留言'}, {'icon': Icons.emergency, 'title': '紧急呼叫'}, ], '信息发布': [ - {'icon': Icons.campaign, 'title': '发布信息'}, - {'icon': Icons.list_alt, 'title': '信息列表'}, + {'icon': AppImages.iconBroadcast, 'title': '广播'}, + {'icon': AppImages.iconAnnouncement, 'title': '公告'}, + {'icon': AppImages.iconInfoPublish, 'title': '信息发布'}, {'icon': Icons.schedule_send, 'title': '定时发布'}, {'icon': Icons.group, 'title': '目标群体'}, - {'icon': Icons.image, 'title': '多媒体'}, {'icon': Icons.analytics, 'title': '发布统计'}, {'icon': Icons.edit, 'title': '编辑模板'}, {'icon': Icons.history, 'title': '发布历史'}, ], '其他应用': [ - {'icon': Icons.apps, 'title': '应用中心'}, - {'icon': Icons.extension, 'title': '插件管理'}, - {'icon': Icons.download, 'title': '下载中心'}, - {'icon': Icons.update, 'title': '应用更新'}, - {'icon': Icons.settings, 'title': '应用设置'}, - {'icon': Icons.help, 'title': '帮助中心'}, - {'icon': Icons.feedback, 'title': '意见反馈'}, - {'icon': Icons.info, 'title': '关于我们'}, + {'icon': AppImages.iconPersonnelManagement, 'title': '人员管理'}, + {'icon': AppImages.iconTeamQrcode, 'title': '团队二维码'}, + {'icon': AppImages.iconDeviceManagement, 'title': '设备管理'}, + {'icon': AppImages.iconVideoCenter, 'title': '视频中心'}, + {'icon': AppImages.iconIntelligentInspection, 'title': '智能巡检'}, + {'icon': AppImages.iconVideoSearch, 'title': '录像智搜'}, + {'icon': AppImages.iconIntelligentAnalysis, 'title': '智能分析'}, + {'icon': AppImages.iconPersonCapture, 'title': '人员抓拍'}, ], }; @@ -192,7 +269,7 @@ class _HomeFunctionListAreaWidgetState extends State shrinkWrap: true, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 4, - childAspectRatio: 1.6, + childAspectRatio: 1.4, ), itemCount: currentFunctions.length, itemBuilder: (context, index) { @@ -208,7 +285,7 @@ class _HomeFunctionListAreaWidgetState extends State ); } - Widget _buildFunctionItem({required IconData icon, required String title}) { + Widget _buildFunctionItem({required dynamic icon, required String title}) { return GestureDetector( onTap: () { print('点击了: $title'); @@ -218,18 +295,21 @@ class _HomeFunctionListAreaWidgetState extends State mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, // 重要:让Column最小化 children: [ - Icon( - icon, - size: 20.sp, // 稍微减小图标 - color: Colors.blue.shade600, - ), + // 优化后的图片渲染 + icon is String + ? _buildOptimizedImage(icon) + : Icon( + icon, + size: 26.sp, + color: Colors.blue.shade600, + ), SizedBox(height: 4.h), // 减少间距 Flexible( // 使用Flexible而不是Expanded child: Text( title, style: TextStyle( - fontSize: 12.sp, // 稍微减小字体 + fontSize: 10.sp, // 稍微减小字体 color: Colors.black87, fontWeight: FontWeight.w400, ), @@ -242,4 +322,27 @@ class _HomeFunctionListAreaWidgetState extends State ), ); } + + // 优化的图片组件 + Widget _buildOptimizedImage(String imagePath) { + return RepaintBoundary( + child: Image( + image: _imageCache[imagePath] ?? AssetImage(imagePath), + width: 26.w, + height: 26.w, + fit: BoxFit.contain, + gaplessPlayback: true, + // 防止闪烁 + filterQuality: FilterQuality.medium, + // 优化过滤质量 + errorBuilder: (context, error, stackTrace) { + return Icon( + Icons.image_not_supported, + size: 26.sp, + color: Colors.grey, + ); + }, + ), + ); + } } diff --git a/lib/views/main/main_controller.dart b/lib/views/main/main_controller.dart index aff0db7..576429a 100644 --- a/lib/views/main/main_controller.dart +++ b/lib/views/main/main_controller.dart @@ -1,28 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/common/constant/app_images.dart'; import 'package:starwork_flutter/views/home/home_view.dart'; import 'package:starwork_flutter/views/messages/messages_view.dart'; import 'package:starwork_flutter/views/mine/mine_view.dart'; class MainController extends BaseController { - // 定义底部导航的标题和图标 - final List bottomNavItems = [ - const BottomNavigationBarItem( - icon: Icon(Icons.home_rounded), - label: '首页', - ), - const BottomNavigationBarItem( - icon: Icon(Icons.messenger_outline_rounded), - label: '消息', - ), - const BottomNavigationBarItem( - icon: Icon(Icons.person), - label: '我的', - ), - ]; + GlobalKey scaffoldKey = + GlobalKey(); // 当前选中的索引 var currentIndex = 0.obs; @@ -37,4 +26,56 @@ class MainController extends BaseController { void changeIndex(int index) { currentIndex.value = index; } + + // 定义底部导航的标题和图标 + List get bottomNavItems => [ + BottomNavigationBarItem( + icon: _buildTabIcon(AppImages.iconHome, AppImages.iconHomeSelected, 0), + label: '首页', + ), + BottomNavigationBarItem( + icon: _buildTabIcon(AppImages.iconNotification, AppImages.iconNotificationSelected, 1), + label: '消息', + ), + BottomNavigationBarItem( + icon: _buildTabIcon(AppImages.iconMine, AppImages.iconMineSelected, 2), + label: '我的', + ), + ]; + + // 构建自定义图标组件 + Widget _buildTabIcon(String unselectedIcon, String selectedIcon, int index) { + return Obx(() => Image.asset( + currentIndex.value == index ? selectedIcon : unselectedIcon, + width: 24.w, + height: 24.w, + fit: BoxFit.contain, + )); + } + + // 打开抽屉的方法 + void openDrawer() { + try { + final scaffoldState = scaffoldKey.currentState; + if (scaffoldState != null && scaffoldState.hasDrawer) { + scaffoldState.openDrawer(); + } else { + print('Cannot open drawer: Scaffold state is null or has no drawer'); + } + } catch (e) { + print('Error opening drawer: $e'); + } + } + + // 关闭抽屉的方法 + void closeDrawer() { + try { + final scaffoldState = scaffoldKey.currentState; + if (scaffoldState != null && scaffoldState.isDrawerOpen) { + scaffoldState.closeDrawer(); + } + } catch (e) { + print('Error closing drawer: $e'); + } + } } diff --git a/lib/views/main/main_view.dart b/lib/views/main/main_view.dart index 80406f8..74c25b8 100644 --- a/lib/views/main/main_view.dart +++ b/lib/views/main/main_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:starwork_flutter/views/main/widget/main_left_drawer_widget.dart'; import 'main_controller.dart'; @@ -10,6 +11,7 @@ class MainView extends GetView { @override Widget build(BuildContext context) { return Scaffold( + key: controller.scaffoldKey, // 添加这一行! // 使用 Obx 响应 currentIndex 的变化 body: Obx( () => IndexedStack( @@ -28,6 +30,9 @@ class MainView extends GetView { unselectedFontSize: 12.sp, ), ), + drawer: MainLeftDrawerWidget( + teamList: ['家庭群组', '测试团队1', '测试团队2'], + ), ); } } diff --git a/lib/views/main/widget/main_left_drawer_widget.dart b/lib/views/main/widget/main_left_drawer_widget.dart new file mode 100644 index 0000000..cf84e84 --- /dev/null +++ b/lib/views/main/widget/main_left_drawer_widget.dart @@ -0,0 +1,280 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class MainLeftDrawerWidget extends StatefulWidget { + MainLeftDrawerWidget({ + super.key, + required this.teamList, + this.selectedTeam, + this.onTeamSelected, + }); + + final List teamList; + final String? selectedTeam; + final Function(String)? onTeamSelected; + + @override + State createState() => _MainLeftDrawerWidgetState(); +} + +class _MainLeftDrawerWidgetState extends State { + @override + Widget build(BuildContext context) { + return Drawer( + width: 0.85.sw, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.zero, // 去掉圆角 + ), + child: Container( + color: const Color(0xFFF6F7FB), + child: SafeArea( + child: Column( + children: [ + // 头部标题栏 + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '我的团队', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + color: Colors.black, + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Container( + width: 28.w, + height: 28.w, + decoration: BoxDecoration( + color: Colors.grey[300], + shape: BoxShape.circle, + ), + child: Icon( + Icons.refresh, + size: 18.sp, + color: Colors.grey[600], + ), + ), + ), + ], + ), + ), + + // 团队列表 + Expanded( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 16.w), + child: ListView.builder( + itemCount: widget.teamList.length, + itemBuilder: (context, index) { + final team = widget.teamList[index]; + final isSelected = team == widget.selectedTeam; + + return Container( + margin: EdgeInsets.only(bottom: 8.h), + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: isSelected + ? const Color(0xFFE3F2FD) // 选中状态:蓝色背景 + : Colors.white, // 未选中状态:白色背景 + borderRadius: BorderRadius.circular(12.r), + border: isSelected + ? Border.all(color: const Color(0xFF2196F3), width: 1) + : Border.all(color: Colors.grey[200]!, width: 1), + ), + child: GestureDetector( + onTap: () { + // 处理团队选择 + if (widget.onTeamSelected != null) { + widget.onTeamSelected!(team); + } + }, + child: Row( + children: [ + // 用户图标 + Container( + width: 40.w, + height: 40.w, + decoration: BoxDecoration( + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[400], + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + Icons.person, + color: Colors.white, + size: 24.sp, + ), + ), + SizedBox(width: 12.w), + + // 团队信息 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + team, // 使用teamList中的元素作为团队昵称 + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w500, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.black, + ), + ), + SizedBox(height: 4.h), + Row( + children: [ + Icon( + isSelected + ? Icons.check_circle + : Icons.check_circle_outline, + size: 14.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[400], + ), + SizedBox(width: 4.w), + Text( + isSelected ? '已选中' : '未选中', + style: TextStyle( + fontSize: 12.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[600], + ), + ), + ], + ), + ], + ), + ), + + // 设置图标 + GestureDetector( + onTap: () { + // 处理设置点击事件 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.all(8.w), + child: Icon( + Icons.settings, + size: 20.sp, + color: isSelected + ? const Color(0xFF2196F3) + : Colors.grey[600], + ), + ), + ), + ], + ), + ), + ); + }, + ), + ), + ), + + // 底部按钮区域 + Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + child: Column( + children: [ + // 创建团队和加入团队按钮 + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + // 处理创建团队 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 12.h), + decoration: BoxDecoration( + border: Border( + right: BorderSide( + color: Colors.grey[300]!, + width: 0.5, + ), + ), + ), + child: Text( + '创建团队', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ), + Expanded( + child: GestureDetector( + onTap: () { + // 处理加入团队 + Navigator.pop(context); + }, + child: Container( + padding: EdgeInsets.symmetric(vertical: 12.h), + child: Text( + '加入团队', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ), + ], + ), + + // 分割线 + Container( + margin: EdgeInsets.symmetric(vertical: 8.h), + height: 0.5, + color: Colors.grey[300], + ), + + // 快捷添加我的设备按钮 + GestureDetector( + onTap: () { + // 处理快捷添加设备 + Navigator.pop(context); + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 12.h), + child: Text( + '快捷添加我的设备', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16.sp, + color: Colors.black87, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/views/messages/messages_controller.dart b/lib/views/messages/messages_controller.dart index 950a8dc..4899349 100644 --- a/lib/views/messages/messages_controller.dart +++ b/lib/views/messages/messages_controller.dart @@ -1,5 +1,81 @@ +import 'package:get/get.dart'; import 'package:starwork_flutter/base/base_controller.dart'; +import 'package:starwork_flutter/views/home/home_controller.dart'; +import 'package:starwork_flutter/views/main/main_controller.dart'; +import 'messages_model.dart'; class MessagesController extends BaseController { + final mainController = Get.find(); + final homeController = Get.find(); + + // 响应式消息列表 + final RxList messageList = [].obs; + + @override + void onInit() { + super.onInit(); + _initializeMessages(); + } + + // 初始化消息数据 + void _initializeMessages() { + messageList.value = [ + MessageItem( + type: MessageType.deviceStatus, + title: '设备状态', + subtitle: '[设备在线] DS-K(L40959329)', + time: '25/08/28', + hasRedDot: true, + ), + MessageItem( + type: MessageType.accessControl, + title: '通行权限', + subtitle: '[通行权限] 下发已结束,成功0条,失败1条,成功0条,失败1条,成功0条,失败1条成功0条,失败1条成功0条,失败1条', + time: '25/08/26', + hasRedDot: false, + ), + MessageItem( + type: MessageType.systemNotice, + title: '系统通知', + subtitle: '没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息没有新的消息', + time: '', + hasRedDot: false, + ), + ]; + } + + // 刷新消息数据 + Future refreshMessages() async { + // 模拟网络请求延迟 + await Future.delayed(const Duration(seconds: 1)); + + // 这里可以添加实际的API调用 + // 模拟新数据 + _initializeMessages(); + + print('消息数据刷新完成'); + } + + // 添加新消息 + void addMessage(MessageItem message) { + messageList.insert(0, message); + } + + // 移除消息 + void removeMessage(int index) { + if (index >= 0 && index < messageList.length) { + messageList.removeAt(index); + } + } + + // 清除所有未读标记 + void clearAllUnread() { + for (int i = 0; i < messageList.length; i++) { + if (messageList[i].hasRedDot) { + messageList[i] = messageList[i].copyWith(hasRedDot: false); + } + } + messageList.refresh(); + } } diff --git a/lib/views/messages/messages_model.dart b/lib/views/messages/messages_model.dart new file mode 100644 index 0000000..194eccb --- /dev/null +++ b/lib/views/messages/messages_model.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; + +// 消息类型枚举 +enum MessageType { + deviceStatus, // 设备状态 + accessControl, // 通行权限 + systemNotice, // 系统通知 +} + +// 消息数据模型 +class MessageItem { + final MessageType type; + final String title; + final String subtitle; + final String time; + final bool hasRedDot; + + MessageItem({ + required this.type, + required this.title, + required this.subtitle, + required this.time, + required this.hasRedDot, + }); + + // 根据消息类型获取图标 + IconData get icon { + switch (type) { + case MessageType.deviceStatus: + return Icons.devices; + case MessageType.accessControl: + return Icons.security; + case MessageType.systemNotice: + return Icons.notifications; + } + } + + // 根据消息类型获取图标颜色 + Color get iconColor { + switch (type) { + case MessageType.deviceStatus: + return const Color(0xFF4A90E2); + case MessageType.accessControl: + return const Color(0xFF4A90E2); + case MessageType.systemNotice: + return const Color(0xFFFF8C42); + } + } + + // 根据消息类型获取图标背景色 + Color get iconBgColor { + switch (type) { + case MessageType.deviceStatus: + return const Color(0xFFE8F4FD); + case MessageType.accessControl: + return const Color(0xFFE8F4FD); + case MessageType.systemNotice: + return const Color(0xFFFFF2E8); + } + } + + // 创建副本,用于更新某些属性 + MessageItem copyWith({ + MessageType? type, + String? title, + String? subtitle, + String? time, + bool? hasRedDot, + }) { + return MessageItem( + type: type ?? this.type, + title: title ?? this.title, + subtitle: subtitle ?? this.subtitle, + time: time ?? this.time, + hasRedDot: hasRedDot ?? this.hasRedDot, + ); + } +} \ No newline at end of file diff --git a/lib/views/messages/messages_view.dart b/lib/views/messages/messages_view.dart index 1fdf353..9e5989a 100644 --- a/lib/views/messages/messages_view.dart +++ b/lib/views/messages/messages_view.dart @@ -1,7 +1,11 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:starwork_flutter/base/app_permission.dart'; import 'messages_controller.dart'; +import 'messages_model.dart'; class MessagesView extends GetView { const MessagesView({super.key}); @@ -9,16 +13,400 @@ class MessagesView extends GetView { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: const Text('MessagesView'), - centerTitle: true, - ), - body: const Center( - child: Text( - 'MessagesView is working', - style: TextStyle(fontSize: 20), + backgroundColor: const Color(0xFFF6F7FB), + body: SafeArea( + child: Container( + width: 1.sw, + padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 4.h), + child: Column( + children: [ + // 固定的上半部分 + _buildPageHead(context), + _buildSystemNotificationPermissionRow(), + Obx( + () => Visibility( + visible: controller + .homeController.isOpenNotificationPermission.value, + child: SizedBox( + height: 10.h, + ), + ), + ), + // 消息列表 + Expanded( + child: _buildRefreshableMessageList(), + ), + ], + ), ), ), ); } + + _buildPageHead(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // 左侧标题区域 + Expanded( + child: GestureDetector( + onTap: () { + controller.mainController.scaffoldKey.currentState?.openDrawer(); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + '19104656的互12312联', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + Icon( + Icons.arrow_right_rounded, + size: 22.sp, + ), + ], + ), + ), + ), + // 右侧功能按钮区域 + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.min, + children: [ + // 清除未读按钮 + Flexible( + child: GestureDetector( + onTap: () { + // 处理清除未读点击事件 + controller.clearAllUnread(); + print('清除未读被点击'); + }, + child: Container( + padding: EdgeInsets.symmetric( + horizontal: 6.w, // 减小内边距 + vertical: 3.h, + ), + decoration: BoxDecoration( + color: const Color(0xFFF0F0F0), + borderRadius: BorderRadius.circular(12.r), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: Text( + '清除未读', + style: TextStyle( + fontSize: 12.sp, // 减小字体 + color: Colors.black54, + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ], + ), + ), + ), + ), + // 分隔线 + Container( + height: 16.h, // 减小高度 + width: 1.w, + color: Colors.grey.withOpacity(0.3), + ), + // 通知设置按钮 + Flexible( + child: GestureDetector( + onTap: () { + // 处理通知设置点击事件 + print('通知设置被点击'); + }, + child: Text( + '通知设置', + style: TextStyle( + fontSize: 14.sp, // 减小字体 + color: Colors.black, + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ), + ], + ), + ), + ], + ); + } + + _buildSystemNotificationPermissionRow() { + return Obx( + () => Visibility( + visible: !controller.homeController.isOpenNotificationPermission.value, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h), + decoration: BoxDecoration( + color: const Color(0xFFFEF2E5), + borderRadius: BorderRadius.all( + Radius.circular(8.r), + ), + ), + margin: EdgeInsets.symmetric(vertical: 8.h), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '系统通知未开启,报警消息无法通知'.tr, + style: TextStyle( + color: const Color(0xFFEE9846), + fontSize: 12.sp, + ), + ), + const Spacer(), + GestureDetector( + onTap: () async { + controller.homeController.isOpenNotificationPermission.value = + await AppPermission.requestNotificationPermission(); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 4.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(4.r), + ), + ), + child: Text( + '去开启'.tr, + style: TextStyle( + color: const Color(0xFFEE9846), + fontSize: 12.sp, + ), + ), + ), + ), + SizedBox( + width: 14.w, + ), + Icon( + Icons.cancel, + color: const Color(0xFFEE9846), + size: 18.sp, + ) + ], + ), + ), + ), + ); + } + + _buildRefreshableMessageList() { + return RefreshIndicator( + onRefresh: _onRefresh, + // 基础样式配置 + color: const Color(0xFF4A90E2), // 刷新指示器颜色 + backgroundColor: Colors.white, // 背景颜色 + + // 控制下拉触发距离的关键属性 + displacement: 60.0, // 刷新指示器距离顶部的距离(默认40.0) + edgeOffset: 0.0, // 边缘偏移量(默认0.0) + + // 控制下拉幅度的关键属性 + triggerMode: RefreshIndicatorTriggerMode.onEdge, // 触发模式 + // RefreshIndicatorTriggerMode.onEdge: 在边缘触发(默认) + // RefreshIndicatorTriggerMode.anywhere: 在任何位置都可以触发 + + // 描边宽度 + strokeWidth: 2.5, // 刷新指示器的描边宽度(默认2.0) + + // 语义标签(用于无障碍功能) + semanticsLabel: '下拉刷新消息列表', + semanticsValue: '刷新中...', + + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics( + // 控制滚动物理特性 + parent: BouncingScrollPhysics(), // 添加弹性滚动效果 + ), + child: Column( + children: [ + _buildMessageList(), + SizedBox(height: 16.h), + _buildMessageTip(), + ], + ), + ), + ); + } + + Future _onRefresh() async { + // 调用controller的刷新方法 + await controller.refreshMessages(); + } + + _buildMessageList() { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.r), + ), + child: Obx( + () => Column( + children: [ + // 动态生成消息列表项 + ...controller.messageList.asMap().entries.map((entry) { + int index = entry.key; + MessageItem message = entry.value; + + return Column( + children: [ + _buildMessageItem( + message: message, + onTap: () => _onMessageTap(index, message), + ), + // 如果不是最后一项,显示分隔线 + if (index < controller.messageList.length - 1) + _buildDivider(), + ], + ); + }).toList(), + ], + ), + ), + ); + } + + _buildMessageTip() { + return Container( + padding: EdgeInsets.symmetric(vertical: 10.h), + child: Text( + '只展示最近7天的消息', + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey[500], + fontWeight: FontWeight.w400, + ), + ), + ); + } + + _buildMessageItem({ + required MessageItem message, + VoidCallback? onTap, + }) { + return GestureDetector( + onTap: onTap ?? + () { + // 默认点击事件 + print('${message.title} 被点击'); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h), + child: Row( + children: [ + // 左侧图标 + Stack( + children: [ + Container( + width: 40.w, + height: 40.w, + decoration: BoxDecoration( + color: message.iconBgColor, + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + message.icon, + color: message.iconColor, + size: 20.sp, + ), + ), + if (message.hasRedDot) + Positioned( + top: 0, + right: 0, + child: Container( + width: 8.w, + height: 8.w, + decoration: const BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + ), + ), + ], + ), + SizedBox(width: 12.w), + // 中间内容 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + message.title, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + SizedBox(height: 4.h), + Text( + message.subtitle, + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey[600], + fontWeight: FontWeight.w400, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + // 右侧时间 + if (message.time.isNotEmpty) + Text( + message.time, + style: TextStyle( + fontSize: 12.sp, + color: Colors.grey[400], + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ); + } + + // 处理消息项点击事件 + void _onMessageTap(int index, MessageItem message) { + print('${message.title} 被点击,索引: $index'); + // 这里可以添加具体的业务逻辑,比如跳转到详情页面 + // 如果有未读标记,可以标记为已读 + if (message.hasRedDot) { + // 可以在这里调用controller的方法标记为已读 + } + } + + _buildDivider() { + return Container( + margin: EdgeInsets.symmetric(horizontal: 16.w), + height: 1.h, + color: Colors.grey[200], + ); + } } diff --git a/pubspec.yaml b/pubspec.yaml index eb8960a..da4ca4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,4 +51,5 @@ flutter: - assets/images/ - assets/logo/ - assets/icon/ + - assets/icon/bar/