From 629b8ca17e26a92f1d6d80c52f3b63253d27ab2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=8F=E5=B0=91=E9=98=B3?= <786612630@qq.com> Date: Tue, 23 Jul 2024 11:16:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=81=A5=E6=8E=A7=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- images/.DS_Store | Bin 14340 -> 14340 bytes images/icon_remoteControl.png | Bin 0 -> 929 bytes images/lan/lan_en.json | 3 +- images/lan/lan_keys.json | 3 +- images/lan/lan_zh.json | 3 +- images/main/icon_addRemoteControl_tip.png | Bin 0 -> 80055 bytes lib/appRouters.dart | 8 + .../io_addRemoteControlCancel.dart | 95 +++ ...addRemoteControlWithTimeCycleCoercion.dart | 207 +++++++ lib/blue/reciver_data.dart | 15 + lib/blue/sender_manage.dart | 68 +++ lib/login/login/starLock_login_logic.dart | 4 + .../otherTypeKeyChangeDate_logic.dart | 494 ++++++++++------ .../otherTypeKeyChangeDate_page.dart | 18 +- .../otherTypeKeyChangeValidityDate_logic.dart | 460 ++++++++++----- .../otherTypeKeyChangeValidityDate_page.dart | 2 +- .../doorLockLog/doorLockLog_logic.dart | 4 +- .../fingerprintListData_entity.dart | 195 ++++--- .../lockDetail/lockDetail_page.dart | 4 +- .../lockOperatingRecord_logic.dart | 12 +- .../lockOperatingRecord_state.dart | 38 +- .../burglarAlarm/burglarAlarm_logic.dart | 3 +- .../lockSet/faceUnlock/faceUnlock_page.dart | 2 +- .../lockSet/lockSet/lockSet_logic.dart | 22 +- .../lockSet/lockSet/lockSet_page.dart | 52 +- .../lockSet/lockSet/lockSet_state.dart | 1 + .../addRemoteControl_entity.dart | 43 ++ .../addRemoteControl_logic.dart | 266 ++++++++- .../addRemoteControl_page.dart | 547 +++--------------- .../addRemoteControl_state.dart | 70 ++- .../addRemoteControlType_logic.dart | 98 ++++ .../addRemoteControlType_page.dart | 444 ++++++++++++++ .../addRemoteControlType_state.dart | 32 + .../remoteControlDetail_logic.dart | 226 ++++++++ .../remoteControlDetail_page.dart | 354 ++++++++++++ .../remoteControlDetail_state.dart | 49 ++ .../remoteControlList_logic.dart | 239 +++++++- .../remoteControlList_page.dart | 389 ++++++++----- .../remoteControlList_state.dart | 17 + .../lockMian/lockList/lockList_logic.dart | 5 + .../lockMian/lockList/lockList_xhj_page.dart | 11 +- .../lockMian/lockMain/lockMain_logic.dart | 7 + .../messageList/messageList_xhj_page.dart | 4 +- lib/network/api.dart | 9 + lib/network/api_provider.dart | 112 +++- lib/network/api_repository.dart | 107 +++- 46 files changed, 3598 insertions(+), 1144 deletions(-) create mode 100644 images/icon_remoteControl.png create mode 100644 images/main/icon_addRemoteControl_tip.png create mode 100644 lib/blue/io_protocol/io_addRemoteControlCancel.dart create mode 100644 lib/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart create mode 100644 lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_entity.dart mode change 100755 => 100644 lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart mode change 100755 => 100644 lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart mode change 100755 => 100644 lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_state.dart create mode 100755 lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_logic.dart create mode 100755 lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_page.dart create mode 100755 lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_state.dart create mode 100644 lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_logic.dart create mode 100644 lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_state.dart diff --git a/images/.DS_Store b/images/.DS_Store index 0e394cd1f60489b0fd9cc782f83f2c2e6b67e356..5329245eda062c7ba0ce05efbe9397d27aa5bd2d 100755 GIT binary patch delta 50 zcmZoEXepTB<5aU^hRb&14>dr;{~Bc{V!=p5mLV!057>TY-&ZbEw2Nb|BATGrPfG Fb^x>H5F7vi delta 64 zcmV-G0Kfl)aD;G>PXSx8P`eKSJ(CO&<&%3N3w2(!%>_6M^EF#QLli4^+) diff --git a/images/icon_remoteControl.png b/images/icon_remoteControl.png new file mode 100644 index 0000000000000000000000000000000000000000..8568eee20c2a3d64478182319fcf86d64dacc681 GIT binary patch literal 929 zcmV;S177@zP)`CJ?FfrCgUZ_IRBa; zK!|{hD-cp3;{gyR!H@zW1v0KcNP&z8K==xT6bLEcSbN{%0EU8%$ei4tPsam5pS#)$5BW{| z8*qeFI6k5m8g#NA0gzvYKfOScpYXAQf8%Li3bulMh@i!jwPIBO$dASI_uw2vwwSP> zZyWaAiW>U%ne;WWwJm@aK~KOuNIW!gOty&dgG&c}51RH(YHwSBRXB1H)ImZk!C?i0 z3iT%1x=J}r`!nv}rd{b^rPvmr6|c1i7$yM38o8$pk#>e!@@f^h2%@qbD%d@xa+mv? zt+3~z|4V?~yt@`++e8eaEnGUOFU*7hgCrroiQml-*S_GN;%x*J2%zw6a4q%cygcvPMaJ86A9YnXeqp=%pCBJ`)6q1VY{HfL8imN=nGo z&Z+=%1|p}q=v+W~-vgQ0BQflCB-RY}gRem61=jv2bRmEaLC*qhc}>b&j8{PZrAa4K zTk8T0I>g9oRFj+v>+f=%3SWl2j>N1MX98Gdk>cS&fE40+(KSSX7sHr>M2LVC;(5_E zM1U8=n1V!zfE40+(KSSX7sHr>M2LVC;(5_E^AYeDq+wZ5HXnxJ00000NkvXXu0mjf D7r3Dj literal 0 HcmV?d00001 diff --git a/images/lan/lan_en.json b/images/lan/lan_en.json index 077942d5..f8755200 100755 --- a/images/lan/lan_en.json +++ b/images/lan/lan_en.json @@ -931,5 +931,6 @@ "感应到门前约0.8米有人时,将自动启动面部识别开锁。": "When someone is sensed about 0.8 meters in front of the door, the face recognition unlocking will be automatically started.", "感应到门前约0.5米有人时,将自动启动面部识别开锁。": "When someone is sensed about 0.5 meters in front of the door, the face recognition unlocking will be automatically started.", "感应距离已关闭,需手动触摸键盘任意键,进行面部识别开锁。": "The sensing distance has been turned off, you need to manually touch any key on the keyboard to perform face recognition unlocking.", - "防误开已打开,时间是": "The anti-mistake opening has been turned on, and the time is" + "防误开已打开,开锁后": "The anti-mistake opening has been turned on, and after unlocking", + "秒内不可使用面容开锁": "Face unlocking cannot be used within seconds" } diff --git a/images/lan/lan_keys.json b/images/lan/lan_keys.json index 457f83ac..bf7404a6 100755 --- a/images/lan/lan_keys.json +++ b/images/lan/lan_keys.json @@ -964,5 +964,6 @@ "感应到门前约0.8米有人时,将自动启动面部识别开锁。": "感应到门前约0.8米有人时,将自动启动面部识别开锁。", "感应到门前约0.5米有人时,将自动启动面部识别开锁。": "感应到门前约0.5米有人时,将自动启动面部识别开锁。", "感应距离已关闭,需手动触摸键盘任意键,进行面部识别开锁。": "感应距离已关闭,需手动触摸键盘任意键,进行面部识别开锁。", - "防误开已打开,时间是": "防误开已打开,时间是" + "防误开已打开,开锁后": "防误开已打开,开锁后", + "秒内不可使用面容开锁": "秒内不可使用面容开锁" } diff --git a/images/lan/lan_zh.json b/images/lan/lan_zh.json index 35256364..bcf35060 100755 --- a/images/lan/lan_zh.json +++ b/images/lan/lan_zh.json @@ -929,5 +929,6 @@ "感应到门前约0.8米有人时,将自动启动面部识别开锁。": "感应到门前约0.8米有人时,将自动启动面部识别开锁。", "感应到门前约0.5米有人时,将自动启动面部识别开锁。": "感应到门前约0.5米有人时,将自动启动面部识别开锁。", "感应距离已关闭,需手动触摸键盘任意键,进行面部识别开锁。": "感应距离已关闭,需手动触摸键盘任意键,进行面部识别开锁。", - "防误开已打开,时间是": "防误开已打开,时间是" + "防误开已打开,开锁后": "防误开已打开,开锁后", + "秒内不可使用面容开锁": "秒内不可使用面容开锁" } diff --git a/images/main/icon_addRemoteControl_tip.png b/images/main/icon_addRemoteControl_tip.png new file mode 100644 index 0000000000000000000000000000000000000000..02dc28983da3925e3c6d2f2ef7f840e6c855ec9d GIT binary patch literal 80055 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%0Aog)c@5->pDgikofq z>A_x@x}#(!+cS1QL2&zn%FJZ&_JHz79xDU}vzY20&}UAKCC}8F^p@y(N!P3m{r^wZ zHHlBy>_M^Yd_M@?z){T5wrFZvKxFk;*{=S! z%S1w8egAQH;m3iRt}luyocNTwv{{b?{Y%7DIGTpby9mk7gJ-d>)EOOYar#Pk?>o%}7&{FdCL|~U3*~R$ul7{2p=kg3je>xka zP-ot!GI4j!Z?qK`RvYBu&DYk|;hYGe48niac&@^DOPHDoO9U4&39(z{%VWAeT<&Qe z0fvX*F%3E0=em`*y5~1KVz`z-oxsJ89T;b2enGDN-sXz)W22if`@gnv#t-8va zk4uM~D0_+VgM!CgghLlTrXaB&MxUngH}|mf{g0-v0II6}zP^;8ASFsSQc6lUNJuIm zh#=kFA&t^0g3_WOpoBC?BMlPL-6|q2-QPa%@Bhu*aU4gu_nz~d=h=I$wbwFmO3Pt* z6T(DnXyR#j<)!qv(#3%}+9e+&X?QZvV&h&Uo2>!ATbNvLDqW`TN1%3g49`0p7soHs zE|Cnr(4>R>chA=J-lg)-#VouF`LuYjae5SXxqOLn`D^~T?(5-#glwR^IB7Z|O@99W zCFH_La|^KJ)9h*))1UI$4ZK%xgSF~=2$+Mk_itbg6IfJg7rCs-G~*RBK%@B^o9Iq_ z1@TH>6|U|tqOzt(SVZpK6z0TDjCUWb)o`^((B0Mamkw7fMp-Q~Pq8bf?4nD{B1wol z#h3d{0!7k-UzWGd8=AsEP<^8I!+Wv91yqk;!9CA^t=b=V>*B}{T3;$jXEK0Eaj^c* z2TYBF<;4y-wJkrq+6td^qK{H34U0#1R+_`9OW#{2ubA)$xmRGhU!Y z^k___I!TT>|2wGe=KNML{+MHg_V0B6vOW6MbTYoPb=gWwm(e{+e!twb>93}v;o~Mt z-6@x2hPmg5;%6~U$JGD>9ISMaDs$r;9c4d!_4NDLUB}LiWF_VVA@4oba2&W>cD4jc zL4N7rivbZj_wU}i4`>#6@8t(;mKR#cd?Jl%ubrmF!5kCshleNfe8x%mkR&G4?27+sbpRz|i!R6f$D3_s$**4+Jmhu;E+!>g=g;c`NWoRLSoZ=B6@ZDS z0=s;AU}Ya{&cb99%mahBl0Z3|YP-Z3sDJDK_1po#rv(xYf%jONj5jKOp+VJjU^=`} zO~jG%F-L{rvoz*ur?G{}0h*BvFogF|wMO~;5pC5Iragaa#$-nWQ2di78}b!T*-5Ug z9W>n~xu$w*P`p+9d<{SMomkMY314+dG>FF!F53;yDFbc<9OX%ZgM)rGo{VToN6XcA zU$m`&ZM2KA`3upb3(3nTUfbId_R{EN#t!~OPl{(t-q<)*AJs&>eZ(3JX?$QBjKr2h zZDZ^6@8xjDB-GgDmIDl6+X#SLrvC|BWV^|52V1Oa-R<#4hn-ZREu^UR_&bC8%*|Dc z_I}EM7K@NE>!OG{Rra6VM3DkRRXYcUCP^~Zg6Lt6)90}?*7hoX&BPjMStLjm=nnmP zkb5J4htGJT2M^>n?GpI=sb(x>V)=BKK^QHxIy(rY5wPrdcD;VLUlcmKFH1ZCc%AI8 zeR-PfQE#9OODEt$jLZ}&FlGRdrha$0G1rFkAbmt6-F)X(espzw zKs%}oWL>mPmqa~lBu6Sa8b_Ize`?ObKitbZU_ANJjfNZT zm*WbBPd-P`1MBlOAb>dFU#H`u@l5k@N>z00Q;CrM8h4e^udX zJFa-wb%VbT?#qMT=e7+sUH%sAvLs;~{F=xBV?$IqU7@Q6j2`5B6=s&i%_=8G&{-FI zz3*GN72tWessF6TCFjGy4I;#ZIbi>f;o_nH*5UTwJEYqijscVa3f=eg^lY8idx%+> zM8qnQ1jE+g4Y2gz{~FT^h@DKq`l+ zX~E^v;s1Sxq)RrFwLbL!_g~PmY%l}AS3)Qnhy)A|}N=3Wh z{&rV9Vqf0byK%O4`FnoJGhq9^Sit7pIp1AX!1!_gms$vQb!xsg^I3;}hDXvzuP>Cp zb#O!OBEu>VB^yE{k6ubPpVImxE^bMc$qZ+!)!G+NR<)ZW=nd~K#Q_MtGqsj^pSz@H znUNx92`0Lb;AL*eKZjemu)WyYNJMKw1%C6$pfLoYg3B71Js|OdvxRnU^-I3G4->Bn zHi+NQWr+7;lm%K1JCI&TX z%m4)8AnAk`46Yy^rL#T+PC!8-sn>Of25r6K3joI>5#8P*8Z*>?s zyO8P@tnk3d`!J5OWbLvXtaezt2Ps(l(&@s7M|2lNS@=a_Xj zqj)T)KHwz3OX=L}6}vdgI0QKt*v`S^=OFMrFo+E52}Ur{rosnHXRzab!4nE{(<&+U zTZj=>&E#_=itf4jRgm)ndG+70&V>JWZ%#o2{>D6vVgnDCe?-hIwBiY@7%HTTaKs}n zu84^)oJAl&8K~&DFq-!md;{j~km3$S#56c8#>ChkojD0GkqRAFH^{%ICBDyu9Qx)v zJ=w)ErtuFbO!C5cEpaJU$l6c!w6F^R@H#)7FV^{+S{ER+0xJSuh>;rzD$r2Tf3b%t zKdocazms)!`-g8qT3i>=v+wW8(1K0t8wlD*2ccWN8+F0Ak$ED(IgWlNJVb=NPxdYB zNe@gxT!oa^@Xe6h2b(8M9z&1+P8I7!G~}%-wTJpxkHi<^2wLl(9Zz)HA14bdT|_>5 z^Wd3P#X)lBofBxm4Hup4b`k2QdGt$gu!W06YVAvB7aYzq4CbGs6}M|4QoZd9UR~ky7Su0 zBuc5di|~Lzq%;azOymOB)YPaJl^e_}m!>zoF9v4m>6Y%rrRCOu31fE^zv}w@bg!=m z%WOvf^T-Gub6E@7^mMg%JMUccYV)Q4Hjc^&jz}qDiLXM~pA}jy`UuB+tT$hv>Sw|! zvUyK&&hS34;N3{Ro_ns?%FjOGdknl5{dlkYyA?An%ZspAs<;JH6V!$jX}?MY5>fe1 zI5;#OI@%VUbJb{+o>LC@D`zcRSE;Rs-y*dSadn7o0=jjQLZ@N$qt^W#4eJ^LZJ6Xq`ZaE$X7U22?= z4Y$GjCOm@=GbP$PI^>5BCD+BP^0>Oqr9aW;zbKD5%#sET>A-=iX zviSX4|BHV@0KtH%Ynq^=%(%NRq*hW7(hf&bMdex}Aabvz{|Hdt=v4I!U8y(Y*O<*giPI3)P z%;x}R+x~TRmPD3W`2CtXuf3WzOO=Vw0N#yXsnVK3Tml|+){FP1F=l_)NZE?;8 z;om%hg5yJ->lV;pz+C!F?e&|d=RAnPFRB^V02oAyR87n-gDmPLHe{sBLj3|IH-u~8 zkDMz~JT;jpjpVIG(S>K18n(P*WfYr#(!Je2Q$wlFtv1_bIS-FSZO`oYKjBLcVuP2aWrXH zwYmF|wI`mEG4QwT0M1g@;`Z|&LEh@Qf!EF>IGg`2kFMK`-qv7_Usl%|-J?&KSnCPY zs|iAHkj%$#EM>3S{ekK%?=rs4u>#WfJ+QP(*js`l|6i&`B{PQaw+yx;f`>)Z7XfF00-P|%B^sSYEE4Q$Po z;yIo=)A=B@L#{5&;MdjFLDT*rn_szng*c^?Sk{!xl&U10mp(X-N1psT>_^&(?zPT2 zgy`^}rTn~yL@XxbN+%zr@Ut(P^@FbuW!2@X?4tLaSil9m6-j;G8*N`EqD(I zR6sSKo%wzjxtfkOM*R`KDY#96ojPJAQ9GyRer##5K*01)G5Iv$nzwk@3a8=@ZqUnel$PK`-!I&&eP)!nV?U>Dz zOX>7Rzc3Q;4u!~L_M2kI6bA%9L5PVO8C}yUjV#k1q2u7;Z|>LWl9Z8!H@E>$BW|((eo^(85A9@$sRAuWZ-zy5r>3 zBhTh))(xvE;+H(yZT?5mbRsG87Ns5{lZ=Y7;w0D5gvi(umOW~oxr_2W{N6HjO(P|0 zR(F;AV2J$g-QtRM-}rdm0#*#0t>VJc9y8t}IEzJX6mm8Hic;Ytalm|x76;Sn-mmFm ze+x;N-=vp44510fRk#<^wc$4O!npWwDLq@`^GF1z?e*B156`FM^l*Y8D%pKxY2F%C z3m}rJe3q!_%n@H{k)JQ4PGVf{)W^uE`97GU77=VPaS%J7#+ z#>m6tpsjQ2WKNs?CeSLK7ja{{<>1|vGEi4i;`HxZb$II-S_hfDy!?L@GiMU6436*q z=o8hPG11j~4+uAwSToFL{Z_H0@}vKFnSJcF4*T#1c2QmDx>?km?8Eh_q{2I*MXb{k z6U~77f2LVxfK2hy(o*NFQ{8#d8s&Ibf2qW73f|0tu*F4Vh>5-q8{k@T(`FZS$^k{W z&fQSNN`xj3o05|9`fzc^gw{(s5%M*a#ax+yEJ`bFBli;qI?_8$^8~2a4ZA==QNG>d zv(LBIVvg{u1aABu$nf?}B_B_Txb>}sXuj?DSnTS0Kw+I;hxtBh0&_F*u|b#&J#e&e z1j2EIYObF5kCJ5=RB!2B6(^^lKpIgvv*9D}0Iv3SL>!)xj?F*wDdl|9I#y%!GZbuV zAFkf;@o zafk~$o?NrQrFF1jWR}+-Kz6h^=^#-a#+- zIY0mR>GLrLrZ-CTZeF+c2Tlb`Y9|5tlRe_E`;%c*!qd7DVrmj%H;-E zo3xLt*AU|sW2l~>Ps{ZEr>Kvyt44HzIoSP4VLq5nB+4S>EkF8bM3%}m=D1Fb>ggDh z(utB;H8|Daf)ldYT1X4vVSrA85I-ubA+XWZ2p!Jw`G89dYNts_vd&i1y3SKTvCO*_49zU#@ap zvR(9X1q7MoM6caT+6e%8+Ksb@rqjGf$CG!D6monCT2mX>*VgXMYwC}y>Jj_IH{F1} z6sVQJftCAEGQ+2ZIp5v`d1wo2o^6iMcofOjUd(@WZmp+#&F_XOO@%Ml6aMA?wW923 z_M37%q%YK>fG^VgUduR%F>DZKHv0igL1J$Vq48(D|1`yL*VUn~Wj(lQcLw;a;c$6W3Q>@~FnY|~y0=!z>bC44p&9r^D1 za4cqWp5^!gecxIY33h?<3$(tlc0OqrAC^9n^CQ1pslhq?wjg8%c>8tx(BINuDh7hTSV5$u9T@f1X zE7Fj;_eH-hl&k5CI4vy=IM9xTh4;($im!+;;mYI2hqNkOwGHZcFz4z!0B;FCJ^6;Y-PgMH_pCd( zB<`KDt1B+N31Zxy6QS5Pgw^{S%&7Z>%3rJT_I&NJ1tfysm(mTfXr&Fx4*WcdAJ*HiQh9U=57u~`$kTNLH76b z83oFXo{r2&3YDHhFv$*{zn>rT1~_QT;?S$`bdn*ba!F&Og8`Y4T=-+Hkng`_K$Zg8 zD^TaAO=V1Sz}-WlhY;W%kV0VFyb4Lgq@R%#W_nXI^@MXu=|0dAZ=lGLG5gR|@!sli zlkc=&wJh$wx(+@*Uvy~Q(0`K-MdyBn~_27DwYXTj6u^~ z%gg)TJTSx!Lqq=VwtR);AaY0g1jHTF_n3IAepgLDIAJISQ=O|vDVj%`_F5L@E37jO zD}E72b3VusjU_Y>^J|2k0&NdP?a6m66#?d?tir)fO~o9Ipdj7H5&XX1^wH`>}>Q1oCLJ^q94Et(qmRa<_7e z*kI@07>ZJvV(J=Ger?1J$x#r-YP#4sds)z59J6yj%dFX&(M&s-&NqIx;jek{R13S<2I)9RV zv{!_dl*C*H$4U8WK?1Aj)?qKF#YRiAYb>;32=g^*cMwRn3x1}ssljN&nSa2;@$d{1 zYj!v(qF{%D(*Z#CY9Ad;XHLFUg0{x3IGf`T>`Ie>7k{kCOf75%x+Pk?hrW+IV2R=_ z06`==SQ$X#S}tq310LL)+~K+#YNX(*SFp@Lq*6brZhg^jT=u#+Y%74;>5lCqs(yM; zYnq3IPd5b3qL?Oc8hd*FmnQ};v9o@BsxRlaI$^y5cIW(_@}(~|Q~w;)Iv{|GKyW;e zZGfxrx{Wj;X(D?dMm#V39*YPKeQ?b-^{=MeQOo8Z0$3IwZk zx2pF3xum_4OoG!{GU zjH$0__m7A8uIJ~2WgBF%P!pOD2~|Jigrs$mD$+7q?Bek#s2Ft;+}}-fj&^kxq|7VE ziW+uz|Co9*%#Fuw;8SRu#JQL!dCulATVKQV3P~YFxF9#Tk-?HA%*pOoof}|I0aPX2 z%Vy}jVD@NQjQ53*9)4Q&U#fF*)0+VOurQKp)trLK05!Q{y!h?9#Th_Coa$zCS)=5$ zu^pB@>iSxCOG|m6Jc)dq^gTq1&M9}Ucs+nYU~dOPzul?NI)=wXAOcVX5QZ?B)wiY>tX3`&x}LqLZqcNs=~pq z6w!^PqooBSwqG^Nhv8?py~MCRbaV}$K%WE8&JFW}2Omb?0zx9o;J0!rv;F&M+*|+M z>98R74aAqBY1i`-8`0pCnf~CrnUIj+b-Gba?WsYW*XsX;J@N%KUr5R;RI5n+lAdn2 zjB|BqUFh#pzrp>ni!*#_zM#GRvV$f}e2P!#+zq+?S&}9N@uC-jspU=X)tRyaE_1W9 zSU-f^?*y!P)ba50y5ufzYJ&ha0+EOZv2`KHNh|FI;o;W&*5}X#@c5{in*VRTK#VC= z&Twa|jLk3y{aH1jz|p9uGLJy3Co+%0dm?&0rS`LS0o0?|9%??L+m+p)`K@;`SuG#M-_jg8afe%_&k2lTmoh?IG;avpnoUa8gH<{aKCS7{^HN~O;W}I=xGf5I zZ@E^HqR~B?$B(qS{?AJ<&Ic+gP{|(PO7g! zrK;6A^$&Oq1QV*}h=`(9PuqLH=@t=jl_&>Qh2KvQ2jyKJ-luQ1(nl+Ocb5>O}3tEC=b-n13iwxl!tD7>8``3`5K?8 zM4lcV9%49cCQL?fcP#99f%F!_qq&8IbgB~uH$2kBxe4yYR}$lgIiGjj6f2A1{btkh zmU@p{>}Fjzp8rX?q|K_S$k5Nn?eqwVBzci7891KJ039`y#<^PIBjMOslgD&vUCIfj zApYk}THN>MN)!*CK|WbvOw4sSDZ%Pv*m0ZtK?TulV5=~@(PXAWG22?KR@B$+E}2V< zq1P_zYq`|=Q0_zXDotoM1cNDUhIHk>f&#c_^YLLtM8GTM$Ua# zmhl4qu>`z;!oB>Hp@)JH*Ls%pxI0i$wZ;Dh353C)(09Q;<9oCiMw-vUv_b_nCcgji zcSA(n2#VAhXJ0a=u5g291I;n&W9lUt#)j&T9P;;wxSx{Eb3i|U7&}8k4-YzBhy_g8 zL2b6cw>2{K2hKX6IH5BrxM9KXIzHD*p?|je&_7sDu=qX>G;cnnfQqxE|XY{_x z)`MxYtKsQ&g^-kjoa6N%MxHLunk-EpQP!N!0$)4Vt~{Z7o|a|{?3LdsdMks!Nao3CpjA54h{rhY*Ii# zpf?JqX8F;79&8}jgn21C;s_828GQPaOx;0-glg8-BDs|i6OAELn{pULClX=Nuf^7b zhf3#pD>+O=)W%j?J3@PltvvG#S}0(n;YAbINpd$MI@cPYG%zT!;Hf2f4W)_7tjKZk zXwB*t1QI%(cW41eaMU-i_e^=rnj8OYTR}3gbNNN*GT5T`er{LGj?khR24o+436LGY za2&kuDHY_iLnsHJ%Du%ou#X#eSxTz4eO z1tqY}Y$H!d9hUA^|N- z7g5=C^74|kPHYpBnL^z!`h$A$V;7D+aGxNxLU!b3yF2BiR(n{Lt{zn?j&v8mJ@#5PyT+^#CPiIoWy!ZuR-m8S#B*k-c(5|_;P z=T~t}3=NASzTWMvj^hXAMJAFQD?B&<*_<6##A_bH* zlpIjP0YGSbz!D?N04G_*qXpbCmvz7<>uA_Qi{FPs&W9uor-dktvTwe8>{Z^EF``OTdH8{_tsC4XHsKvKl zJfi(#kkYcuVrk1T&AGm{mh)ZH=vng?!FncFS*XK&W{hIg`!yF81I{ye=@m2MFb z$lLoQP>N1>;KvU_=$T5&%U{BJ2U)@BS(=!o&*Ir_@1!far8iOJBj_f2s9W>%O6V-DiE5WkHtRmxdn?J4Z*MjPSh-U=P#2cKsu%WM;WppBs229Zv#EIA69 zBu1{}(r$5I4B;%Hu-&b-Q_g|-Ws;-3VQaDH|1hD*gMA2cOF(f4k0c1CV~L&;V_Jf% z8PtXl{VoSC=f8jFK4noN)Q_AoOOfYL(#D*7#(Zr=#!9hc;_e)Y0ikRs1EV>wADgp{ z5~r#Nn%f?}O5I{nrCeeqV-SftY{ybXNA={G7+IxgXREXqM33tu{_PQund%G{5aPL0 ziosQ9TlxKayTI`L8lX?1#$-sJK7XPYfD0;Q(3b zgr+XtSJ4#ccZb(@-2nR)R!K9NZ8|PPIKDLJtAo9qgf1B32HVi5R&ESKBV#H}8+o(q z{$~F$I6bW#?Ctr_snT8l3*=i^Fe(V0E$xcM={74{w0G0*gvV0id{5JNw@(lXPK$wV zC?=cjbk!;{VY`4KZJ=+!gCMObFPwZZM@Nnvwelj8$p$@!PJ}{rxcA*#h5bsm|B4<$ z88^3&c%KZ7%!0fb6LcmM#WM$J3l>k_C=Y-2St%MOsA`>@oed!b%cwGmb`C*(bhzs6 zq=vph;OG_C3G^_{!mOw5InPFXsX-LG2W4bdvOYOzENX3*@tsRc^@X|^F7F2Fqax1d zg(J)UUXV(=@ePq2e3A)Rh+)M@_c8Zi2LpY(Jf=Ry4eO`mXdu8;ka$;E3E-(0a4bH& z)Ry}1VVv_D*-LQk^>>hY|BQVREb*EV2%a+uk~X%7GcQPzIQ4l4ZiaG4 zHyJ3I{WTxnwlVbRW$?N(k;J$KH?a^YU-s(XBnDB3Tbp)7Gmc}oJ~&&kuydIwPMZCV$Upa|m;Nww4daW| z7R}Tz#9x0cFE<@a*jQgrKA;D@PPnS7YQdv!=NmTd!c?U#hwV4nbdG2qL{SKqjE`82 zTwQNK^48bu(r(udqT2lm6Dp4t>R1!}xYCL#n8O7au4|=5_;+VSkJ{`vGtF7+v=2i& zKxP`7rQ%(JR!JSo8*@iz{T8`t zMuQCzDRG_Ct5xNv+ddQLW@%YAm61w0E`K&eBfxyOgA77#mP4!o8ejOJ`F zPm+IY9yhD)-CWL(~*E2v|4OO1$<2`?mE+V&-sc0&z)ZX#D__8jA1wjFa=r!|^9bg{HtX|}&9 z7nExaF}Zxom6Bt|=s$s0Zr`^}qI3&dSL=<%$$vpP{zXPh3qh-ETrKDE#+uWJrwqJ* zkz%mbKI0^XgyRqS?8FhlH5ijq35h4Ki8pek?}$=YDt_8jnzBALr>W$#aP`x}$x_u} z`R!8UYqrx!;?(Dc`h!`B8!^3c#S{-_7}Xy2Om{udWn4LtFILA10#PcGH?;cMD&<*p zSXdZj+g|S=gR%!oWocYD0wZ^9*oh0KBdi^3#%vq(BQ`&Ld(IMLdJl>Z`)_48h7}Xd z$xNrycg;r}Cz--yTc&6=aEuQG#q0*($QO)JRHzw#BsWDg#kpvo9Mw1__W-IIG~yBgV~<8pJXlZx_z^tki2VALqflH^RZUZbOf8^G*er+$1t5T zeT?a$(Y;u#bj#b+QYKA2&n;KH!f^>qduOy#`Yhs9o^;K6%vOXLh-@n>6R@mYAo_){ zL$3-_MZFpTJV1-dk~OyK<1iIn*i48ER|=g$sc)Cll9p$@A^hF(8X5}dPT>XB`@Q@+VD>(I z`0yo0#REN>2YOaKe0-!&1cXY^an3Hej3s<`;gxGY42N2=_5>4>cj$z88xdy&yDz?u zN+r27rh8{?qo_+i8pgkEIr)AHng4}2SP(g-WvS34z+(sY(0zUO02EesD+HL*BQ7lg zTLYK+XWs*kxzul-j+?64qfz`&?BrcIH1W1UM}Z5j?`9f6?z}KTi45e!Cp6==HoyU; z6I6m=YzRKwx$hKTWgurK9MyIX4v5SN#`+S=n5V9p_DUT7@4Orlcs!<>9wVEAA4ncr zic2?j@`1RmyM*%@@7+Rc3J@e)lq+0=o9aWb+O^JEXm64ZwkfT*LF~%j;0jrN+*=rM`y;=-%|hkk&<4@=Rkgz!`O5 z&h+Zk&bj<>oPLL%U{)0%=9Z&g7@e74`m$p2Tp6RiX)&xh@1Ri7w{m&+nZT1nd`Y z3ucZ{c#=l(e<#A2rKf{7TJ=&ObHLLAw|u4>LH_@zlI>LaWs@2TcXV~t8sOEmD(1FH zfT)mZtA~>3Byc4CN3if*U8=VxWK%{xX!+1ZYjwAWH@Z~6{}?m?0avC()!ev;5F_O5 z$LuMpJsD7o-+6NlATIs+PKWUH<8yx~z2OCmaO}MAzh?4-&RvESm6ol;m&TWs{KrRS zSJsey6^_Bun#z|jqQ1DZ%z}))oGlT<@!fmUpeAY&nFY(HTHW^s7zkX~z*-DYBaigg zB^UtBt2}(p!y^%iU`R61nVy`8ARs-AS0F0jDBSO`zB>B*@nOo$?fEftW^%-D|F@pg znc2zb8xxmUf&q?!uCY!5lF;_Uok6A$V07gwjB)^P55BPg5C4{bWg*ONUf0kN19u86 zj+5W%fCvGJ*xueQNl9gz!hlg=5G+_``h~*b1}(#nQ|XG3glDh#iOr=T+1|C^6Wr?) zAO7HoB6W6tAlI4G60~9lf+we^gS2>Xzkp&uhtuWH zG*|N0)>g6Hm8Tr~r%*?Ow-w}DnMU#EMO5e(!D?9o*`SW0A@(CtNTMY}a<&8ny;1hcsIaN7b2tjb7IZBu!z!|v3u zu_LI77>xq3yPypQuQb3;pgHZmjwQRY0Aw;?T)Er|^78#mF{*?yCH>}A!V{${IX}78 z-=e)*N^j!ETV{NuPd*sb)#6vfEfT@I7WTpw(s&`(CujOV@QT~8R>k<59K`AZHgR<1 zHH&RW7OX#FEc3BD&S=A^Xi@4e0F5s1(9#9 z?2aC{@namn%AozvnP~xlJg6Fhx8(JCKjoK63Ue2@OMeCLuI0Pw6R|OO6Pk2 zwg-xcU|0O&aa;MGSeFmupeHMQqm=VSk2QNRRXJv66}vtK^^pLwURZKcLV8xeR+5C>SCkmyc$GVGL^dR;0toGKyZNqt{2e7NB5;YCMN1O z#MV4uCBa9=Z{;olNwm^0Bcm6XAHm*ZQxwesUc;^lpIN99%Q)e)tpP zi_&OUd!KITIa9?E>mHN}&8l4(Y<9U5tEq05eK2eGZwX~W>L`^rTI_EChXIUH zWzF66qe`xA!DxQ=_G@&lDZYx-Y|M$LcRl(fa`{!SQ_=8LD2%+542W$yPHRHOc!gHH zRx&)dA@kSR&Tf6;vr^VQSk5qsjtT@hf~$j20q(UIuc)eFF|n%Wy7s$9mzJl@UR_Fr zDCS%BPvDJ*Nud0oiOXA!LCXl8-Vl)cq?xHSS;dl~GIoCPk*!-^NZjCM#Getq(`4Qg z^Vw~{RVW%gI>=ylLE`x54S+n!;AAb;!3#L+42T9L;_F3Q0;I(Ng$>jrh&<)2p&^7$ zu0LFxUXW5aUGut+ffD`=Kn!V`nN972cs$(p?@FiNb0K;=XBNX?J{F>-r3L3=2N?uA zjH`A?WDd+IT96tvQ>rshGbyGLk>-itvUIsxB6uA=P&}6|tACx++ zTZf1v$kCtdI3OJK1ii9~pp%3X4$xySF$eji&*#L>+CsgRWABoDoc=s#6IMzzM_cFa zFo<-}s|m+_N!X&IphOv({c81-OAVg>cpW}7-G~7pv1OeeNZ&m5r$v$Wxv;yp0Fu2h$C-4Y% z?ftEJSV0>$*MhD}a*T0ZHszPuD4H3$X?Zk{!o%w?{$PH5WS!VpVH#KHK8?xIX~>z> zH*U+}a(H@9CZw6iQ`0=j9JH3O&-|W;{R!5GL+%{_L$;yoeP3K0m&GEk zL`T>RQ@)^tT@_?m9UVXoN>KTdTpu~nr_udMAQa9Umime~@H(n4kCt6Jof_buP-8xG ztikbd?DqyEZa2!d%mJ%kTPmz$z`k+?&{HuQNAJPaH|>fP*Ytd7z&L;evZG)+3?^h+ z5Xo&myTfd42*_@V3n+$fu$Tf=1?$Ym2azFRNY!yilz;fvu76VjzBoR_<$B!90M-^a zkA_V;ou1eGX9eS+#Ev`z5vmfv?aI;X;6~=uuL?Qm(LCiuRh7@bQnNfcGmugXhox?D z%@tGyfF%#S6K{o01dbU{l?Q_UJENeRV^9KASzinqz_ok(-!Ek)V`5ABm(&gBRO&wo zs4^N8`!B#j|3-(-aiZz%a82h4CibXuXchDW;h=ZwPgC>spcvvF=Z>ze<$3P=6JHg6 z4a^VXny0gI-JNw11mO?nd*%s%VoFO&P#t8*w}v?d&1UKg0d!a2bGugULiV{E=a#7N}T5h{$-a@$&Oy0f8^`Au#V@CX7b`Pg%fO zX`K`A%rQs_-(=UdysT{K&08T^%i-p3$)Hp^y;gd;3#pf>yp%4azv~H2ekCs74u5Mr zzqoXG+V}45*fd`Y#nUzUQeFuO{P)^j_#;i)a_X>?MF0?gA14U;0ObP`tspg@{;}Fb z8TU;c-e#Bv=zc_e2Db&Iery2s-Q}e5{{}rT6nqntlRTa#=$7O0FMzo4H9aTa z&n>NkhMii(nkMfmV*})F^^_Tv-I#}0;fS< zC4Gs+p|R=fdE3-0^q2r&khc<}Y%;@uqg{kj04_9;%ovZ>P|{h^R`geFWb8a$9i???kmyFz*JUB3JMXo7r)ZeVj7~_8R`ujrx_(XrE!Ab znjvEWz(YXdE+QNTs|pzQ{J`3SwgYh&0N^7B2)J@UZ3|Qx;P+tg4QkGHkmiG)Qi|hz z98~YnI{vSFgcKQY5UsmPuJ3eW!{=~EjbVdzkOlXc;57d+J5sWm@sgm?tNg6thvf?l z!}{i?iLEUzgkHGC66Uph%T#&)wroaP^%ZqZ`A?y&yWh$5ETcKlzxqWx6SvhEZ3Lw# z<)o$>H5hOKmIpC7WE-YqwC~ zlm`VYxY5%Cxs;)n{fJd++#S9Z6LCcCX^$>P_4IWutv>%OfQTiP2fqsoQttI^6)|*+ za29wbo*A5Uk%ywSaw9VgT1DC))@rToHHx%>pr)R-cP}`Goy4SFGb3DcAaHYP$hTH0i_r;ok^nWM}mU$*~=!^ z0bVx%nu{VbZfwIEVfAK}26F1J)x_>jb&8GMfIi zeP~!=Fgs<53I$9TS7eeO`_AztV$I!M*T%(p+V!s6%-f=L6XZTVTMc2r z#R5HyERzI9WRz-Pz!X+KXbAk9=FeVNTEO1`L6uTc=X~Tc6#v;Ih120mN@Rt6(|SCY zEoNWpK}D`uT=}DG^f0x@r+U9qm%rVctA0S%7LgG_SB>c0;hchTcnqt{ev_=al0MZJ zJQg_g2dK8grqm^&EENf38TG`i{L#OsLM@Ij>G~-_z`}Yhm=IewWti#CS~~k17-Dc4 zSnpp>Ppe~vB?!a=G|I@dx|@i-0PjO2;y9&KRX!6QlmB`@!wf6YKAGE@D|}Rk$KQYP zMz&|I8djH=W3+Np@Z&La(g8XSic#p6+Msxbnd5m-oau+#tMLRAYDD*8=*Zmoth|t% zWtYidy8fAvp^uDlm^LdLHqcxwnS0u#WxpSIr|n0wmILp!=urn-3ba;e^q6o;_Er>C zN*Tz1F-t=Em?;hOX}*WxgeQNv;&Mp54o3bcW@&m5F>7X*bgCV_)7qNLqy);waF2LmyQAJr;AaJ(MRD_C8?g@9BEL zD0X>OG+k_q#}_h`fcl;FfGZ2PQ=?y_vD`2hMioS6`RWyE&!gpVN8m%(2-{67cPxo{ zS+0t%G1VB>`{{U5a>@w$RY~Sc<_#7zvmQ=}zNY$jTu=U{Y;z*nGZjLR_xr{q8Xer;(X(PW(J}Jat-<@ zdaV+BawC41`1YG8+F`JS!v|9~{EOmE-`#q&yC7smt43%q8;P6JZ}Yg5$u`|=BtjZ2^Fm9u4qf2LR%w^K`F%cvn)*x`Ty5&RtjrQcW3;)T0x1lwt z{`!|ME2tq4iN}98Z5~nX0$^@=k}hit0}`FU?*|hC4OKM#Rff>RF22lE^k`c6GwTi1 zP;}{&=+fL&JL0zIkQrk{))7SgMomWf;6b7v4M82pp!fu-dR(XJ7$rn{+*8)VrALZ* z-+$dKUdR7(o~!i@40!tWEy*?iaxwPxrGi}P_y_O=QQ0QoIXjn(BxHO#{<>`XZrdMS z>Q?hL({*7RW6>9X$bN1%L&(Fzc10RtOT`1g-D0bA6su2-qv5y!Uw`o3p!ivp3ey!Z zf!L+u*PT%a6HS1JTK-FyT?67B-U_s!@NYn4s_qZo=Dbyv=%K8U)Z8scUXt1JGhGt> zl}P(Cp{%lTbV~cFMj|!8bx-It)Q%sV7;6dEPML$7VfdBI6be{XcVD5axVX5#521KK zgq?l6w&v#Tc4KO8oG`gjR6X~C5FK!aSR&8bM|EF?CjL|~xnE6JOYDAenU}tMtSN>s zT|q)03>w^umRLWT30O}!a6)mi)V(X}p%l^ZX6bH*y?PgQOm0#HL`1#()ae%Pve zaYM8vxduf*YXi=5aF+nzUfMbFA9A^W^=f%k1YhZ#LN2GLbMNnEwRG*oUkJxSyA=FE z1VRY~c4kO!!=i&=#nuG;`y@K|G!^ zPMo0(8!gyx0j_j*_AQuDPK1jY-~P=K>aF^k`*ZfKwBbnJpw!zP_c|t)*l45Zbg!(; zJOiBPsf)>VOmtNL9A}Uf7oZx^JdX>splYr@+cD?Tdf%A&7jz%sS__UB57S5vi2(|L zbAKV-)Yi7ZSQyj!|FLu(;8gzY|5&9^hh$|dG|0|~kWp4qWJLBZ$*5FTb|=xWrH&GX z?4m^BD4~qZj7X=VREmVWpQqn@U9R`~zyIsaIiBY`?)$Uu(&N(KMzo8HI=5yi_?+@J z|3E(0%is%W;f7?VfbOC7ji*t<^ET(|rI@%Ft+P3O#z^LuWL7}=WP{DASR4KHpz%0R z!lR?2xLtX8VZ~6A3RVFeXJ&@_1GS@4<+>{$kNVci79G8o;@=|8f1J_0XEdtSzdeUR zwZbTaM{VqJNtNBe;GoZEtxg^U%Bbf(@l^OKS4; zS~J3N@M&%F_TdnPqZDt;PUD_<+-L1=X=gjSUCsPYr+V_#K9$q7z5rUEZi5DAqnh~O zXE|C1kJ~?6HyDcY{M5%SA#25=Y^JtH)q^Ps@2Rbu4UwOGg)1G6f3$AI&X+ZD|5Y3k zS6-yi+5Eqke68Wut*EF!azfnP-7cS)j$YNcd#- z*(z?-AL-lzI@w9foub*m$+FDsZ)GiIftwc}B&K)ZQQq5Qb2q82W5&A!)nH!U_WTun zMn;(45{(N~Dov(tzZsE0H1GAe2t#D1OuOE2g!T)80{g&RS^XjS(b`3Ob8alOjO%WJ4nZqVNaZz;-^@D@E3x0P&}-PeAVDt$zeX0>54TeO{f@^qdwu$TMkPki$VN)G2=XrfkY zB(@I?KeZiMk1&dl9$R^>eeNE-dMGX6ty<9f<0>#bn)e>j98~tvCQ^I@ujD@a&| z=oCo@r$&seM_ytm+uz5vwL=Q#?nUda(60zV;g53=R+U$SFNl09ien^k zr=RE7k6KgH>5t_`>cX$E{N0fMrRr=u-@S?-^LF+db8~q*&)~n^E6lrcMb!_=I^(c% z4LO%mzVHxpgP%MnIDaiHx!cZzkgn(ItMFO1>t3DUcNsa+ zzH9dv7II^?#C>0Y?W%Na5K|5Rmmh5vRRr|roYq@mBYc=cu2fBN&KC7+dp4Sw^ISBz zDQ(m+h$COIwz{gyAh}=m+4y%ypkEYuh#^%Thv)9`@T*sQfKQa}H?B}MT2bL}m7&sp zRe1t+adrLbXutSi(ZP+7XR@y_FF0tD`nLf$Eqb@`a+wt!Lw!4rp|U%m3b6cua&B*s z5U@n|r5E+};nmVRjN-vB!af0_vAmYIWBz$|8q^@LoNH)k1ZD1t8+Hooov7A(5nj&e zM%Lxijhp7ypLZYGa$a=6AS`SbNvZOsDQg6P4xxvdrd72O+?rNfxlf0ZR4v)EF-oxN z=W1p~6f*bE2uq;^CDLS^OZV`w*%M0LYg-T9m<(yyBUPI-^y*1bC-E_gTy7Nk-K?c; zYReh&RRKJ&bjWRv_JNs|!VttjkXyXwqGa#zvp=D>`u)Sh!|`?>!QLxQAUDAMn0SdZ zuSZtEjQA+|9-7h?wfp#LHy3v}O%X8Xhmsew_X+J75sVk0TvBNVjFGqk;d93W50 ztZ2j2HMHZu!sq+;@#%HCtmRUYt0uoT5AbkQBDOsLwk-WP@sU${>1WN2vu30`o!A&7 zJ$b67Avi)aZz9?CGir0LRQHNIbEUISTj?_^8yC!{!{Fxg3tObvgj`*e0v0p^m|Fq6Po3{bI4|8S~=57X`QySz8>&7sJa9cRyVt>;fr=pI<( zD^C-?nWwO7>}?58AauZ3-bS}O1M6zgfRmK55mnF@ zW$o;Inae3tI=j-|bXT}yw?B!mh7@Z;YkOV(k zb$hoTyvnfW%b65SCbh+N^thl7w7M6H-wK`NhLDXSf6Jm^T`%816_>)%{ow;@&P5z$ zhFVeN*Fi#XV~d0>mTsmZR1RTzA=Ewb*p=9SBu6Kw2&$V<;?h#Gru@wOsvz8=}EKWUB_<{x`;%&jtTejK^*$ zt;5~?5j*8-&(xYinQMG5>a%Q%{4U%5X$8il{jYw1X9?G&^`XSf5s3L-t{AEnsY;>? z;t2zTH!unPRyeGyFtIrbPA=TX2wygkP2DZc?LBi(w4LkIA$PtNaDs;}431Fm-(L#> zHCBH9!7YGj2_v#r&W?*n3~xZ5qP&(?Xui3xB0C2MLX~-dyWSAM#Vhz|Z+=BJ zG;<3h5g}`dU=>i=KqU-Xo(q5Zgc`MQ`Nt-~A`+fWjp@hd2}e}&p7-@%rUdDa)6>$3Dh(ADfXMkXcMU(U0mh5k zpnF~Uf+OX{t0$H9VNCS`8h2PmcZ&Zb*^JxgAJh~dVbV?wYfOLb`Ta!o;m&k^{Ly&* z+V5Wqd?D+msjTYMvr15+K1iB=)I9!_@YovNW^9lnP%($Je^nj^pPF_hu4gDUaJZLRTN8~!{25}otPqiS!0 zK`tCbnDmXR1&>b3Y_IYLUd9a zc(>`*TAJEz*@zi}b6ioMy70s;XqK<+VTNe?Y9OydX|ePBZK4NIbN}ndU@s2<7oPs1 z2`#MBu3-K$sXQ_xmH@P~+<_mtut}oHREn);{|fKFu5{XID={nct-p6UUeb};<}bX* z+)A2zf04Q$wX~NEhm6r}-I%1tYUlu=_dr_7zyKsXx7EswlV%9(Yio~@>J$*+^8+$madaEsa>RyDB&ap60Dk~&%7 zNO5@H@DcznjRQ^QgMH-TDeNk!@jiz*GrHGu)#ez;nbq8$gAF;#!la~j%1ymyAk8q#8rNt3H-@n7p< zfPg0z%yOZCx0`Y+pgAMLnJepa}t{FdxVhQeI;X#cMj##bj8cH&59Be8_97KxwH zvE}H~x5-U0CLsuvp*tgeUE^u7DEQp<{#_NvjwO1(tVx1E3&jui6=XZzOBZb)Wbc|4 zN=+H}BlYREt#A>p7jIV!T2*0ql|kE*wUnl|tY`4@W8=nlrvCS8z!P`UeMJr`)&zIU zs@9Z<+FtyZB+oz>gk1z+W_!8pZgcLph&ItSwYHnI$qr`VqOYXC&8XUz+jI6Tf4yRb z7=AfYUTM}`^$*aYC92e(8XLnzfKOFJ`F$e=#U}swIqnNhc@&RTW4EkdZ5LyPP6v?~ zKon5aOWXED|0mLdju0cXzSaxsPJfzj;42?+e9pMcK?)!_+~hD90{ zNaGn)T{9LbRwSQcYKXEDB3ly7vbpNyF_ThWk$ZO&aIIl&HZ|$v?UGU6qo{zQ5D;M3 z+-oaWv#fO%NemzKleT>u(QVsSbu@xn6{ql0XAjeA60*12Kg}O#-*p#KeTk#RSzgvjex$y}|^ErH>plA=_r7EX@?*Dl5M- zj&63BK?6IzbT{*RjHlZ87AOHvIulFDylh^OBKd^u$i%_lJdVo-=}s|DNpceH+;>7# zVckH6AAFP?GWK!pPqhsHlM$~ZVPKIFkH*nP@#oJm^Uk0AHz(s0x_#va?xqaNw1j7*PhoCv&MGl!*?n1R|40V5nqraT z{V9DC>pP9$^Hmw*2p6>_Y8iqIyilQ}elos2Iv^2rJCHMwE)dpmxzH(Y&6~bS2323M z{ruzY;FkVR%;xSltPby>pQsjAq-AAtap|!+Qr@ips!_S`#N99GO;+spaUY&4HjVUD z2BHG?4742_rh)B2Vh~sWpo(zf3u!gVVMD=`LM)xvkH|PEK%ZM$ zmX?+K1nQdFydEL<#b>PW(#YNjxNyn`(g!i-HYLE;nG}unUw34%OG!yF)cZ4Tiks2Cp4s8a z4ebJ?%J&^=sTX==HzF7Czd$jdxku-YhY0w{q&G=f=E?40*f^k~x&gm7Jj3GL3${!`TBMF4Z9D<6)OOQr*5AfU&8bH0zJSxBk#()f`1 zvjT6asHjNd+`3Oon*6$~J{p_m>k@4*y|lz?9s3yGz3KI7s)@nNCml*(Hqflss9KNg zCQIpu%4rI(eS7R=7Z2}`#VcO>)FL`h@2$81rUVio%pOB4+F9KfjsF6;FkC+PhK*W> z0JpyXk4;;t?p%-99XW+A*D$N^LuYzkaqZ|Uezr>HLn}Mh+^T#DL>(A+W~Rh}aald!DEsy`K=+VsCi1b_C;NzJv6@!d$wCgH z`nzQ3^~&Z|k!#{FGh}aW2^7;4tY4>ZF3j|t)PB^EbxaAAE2-VVEj)nRq=#zrh~ahh z-YnkP=+oG^&>KULck0j2&DpW`DC3_tHZp^Z92l^*uP}L%qZ=2a(GcI@uWH3u&*Nh9 zwPq?!n*Vh=m3<+FkBw65$E|Nuw)o`SdGV0dVo_fH0Dv=|}Gz`M7>9!aAPY8z&-UU zXV)DicV1Bb-)g7NL4~#w2p8c}DMg+~VH}_StIu9?JM3y-^t=`ucC{jYzgeL|xEif* z$KAW_inNau3A2ksvD?V3H*AYwx4d7lu=2bP8cmY&y8Z@%4BVxli_w6A1cmgxq_R?< zEs&QB<|w4nOYpIaBZ{ABmJo)6J~HBTjO>c+Qini|?#m2*k6%>npA70ZFEGBrq@T`G z;F)v0U1XJ5@PMW$buLNSxh?M;w|)dX8YHw?NSm-L+1`Tf0t4Rbplqs}{c0C>)bzh! z>5Vc%tu!Nv8iL@3eMr3gY%R5az9xe_drxb*X((7+8i*W=k+bJ^sf>%qu9K zn-ro~jqbT5x)pnnB&gZWI<_z5hAai$Y4dd4iZ|3 zwh582=aEzE?BNkBelr$NfLov8_>H62);O+GK2nzWL$bW{6S09Dq!WI~c)vpenDP(r zuE}2+UODbkH7N5)?L#D1pvlvJEG>!5g=nfY5hKWNBc^^SH+VmCCy;9?vi!UG_tUkZ z;gI_QTRXKgkw{M?a+~1C#?_-gnYlaUiVRh|r^ zCu8>AqolOS)1DmQcC+pMRS>*3;UmYbCYJp(TS58*8IOgR8lqa2Oy$8Lvfd+e%k{Xk zi5wq9mO=fDGv1W$k(%ozfEG+1IsoLSpfqs8APT^6M0cZEochx?r*AhOU!$R6cKda5^zn8V+Q^6rBE=4D(Ezku+gNOZYIowv zmX^4^v%X8OqN?!`wx^O}~;q3hE@i)<$4ee@>>_Fp0L{Y&-gX;|f8X}0|HK9(& zJmxCCGL`o!eYp!m!e35$-r1OJUUt9Z9qxrkdNxkW%c9|FPJHLT2yt~>?2{7GH%A;U zP6&9VVc+oeJ3M)61V#-k1B4S`@W`jBm1bnqXMDQ>pP|o3gAO{Icq!5e+JQuEQtJSY z#sv{F*c8CxvI0;yLCf%eokYRG)%X)*obYFr>zuq_;Xjtut1R?Dw6ftretg+|lJB_b zjXUEXV}9Pg#X71%>yxH03b<^Oj6y_`;DrWTDTIRp>s6eZet%u|`}|qxDL}^Io^rT9 z$8Km!Zj(5}a-{kM3sjNl>rLn8lxi&r1xFP^OBmT#^eXoLZ3jWG}X|BjM zZrwA8KG3-|jeoC(@Ay-5b0r!ddxK|YiI3>>aNDcCoRz|<#ve;CJp#$pOyOl@=e?}* zcf1BFvCh`%2Ht-xr4CCAvlug$K8S&AX${}Y-xrpU4Fri7j_bzL9I2uih@F5gb1m@6 znVA`gMEUsmK7NjSu20W@&O;Z()51vzaSQA^gxCJZ4+R(qVG?P*C`veO@7_f|cO$M$ zK#?~v<4|CKLS=4ydhpjDn$)&z?IMZSlIq>tddwcgE6CBRN{$pZ^PRC{)sa6vruTf6 z6qz+mY#G|{KS@GwNT)nKS@9~M97Wfg2)lDeip>9olth~L-R+M^*7Ra14PXHvq9=*1 zH8g}Q)W+fA>*-^M?d$;kL|~UlbL*mC`ImyH-Q_hM9Cwb^=QLhETYXjbuh2ex6MNQD zw$WALPL`Ikf8o>5*)5F8$OD#V;uwGBv)Kj{N6WMF)^n zK}b;VWAe{(x&ZFA^djj%~uAi_Z;Ff9Azp4YNgMd%DqsjaDU5=5&EG?Pf@vI_8CW}fWKKmSX zY`q6oE*21;4~$5ObEV@as&R@U%1$fCP>ye7xf!*{n)eH$Ih;#b+trAPT=$>+Ssf4% zfRo7eO2_Sr&zeYo#H+*$#rJ%O30g1^;8?`lgo*(OK5`hb%Ss)QvYlPFhtXS?sW!FV2JEe4I_tXdvlSuuZCMIXQ?19LEP?fhF*o>sselqd0x~^l2XN=@#9e z!n1QoZg+ya2IgEui|2_VE0cifu=?O`!BcIU(uF__ZyAlLH1`RLZ9ly;S7p7DM({n+ zfKw*?o9sw(7i6Wsr3M1DB+;y~D^`x)q)MkUu=B@s@RwRrx0c8%VNDaD>E;pj zV}5>U6_{{w5UWx=Ih-mTRM3v6NH7IAgsLtK{e9b(p!{e$<0#$ZPHJrF?V z{THGZM_2Bu-ySw^j1@LUC}3knLQlEA+qJ6f0-H-~;r5w@CHx5wLzmR?veEjYnM=TW zEdO-?+Bz6*a7Ytg&7C_b+M@gE7klCiB3nb@?b;>xYC!mq!HW;(7cSQtq=H2FDu1b{ zAuo@c_w^cw^+V@|=u z%T!W0W|*X3NTW*Q{z0wy!CBy7G&_O&T1ofb&M?Hti08D8SvM$k#G{NYM*CbzqHQKY3VqVpRLqwi-MY9W3##P>tQ%8a9!xAXXiol zYlyRgL_%H15#0kuNGz%z_PuY&@1R0%|M|_mgS>kyxS{< z9gZGsI&ko)CeTH%w8)#~IlvKoIM@wiybfudQxCW7&PYs@PEcSRP2_gxR+bQOTH5~z zXv@&Ca?cPtM+u9!j0LO z=Sm->U3?J%pk2wS=TaCsgw5U||43THr0+;g;3`aRkU`*l#K#TxWHQ}UHdRr>)2RinS9Fw~)igB2tvLRjgnGn4BYbL(p4IRr7yt+cGeP0L zTtANP0^(7y)}XH$BkGg??nA^Nx-?u=;D3&U&TqoABAiMO2G6~p>4;wGZ_DR`xF!4$ z14{@Y8w`F3{vdeA%6qjQR9Dh#AIofoi}M^VQvvPsUBPSPKCXFB-BcuZYuZ|(u#%%^ z;i#XVNXVH;RUiEBC^9$|AmVxVudn;zhf|Q`9dwOEgkclG={2E&B~Sm_h{h3gF%fn6 zU^s*gNnw802F^F}S=!U_3XXLbe1_?zg9{Pf30&nQWPRr$>4b>wI$X)>xFl6C9Hzk?nJ)`p5{ zZfAuxCtbh#u*!DHXYWeJt38&pDXS6N-PJn<1CzbeTPLmf(q)F)t*GWRe+h+OVpudVzZ$EQPPP&__YrJk6gh~8*DzZH zZ3^fm(sP0Ho*NV_Xllp}&KW;CMXxQ?DL)qIL?L_=BIkZ`A_Euv_cs7Kgw756^}w4t zSs4cD;PEpH+^!Cty?&#|;uB|xC>Cys^k7)-h5TPn{%rWy6w6iy?bkEx*yGvcERU$& zLxCD*Y?pf{l`q_Kd;c`V&bAS-j*8gY73WQdhUM4~xXv&ARUfw&V}>Aspqg-oG=Q{6 z{1A~Pi@c>vC#omUbLnmH&j^D-Zk=oFlwc1-;SI^>PYg`2{?aqPc745D%8oMjQU2lX z)S%kSlI?3~eB#Nhs`R|+w63FBNy%jxs4Q!3Wz50l-e}{fDRNudr>a~JfW6icACb2s zBS+|0WkvpP>cE@;SPY1fiI{XlOlNaGdGbNACCCqMG3G7TS|Ut0t(hq@v1_M(F#W@* znN)T4XQ6bt=urjNbNvkl`pXrbTng$iY~V0%r7pRnvL*>kzrFQS&7a3Qw^Si0^i${U z-P01Unod;ve(kxcaVgujPmYL#BIbq>EL;NB5%omfe4Ag%!-p8~sW&6#za^ve$kV44 z6AwNnSKNLt9;YyBdafUpSe+I3M0(dn&yI~76_(1YtNXvH1TWvQ-nOD$O<78+ef?%C zdH`G{2Q4)_e_CFD0GAv}>#P9y!fWc(a8p3?@~vHC+7Ub&>D!w_N8X*RTM&YaY)xgd zny+AE6HSidE>YZ5F>&bQO?)zyI3Q`~I7T02&AeK#e|qRE1826egt`0rubpio`7#g- zVyR{ZsWbZvTi4RUZ3Tv|M^;EQ?Ju>~LxCI?R@ZO!DgVL&8XvBx;|sG#iG)#9ryv0? z32Qzh6z!mNlzcq2d7njjh51v4QtrP#?`8Z#AC}@T9B0>z8ta>1w>Zb&!HRry8Xp3% zHt@a!mGxLMe?33~8;PCMSF$NAGTcLtRyZj_lu$%5z&xd zahuz9AH$-sa^}6}kEO($h{u~S&|(;Mqp+;k_umlX&wE4YL}O+E4l?K`0Dz;$g}F55 z!Gp11A$ZfccA&Gx$Y`=hH2K-v@AsrvW8Umg@rZ#O4Gq9kH9yar{#k7CXtcCN!@-~u z0SJ)$qc20{MmU_%?P0kH{JF!)%Vytu$TfOkS4#H7gOYU+$q2A z-4*B#I%mtObCbfB$UT8dY+H)ioh|_ET0dv4{)3I6|Az;I9;p-_^k|4aj*lJc3R`)tFtQ<#*5}hbJQa8MZt{uh zMW^$F*QZD05{ZNb)(FT>a2izIP+MKSAoI_sldg%xgKcu`Oep_JVE85v&lmOazQ77) z6>AN7QLe4^7taLsDf}*YEJUE-e+?&*mG__BE19Y&yZ80C-na2E$W>WsodO!>d2>5~d!nhNW9h%axQC9Vu zQPaN!RO)udEX@@PQ&5xpAw0%MC17Yl0^T9D*o}Q?~Eu|A*{i{ z3b@wPFh9Qi&I@&gd!h@Q!s`r*#(yv2KFrp3b!WzY~pf<5)%Cs zz(hi$%q#sel8!F~2}Oc}(fM^An52N1VP+S02r2TuohW;lRCmRo9DYTwKUP_phMI|s zP#z;jgKz=BDvZ=WOfEy}M?AWFh~93%wEDK%>%o+0&06AGlPG5^3%e-1edP}@W;qEsfn=2*%n!4vRxo+UZ6^&vm#7L3rs4&9pn*U{mT=%msa9UhyFnT3a0j% zoZEjqzPf?QHt5(u6S6xD|KvpLilNaYQt^>SoVVgzgyw5k}`0HlOH; zMu_Q-(PwxbW9iK*x{y|aZ6kfdBpf@1luh9~mbTrN_fV9hA*p zTMs%i)7Ot!#en1Q`Yb4+Q}rqunb8X7X}{b^k6|*lC^%aZCX$eOMDG1*aeW&MMd?{W z@k*%R%hgVo|MFz^=ML_2?$<<0-K}@$3fZVow7-UwW%z0S?hguUlaS*WF@3&4=oZy4QU2RR}!x?u1S&^V#(L(BWbGcqQWE=~SdazdAdMM!W7 zh|nPd8Zl9rh!rj7iOu7cEI%GX!;m?dT#{p;{Re?KVQ2<;o6 z0+4?K<7+yvpxLFt7m!T6tDPGrJQnB-@X(2*)-l*q-OmS&O&wQ%GJE>9L*Mjq5iVB_ zQS%SsWGd_EDwnB4q|m2oVfoStn_EBb4l7M^FF~w)?AOIIwkk9T4Eb*s_d6+cte)a! zEW7!;zFxcQ5!b|rE710NW&}%)?=%J~Jb#w(d=f|$sA}}g^Yf?wStWjbeiQM^E>|t{ z!;kT<=iFA#gy+b+oK~{&x$ohJ8D;KCv@+eeSQ0mh5`cR8NpJr8bsdkM+aHC-5`g-C z%gTZQ1;|c=AHlGM3yX72>?^NEz5C9UR-C$NRA^7M&9CO^#ZV;sO)oww3br!%GaNE0 z!@hRyIQl|@vce#DphyHYnTvBfYi9SE(_BTnXDcdl3{x`nxm*pklgk=5Q&VV*KBTZB z>6T_nsp%v~jTe`MSq#co`&S1F4tA}p{^$AAWySR+jg(G<%<6u@?e|R<$F^VYjvx4I5UOGVYgZ$%s$k;NXamH-Ph1t+NLQeY$&7 z8-YW#tD*H{DDOS?)a%{JM?=I-^=SGDp>ISvLy#B*6#|cFLDUTvLCLrG-gxI$lu{Dg zs60XoJ0Sid8y!)J{V6x-L@6q`n^AmlOBg5-X8Pg%>g5kQl-dsebUkNXUVvMislB@Y zZv0|PoRw%TjlBV?JH;Kjg8INB5xpYK4M4|Uik1k27l1v4JrkpRfv^C0{ykOxintb_ zy?pj1xcALS_HCg(_d7nS+SESx)jV`t^vvo}yY8M2zvDU?7jl@n+}aK>m#{htX&b@s zH~A+A$8gUFe@?nKkbVO0csT)|AKZ1BH91iJ0W*W_Z_71tbAt+IL@~UaAa#rD-5q#) z@JVT@Q;+kDKNSw#8!-k9X|jTAT-@AP0LT*wA4n>Ja`rgL>wqh}w?Fwi(>UDKX`zuf zZmK;#NBX-$`B6bH@qD=4nj1*_N#Ax*eSeBW# zuTj|`=yY>SOw7lFVrG|ob=TbZB+XBPb%QCqKkDs((2d^4RW9rRM2Hw2fc%706!#IP zHm$)|dKk8>g;*M)>A?%l)u(Frxj40)x~T;Zl-N%S>tnmWAdk2TzALY-{F>MA93Ij z%?bcAc^0`01Q-k#2_WmxrO6|(c3uzZx}GG#+%M4`)psP3LexH9rJah=vZ1pP?JW>ymFqp_Wo4q3Al&Au7g{2 z%&#)_@Lc@5^`uYD;bQfLS(UP`O;J^q^~Uhc zk-rWarXS$nM7$$9ovmB98V+9<4L^VMabeS^6ZG3kD#un-%WGS{sTs-`s$rUuTQjo> z-yBJ6Do9O$wU-!B*YA<@4rd1Ui(`=z;;x-xQ+0+ix+-hAT$>~H`af-k67`I}`Cm3m z%VlL3-}^&M;>R~-D?pWJ+oBr3dj%c?=nVilGT>O%k21qIQ+zSzy!QBg^#Q+Hs{1HmH#YLI zj}ey$5I;^<<`R^>Blt$A{w`MAlpA)d#)wKRmma|4U{*?doS++bNsYdY33gA^rsX+{ z-jxWS?lb}=4l|tKA611Pj`k!XmjZ_psUDs`0^Edf^?;Yem~S#?HiVvaBhzUyzJYaT zwbqh22G9L!Io;=*V?574k>wH+B0z-TBaMfEMK#aFjQpK_0CZCAlGw?eokb%&I@}BA zsC$zt$rIcEj%^>%j42*ZdSOg@?%zJ78z&~#II6bhf+vHaj0q-bq3#mCMDLsBL8A?4 z09s;{VL&XgRzPV2&00z2ZEVs{VA+ZVMAQq={$iWdo|%K@zV~X{7Es&P)`*#UhUe0@ zW_@kklJlOwXS@9YG!qJ;JTD-C?i~3qCqPs@F3H(v)4ld_x=1uRQa;6lqG?c+wtQbF zvT#$9w2zF!)y(m%GbDCKRwmESTos3k51vU!g^T1i7%}nMe(7BTpGgGN!F!8f4eVsJ zb>+i`0ui>nsaErn9csZ<6`z0Girj%~7Xf?0!96B+$fe)~8ND*fP6F&Ry7_vwW_;JR zl=9SsC7~AM#Gqwd+0b|>PHec;Y&!1oX}X8Kb~H#*!gUs2n!?S-T&^-vtuA(#*y$m& z67FG=Q4hOYh0fg0%S}x_C#RUsj(!)5H(qiWBri@Ac-)DZn&=bpr&}YYuCFhx+NBGj zdA{EREj|ir2pO(#99$0Wd-m4E{~S);|6+*{&jlGVxQjSXrGW}jCU4GR#ESki2Cm(C z^1+2ztN*xN#)xn)^+!lVh9Mm>=4lQdZ9B~#h`-{vE2a@9Z4s<;kO8WEi>!cDvh#k{ zjK>Bv4rg)!* zjU;&piybo=@ZR%sI{Z8L#NNIKTnFbDaixN5CxW5@31d;<2PZQ0a3E393*rY%K(hn4 zbaX%8e81)=dG8Op##*7Er_fWPLBImTbR@4z$g&}rC4LJW(wxSKjvl22@{ zp+_hE>Fq*j!-;kmNyF%X{wt>mVsm!3sy`QE8(5^$j&wc+&4B@2>-@|fvSw|x-8fkf z#0c-8KeY^7uw5fP7D;M+*XKe|F5Mzdr{gj-k8Vj_Z?QSF2q(EVH3ksLc-jodv89Q z+>==wR}PH;bQu7It{UB5x2xTs8QMiG84!j@#yY%s@68qOZ|aC{?I^Hbo!Ttx>j1jOp~tBFTRGyRT~m*m#p%=lZvzIOx$k)Zr1xr*ax{Id{N_Q^NMaTlEWjluM+xSD8ql^2XAqHlY|2v6&mW-rHZuH zaqJ(c($G;uE)f$G1Gur%qM^tAKpO5m=nb$@L+k3?UWFx>^pfme`Qg3e1!4>SHxgTz z7(W9n5FXdIb*sM4=-N)H_B-@3^M5JZi4l(3yYxv%7^OESl(C5xO#WdaWJ^;V|PY- zfKmX~{!M@=2^TLej9^zGkzZv~Ci$39V}j>LOw%g<+9EKd zUYIttS=0NX!`|du2%;;(%wCi7mY?@bnpAAbcD2^m=UA?u)gnNoW~hapEX~@<(FmL! z5`gSM>$m}ySH!?YC}W^jLHR)hR)K}&@M&u|E_rc5c7Z60Bt*@7_)n1*O?rQYA&24B zt%bTMao22PB%ET^KtslHqetr#7+!#IK?UkQ>a7l>(3I&f{vsoxXC`$!*fP!AXUe-1 z$C3R=cnpEjMg}Cp@PV_ZviR>MRpPya3g+SAK~@kdWg=e)$)_w@Irn8;s8`+SyQzf% z-$=ia2J=@6yTya#OpzBms<n?|8MOO(&8T0@6(etf16{R*fn-Ccicq5N8F>RP$5<8QhK~JJNTjf(eB}s)V)*9 z)2hwoy@|CZmU$S7qqv!|{0lN-%=k{oh6{&E$kOsVLHKxk zx0p2KY$~JFYi&ysomAa(HAq;Pv_hnfT##uG=_Ga?3Q;~GANZn&8-b_OVL18NzC4q$yw=Yr!qLN7g=~-%)F=i z6zTEG(UBrtved~x%C_4ct@SDOLrlF7!TcatosjI`@169#f4n>LtLyS{?t@)Dwzgu< z8)~H#G-1RhLZ*?2g}MTBsnLS}M?69IuMqP^{QW}q%L#&E#ZyuA4hH)NRxaRk3^`Iz^buK%nSitZlNyx(z2H1=UN5Z!v)Ig_3AhcalfPwt6!}QHd z`hh&N0l5|hj+oN~G98FJxD^5;KmnF`RC}Aqpu=mw(`H<*m>5yk-pn*AL)wM1N~h9x zNt;#v^;aFvHm8g3d-oNihR4AI3k?2lq(!7Xxb}rYTr;t;a4VsQqCfno;&Z5@HS%2J z3U0>cb%o9A_o697Jw#ZsAauPL5=|b>B0xF-`$UwP%W3MPWI7aQ1dKO3#g8=me%cqb zIk=RYLt0m8Y>U_tIkhVMNI0`ivo5vpy#MiK;`|ut3R0K;ep&<5dN>ymym(E0IUmgGRNx!PX67Vpnn><*(~K;1;Ja`BW_zDtxyI!s@&i8Ibgssc2mjr=TU^Rua5L>nQ2v7Vzi!3 ztP7|6Vb*u!S!~ z`sSahftdIwT>WDA>{=P_)gPAOqM6#^ebhhq?iMsj{W%ISQFE_;v;NBg=JE%sg^9=U z-C@f|xdD_I^(T=K7#0@R5TNbQs^)kxX^Cs&b9G@Zx1rdT^@h4}7|i)=mlC^64KNL} zOI;gf9+V-Tl_|)eXR9+tUYcu%7TkqsZ)M$xpX_go=P$ZbeQml;m}B&GG~al?;kE@? zg>S^&S#~G?*UJ!!rKLUunE=I5DdX9In4n4{c#K113%T~M>lfmMH4b|uL@ z!6dIWi#e6sca}91LePIUC;2bxg|(7JUKVHe87<6xp~?DgJn_9uL}b2hs?Fb-rfEw@_V$2Yz5rwp3_EBEf zgWV%b_-4AZyG{v{PMS}q&*P|myG}Q1KvQLG%74qZ2dm@J!~fG{#$h3Mak)VdLxKER^rc7ci$xRX%6 zVg+jmwkmsknXCcpI~U4DhEpSQl)8J9e=bFl&wml-?Qoc)nkI1jaGUl9p$8^@2#3t3 znFZ*z|GvFpcDsOnY~#bJv3u!p5nb&E7UpJ`FDY87t=U|L!%xXnvFV!S@x4!5_%uWf zE?wc0j5Fvvx%*|X?RJO9+Ny#?gbPrp1C1A%ZBlWkE};I%|JAw}VBfp^T#o%SNt-}w z0duwfQQ7>V@YgYmDVvjtEfoot%(go=Sf314|GhcAPyhN^_Q@}<2it2TEJN>mW!UVN z+OZ4gD1u1#+<1JE;@Kbl#($eWD|Nk+dXDSWnvNkHnv>>(nxwIAKGJHE)`u)!hWGo8 zy&U@Y`FCE^a5{1LyC_SmxqDTcbI~U_rVhyU)D1R!7d5HhjrnqmzT{+H-EjS0`rvDN zlIrR^0ec-hm5jbC()J|%V%^vh5=$kI1<F8F#KL0 zUbkXZS#gY>&9pgliD*cNoyYPju_1*qiQboP`4Fl; zBcyXq@yzwy#x|$tKgUiR)(WiJa3`ev%Fe{D@k)Wa{1Rptc7{!S9bUmovlgyj9iFNi z=ePgD<#T|H?bm*JGIh>%q;-Mxx5A=e@i43W@CSKLW*xGMYt0WiofzAtz60Iju50I( z!H&K;oBgfclFOfvMu3aK63Rjvu z4Hk^~zcMetF!QNjYP*3nHud|I5Wu>wx`Ea^CcvSK`aj$$8oLI^_ zvy7F26E6D$3#D%E>2Jzqbvzw*@e zmUNxr)G96)ULG#Ds9SNi(@OnRFH1ZN#FMYm_Ng&n$EPj()`&`H-Adf6|2XI+}dqz?+@k z=B0kx1xC*pjE-=%i7t3NVH#bF*vPdU4QUM88cM4oAC^3Sp02De8I|PV!l?YTGHQV3 z>rU^27Q=-j8)nEF0sy}b8Kg!Isr&!8lmSb(0&KcK)y zK4yY!l=NybBpj95JRFLydn}?aA#VV8%J%p!ai6wf9^JFg`Oef?6g=i|G-7>sZHM>! ziQQ!8j=g=xJEF)Z4&5Hj($J^nZ{q4ufI_F}Sp(k&9^Jz%$M>;*N3mDC*3pDjXV{%t zry{#Sc1`wYPkxuk03hTY{4U5w^68FB_2oV$Gb|-(!!#Z5~ zXhI~W_g)5My&d&@>lT7sSlZ{=#PkHTEm%ug93$jsxsjpLw4i4!1~TvJ(pK}$?KIM3 zj#0xHr^gK7p~>~9x77es?{(a$necE4Bf?%}b^k1t_K;*)4+#izb3y~SEM z>^{Qu4PW`(K34yWRD9z=t~JgkUvC|Dq%=j72T8%&&J@+%o^zZ0Q*c`#kcZDjgOp-v zX+OBn#{uj4vZ@3tHG8V}qUC!1^oC6Pt7SI78N+*Z;}pEtEMd_oMvJ>Oo_4yJQM)zj zlxWncmvR*2tDPHJsY22T7VolN!9Ko!>pSHXZ+cUoe>(}CrnL$U!!oZpGaSv9RQ1toc ze(CT*-MF<;WRZ_Gp=XX{aWeZqRlR%GsV+P$MP72jyq&?Cu_Pj+*;=r2_!@IZGQO(1 zkk3&6ASo1Tp;+uaRf0p7Vj@fJ?872Uh2kKlJse5 z!#xpPjq;-A27w!dyFD0H(Zz!DhtMOJgl>W)GXFM)cZz|RpWZ%0Tu7xP_a8+WGXk1FE$ofGSiVVz+!>)Z*US&(vL$Rob5>XG2@zJ83_?Q+RcFibGs}<8ZGoE zV4BPrClf~28y{k0mg2J$+E}See5A4k=P8IuHgr>+CrMQdzN^GqDDv5|H{u+k@zmc4 zmR%eg*yKo3Rvg%h8*O0&Q}HUX6~`lmGnsX=*eB^h%aMEJXg^NyZgQEz4~xrGQ)s|9 zj{QY^{tk*eEUvSyAVJe|JS6q@j3 zd1SbHhoS#r|DSIi#I(bcwD;)l^mS^UFq$&@v9wDMQ=O9>*VVJWUoXny%gC7L_xIt@ zK+hSLkjayG^Q>~yv+a4F3Jx9G#eAJJ$a#Bzo|TEYf`3Nx)%bF~t)2n(npB^eeQNYw zAF}E!f11QGO&m4)blT`ssL?0C#%H7ZhHCxX&vCC`=OMT^UG-k~&kx*ky91B?O$+#I z?6oE>V9x*bEkFON`OD7_&Oexuzx}hNRrps(?Lt)C(iI(76LCpB9Sg~J^`k$hRKBeo zjlDd)SR8oF!6UWZH0g1C*WRV-;qg}|Rmw-&rfiHpjksksF`W$4HF+DBXkf!yx@vDa znQ5TfvA zGy4ZIX@99~N>K_i1d=i#wr68%^DE#kA!&Eq{VR9B({*vuA)2-piV`xo#NA!HIUA@+!*Oo-vA(X$x_%>gsOteJh2bN*}BbwA{sd*xgq zPBa?^D`zu%XJ$)g_H!xNuky7Fya9eA(v(V13h|jp%7oY(ZS-u0NN?VD_aDt{-raAM zZ93;XanUqDq-@5yYUa$A440e40{DR0-l&z-`!uK z)T9sxf}~7{{ow*IM%<{TfJq{XHe0q^z&&@L@4D_AcTWKBZlBx5xtHf@3cA<|Gjm|o z%vMrLzowKHh?L|ak&0XZ9-|sDAr1venGpL&B5qh)r8sAaF&p#19ip>yw(Gk2ob%cW z0y{<0BvxWSC?W>1l2UqFmN|Mvq$5{|Wsa7J;{86V5fkFjkdz5=u(X19msk{i-pprUeYIJm1L9Y^KW~7u>&FqPpJtYeD@0-~}cV7nH5(WDjVIjm3 zAt@8$@TeV}H(B4odCknG+a`#(%l{5{sJ=-*Y_g*9{o7rj^Tk$I)T#N4h zxQws(nV7Kup*|0Zm=H&Xq)doI#>`SlQ;rg8%_#;c&Pm`h5k;FO1noR<-rZ;2{g%6r zmZ7!o?xq2@r-x=H*j^%GwNui8Dq`=64#|Gm78#@?7l;nZUr=gNh+~DMOo$_(x#_m) z&1qnQ=-j+Uh}t-C4j3uNXUee)?mp)3m)-q(&Up%Uu1OQ8gCgMG>-F5saxK;;){}fi zq##!q)P*gTV|`R7Cd9EuQYOUlA-Q`hH`yv_HBvT0MAWLdS=la>@6Q60WiHy4uIna& z33ngMIa^A}C2S{$HuCB2q?EkZ>vc_${wmOS_hmDCO~_cE807jp@R~?hwj!oFF(HmQ zk}@HVFExr*siHPXNSYB*wAwIm3D%-;mx-mI?z;Oda3$w_i4eSVz^J>Q2SwM8idMYp z*G(F7Ege}>kxc^^vDA-pguMo!*NtqBQi;bcNn`Nz@Q=17U z%D$Ilmw=n@ejBJO4@|oIRah@+x_R#}DMi0VD_%|Xs}-;%;3M#!5Ux6=;#2wl8$!~y zk}yz2Oo$VSq)dp@!7#Gc9J$LUBwQN@j(Hm{wR=eC(`v`2>-|f%0*W>MMFSbZ25;D{`vsdo^3CL#F z5#rV23`FW9&=Kj!<+82T*I*D4TQ281gGiYKOo%gzq)Z4=*-FyJ2_ZX2q&TaT4MQt7 zMu^;8bB%FA^yY{Lg!bGXAvK4YBV9GKg=WF+T;4+!rCR6J?Qo3@d@!?5B|6v>?O!wzrsjC=FuzpDH1R-V@2w9sZq~#3}VLC`^J=Zc3y;>n6T$%Mz zt^O|8aYC}*mTQd>pJ7c=Zai1=C4^Xqq)Z607q+WW8Kj{CY zs(dXom<86NsFo1oP>_@fA@(xjt=91R#S literal 0 HcmV?d00001 diff --git a/lib/appRouters.dart b/lib/appRouters.dart index bc56cf3e..04c9a2b1 100755 --- a/lib/appRouters.dart +++ b/lib/appRouters.dart @@ -138,6 +138,8 @@ import 'main/lockDetail/passwordKey/passwordKeyList/passwordKeyList_page.dart'; import 'main/lockDetail/passwordKey/passwordKey_perpetual/passwordKey_perpetual_page.dart'; import 'main/lockDetail/realTimePicture/realTimePicture_page.dart'; import 'main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart'; +import 'main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_page.dart'; +import 'main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_page.dart'; import 'main/lockDetail/remoteControl/remoteControlList/remoteControlList_page.dart'; import 'main/lockDetail/videoLog/editVideoLog/editVideoLog_page.dart'; import 'main/lockDetail/videoLog/videoLog/videoLog_page.dart'; @@ -448,6 +450,8 @@ abstract class Routers { static const String remoteControlListPage = '/RemoteControlListPage'; // 遥控列表 static const String addRemoteControlPage = '/AddRemoteControlPage'; // 添加遥控 + static const String addRemoteControlTypePage = '/AddRemoteControlTypePage'; // 添加遥控类型 + static const String remoteControlDetailPage = '/RemoteControlDetailPage'; // 遥控详情 static const String cardListPage = '/CardListPage'; // 卡列表 // static const addCardTypeManagePage = '/AddCardTypeManagePage'; // 添加卡 @@ -1150,5 +1154,9 @@ abstract class AppRouters { name: Routers.expireKeyChangeValidityDatePage, page: () => const ExpireKeyChangeValidityDatePage()), GetPage( name: Routers.doorLockLogDetailPage, page: () => const DoorLockLogDetailPage()), + GetPage( + name: Routers.addRemoteControlTypePage, page: () => const AddRemoteControlTypePage()), + GetPage( + name: Routers.remoteControlDetailPage, page: () => const RemoteControlDetailPage()), ]; } diff --git a/lib/blue/io_protocol/io_addRemoteControlCancel.dart b/lib/blue/io_protocol/io_addRemoteControlCancel.dart new file mode 100644 index 00000000..d09f64a0 --- /dev/null +++ b/lib/blue/io_protocol/io_addRemoteControlCancel.dart @@ -0,0 +1,95 @@ + +import 'dart:convert'; + +import 'package:crypto/crypto.dart' as crypto; + +import '../io_sender.dart'; +import '../io_tool/io_tool.dart'; +import '../io_type.dart'; +import '../sm4Encipher/sm4.dart'; + +/// TODO:取消添加遥控 +class SenderCancelAddRemoteControlCommand extends SenderProtocol { + + SenderCancelAddRemoteControlCommand({ + this.keyID, + this.userID, + this.token, + this.needAuthor, + this.signKey, + this.privateKey, + }) : super(CommandType.generalExtendedCommond); + String? keyID; + String? userID; + List? token; + int? needAuthor; + List? signKey; + List? privateKey; + + @override + String toString() { + return 'SenderCancelAddRemoteControlCommand{keyID: $keyID, ' + 'userID: $userID, token: $token, needAuthor: $needAuthor, signKey: $signKey, privateKey: $privateKey}'; + } + + @override + List messageDetail() { + List data = []; + List subData = []; + List ebcData = []; + + // 指令类型 + data.addAll(intChangList(commandType!.typeValue)); + + // 子命令类型 + data.add(28); + + // keyID 40 + int keyIDLength = utf8.encode(keyID!).length; + subData.addAll(utf8.encode(keyID!)); + subData = getFixedLengthList(subData, 40 - keyIDLength); + + //userID 20 + int userIDLength = utf8.encode(userID!).length; + subData.addAll(utf8.encode(userID!)); + subData = getFixedLengthList(subData, 20 - userIDLength); + + if(needAuthor == 0){ + //AuthCodeLen 1 + subData.add(0); + } else { + List authCodeData = []; + //KeyID + authCodeData.addAll(utf8.encode(keyID!)); + + //authUserID + authCodeData.addAll(utf8.encode(userID!)); + + //token 4 首次请求 Token 填 0,如果锁需要鉴权操作者身份,则会分配动态口令并在应答消息中返回,二次请求时带上。 + authCodeData.addAll(token!); + + authCodeData.addAll(signKey!); + + // 把KeyID、authUserID、时间戳、公钥通过md5加密之后就是authCode + var authCode = crypto.md5.convert(authCodeData); + + subData.add(authCode.bytes.length); + subData.addAll(authCode.bytes); + } + + data.add(subData.length); + data.addAll(subData); + + if ((data.length % 16) != 0) { + int add = 16 - data.length % 16; + for (int i = 0; i < add; i++) { + data.add(0); + } + } + + printLog(data); + // 拿到数据之后通过LockId进行SM4 ECB加密 key:544d485f633335373034383064613864 + ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB); + return ebcData; + } +} diff --git a/lib/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart b/lib/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart new file mode 100644 index 00000000..f81c35b8 --- /dev/null +++ b/lib/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart @@ -0,0 +1,207 @@ + +import 'dart:convert'; + +import 'package:star_lock/tools/dateTool.dart'; + +import '../io_tool/io_tool.dart'; +import '../sm4Encipher/sm4.dart'; +import '../io_reply.dart'; +import '../io_sender.dart'; +import '../io_type.dart'; +import 'package:crypto/crypto.dart' as crypto; + +///TODO:添加遥控 +/* +备注: +删除单个指纹规则:UseCountLimit 设置为 0。删除全部指纹规则: UseCountLimit 设置为 0,FingerNo 设置为 255,userId 设置为“Delete All !@#”,只有门锁管理员才有权限 +**/ +class SenderAddRemoteControlWithTimeCycleCoercionCommand extends SenderProtocol { + + SenderAddRemoteControlWithTimeCycleCoercionCommand({ + this.keyID, + this.userID, + this.remoteControlNo, + this.useCountLimit, + this.isForce, + this.operate, + this.isAdmin, + this.token, + this.isRound, + this.weekRound, + this.startDate, + this.endDate, + this.startTime, + this.endTime, + this.needAuthor, + this.signKey, + this.privateKey, + }) : super(CommandType.generalExtendedCommond); + + String? keyID; + String? userID; + int? remoteControlNo; + int? useCountLimit; + int? isForce; + int? operate; + int? isAdmin; + List? token; + int? isRound; + int? weekRound; + int? startDate; + int? endDate; + String? startTime; + String? endTime; + int? needAuthor; + List? signKey; + List? privateKey; + + + @override + String toString() { + return 'SenderAddRemoteControlWithTimeCycleCoercionCommand{keyID: $keyID, ' + 'userID: $userID, remoteControlNo: $remoteControlNo, useCountLimit: $useCountLimit, ' + 'isForce: $isForce, token: $token, isRound: $isRound, ' + 'weekRound: $weekRound, ' + 'startDate: ${DateTool().dateIntToYMDHNString(startDate)}, ' + 'endDate: ${DateTool().dateIntToYMDHNString(endDate)}, ' + 'startTime: $startTime,' + 'endTime: $endTime, ' + 'needAuthor: $needAuthor, signKey: $signKey, privateKey: $privateKey}'; + } + + @override + List messageDetail() { + final List data = []; + List subData = []; + List ebcData = []; + + // 指令类型 + data.addAll(intChangList(commandType!.typeValue)); + + // 子命令类型 + data.add(26); + + // keyID 40 + final int keyIDLength = utf8.encode(keyID!).length; + subData.addAll(utf8.encode(keyID!)); + subData = getFixedLengthList(subData, 40 - keyIDLength); + + //userID 20 + final int userIDLength = utf8.encode(userID!).length; + subData.addAll(utf8.encode(userID!)); + subData = getFixedLengthList(subData, 20 - userIDLength); + + // remoteControlNo + subData.addAll(intChangList(remoteControlNo!)); + + // UseCountLimit + subData.addAll(intChangList(useCountLimit!)); + + // Operate 0:注册 1:修改 2:删除 3:删除全部 + subData.add(operate!); + // AppLog.log("addCard operate:$operate"); + + // isAdmin + subData.add(isAdmin!); + // AppLog.log("addCard isAdmin:$isAdmin"); + + // isForce + subData.add(isForce!); + // AppLog.log("addCard isForce:$isForce"); + + // token + subData.addAll(token!); + + // isRound + subData.add(isRound!); + + // weekRound + subData.add(weekRound!); + + // startDate 4 + subData.add((startDate! & 0xff000000) >> 24); + subData.add((startDate! & 0xff0000) >> 16); + subData.add((startDate! & 0xff00) >> 8); + subData.add(startDate! & 0xff); + + // endDate 4 + subData.add((endDate! & 0xff000000) >> 24); + subData.add((endDate! & 0xff0000) >> 16); + subData.add((endDate! & 0xff00) >> 8); + subData.add(endDate! & 0xff); + + // startTime 4 + final List startTimeList = [0,0,0,0]; + if(startTime!.contains(':')){ + final List getStartTimeList = startTime!.split(':'); + startTimeList[2] = int.parse(getStartTimeList[0]); + startTimeList[3] = int.parse(getStartTimeList[1]); + } + subData.addAll(startTimeList); + + // endTime 4 + final List endTimeList = [0,0,0,0]; + if(endTime!.contains(':')){ + final List getendTimeList = endTime!.split(':'); + endTimeList[2] = int.parse(getendTimeList[0]); + endTimeList[3] = int.parse(getendTimeList[1]); + } + subData.addAll(endTimeList); + + if(needAuthor == 0){ + //AuthCodeLen 1 + subData.add(0); + } else { + final List authCodeData = []; + //KeyID + authCodeData.addAll(utf8.encode(keyID!)); + + //authUserID + authCodeData.addAll(utf8.encode(userID!)); + + //token 4 首次请求 Token 填 0,如果锁需要鉴权操作者身份,则会分配动态口令并在应答消息中返回,二次请求时带上。 + authCodeData.addAll(token!); + + authCodeData.addAll(signKey!); + + // 把KeyID、authUserID、时间戳、公钥通过md5加密之后就是authCode + final authCode = crypto.md5.convert(authCodeData); + + subData.add(authCode.bytes.length); + subData.addAll(authCode.bytes); + } + + data.add(subData.length); + data.addAll(subData); + + if ((data.length % 16) != 0) { + final int add = 16 - data.length % 16; + for (int i = 0; i < add; i++) { + data.add(0); + } + } + + printLog(data); + // 拿到数据之后通过LockId进行SM4 ECB加密 key:544d485f633335373034383064613864 + ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB); + return ebcData; + } +} + +class SenderAddRemoteControlWithTimeCycleCoercionReply extends Reply { + SenderAddRemoteControlWithTimeCycleCoercionReply.parseData(CommandType commandType, List dataDetail) + : super.parseData(commandType, dataDetail) { + data = dataDetail; + final int status = data[2]; + errorWithStstus(status); + } +} + +class SenderAddRemoteControlConfirmationReply extends Reply { + SenderAddRemoteControlConfirmationReply.parseData(CommandType commandType, List dataDetail) + : super.parseData(commandType, dataDetail) { + data = dataDetail; + final int status = data[2]; + errorWithStstus(status); + } +} diff --git a/lib/blue/reciver_data.dart b/lib/blue/reciver_data.dart index 6069abb7..d57f4c3e 100755 --- a/lib/blue/reciver_data.dart +++ b/lib/blue/reciver_data.dart @@ -24,6 +24,7 @@ import 'package:star_lock/blue/io_protocol/io_transferPermissions.dart'; import '../tools/storage.dart'; import 'io_protocol/io_addFingerprintWithTimeCycleCoercion.dart'; import 'io_protocol/io_addICCardWithTimeCycleCoercion.dart'; +import 'io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; import 'io_protocol/io_addStressPassword.dart'; import 'io_protocol/io_addUser.dart'; import 'io_protocol/io_checkingCardStatus.dart'; @@ -308,6 +309,20 @@ class CommandReciverManager { commandType, data); } break; + case 26: + { + // 注册遥控开始 + reply = SenderAddRemoteControlWithTimeCycleCoercionReply.parseData( + commandType, data); + } + break; + case 27: + { + // 注册遥控确认 + reply = SenderAddRemoteControlConfirmationReply.parseData( + commandType, data); + } + break; case 30: { // 查询指纹状态 diff --git a/lib/blue/sender_manage.dart b/lib/blue/sender_manage.dart index 04e3097c..20cd27b9 100755 --- a/lib/blue/sender_manage.dart +++ b/lib/blue/sender_manage.dart @@ -13,6 +13,8 @@ import 'io_protocol/io_addFaceCancel.dart'; import 'io_protocol/io_addFingerprintCancel.dart'; import 'io_protocol/io_addFingerprintWithTimeCycleCoercion.dart'; import 'io_protocol/io_addICCardWithTimeCycleCoercion.dart'; +import 'io_protocol/io_addRemoteControlCancel.dart'; +import 'io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; import 'io_protocol/io_addStressPassword.dart'; import 'io_protocol/io_addUser.dart'; import 'io_protocol/io_checkingCardStatus.dart'; @@ -605,6 +607,72 @@ class IoSenderManage { callBack: callBack); } + //todo:添加遥控开始(带限时、循环、胁迫...) + static void senderAddRemoteControlWithTimeCycleCoercionCommand( + {required String? keyID, + required String? userID, + required int? remoteControlNo, + required int? useCountLimit, + required int? operate, + required int? isAdmin, + required int? isForce, + required List? token, + required int? isRound, + required int? weekRound, + required int? startDate, + required int? endDate, + required String? startTime, + required String? endTime, + required int? needAuthor, + required List? signKey, + required List? privateKey, + required bool? isBeforeAddUser, + CommandSendCallBack? callBack}) { + CommandSenderManager().managerSendData( + command: SenderAddRemoteControlWithTimeCycleCoercionCommand( + keyID: keyID, + userID: userID, + remoteControlNo: remoteControlNo, + useCountLimit: useCountLimit, + operate: operate, + isAdmin: isAdmin, + isForce: isForce, + token: token, + isRound: isRound, + weekRound: weekRound, + startDate: startDate, + endDate: endDate, + startTime: startTime, + endTime: endTime, + needAuthor: needAuthor, + signKey: signKey, + privateKey: privateKey, + ), + isBeforeAddUser: isBeforeAddUser!, + callBack: callBack); + } + + //todo:取消添加遥控 + static void senderCancelAddRemoteControlCommand( + {required String? keyID, + required String? userID, + required List? token, + required int? needAuthor, + required List? signKey, + required List? privateKey, + CommandSendCallBack? callBack}) { + CommandSenderManager().managerSendData( + command: SenderCancelAddRemoteControlCommand( + keyID: keyID, + userID: userID, + token: token, + needAuthor: needAuthor, + signKey: signKey, + privateKey: privateKey, + ), + callBack: callBack); + } + //todo:添加人脸开始 static void senderAddFaceCommand( {required String? keyID, diff --git a/lib/login/login/starLock_login_logic.dart b/lib/login/login/starLock_login_logic.dart index c0edc5bd..7f02667e 100755 --- a/lib/login/login/starLock_login_logic.dart +++ b/lib/login/login/starLock_login_logic.dart @@ -11,6 +11,7 @@ import 'package:star_lock/mine/mine/starLockMine_state.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import 'package:star_lock/tools/push/xs_jPhush.dart'; +import '../../main/lockMian/lockMain/lockMain_logic.dart'; import '../../mine/mine/starLockMine_logic.dart'; import '../../network/api_repository.dart'; import '../../tools/eventBusEventManage.dart'; @@ -53,6 +54,9 @@ class StarLockLoginLogic extends BaseGetXController { Storage.saveLoginData(entity.data); Storage.setBool(saveIsVip, entity.data!.isVip == 1); eventBus.fire(MineInfoChangeRefreshUI()); + if (Get.isRegistered()) { + Get.find().getStarLockInfo(isUnShowLoading: true); + } Get.offNamedUntil(Routers.starLockMain, (Route route) => false); BlueManage().scanDevices.clear(); //清除设备缓存 // 初始化JPush服务并绑定设备ID diff --git a/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_logic.dart b/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_logic.dart index 170ea711..df21ce95 100755 --- a/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_logic.dart +++ b/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_logic.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; +import 'package:star_lock/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import '../../../../blue/blue_manage.dart'; @@ -35,6 +36,11 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ _replyAddFingerprintBegin(reply); } + // 添加遥控开始(此处用作修改遥控) + if ((reply is SenderAddRemoteControlWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) { + _replyAddRemoteControlBegin(reply); + } + // 添加人脸开始(此处用作修改人脸) if ((reply is SenderAddFaceReply) && (state.ifCurrentScreen.value == true)) { _replyAddFaceBegin(reply); @@ -42,7 +48,157 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ }); } - // 添加人脸开始 + // 修改卡片,接收消息 + Future _replyAddICCardBegin(Reply reply) async { + final int status = reply.data[2]; + + switch(status){ + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editICCardData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.cardId!.toString(), + userID:await Storage.getUid(), + cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.cardRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改指纹,接收消息 + Future _replyAddFingerprintBegin(Reply reply) async { + final int status = reply.data[2]; + switch(status){ + case 0x00: + //成功 + state.sureBtnState.value = 0; + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editFingerprintsData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddFingerprintWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.fingerprintId.toString(), + userID:await Storage.getUid(), + fingerNo:int.parse(state.fingerprintItemData.value.fingerprintNumber!), + useCountLimit:0xffff, + operate:1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.fingerRight, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改遥控,接收消息 + Future _replyAddRemoteControlBegin(Reply reply) async { + final int status = reply.data[2]; + + switch(status){ + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editRemoteControlData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.remoteId!.toString(), + userID:await Storage.getUid(), + remoteControlNo:int.parse(state.fingerprintItemData.value.remoteNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.remoteRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改人脸,接收消息 Future _replyAddFaceBegin(Reply reply) async { final int status = reply.data[2]; @@ -67,23 +223,23 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ Storage.setStringList(saveBlueToken, saveStrList); IoSenderManage.senderAddFaceCommand( - keyID:state.fingerprintItemData.value.cardId!.toString(), - userID:await Storage.getUid(), - faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.faceRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:0, // 是否是循环 - weekRound:0, // 周循环 - startDate: state.beginTimeTimestamp.value~/1000, - endDate: state.endTimeTimestamp.value~/1000, - startTime:'0', - endTime:'0', - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: token, + keyID:state.fingerprintItemData.value.cardId!.toString(), + userID:await Storage.getUid(), + faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.faceRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, isBeforeAddUser: false ); break; @@ -93,162 +249,7 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ } } - // 删除/修改人脸信息 - Future senderAddFace() async { - if (state.sureBtnState.value == 1) { - return; - } - state.sureBtnState.value = 1; - - showEasyLoading(); - showBlueConnetctToastTimer(action: () { - dismissEasyLoading(); - state.sureBtnState.value = 0; - }); - BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { - if (deviceConnectionState == BluetoothConnectionState.connected) { - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); - - final List? token = await Storage.getStringList(saveBlueToken); - final List getTokenList = changeStringListToIntList(token!); - - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - IoSenderManage.senderAddFaceCommand( - keyID:state.fingerprintItemData.value.faceId!.toString(), - userID:await Storage.getUid(), - faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.faceRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:0, // 是否是循环 - weekRound:0, // 周循环 - startDate: state.beginTimeTimestamp.value~/1000, - endDate: state.endTimeTimestamp.value~/1000, - startTime:'0', - endTime:'0', - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: getTokenList, - isBeforeAddUser: false - ); - } else if (deviceConnectionState == - BluetoothConnectionState.disconnected) { - dismissEasyLoading(); - cancelBlueConnetctToastTimer(); - state.sureBtnState.value = 0; - if (state.ifCurrentScreen.value == true) { - showBlueConnetctToast(); - } - } - }); - } - - // 添加卡片开始(此处用作删除卡片) - Future _replyAddICCardBegin(Reply reply) async { - final int status = reply.data[2]; - - switch(status){ - case 0x00: - //成功 - cancelBlueConnetctToastTimer(); - dismissEasyLoading(); - editICCardData(); - break; - case 0x06: - //无权限 - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); - - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - final List token = reply.data.sublist(5, 9); - final List saveStrList = changeIntListToStringList(token); - Storage.setStringList(saveBlueToken, saveStrList); - - IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( - keyID:state.fingerprintItemData.value.cardId!.toString(), - userID:await Storage.getUid(), - cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.cardRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:0, // 是否是循环 - weekRound:0, // 周循环 - startDate: state.beginTimeTimestamp.value~/1000, - endDate: state.endTimeTimestamp.value~/1000, - startTime:'0', - endTime:'0', - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: token, - isBeforeAddUser: false - ); - break; - default: - //失败 - break; - } - } - - // 添加指纹开始 - Future _replyAddFingerprintBegin(Reply reply) async { - final int status = reply.data[2]; - switch(status){ - case 0x00: - //成功 - state.sureBtnState.value = 0; - cancelBlueConnetctToastTimer(); - dismissEasyLoading(); - editFingerprintsData(); - break; - case 0x06: - //无权限 - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); - - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - final List token = reply.data.sublist(5, 9); - final List saveStrList = changeIntListToStringList(token); - Storage.setStringList(saveBlueToken, saveStrList); - - IoSenderManage.senderAddFingerprintWithTimeCycleCoercionCommand( - keyID:state.fingerprintItemData.value.fingerprintId.toString(), - userID:await Storage.getUid(), - fingerNo:int.parse(state.fingerprintItemData.value.fingerprintNumber!), - useCountLimit:0xffff, - operate:1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.fingerRight, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:0, // 是否是循环 - weekRound:0, // 周循环 - startDate: state.beginTimeTimestamp.value~/1000, - endDate: state.endTimeTimestamp.value~/1000, - startTime:'0', - endTime:'0', - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: token, - isBeforeAddUser: false - ); - break; - default: - //失败 - break; - } - } - - // 删除指纹 + // 修改指纹调用协议 Future senderAddFingerprint() async { if(state.sureBtnState.value == 1){ return; @@ -305,7 +306,7 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ }); } - // 添加卡片 + // 修改卡片调用协议 Future senderAddICCard() async { if(state.sureBtnState.value == 1){ return; @@ -360,7 +361,117 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ }); } - // 修改指纹信息 + // 修改遥控调用协议 + Future senderAddRemoteControl() async { + if(state.sureBtnState.value == 1){ + return; + } + state.sureBtnState.value = 1; + + showEasyLoading(); + showBlueConnetctToastTimer(action: (){ + dismissEasyLoading(); + state.sureBtnState.value = 0; + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected){ + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.remoteId!.toString(), + userID:await Storage.getUid(), + remoteControlNo:int.parse(state.fingerprintItemData.value.remoteNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.remoteRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false + ); + } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + state.sureBtnState.value = 0; + if(state.ifCurrentScreen.value == true){ + showBlueConnetctToast(); + } + } + }); + } + + // 修改人脸调用协议 + Future senderAddFace() async { + if (state.sureBtnState.value == 1) { + return; + } + state.sureBtnState.value = 1; + + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + state.sureBtnState.value = 0; + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected) { + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + IoSenderManage.senderAddFaceCommand( + keyID:state.fingerprintItemData.value.faceId!.toString(), + userID:await Storage.getUid(), + faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.faceRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:0, // 是否是循环 + weekRound:0, // 周循环 + startDate: state.beginTimeTimestamp.value~/1000, + endDate: state.endTimeTimestamp.value~/1000, + startTime:'0', + endTime:'0', + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false + ); + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + state.sureBtnState.value = 0; + if (state.ifCurrentScreen.value == true) { + showBlueConnetctToast(); + } + } + }); + } + + // 修改指纹调用接口 Future editFingerprintsData() async{ final LoginEntity entity = await ApiRepository.to.editFingerprintsData( fingerprintId: state.fingerprintItemData.value.fingerprintId.toString(), @@ -387,7 +498,7 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ } } - // 编辑iC卡 + // 修改卡片调用接口 Future editICCardData() async{ final LoginEntity entity = await ApiRepository.to.editICCardData( cardId: state.fingerprintItemData.value.cardId.toString(), @@ -414,7 +525,34 @@ class OtherTypeKeyChangeDateLogic extends BaseGetXController{ } } - // 修改人脸有效期 + // 编辑遥控 + Future editRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.editRemoteControlData( + lockId: state.fingerprintItemData.value.lockId ?? 0, + remoteId: state.fingerprintItemData.value.remoteId!, + startDate: state.beginTimeTimestamp.value, + endDate: state.endTimeTimestamp.value, + startTime: 0, + endTime: 0, + remoteType: 2, + weekDay: [], + remoteName: state.fingerprintItemData.value.remoteName!, + addType: 1, + isCoerced: 2, + remoteRight: 2, + ); + if (entity.errorCode!.codeIsSuccessful) { + showToast('修改成功'.tr, something: (){ + Get.back(result: { + 'beginTimeTimestamp':state.beginTimeTimestamp.value.toString(), + 'endTimeTimestamp':state.endTimeTimestamp.value.toString(), + }); + eventBus.fire(OtherTypeRefreshListEvent()); + }); + } + } + + // 修改人脸调用接口 Future editFaceData() async { final LoginEntity entity = await ApiRepository.to.updateFaceValidity( lockId: state.fingerprintItemData.value.lockId!, diff --git a/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_page.dart b/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_page.dart index a0c0908a..f56fded8 100755 --- a/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_page.dart +++ b/lib/main/lockDetail/card/otherTypeKeyChangeDate/otherTypeKeyChangeDate_page.dart @@ -53,13 +53,13 @@ class _OtherTypeKeyChangeDatePageState extends State state.endTimeTimestamp.value || state.beginTimeTimestamp.value == state.endTimeTimestamp.value) { - logic.showToast("失效时间需大于生效时间".tr); + logic.showToast('失效时间需大于生效时间'.tr); return; } if (state.endTimeTimestamp.value < DateTime.now().millisecondsSinceEpoch) { - logic.showToast("生效时间需大于当前时间".tr); + logic.showToast('生效时间需大于当前时间'.tr); return; } switch (state.pushType.value) { @@ -73,7 +73,7 @@ class _OtherTypeKeyChangeDatePageState extends State break; case 2: // 遥控 - + logic.senderAddRemoteControl(); break; case 3: // 人脸 @@ -99,7 +99,7 @@ class _OtherTypeKeyChangeDatePageState extends State isHaveLine: true, isHaveDirection: true, action: () { - PDuration selectDate = + final PDuration selectDate = PDuration.parse(DateTime.tryParse(state.beginTime.value)); Pickers.showDatePicker(context, selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (PDuration p) { @@ -113,7 +113,7 @@ class _OtherTypeKeyChangeDatePageState extends State rightTitle: state.endTime.value, isHaveDirection: true, action: () { - PDuration selectDate = + final PDuration selectDate = PDuration.parse(DateTime.tryParse(state.endTime.value)); Pickers.showDatePicker(context, selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (PDuration p) { @@ -153,7 +153,9 @@ class _OtherTypeKeyChangeDatePageState extends State void didPop() { super.didPop(); logic.cancelBlueConnetctToastTimer(); - if (EasyLoading.isShow) EasyLoading.dismiss(animation: true); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } state.ifCurrentScreen.value = false; state.sureBtnState.value = 0; } @@ -170,7 +172,9 @@ class _OtherTypeKeyChangeDatePageState extends State void didPushNext() { super.didPushNext(); logic.cancelBlueConnetctToastTimer(); - if (EasyLoading.isShow) EasyLoading.dismiss(animation: true); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } state.ifCurrentScreen.value = false; state.sureBtnState.value = 0; } diff --git a/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_logic.dart b/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_logic.dart index 2e771ce6..4b338982 100755 --- a/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_logic.dart +++ b/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_logic.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; +import 'package:star_lock/blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/tools/baseGetXController.dart'; @@ -37,6 +38,11 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ _replyAddFingerprintBegin(reply); } + // 添加遥控开始(此处用作修改或者删除指纹) + if((reply is SenderAddRemoteControlWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) { + _replyAddRemoteControlBegin(reply); + } + // 添加人脸开始(此处用作修改人脸) if ((reply is SenderAddFaceReply) && (state.ifCurrentScreen.value == true)) { _replyAddFaceBegin(reply); @@ -44,10 +50,159 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ }); } - // 添加人脸开始 - Future _replyAddFaceBegin(Reply reply) async { + // 修改卡片,接收协议数据 + Future _replyAddICCardBegin(Reply reply) async { final int status = reply.data[2]; + switch(status){ + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editICCardData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.cardId!.toString(), + userID:await Storage.getUid(), + cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.cardRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改指纹,接收协议数据 + Future _replyAddFingerprintBegin(Reply reply) async { + final int status = reply.data[2]; + switch(status){ + case 0x00: + //成功 + state.sureBtnState.value = 0; + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editFingerprintsData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddFingerprintWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.fingerprintId.toString(), + userID:await Storage.getUid(), + fingerNo:int.parse(state.fingerprintItemData.value.fingerprintNumber!), + useCountLimit:0xffff, + operate:1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.fingerRight, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改遥控,接收协议数据 + Future _replyAddRemoteControlBegin(Reply reply) async { + final int status = reply.data[2]; + + switch(status){ + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + editRemoteControlData(); + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.remoteId!.toString(), + userID:await Storage.getUid(), + remoteControlNo:int.parse(state.fingerprintItemData.value.remoteNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.remoteRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + break; + } + } + + // 修改人脸,接收协议数据 + Future _replyAddFaceBegin(Reply reply) async { + final int status = reply.data[2]; switch (status) { case 0x00: //成功 @@ -95,162 +250,63 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ } } - // 删除/修改人脸信息 - Future senderAddFace() async { - if (state.sureBtnState.value == 1) { + + // 修改卡片,发送协议 + Future senderAddICCard() async { + if(state.sureBtnState.value == 1){ return; } state.sureBtnState.value = 1; showEasyLoading(); - showBlueConnetctToastTimer(action: () { + showBlueConnetctToastTimer(action: (){ dismissEasyLoading(); state.sureBtnState.value = 0; }); BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { - if (deviceConnectionState == BluetoothConnectionState.connected) { + if (deviceConnectionState == BluetoothConnectionState.connected){ + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); final List getPrivateKeyList = changeStringListToIntList(privateKey!); final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - IoSenderManage.senderAddFaceCommand( - keyID:state.fingerprintItemData.value.faceId!.toString(), - userID:await Storage.getUid(), - faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.faceRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:1, // 是否是循环 - weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 - startDate: state.starDateTimestamp.value~/1000, - endDate: state.endDateTimestamp.value~/1000, - startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), - endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: getTokenList, + IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.cardId!.toString(), + userID:await Storage.getUid(), + cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.cardRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, isBeforeAddUser: false ); - } else if (deviceConnectionState == - BluetoothConnectionState.disconnected) { + } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); cancelBlueConnetctToastTimer(); state.sureBtnState.value = 0; - if (state.ifCurrentScreen.value == true) { + if(state.ifCurrentScreen.value == true){ showBlueConnetctToast(); } } }); } - // 添加卡片开始(此处用作删除卡片) - Future _replyAddICCardBegin(Reply reply) async { - final int status = reply.data[2]; - - switch(status){ - case 0x00: - //成功 - cancelBlueConnetctToastTimer(); - dismissEasyLoading(); - editICCardData(); - break; - case 0x06: - //无权限 - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); - - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - final List token = reply.data.sublist(5, 9); - final List saveStrList = changeIntListToStringList(token); - Storage.setStringList(saveBlueToken, saveStrList); - - IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( - keyID:state.fingerprintItemData.value.cardId!.toString(), - userID:await Storage.getUid(), - cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.cardRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:1, // 是否是循环 - weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 - startDate: state.starDateTimestamp.value~/1000, - endDate: state.endDateTimestamp.value~/1000, - startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), - endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: token, - isBeforeAddUser: false - ); - break; - default: - //失败 - break; - } - } - - // 添加指纹开始 - Future _replyAddFingerprintBegin(Reply reply) async { - final int status = reply.data[2]; - switch(status){ - case 0x00: - //成功 - state.sureBtnState.value = 0; - cancelBlueConnetctToastTimer(); - dismissEasyLoading(); - editFingerprintsData(); - break; - case 0x06: - //无权限 - final List? privateKey = await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = changeStringListToIntList(privateKey!); - - final List? signKey = await Storage.getStringList(saveBlueSignKey); - final List signKeyDataList = changeStringListToIntList(signKey!); - - final List token = reply.data.sublist(5, 9); - final List saveStrList = changeIntListToStringList(token); - Storage.setStringList(saveBlueToken, saveStrList); - - IoSenderManage.senderAddFingerprintWithTimeCycleCoercionCommand( - keyID:state.fingerprintItemData.value.fingerprintId.toString(), - userID:await Storage.getUid(), - fingerNo:int.parse(state.fingerprintItemData.value.fingerprintNumber!), - useCountLimit:0xffff, - operate:1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.fingerRight, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:1, // 是否是循环 - weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 - startDate: state.starDateTimestamp.value~/1000, - endDate: state.endDateTimestamp.value~/1000, - startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), - endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: token, - isBeforeAddUser: false - ); - break; - default: - //失败 - break; - } - } - - // 删除指纹 + // 修改指纹,发送协议 Future senderAddFingerprint() async { if(state.sureBtnState.value == 1){ return; @@ -307,8 +363,8 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ }); } - // 添加卡片 - Future senderAddICCard() async { + // 修改遥控,发送协议 + Future senderAddRemoteControl() async { if(state.sureBtnState.value == 1){ return; } @@ -331,25 +387,25 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); - IoSenderManage.senderAddCardWithTimeCycleCoercionCommand( - keyID:state.fingerprintItemData.value.cardId!.toString(), - userID:await Storage.getUid(), - cardNo:int.parse(state.fingerprintItemData.value.cardNumber!), - useCountLimit:0xffff, - operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 - isAdmin:state.fingerprintItemData.value.cardRight!, - isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 - isRound:1, // 是否是循环 - weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 - startDate: state.starDateTimestamp.value~/1000, - endDate: state.endDateTimestamp.value~/1000, - startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), - endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), - needAuthor:1, - signKey:signKeyDataList, - privateKey:getPrivateKeyList, - token: getTokenList, - isBeforeAddUser: false + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:state.fingerprintItemData.value.remoteId!.toString(), + userID:await Storage.getUid(), + remoteControlNo:int.parse(state.fingerprintItemData.value.remoteNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.remoteRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false ); } else if (deviceConnectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); @@ -362,8 +418,64 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ }); } - // 修改指纹信息 - void editFingerprintsData() async{ + // 修改人脸,发送协议 + Future senderAddFace() async { + if (state.sureBtnState.value == 1) { + return; + } + state.sureBtnState.value = 1; + + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + state.sureBtnState.value = 0; + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected) { + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + IoSenderManage.senderAddFaceCommand( + keyID:state.fingerprintItemData.value.faceId!.toString(), + userID:await Storage.getUid(), + faceNo:int.parse(state.fingerprintItemData.value.faceNumber!), + useCountLimit:0xffff, + operate: 1, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.fingerprintItemData.value.faceRight!, + isForce:state.fingerprintItemData.value.isCoerced, // 是否是胁迫 + isRound:1, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: state.starDateTimestamp.value~/1000, + endDate: state.endDateTimestamp.value~/1000, + startTime:DateTool().dateToHNString(state.starTimeTimestamp.value.toString()), + endTime:DateTool().dateToHNString(state.endTimeTimestamp.value.toString()), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false + ); + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + state.sureBtnState.value = 0; + if (state.ifCurrentScreen.value == true) { + showBlueConnetctToast(); + } + } + }); + } + + + // 修改指纹,调用接口 + Future editFingerprintsData() async{ final LoginEntity entity = await ApiRepository.to.editFingerprintsData( fingerprintId: state.fingerprintItemData.value.fingerprintId.toString(), lockId: state.fingerprintItemData.value.lockId.toString(), @@ -392,8 +504,8 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ } } - // 编辑iC卡 - void editICCardData() async{ + // 修改卡片,调用接口 + Future editICCardData() async{ final LoginEntity entity = await ApiRepository.to.editICCardData( cardId: state.fingerprintItemData.value.cardId.toString(), lockId: state.fingerprintItemData.value.lockId.toString(), @@ -422,8 +534,38 @@ class OtherTypeKeyChangeValidityDateLogic extends BaseGetXController{ } } - // 修改人脸有效期 - void editFaceData() async { + // 编辑遥控 + Future editRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.editRemoteControlData( + lockId: state.fingerprintItemData.value.lockId ?? 0, + remoteId: state.fingerprintItemData.value.remoteId!, + startDate: state.starDateTimestamp.value, + endDate: state.endDateTimestamp.value, + startTime: state.starTimeTimestamp.value, + endTime: state.endTimeTimestamp.value, + remoteType: state.fingerprintItemData.value.remoteType!, + weekDay: state.weekDay.value, + remoteName: state.fingerprintItemData.value.remoteName!, + addType: 1, + isCoerced: 2, + remoteRight: 2, + ); + if (entity.errorCode!.codeIsSuccessful) { + showToast('修改成功'.tr, something: (){ + eventBus.fire(OtherTypeRefreshListEvent()); + Get.back(result: { + 'starDate':state.starDateTimestamp.value.toString(), + 'endDate':state.endDateTimestamp.value.toString(), + 'starTime':state.starTimeTimestamp.value.toString(), + 'endTime':state.endTimeTimestamp.value.toString(), + 'validityValue':state.weekDay.value, + }); + }); + } + } + + // 修改人脸,调用接口 + Future editFaceData() async { final LoginEntity entity = await ApiRepository.to.updateFaceValidity( lockId: state.fingerprintItemData.value.lockId!, faceId: state.fingerprintItemData.value.faceId!, diff --git a/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_page.dart b/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_page.dart index 2fc418fe..7bc49ece 100755 --- a/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_page.dart +++ b/lib/main/lockDetail/card/otherTypeKeyChangeValidityDate/otherTypeKeyChangeValidityDate_page.dart @@ -84,7 +84,7 @@ class _OtherTypeKeyChangeValidityDatePageState extends State passwordData = indexList.sublist(7, 17); final String password = utf8String(passwordData); indexMap['password'] = password.toString(); + AppLog.log('passwordpasswordpassword:$password'); } else { final int userNo = (indexList[1] * 256) + indexList[2]; indexMap['user'] = userNo.toString(); diff --git a/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintListData_entity.dart b/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintListData_entity.dart index 920eec33..55d3eff4 100755 --- a/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintListData_entity.dart +++ b/lib/main/lockDetail/fingerprint/fingerprintList/fingerprintListData_entity.dart @@ -1,8 +1,4 @@ class FingerprintListDataEntity { - int? errorCode; - String? description; - String? errorMsg; - Data? data; FingerprintListDataEntity( {this.errorCode, this.description, this.errorMsg, this.data}); @@ -13,6 +9,10 @@ class FingerprintListDataEntity { errorMsg = json['errorMsg']; data = json['data'] != null ? Data.fromJson(json['data']) : null; } + int? errorCode; + String? description; + String? errorMsg; + Data? data; Map toJson() { final Map data = {}; @@ -27,11 +27,6 @@ class FingerprintListDataEntity { } class Data { - List? list; - int? pageNo; - int? pageSize; - int? pages; - int? total; Data({this.list, this.pageNo, this.pageSize, this.pages, this.total}); @@ -47,6 +42,11 @@ class Data { pages = json['pages']; total = json['total']; } + List? list; + int? pageNo; + int? pageSize; + int? pages; + int? total; Map toJson() { final Map data = {}; @@ -62,6 +62,96 @@ class Data { } class FingerprintItemData { + + FingerprintItemData( + {this.fingerprintStatus, + this.lockId, + this.createDate, + this.fingerprintName, + this.endDate, + this.apiUserId, + this.nickName, + this.isCoerced, + this.startDate, + this.fingerprintNumber, + this.fingerprintType, + this.fingerprintId, + this.senderUsername, + this.weekDay, + this.validTimeStr, + this.cardName, + this.cardNumber, + this.cardType, + this.cardId, + this.faceId, + this.faceName, + this.faceNumber, + this.status, + this.addType, + this.faceType, + this.cyclicConfig, + this.featureData, + this.faceRight, + this.cardStatus, + this.cardRight, + this.fingerRight, + + this.remoteId, + this.remoteName, + this.remoteNumber, + this.mac, + this.electricQuantity, + this.remoteStatus, + this.remoteType, + this.adminUid, + this.remoteRight, + this.lockAlias + }); + + FingerprintItemData.fromJson(Map json) { + fingerprintStatus = json['fingerprintStatus']; + lockId = json['lockId']; + createDate = json['createDate']; + fingerprintName = json['fingerprintName']; + endDate = json['endDate']; + apiUserId = json['apiUserId']; + nickName = json['nickName']; + isCoerced = json['isCoerced']; + startDate = json['startDate']; + fingerprintNumber = json['fingerprintNumber']; + fingerprintType = json['fingerprintType']; + fingerprintId = json['fingerprintId']; + senderUsername = json['senderUsername']; + weekDay = json['weekDay']; + validTimeStr = json['validTimeStr']; + cardName = json['cardName']; + cardNumber = json['cardNumber']; + cardType = json['cardType']; + cardId = json['cardId']; + faceId = json['faceId']; + faceName = json['faceName']; + faceNumber = json['faceNumber']; + status = json['status']; + addType = json['addType']; + faceType = json['faceType']; + cyclicConfig = json['cyclicConfig']; + featureData = json['featureData']; + faceRight = json['faceRight']; + cardStatus = json['cardStatus']; + cardRight = json['cardRight']; + fingerRight = json['fingerRight']; + + remoteId = json['remoteId']; + remoteName = json['remoteName']; + remoteNumber = json['remoteNumber']; + mac = json['mac']; + electricQuantity = json['electricQuantity']; + remoteStatus = json['remoteStatus']; + remoteType = json['remoteType']; + adminUid = json['adminUid']; + remoteRight = json['remoteRight']; + lockAlias = json['lockAlias']; + } int? fingerprintStatus; int? lockId; int? createDate; @@ -96,72 +186,16 @@ class FingerprintItemData { String? featureData; int? faceRight; - FingerprintItemData( - {this.fingerprintStatus, - this.lockId, - this.createDate, - this.fingerprintName, - this.endDate, - this.apiUserId, - this.nickName, - this.isCoerced, - this.startDate, - this.fingerprintNumber, - this.fingerprintType, - this.fingerprintId, - this.senderUsername, - this.weekDay, - this.validTimeStr, - this.cardName, - this.cardNumber, - this.cardType, - this.cardId, - this.faceId, - this.faceName, - this.faceNumber, - this.status, - this.addType, - this.faceType, - this.cyclicConfig, - this.featureData, - this.faceRight, - this.cardStatus, - this.cardRight, - this.fingerRight}); - - FingerprintItemData.fromJson(Map json) { - fingerprintStatus = json['fingerprintStatus']; - lockId = json['lockId']; - createDate = json['createDate']; - fingerprintName = json['fingerprintName']; - endDate = json['endDate']; - apiUserId = json['apiUserId']; - nickName = json['nickName']; - isCoerced = json['isCoerced']; - startDate = json['startDate']; - fingerprintNumber = json['fingerprintNumber']; - fingerprintType = json['fingerprintType']; - fingerprintId = json['fingerprintId']; - senderUsername = json['senderUsername']; - weekDay = json['weekDay']; - validTimeStr = json['validTimeStr']; - cardName = json['cardName']; - cardNumber = json['cardNumber']; - cardType = json['cardType']; - cardId = json['cardId']; - faceId = json['faceId']; - faceName = json['faceName']; - faceNumber = json['faceNumber']; - status = json['status']; - addType = json['addType']; - faceType = json['faceType']; - cyclicConfig = json['cyclicConfig']; - featureData = json['featureData']; - faceRight = json['faceRight']; - cardStatus = json['cardStatus']; - cardRight = json['cardRight']; - fingerRight = json['fingerRight']; - } + int? remoteId; + String? remoteName; + String? remoteNumber; + String? mac; + int? electricQuantity; + int? remoteStatus; + int? remoteType; + int? adminUid; + int? remoteRight; + String? lockAlias; Map toJson() { final Map data = {}; @@ -196,6 +230,17 @@ class FingerprintItemData { data['cardStatus'] = cardStatus; data['cardRight'] = cardRight; data['fingerRight'] = fingerRight; + + data['remoteId'] = remoteId; + data['remoteName'] = remoteName; + data['remoteNumber'] = remoteNumber; + data['mac'] = mac; + data['electricQuantity'] = electricQuantity; + data['remoteStatus'] = remoteStatus; + data['remoteType'] = remoteType; + data['adminUid'] = adminUid; + data['remoteRight'] = remoteRight; + data['lockAlias'] = lockAlias; return data; } } diff --git a/lib/main/lockDetail/lockDetail/lockDetail_page.dart b/lib/main/lockDetail/lockDetail/lockDetail_page.dart index 8c8ff1fd..320616f5 100755 --- a/lib/main/lockDetail/lockDetail/lockDetail_page.dart +++ b/lib/main/lockDetail/lockDetail/lockDetail_page.dart @@ -1096,7 +1096,9 @@ class _LockDetailPageState extends State 'images/main/icon_main_remoteControl.png', TranslationLoader.lanKeys!.remoteControl!.tr, state.bottomBtnisEable.value, () { - Get.toNamed(Routers.remoteControlListPage); + Get.toNamed(Routers.remoteControlListPage, arguments: { + 'lockId': state.keyInfos.value.lockId, + }); })); } diff --git a/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_logic.dart b/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_logic.dart index 0e30ab29..6d710392 100755 --- a/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_logic.dart +++ b/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_logic.dart @@ -56,10 +56,18 @@ class LockOperatingRecordLogic extends BaseGetXController { indexMap['type'] = indexList[0].toString(); if(indexList[0] == 2){ + // final List passwordData = indexList.sublist(7, 17); + // final String password = utf8String(passwordData); + // AppLog.log('password:$password passwordData:$passwordData'); + // indexMap['user'] = password; + + final int userNo = (indexList[1] * 256) + indexList[2]; + indexMap['user'] = userNo.toString(); + AppLog.log('userNouserNouserNouserNo:$userNo'); final List passwordData = indexList.sublist(7, 17); final String password = utf8String(passwordData); - AppLog.log('password:$password passwordData:$passwordData'); - indexMap['user'] = password; + indexMap['password'] = password.toString(); + AppLog.log('passwordpasswordpassword:$password'); }else{ final int userNo = (indexList[1]*256) + indexList[2]; indexMap['user'] = userNo.toString(); diff --git a/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_state.dart b/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_state.dart index 4574189a..8f96ed49 100755 --- a/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_state.dart +++ b/lib/main/lockDetail/lockOperatingRecord/lockOperatingRecord_state.dart @@ -4,31 +4,31 @@ import 'package:get/get.dart'; import '../../lockMian/entity/lockListInfo_entity.dart'; import 'keyOperationRecord_entity.dart'; -class LockOperatingRecordState { - final lockOperatingRecordListData = [].obs; - final TextEditingController searchController = TextEditingController(); - - var ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 - var operateDate = 0; // 按日期查询消息记录的时间戳 - var ifHaveNext = false; // 页码 - var logCountPage = 10; // 蓝牙记录一页多少个 - var idStr = ""; // - var type = 0; // 1:密码 2:卡 3:指纹 4:人脸 5:钥匙 - var recordName = "".obs; // 记录名称 +class LockOperatingRecordState { // 记录名称 LockOperatingRecordState() { Map map = Get.arguments; - if(map["id"] != null){ - idStr = Get.arguments["id"]; + if(map['id'] != null){ + idStr = Get.arguments['id']; } - if(map["idStr"] != null){ - idStr = Get.arguments["idStr"]; + if(map['idStr'] != null){ + idStr = Get.arguments['idStr']; } - if(map["type"] != null){ - type = Get.arguments["type"]; + if(map['type'] != null){ + type = Get.arguments['type']; } - if(map["recordName"] != null){ - recordName.value = Get.arguments["recordName"]; + if(map['recordName'] != null){ + recordName.value = Get.arguments['recordName']; } } + final RxList lockOperatingRecordListData = [].obs; + final TextEditingController searchController = TextEditingController(); + + RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 + int operateDate = 0; // 按日期查询消息记录的时间戳 + bool ifHaveNext = false; // 页码 + int logCountPage = 10; // 蓝牙记录一页多少个 + String idStr = ''; // + int type = 0; // 1:密码 2:卡 3:指纹 4:人脸 5:钥匙 + RxString recordName = ''.obs; } diff --git a/lib/main/lockDetail/lockSet/burglarAlarm/burglarAlarm_logic.dart b/lib/main/lockDetail/lockSet/burglarAlarm/burglarAlarm_logic.dart index ec231e32..1d7ec440 100755 --- a/lib/main/lockDetail/lockSet/burglarAlarm/burglarAlarm_logic.dart +++ b/lib/main/lockDetail/lockSet/burglarAlarm/burglarAlarm_logic.dart @@ -80,9 +80,10 @@ class BurglarAlarmLogic extends BaseGetXController{ break; case 0x06: //无权限 - + state.sureBtnState.value = 0; break; default: + state.sureBtnState.value = 0; break; } } diff --git a/lib/main/lockDetail/lockSet/faceUnlock/faceUnlock_page.dart b/lib/main/lockDetail/lockSet/faceUnlock/faceUnlock_page.dart index 1a0dfdd1..cdcedb5f 100755 --- a/lib/main/lockDetail/lockSet/faceUnlock/faceUnlock_page.dart +++ b/lib/main/lockDetail/lockSet/faceUnlock/faceUnlock_page.dart @@ -59,7 +59,7 @@ class _FaceUnlockPageState extends State { TranslationLoader.lanKeys!.preventWrongOpening!.tr, state.antiMisoperation.value == 0 ? TranslationLoader.lanKeys!.preventWrongOpeningTip!.tr - :'防误开已打开,时间是'.tr + state.antiMisoperation.value.toString() + '秒'.tr, + :'防误开已打开,开锁后'.tr + state.antiMisoperation.value.toString() + '秒内不可使用面容开锁'.tr, // state.antiMisoperation.value == 0 ? '关闭' : '${state.antiMisoperation.value}' + '秒'.tr, state.faceOn.value, () { diff --git a/lib/main/lockDetail/lockSet/lockSet/lockSet_logic.dart b/lib/main/lockDetail/lockSet/lockSet/lockSet_logic.dart index 83849fc0..b99435f0 100755 --- a/lib/main/lockDetail/lockSet/lockSet/lockSet_logic.dart +++ b/lib/main/lockDetail/lockSet/lockSet/lockSet_logic.dart @@ -124,15 +124,11 @@ class LockSetLogic extends BaseGetXController { break; case 0x06: //无权限 - final List? privateKey = - await Storage.getStringList(saveBluePrivateKey); - final List getPrivateKeyList = - changeStringListToIntList(privateKey!); + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); - final List? publicKey = - await Storage.getStringList(saveBluePublicKey); - final List publicKeyDataList = - changeStringListToIntList(publicKey!); + final List? publicKey = await Storage.getStringList(saveBluePublicKey); + final List publicKeyDataList = changeStringListToIntList(publicKey!); final List? token = await Storage.getStringList(saveBlueToken); final List getTokenList = changeStringListToIntList(token!); @@ -180,6 +176,7 @@ class LockSetLogic extends BaseGetXController { //成功 // Toast.show(msg: "操作成功");featureEnable = state.isOpenStayWarn.value == 1 ? 0 : 1; dismissEasyLoading(); + state.sureBtnState.value = 0; cancelBlueConnetctToastTimer(); if (state.settingUpSupportFeatures == 55) { // APP开锁时是否需联网 @@ -208,8 +205,10 @@ class LockSetLogic extends BaseGetXController { break; case 0x06: //无权限 + state.sureBtnState.value = 0; break; default: + state.sureBtnState.value = 0; break; } } @@ -354,8 +353,14 @@ class LockSetLogic extends BaseGetXController { // 设置支持功能(带参数) Future sendBurglarAlarm(int type) async { + if(state.sureBtnState.value == 1){ + return; + } + state.sureBtnState.value = 1; + showEasyLoading(); showBlueConnetctToastTimer(action: () { + state.sureBtnState.value = 0; dismissEasyLoading(); }); BlueManage().blueSendData(BlueManage().connectDeviceName, @@ -402,6 +407,7 @@ class LockSetLogic extends BaseGetXController { } else if (connectionState == BluetoothConnectionState.disconnected) { dismissEasyLoading(); cancelBlueConnetctToastTimer(); + state.sureBtnState.value = 0; if (state.ifCurrentScreen.value == true) { showBlueConnetctToast(); } diff --git a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart index d943e667..81e7153f 100755 --- a/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart +++ b/lib/main/lockDetail/lockSet/lockSet/lockSet_page.dart @@ -701,7 +701,7 @@ class _LockSetPageState extends State trackColor: CupertinoColors.systemGrey5, thumbColor: CupertinoColors.white, value: state.isOpenLockNeedOnline.value == 1, - onChanged: (bool value) { + onChanged: state.sureBtnState.value == 1 ? null : (bool value) { setState(() { logic.sendBurglarAlarm(55); }); @@ -740,31 +740,31 @@ class _LockSetPageState extends State // } // 支持蓝牙广播 - CupertinoSwitch _lockBlueBroadcastSwitch() { - return CupertinoSwitch( - activeColor: CupertinoColors.activeBlue, - trackColor: CupertinoColors.systemGrey5, - thumbColor: CupertinoColors.white, - value: state.isOpenBlueBroadcast.value == 1, - onChanged: (bool value) { - setState(() { - logic.sendBurglarAlarm(56); - }); - }, - ); - } - - CupertinoSwitch _otherUnHaveDoneSwitch() { - return CupertinoSwitch( - activeColor: CupertinoColors.activeBlue, - trackColor: CupertinoColors.systemGrey5, - thumbColor: CupertinoColors.white, - value: false, - onChanged: (bool value) { - logic.showToast('功能暂未开放'); - }, - ); - } + // CupertinoSwitch _lockBlueBroadcastSwitch() { + // return CupertinoSwitch( + // activeColor: CupertinoColors.activeBlue, + // trackColor: CupertinoColors.systemGrey5, + // thumbColor: CupertinoColors.white, + // value: state.isOpenBlueBroadcast.value == 1, + // onChanged: (bool value) { + // setState(() { + // logic.sendBurglarAlarm(56); + // }); + // }, + // ); + // } + // + // CupertinoSwitch _otherUnHaveDoneSwitch() { + // return CupertinoSwitch( + // activeColor: CupertinoColors.activeBlue, + // trackColor: CupertinoColors.systemGrey5, + // thumbColor: CupertinoColors.white, + // value: false, + // onChanged: (bool value) { + // logic.showToast('功能暂未开放'); + // }, + // ); + // } @override void didChangeDependencies() { diff --git a/lib/main/lockDetail/lockSet/lockSet/lockSet_state.dart b/lib/main/lockDetail/lockSet/lockSet/lockSet_state.dart index 4ad0d0d7..b5fef828 100755 --- a/lib/main/lockDetail/lockSet/lockSet/lockSet_state.dart +++ b/lib/main/lockDetail/lockSet/lockSet/lockSet_state.dart @@ -27,6 +27,7 @@ class LockSetState { int settingUpSupportFeatures = 0; RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 RxBool deleteAdministratorIsHaveAllData = false.obs; // 删除管理员是否有所有数据 + RxInt sureBtnState = 0.obs;// 0普通状态(可用) 1连接中(不可用) LockSetState() { Map map = Get.arguments; diff --git a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_entity.dart b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_entity.dart new file mode 100644 index 00000000..07c746ff --- /dev/null +++ b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_entity.dart @@ -0,0 +1,43 @@ +class AddRemoteControlEntity { + + AddRemoteControlEntity( + {this.errorCode, this.description, this.errorMsg, this.data}); + + AddRemoteControlEntity.fromJson(Map json) { + errorCode = json['errorCode']; + description = json['description']; + errorMsg = json['errorMsg']; + data = json['data'] != null ? Data.fromJson(json['data']) : null; + } + int? errorCode; + String? description; + String? errorMsg; + Data? data; + + Map toJson() { + final Map data = {}; + data['errorCode'] = errorCode; + data['description'] = description; + data['errorMsg'] = errorMsg; + if (this.data != null) { + data['data'] = this.data!.toJson(); + } + return data; + } +} + +class Data { + + Data({this.remoteId}); + + Data.fromJson(Map json) { + remoteId = json['remoteId']; + } + int? remoteId; + + Map toJson() { + final Map data = {}; + data['remoteId'] = remoteId; + return data; + } +} \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart old mode 100755 new mode 100644 index 07161d66..ec634e41 --- a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart +++ b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_logic.dart @@ -1,19 +1,277 @@ +import 'dart:async'; + +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:get/get.dart'; import 'package:star_lock/tools/baseGetXController.dart'; -import 'package:star_lock/tools/pickers/time_picker/time_utils.dart'; +import '../../../../app_settings/app_settings.dart'; +import '../../../../blue/blue_manage.dart'; +import '../../../../blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; +import '../../../../blue/io_reply.dart'; +import '../../../../blue/io_tool/io_tool.dart'; +import '../../../../blue/io_tool/manager_event_bus.dart'; +import '../../../../blue/sender_manage.dart'; +import '../../../../login/login/entity/LoginEntity.dart'; +import '../../../../network/api_repository.dart'; +import '../../../../tools/dateTool.dart'; +import '../../../../tools/eventBusEventManage.dart'; +import '../../../../tools/storage.dart'; +import 'addRemoteControl_entity.dart'; import 'addRemoteControl_state.dart'; -class AddRemoteControlLoigc extends BaseGetXController{ +class AddRemoteControlLogic extends BaseGetXController{ AddRemoteControlState state = AddRemoteControlState(); - List get weekDayStr { - return state.weekdaysList.map((e) => TimeUtils.translateWeekday(e)).toList(); + // 监听设备返回的数据 + late StreamSubscription _replySubscription; + void _initReplySubscription() { + _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) async { + // 添加遥控开始 + if((reply is SenderAddRemoteControlWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) { + _replyAddRemoteControlBegin(reply); + } + + // 添加遥控确认 + if(reply is SenderAddRemoteControlConfirmationReply) { + _replyAddRemoteControlConfirmation(reply); + } + }); + } + + Future _replyAddRemoteControlBegin(Reply reply) async { + final int status = reply.data[2]; + + switch(status){ + case 0x00: + //成功 + final List remoteControlNumberList = reply.data.sublist(reply.data.length - 2); + final String remoteControlNumber = listChangInt(remoteControlNumberList).toString(); + // AppLog.log("添加卡号:$cardNumberList cardNumber:$cardNumber"); + state.remoteControlNumber.value = remoteControlNumber.toString(); + cancelBlueConnetctToastTimer(); + + state.ifAddState.value = true; + state.ifConnectScuess.value = true; + break; + case 0x06: + //无权限 + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + // var publicKey = await Storage.getStringList(saveBluePublicKey); + // List publicKeyDataList = changeStringListToIntList(publicKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + AppLog.log('添加卡token:$token'); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:'1', + userID:await Storage.getUid(), + remoteControlNo:0, + useCountLimit:0xffff, + operate:0, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.isAdministrator.value == '2' ? 1 : 0, + isForce:state.isCoerced.value == '1' ? 1 : 0, // 是否是胁迫 + isRound:state.selectType.value == '2' ? 1: 0, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: int.parse(state.startDate.value)~/1000, + endDate: int.parse(state.endDate.value)~/1000, + startTime:DateTool().dateToHNString(state.effectiveDateTime.value), + endTime:DateTool().dateToHNString(state.failureDateTime.value), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: token, + isBeforeAddUser: false + ); + break; + default: + //失败 + state.ifAddState.value = false; + break; + } + } + + Future _replyAddRemoteControlConfirmation(Reply reply) async { + final int status = reply.data[2]; + state.ifAddState.value = false; + switch(status){ + case 0x00: + //成功 + switch(reply.data[5]){ + case 0xff: + // 注册指纹失败 + showToast('退出添加'.tr); + Get.close(1); + break; + case 0xFE: + // 管理员已满 + showToast('管理员已满'.tr); + Get.close(1); + break; + case 0xFD: + // 用户已满 + showToast('用户已满'.tr); + Get.close(1); + break; + case 0xFC: + // 遥控已满 + showToast('锁上面添加卡已满'.tr); + Get.close(1); + break; + case 0xFB: + // 遥控已存在 + showToast('遥控已存在'.tr); + break; + default: + // 添加指纹中 + // 当前注册数 + _addRemoteControlData(); + break; + } + break; + default: + //失败 + break; + } + } + + // 添加卡片 + Future senderAddRemoteControl() async { + showBlueConnetctToastTimer(action: (){ + Get.close(1); + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected){ + cancelBlueConnetctToastTimer(); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID:'1', + userID:await Storage.getUid(), + remoteControlNo:0, + useCountLimit:0xffff, + operate:0, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin:state.isAdministrator.value == '2' ? 1 : 0, + isForce:state.isCoerced.value == '1' ? 1 : 0, // 是否是胁迫 + isRound:state.selectType.value == '2' ? 1: 0, // 是否是循环 + weekRound:DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: int.parse(state.startDate.value)~/1000, + endDate: int.parse(state.endDate.value)~/1000, + startTime:DateTool().dateToHNString(state.effectiveDateTime.value), + endTime:DateTool().dateToHNString(state.failureDateTime.value), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false + ); + } else if (deviceConnectionState == BluetoothConnectionState.disconnected){ + if(state.ifCurrentScreen.value == true){ + showBlueConnetctToast(); + } + cancelBlueConnetctToastTimer(); + Get.close(1); + } + }); + } + + // 取消添加指纹 + Future senderCancelAddRemoteControlCommand() async { + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected){ + final List? privateKey = await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = changeStringListToIntList(privateKey!); + + final List? signKey = await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + IoSenderManage.senderCancelAddRemoteControlCommand( + keyID:'1', + userID:await Storage.getUid(), + needAuthor:1, + signKey:signKeyDataList, + privateKey:getPrivateKeyList, + token: getTokenList, + ); + }else if (deviceConnectionState == BluetoothConnectionState.disconnected){ + if(state.ifCurrentScreen.value == true){ + showBlueConnetctToast(); + } + cancelBlueConnetctToastTimer(); + Get.close(1); + } + }); + } + + Future _addRemoteControlData() async { + final AddRemoteControlEntity entity = await ApiRepository.to.addRemoteControlData( + lockId: state.lockId.value.toString(), + remoteName: state.remoteControlName.value.toString(), + remoteNumber: state.remoteControlNumber.value.toString(), + remoteType: int.parse(state.remoteControlType.value), + startDate: int.parse(state.startDate.value), + endDate: int.parse(state.endDate.value), + addType: int.parse(state.addType.value), + weekDay: state.weekDay.value, + startTime: int.parse(state.effectiveDateTime.value), + endTime: int.parse(state.failureDateTime.value), + // remoteRight: state.isAdministrator.value == '2' ? 1 : 0, + remoteRight: 0, + ); + if (entity.errorCode!.codeIsSuccessful) { + updateRemoteUserNoLoadData(entity.data!.remoteId ?? 0); + } + } + + Future updateRemoteUserNoLoadData(int cardId) async{ + final LoginEntity entity = await ApiRepository.to.updateRemoteUserNoLoadData( + lockId: state.lockId.value, + remoteId: cardId, + remoteUserNo: state.remoteControlNumber.value, + ); + if(entity.errorCode!.codeIsSuccessful){ + showToast('添加成功'.tr, something:(){ + eventBus.fire(OtherTypeRefreshListEvent()); + Get.close(2); + }); + } + } + + @override + void onReady() { + super.onReady(); + + _initReplySubscription(); } @override void onInit() { + super.onInit(); + senderAddRemoteControl(); + } + + @override + void onClose() { + _replySubscription.cancel(); } } \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart old mode 100755 new mode 100644 index 5145000f..2f959d73 --- a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart +++ b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_page.dart @@ -1,22 +1,14 @@ + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; -import 'package:star_lock/tools/pickers/pickers.dart'; -import 'package:star_lock/tools/pickers/time_picker/model/date_mode.dart'; -import 'package:star_lock/tools/pickers/time_picker/model/pduration.dart'; -import '../../../../appRouters.dart'; import '../../../../app_settings/app_colors.dart'; -import '../../../../tools/CustomUnderlineTabIndicator.dart'; -import '../../../../tools/commonItem.dart'; -import '../../../../tools/dateTool.dart'; -import '../../../../tools/storage.dart'; -import '../../../../tools/submitBtn.dart'; +import '../../../../tools/appRouteObserver.dart'; import '../../../../tools/titleAppBar.dart'; -import '../../../../translations/trans_lib.dart'; import 'addRemoteControl_logic.dart'; +import 'addRemoteControl_state.dart'; class AddRemoteControlPage extends StatefulWidget { const AddRemoteControlPage({Key? key}) : super(key: key); @@ -25,472 +17,105 @@ class AddRemoteControlPage extends StatefulWidget { State createState() => _AddRemoteControlPageState(); } -class _AddRemoteControlPageState extends State - with SingleTickerProviderStateMixin { - final logic = Get.put(AddRemoteControlLoigc()); - final state = Get.find().state; - - @override - void initState() { - // TODO: implement initState - super.initState(); - - state.tabController = TabController( - vsync: this, - length: state.fromType.value == 1 - ? _itemTabs.length - : _fromCheckInTypeItemTabs.length, - initialIndex: 0); - state.tabController.addListener(() { - WidgetsBinding.instance.addPostFrameCallback((_) { - state.selectType.value = state.tabController.index.toString(); - }); - - if (state.tabController.animation!.value == state.tabController.index) { - FocusScope.of(context).requestFocus(FocusNode()); - } - }); - } +class _AddRemoteControlPageState extends State with RouteAware { + final AddRemoteControlLogic logic = Get.put(AddRemoteControlLogic()); + final AddRemoteControlState state = Get.find().state; @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.mainBackgroundColor, appBar: TitleAppBar( - barTitle: - "${TranslationLoader.lanKeys!.addTip!.tr}${TranslationLoader.lanKeys!.fingerprint!.tr}", - haveBack: true, - backgroundColor: AppColors.mainColor), - body: Column( - children: [ - _tabBar(), - _pageWidget(), + barTitle: '添加遥控', + haveBack: true, + backgroundColor: AppColors.mainColor, + ), + body: ListView( + children: [ + SizedBox(height: 180.h), + Center( + child: Image.asset( + 'images/main/icon_addRemoteControl_tip.png', + width: 300.w, + height: 300.w, + fit: BoxFit.cover, + ), + ), + SizedBox(height: 90.h), + CupertinoActivityIndicator( + radius: 25.h, + ), + SizedBox(height: 120.h), + Container( + width: 1.sw, + // height: 50.h, + padding: EdgeInsets.all(10.w), + margin: EdgeInsets.only( + left: 15.w, right: 15.w, top: 10.h, bottom: 10.h), + // color: AppColors.blackColor, + decoration: BoxDecoration( + color: AppColors.blackColor, + borderRadius: BorderRadius.circular(10.w), + ), + child: Center( + child: Obx(() => Text( + state.ifConnectScuess.value + ? '已连接到锁,请按遥控'.tr + : '尝试连接设备...'.tr, + style: TextStyle(color: Colors.white, fontSize: 24.sp)))), + ), ], ), ); } - Widget indexChangeWidget() { - switch (int.parse(state.selectType.value)) { - case 0: - { - // 永久 - // return sendElectronicKeySucceed(); - return Column( - children: [ - perpetualKeyWidget( - TranslationLoader.lanKeys!.name!.tr, - TranslationLoader.lanKeys!.pleaseEnter!.tr, - state.nameController), - keyBottomWidget() - ], - ); - } - case 1: - { - // 限时 - return Column( - children: [ - perpetualKeyWidget( - TranslationLoader.lanKeys!.name!.tr, - TranslationLoader.lanKeys!.pleaseEnter!.tr, - state.nameController), - keyTimeLimitWidget(), - SizedBox(height: 10.h), - keyBottomWidget() - ], - ); - } - case 2: - { - // 循环 - return SingleChildScrollView( - child: Column( - children: [ - perpetualKeyWidget( - TranslationLoader.lanKeys!.name!.tr, - TranslationLoader.lanKeys!.pleaseEnter!.tr, - state.nameController), - keyCyclicDate(), - SizedBox(height: 10.h), - keyBottomWidget() - ], - ), - ); - } - default: - return Container(); + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + /// 路由订阅 + AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(context)!); + } + + @override + void dispose() { + /// 取消路由订阅 + AppRouteObserver().routeObserver.unsubscribe(this); + super.dispose(); + } + + /// 从上级界面进入 当前界面即将出现 + @override + void didPush() { + super.didPush(); + state.ifCurrentScreen.value = true; + } + + /// 返回上一个界面 当前界面即将消失 + @override + void didPop() { + super.didPop(); + logic.cancelBlueConnetctToastTimer(); + state.ifCurrentScreen.value = false; + + if(state.ifAddState.value){ + logic.senderCancelAddRemoteControlCommand(); } } - // 密码命名输入框 - Widget perpetualKeyWidget( - String titleStr, String rightTitle, TextEditingController controller) { - return Column( - children: [ - CommonItem( - leftTitel: titleStr, - rightTitle: '', - isHaveRightWidget: true, - rightWidget: getTFWidget(rightTitle)), - Container(height: 10.h), - ], - ); + /// 从下级返回 当前界面即将出现 + @override + void didPopNext() { + super.didPopNext(); + state.ifCurrentScreen.value = true; } - // 限时顶部选择日期 - Widget keyTimeLimitWidget() { - return Column( - children: [ - Obx(() => CommonItem( - leftTitel: TranslationLoader.lanKeys!.effectiveTime!.tr, - rightTitle: state.timeLimitBeginTime.value, - isHaveLine: true, - isHaveDirection: true, - action: () async { - PDuration selectDate = PDuration.parse( - DateTime.parse(state.timeLimitBeginTime.value)); - Pickers.showDatePicker(context, - selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (p) { - state.timeLimitBeginTime.value = - DateTool().getYMDHNDateString(p, 1); - }); - })), - Obx(() => CommonItem( - leftTitel: TranslationLoader.lanKeys!.failureTime!.tr, - rightTitle: state.timeLimitEndTime.value, - isHaveDirection: true, - action: () { - PDuration selectDate = PDuration.parse( - DateTime.tryParse(state.timeLimitEndTime.value)); - Pickers.showDatePicker(context, - selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (p) { - state.timeLimitEndTime.value = - DateTool().getYMDHNDateString(p, 1); - }); - })), - Container(height: 10.h), - ], - ); - } + /// 进入下级界面 当前界面即将消失 + @override + void didPushNext() { + super.didPushNext(); + logic.cancelBlueConnetctToastTimer(); - // 循环顶部选择日期 - Widget keyCyclicDate() { - return Column( - children: [ - Obx(() => CommonItem( - leftTitel: TranslationLoader.lanKeys!.periodValidity!.tr, - rightTitle: - '${state.cycleBeginTime.value}\n${state.cycleEndTime.value}', - isHaveDirection: true, - isHaveLine: true, - action: () async { - var result = - await Get.toNamed(Routers.seletKeyCyclicDatePage, arguments: { - 'validityValue': state.weekdaysList.value, - 'starDate': state.cycleBeginTime.value, - 'endDate': state.cycleEndTime.value, - 'starTime': state.effectiveDateTime.value, - 'endTime': state.failureDateTime.value - }); - if (result != null && result.isNotEmpty) { - state.weekdaysList.value = result['validityValue']; - state.cycleBeginTime.value = result['starDate']; - state.cycleEndTime.value = result['endDate']; - state.effectiveDateTime.value = result['starTime']; - state.failureDateTime.value = result['endTime']; - } - })), - Obx(() => Visibility( - visible: state.weekdaysList.isNotEmpty, - child: CommonItem( - leftTitel: '有效日'.tr, - rightTitle: logic.weekDayStr.join(',').toString(), - isHaveDirection: true, - isHaveLine: true, - action: () async { - var result = await Get.toNamed(Routers.seletKeyCyclicDatePage, - arguments: { - 'validityValue': state.weekdaysList.value, - 'starDate': state.cycleBeginTime.value, - 'endDate': state.cycleEndTime.value, - 'starTime': state.effectiveDateTime.value, - 'endTime': state.failureDateTime.value - }); - if (result != null && result.isNotEmpty) { - state.weekdaysList.value = result['validityValue']; - state.cycleBeginTime.value = result['starDate']; - state.cycleEndTime.value = result['endDate']; - state.effectiveDateTime.value = result['starTime']; - state.failureDateTime.value = result['endTime']; - } - }))), - Obx(() => Visibility( - visible: state.effectiveDateTime.value.isNotEmpty, - child: CommonItem( - leftTitel: '有效时间'.tr, - rightTitle: - '${state.effectiveDateTime.value}-${state.failureDateTime.value}', - isHaveDirection: true, - action: () async { - var result = await Get.toNamed(Routers.seletKeyCyclicDatePage, - arguments: { - 'validityValue': state.weekdaysList.value, - 'starDate': state.cycleBeginTime.value, - 'endDate': state.cycleEndTime.value, - 'starTime': state.effectiveDateTime.value, - 'endTime': state.failureDateTime.value - }); - if (result != null && result.isNotEmpty) { - state.weekdaysList.value = result['validityValue']; - state.cycleBeginTime.value = result['starDate']; - state.cycleEndTime.value = result['endDate']; - state.effectiveDateTime.value = result['starTime']; - state.failureDateTime.value = result['endTime']; - } - }))), - ], - ); - } - - Widget keyBottomWidget() { - return Column( - children: [ - // CommonItem( - // leftTitel: TranslationLoader.lanKeys!.remoteControl!.tr, - // rightTitle: "", - // isTipsImg: false, - // isHaveRightWidget: true, - // rightWidget: SizedBox( - // width: 60.w, height: 50.h, child: _isStressFingerprint())), - SizedBox(height: 30.h), - SubmitBtn( - btnName: TranslationLoader.lanKeys!.next!.tr, - onClick: () async { - var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); - if (isDemoMode == false) { - if (state.nameController.text.isEmpty) { - logic.showToast('请输入姓名'); - return; - } - logic.showToast('请确保在设备附近'); - // logic.addFingerprintsData(); - } else { - // Get.toNamed(Routers.selectLockTypePage); - logic.showToast('演示模式'); - } - }), - ], - ); - } - - // 发送电子钥匙成功 - Widget sendElectronicKeySucceed() { - return Column( - children: [ - Container( - height: 300.h, - width: 1.sw, - color: Colors.white, - child: Column( - children: [ - SizedBox( - height: 30.h, - ), - Image.asset( - 'images/main/icon_main_addLock.png', - width: 150.w, - height: 150.w, - color: AppColors.mainColor, - ), - SizedBox( - height: 20.h, - ), - Text( - '操作成功,密码为', - style: TextStyle( - fontSize: 32.sp, - color: Colors.black, - fontWeight: FontWeight.w500), - ), - SizedBox( - height: 10.h, - ), - Text( - '62689876', - style: TextStyle( - fontSize: 60.sp, - color: Colors.black, - fontWeight: FontWeight.w500), - ), - ], - ), - ), - SizedBox( - height: 20.h, - ), - SubmitBtn( - btnName: '完成'.tr, - fontSize: 28.sp, - borderRadius: 20.w, - margin: EdgeInsets.only(left: 30.w, right: 30.w, top: 30.w), - padding: EdgeInsets.only(top: 25.w, bottom: 25.w), - onClick: () {}), - SubmitBtn( - btnName: '分享', - fontSize: 28.sp, - borderRadius: 20.w, - margin: EdgeInsets.only(left: 30.w, right: 30.w, top: 30.w), - padding: EdgeInsets.only(top: 25.w, bottom: 25.w), - onClick: () {}), - SubmitBtn( - btnName: '标记为:已入住', - fontSize: 28.sp, - borderRadius: 20.w, - margin: EdgeInsets.only(left: 30.w, right: 30.w, top: 30.w), - padding: EdgeInsets.only(top: 25.w, bottom: 25.w), - onClick: () {}), - ], - ); - } - - // 接受者信息输入框 - Widget getTFWidget(String tfStr) { - return Container( - // color: Colors.red, - height: 65.h, - width: 300.w, - padding: EdgeInsets.only(top: 5.h), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: TextField( - //输入框一行 - maxLines: 1, - inputFormatters: [ - FilteringTextInputFormatter.deny('\n'), - LengthLimitingTextInputFormatter(18), - ], - style: TextStyle( - fontSize: 22.sp, color: AppColors.darkGrayTextColor), - controller: state.nameController, - autofocus: false, - textAlign: TextAlign.end, - decoration: InputDecoration( - //输入里面输入文字内边距设置 - // contentPadding: const EdgeInsets.only(top: 12.0, bottom: 8.0), - hintText: tfStr, - hintStyle: TextStyle(fontSize: 22.sp), - focusedBorder: const OutlineInputBorder( - borderSide: - BorderSide(width: 0, color: Colors.transparent)), - disabledBorder: const OutlineInputBorder( - borderSide: - BorderSide(width: 0, color: Colors.transparent)), - enabledBorder: const OutlineInputBorder( - borderSide: - BorderSide(width: 0, color: Colors.transparent)), - border: const OutlineInputBorder( - borderSide: - BorderSide(width: 0, color: Colors.transparent)), - contentPadding: const EdgeInsets.symmetric(vertical: 0), - ), - ), - ), - SizedBox( - width: 10.w, - ), - ], - ), - ); - } - - //isStressFingerprint false:不是胁迫指纹 ture:胁迫指纹 - CupertinoSwitch _isStressFingerprint() { - return CupertinoSwitch( - activeColor: CupertinoColors.activeBlue, - trackColor: CupertinoColors.systemGrey5, - thumbColor: CupertinoColors.white, - value: true, - onChanged: (value) { - setState(() { - // state.isStressFingerprint.value = value; - }); - }, - ); - } - - final List _itemTabs = [ - ItemView(title: TranslationLoader.lanKeys!.permanent!.tr, selectType: '0'), - ItemView(title: TranslationLoader.lanKeys!.timeLimit!.tr, selectType: '1'), - ItemView( - title: TranslationLoader.lanKeys!.circulation!.tr, selectType: '2'), - ]; - - final List _fromCheckInTypeItemTabs = [ - ItemView(title: TranslationLoader.lanKeys!.permanent!.tr, selectType: '0'), - ItemView(title: TranslationLoader.lanKeys!.timeLimit!.tr, selectType: '1'), - ]; - - TabBar _tabBar() { - return TabBar( - controller: state.tabController, - onTap: (index) { - FocusScope.of(context).requestFocus(FocusNode()); - }, - tabs: state.fromType.value == 1 - ? _itemTabs.map((ItemView item) => _tab(item)).toList() - : _fromCheckInTypeItemTabs - .map((ItemView item) => _tab(item)) - .toList(), - isScrollable: true, - indicatorColor: Colors.red, - unselectedLabelColor: Colors.black, - unselectedLabelStyle: TextStyle( - color: AppColors.mainColor, - fontSize: 24.sp, - ), - automaticIndicatorColorAdjustment: true, - labelColor: AppColors.mainColor, - labelStyle: TextStyle( - color: AppColors.mainColor, - fontSize: 24.sp, - fontWeight: FontWeight.w600), - indicator: CustomUnderlineTabIndicator( - borderSide: BorderSide(color: AppColors.mainColor, width: 4.w), - strokeCap: StrokeCap.round, - width: 30.w), - ); - } - - Tab _tab(ItemView item) { - return Tab( - child: SizedBox( - width: 1.sw / 5, - child: Text(item.title, textAlign: TextAlign.center))); - } - - Widget _pageWidget() { - return Expanded( - child: TabBarView( - controller: state.tabController, - children: state.fromType.value == 1 - ? _itemTabs - .map((ItemView item) => Obx(() => indexChangeWidget())) - .toList() - : _fromCheckInTypeItemTabs - .map((ItemView item) => Obx(() => indexChangeWidget())) - .toList(), - ), - ); + state.ifCurrentScreen.value = false; } } - -class ItemView { - const ItemView({required this.title, required this.selectType}); - - final String title; - final String selectType; -} diff --git a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_state.dart b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_state.dart old mode 100755 new mode 100644 index 6f7ba940..8d46925f --- a/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_state.dart +++ b/lib/main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_state.dart @@ -1,40 +1,50 @@ -import 'package:flutter/material.dart'; + import 'package:get/get.dart'; -import '../../../../tools/dateTool.dart'; +import '../../../../app_settings/app_settings.dart'; +import '../../../../tools/commonDataManage.dart'; class AddRemoteControlState{ - final lockId = 0.obs; - final selectType = "0".obs;// 0永久 1显示 2循环 - final fromType = 1.obs; // // 1从添加钥匙列表进入 2从考勤添加员工入口进入 - final isStressFingerprint = false.obs; - final isAdministrator = false.obs;// 是否是管理员 - - var timeLimitBeginTime = DateTool().dateToYMDHNString(DateTime.now().millisecondsSinceEpoch.toString()).obs;// 限时开始时间 - var timeLimitEndTime = DateTool().dateToYMDHNString(DateTime.now().millisecondsSinceEpoch.toString()).obs;// 限时结束时间 - var cycleBeginTime = "".obs;// 循环开始时间 - var cycleEndTime = "".obs;// 循环结束时间 - var effectiveDateTime = "".obs;// 生效时间 - var failureDateTime = "".obs;// 失效时间 - var weekdaysList = [].obs; - var fromTypeTwoStaffName = "".obs; // 从添加员工进入 传入员工名字 - - final TextEditingController nameController = TextEditingController(); - late TabController tabController; AddRemoteControlState() { - Map map = Get.arguments; - if(map["lockId"]!=null){ - lockId.value = map["lockId"]; - } - - if(map["fromType"]!=null){ - fromType.value = map["fromType"]; - // 1从添加钥匙列表进入 2从考勤添加员工入口进入 - if(fromType.value == 2){ - fromTypeTwoStaffName.value = map["fromTypeTwoStaffName"]; // 从添加员工进入 传入员工名字 - } + final Map map = Get.arguments; + lockId.value = map['lockId']; + addType.value = map['addType']; + remoteControlName.value = map['remoteControlName']; + remoteControlNumber.value = map['remoteControlNumber']; + remoteControlType.value = map['remoteControlType']; + isCoerced.value = map['isCoerced']; + startDate.value = map['startDate']; + weekDay.value = map['weekDay']; + // AppLog.log('1111-isCoerced.value:${isCoerced.value}'); + isAdministrator.value = map['isAdministrator']; + effectiveDateTime.value = map['effectiveTime']; + failureDateTime.value = map['failureTime']; + selectType.value = map['selectType']; + //循环类型下,结束时间加一天 + if (selectType.value == '2') { + endDate.value = + "${int.parse(map["endDate"]) + CommonDataManage().dayLatestTime}"; + } else { + endDate.value = map['endDate']; } } + RxBool ifConnectScuess = false.obs; + RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 + RxBool ifAddState = false.obs;// 是否是添加状态,如果是添加状态,返回上级界面发送取消添加指令 + RxInt addFingerprintProcessNumber = 0.obs; + final RxInt lockId = 0.obs; + final RxString endDate = ''.obs; + final RxString addType = ''.obs; + final RxString remoteControlName = ''.obs; + final RxString remoteControlNumber = ''.obs; + final RxString remoteControlType = ''.obs; + final RxString isCoerced = ''.obs; + final RxString isAdministrator = ''.obs; + final RxString startDate = ''.obs; + final RxList weekDay = [].obs; + final RxString effectiveDateTime = ''.obs; // 生效时间 + final RxString failureDateTime = ''.obs; // 失效时间 + final RxString selectType = '0'.obs; } \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_logic.dart b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_logic.dart new file mode 100755 index 00000000..28f89d64 --- /dev/null +++ b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_logic.dart @@ -0,0 +1,98 @@ + +import 'package:get/get.dart'; +import 'package:star_lock/tools/baseGetXController.dart'; +import 'package:star_lock/tools/pickers/time_picker/time_utils.dart'; + +import '../../../../appRouters.dart'; +import '../../../../login/login/entity/LoginEntity.dart'; +import '../../../../network/api_repository.dart'; +import '../../../../tools/dateTool.dart'; +import 'addRemoteControlType_state.dart'; + +class AddRemoteControlTypeLoigc extends BaseGetXController{ + AddRemoteControlTypeState state = AddRemoteControlTypeState(); + + List get weekDayStr { + return state.weekdaysList.map((e) => TimeUtils.translateWeekday(e)).toList(); + } + + // 添加卡数据 + Future addRemoteControlData() async { + int carType = 0; // 永久:1;限时2,单次3,循环:4 + String startDate = ''; + String endDate = ''; + String startTime = ''; + String endTime = ''; + if (state.selectType.value == '0') { + carType = 1; + startDate = '0'; + endDate = '0'; + startTime = '0'; + endTime = '0'; + } else if (state.selectType.value == '1') { + carType = 2; + startDate = DateTool().dateToTimestamp(state.timeLimitBeginTime.value, 1).toString(); + endDate = DateTool().dateToTimestamp(state.timeLimitEndTime.value, 1).toString(); + startTime = '0'; + endTime = '0'; + + if (startDate.isEmpty) { + showToast('请选择开始时间'.tr); + return; + } + if (endDate.isEmpty) { + showToast('请选择结束时间'.tr); + return; + } + + // if(DateTime.now().millisecondsSinceEpoch > int.parse(state.beginTimeTimestamp.value)){ + // Toast.show(msg: "生效时间要大于当前时间"); + // return; + // } + + if (int.parse(startDate) >= int.parse(endDate)) { + showToast('失效时间要大于生效时间'.tr); + return; + } + // AppLog.log("state.timeLimitBeginTime.value:${state.timeLimitBeginTime.value} startDate:$startDate"); + // AppLog.log("state.timeLimitEndTime.value:${state.timeLimitEndTime.value} endDate:$endDate"); + } else if (state.selectType.value == '2') { + if (state.cycleBeginTime.value.isEmpty) { + showToast('请选择有效期'.tr); + return; + } + startDate = DateTool().dateToTimestamp(state.cycleBeginTime.value, 1).toString(); + endDate = DateTool().dateToTimestamp(state.cycleEndTime.value, 1).toString(); + startTime = DateTool().dateToTimestamp(state.effectiveDateTime.value, 0).toString(); + endTime = DateTool().dateToTimestamp(state.failureDateTime.value, 0).toString(); + carType = 4; + } + + Get.toNamed(Routers.addRemoteControlPage, arguments: { + 'lockId': state.lockId.value, + 'addType': '1', + 'remoteControlName': state.nameController.text, + 'remoteControlNumber': '123456', + 'remoteControlType': carType.toString(), + 'isCoerced': state.isStressFingerprint.value == false ? '2' : '1', + 'startDate': startDate, + 'weekDay': state.weekdaysList.value, + 'isAdministrator': state.isAdministrator.value == false ? '1' : '2', // 1:不是管理员 2:是管理员 + 'effectiveTime': startTime, + 'failureTime': endTime, + 'selectType': state.selectType.value, + 'endDate': endDate, + }); + } + + // 校验卡名字是否重复 + Future checkRemoteControlNameDuplicatedData(String remoteName) async{ + final LoginEntity entity = await ApiRepository.to.checkRemoteControlNameDuplicatedData( + lockId: state.lockId.value.toString(), + remoteName: remoteName, + ); + if(entity.errorCode!.codeIsSuccessful){ + addRemoteControlData(); + } + } +} \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_page.dart b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_page.dart new file mode 100755 index 00000000..30332b5c --- /dev/null +++ b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_page.dart @@ -0,0 +1,444 @@ + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:star_lock/tools/pickers/pickers.dart'; +import 'package:star_lock/tools/pickers/time_picker/model/date_mode.dart'; +import 'package:star_lock/tools/pickers/time_picker/model/pduration.dart'; + +import '../../../../appRouters.dart'; +import '../../../../app_settings/app_colors.dart'; +import '../../../../tools/CustomUnderlineTabIndicator.dart'; +import '../../../../tools/commonDataManage.dart'; +import '../../../../tools/commonItem.dart'; +import '../../../../tools/dateTool.dart'; +import '../../../../tools/storage.dart'; +import '../../../../tools/submitBtn.dart'; +import '../../../../tools/titleAppBar.dart'; +import '../../../../translations/trans_lib.dart'; +import 'addRemoteControlType_logic.dart'; +import 'addRemoteControlType_state.dart'; + +class AddRemoteControlTypePage extends StatefulWidget { + const AddRemoteControlTypePage({Key? key}) : super(key: key); + + @override + State createState() => _AddRemoteControlTypePageState(); +} + +class _AddRemoteControlTypePageState extends State with SingleTickerProviderStateMixin { + final AddRemoteControlTypeLoigc logic = Get.put(AddRemoteControlTypeLoigc()); + final AddRemoteControlTypeState state = Get.find().state; + + @override + void initState() { + super.initState(); + + state.tabController = TabController( + vsync: this, + length: _itemTabs.length, + initialIndex: 0); + state.tabController.addListener(() { + WidgetsBinding.instance.addPostFrameCallback((_) { + state.selectType.value = state.tabController.index.toString(); + }); + + if (state.tabController.animation!.value == state.tabController.index) { + FocusScope.of(context).requestFocus(FocusNode()); + } + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.mainBackgroundColor, + appBar: TitleAppBar( + barTitle: + '添加遥控', + haveBack: true, + backgroundColor: AppColors.mainColor), + body: Column( + children: [ + _tabBar(), + _pageWidget(), + ], + ), + ); + } + + Widget indexChangeWidget() { + switch (int.parse(state.selectType.value)) { + case 0: + { + // 永久 + return SingleChildScrollView( + child: Column( + children: [ + perpetualKeyWidget( + TranslationLoader.lanKeys!.name!.tr, + TranslationLoader.lanKeys!.pleaseEnter!.tr, + state.nameController), + keyBottomWidget() + ], + ), + ); + } + case 1: + { + // 限时 + return SingleChildScrollView( + child: Column( + children: [ + perpetualKeyWidget( + TranslationLoader.lanKeys!.name!.tr, + TranslationLoader.lanKeys!.pleaseEnter!.tr, + state.nameController), + keyTimeLimitWidget(), + // SizedBox(height: 10.h), + keyBottomWidget() + ], + ), + ); + } + case 2: + { + // 循环 + return SingleChildScrollView( + child: Column( + children: [ + perpetualKeyWidget( + TranslationLoader.lanKeys!.name!.tr, + TranslationLoader.lanKeys!.pleaseEnter!.tr, + state.nameController), + keyCyclicDate(), + SizedBox(height: 10.h), + keyBottomWidget() + ], + ), + ); + } + default: + return Container(); + } + } + + // 密码命名输入框 + Widget perpetualKeyWidget( + String titleStr, String rightTitle, TextEditingController controller) { + return Column( + children: [ + CommonItem( + leftTitel: titleStr, + rightTitle: '', + isHaveRightWidget: true, + rightWidget: getTFWidget(rightTitle)), + Container(height: 10.h), + ], + ); + } + + // 限时顶部选择日期 + Widget keyTimeLimitWidget() { + return Column( + children: [ + Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.effectiveTime!.tr, + rightTitle: state.timeLimitBeginTime.value, + isHaveLine: true, + isHaveDirection: true, + action: () async { + final PDuration selectDate = PDuration.parse( + DateTime.parse(state.timeLimitBeginTime.value)); + Pickers.showDatePicker(context, + selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (PDuration p) { + state.timeLimitBeginTime.value = + DateTool().getYMDHNDateString(p, 1); + }); + })), + Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.failureTime!.tr, + rightTitle: state.timeLimitEndTime.value, + isHaveDirection: true, + action: () { + final PDuration selectDate = + PDuration.parse(DateTime.tryParse(state.timeLimitEndTime.value)); + Pickers.showDatePicker(context, + selectDate: selectDate, mode: DateMode.YMDHM, onConfirm: (PDuration p) { + state.timeLimitEndTime.value = + DateTool().getYMDHNDateString(p, 1); + }); + })), + Container(height: 10.h), + ], + ); + } + + // 循环顶部选择日期 + Widget keyCyclicDate() { + return Column( + children: [ + Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.periodValidity!.tr, + rightTitle: + '${state.cycleBeginTime.value}\n${state.cycleEndTime.value}', + isHaveDirection: true, + isHaveLine: true, + action: () async { + final result = + await Get.toNamed(Routers.seletKeyCyclicDatePage, arguments: { + 'validityValue': state.weekdaysList.value, + 'starDate': state.cycleBeginTime.value, + 'endDate': state.cycleEndTime.value, + 'starTime': state.effectiveDateTime.value, + 'endTime': state.failureDateTime.value + }); + if (result != null && result.isNotEmpty) { + state.weekdaysList.value = result['validityValue']; + state.cycleBeginTime.value = result['starDate']; + state.cycleEndTime.value = result['endDate']; + state.effectiveDateTime.value = result['starTime']; + state.failureDateTime.value = result['endTime']; + } + })), + Obx(() => Visibility( + visible: state.weekdaysList.isNotEmpty, + child: CommonItem( + leftTitel: '有效日'.tr, + rightTitle: logic.weekDayStr.join(',').toString(), + isHaveDirection: true, + isHaveLine: true, + action: () async { + final result = await Get.toNamed(Routers.seletKeyCyclicDatePage, + arguments: { + 'validityValue': state.weekdaysList.value, + 'starDate': state.cycleBeginTime.value, + 'endDate': state.cycleEndTime.value, + 'starTime': state.effectiveDateTime.value, + 'endTime': state.failureDateTime.value + }); + if (result != null && result.isNotEmpty) { + state.weekdaysList.value = result['validityValue']; + state.cycleBeginTime.value = result['starDate']; + state.cycleEndTime.value = result['endDate']; + state.effectiveDateTime.value = result['starTime']; + state.failureDateTime.value = result['endTime']; + } + }))), + Obx(() => Visibility( + visible: state.effectiveDateTime.value.isNotEmpty, + child: CommonItem( + leftTitel: '有效时间'.tr, + rightTitle: '${state.effectiveDateTime.value}-${state.failureDateTime.value}', + isHaveDirection: true, + action: () async { + final result = await Get.toNamed(Routers.seletKeyCyclicDatePage, + arguments: { + 'validityValue': state.weekdaysList.value, + 'starDate': state.cycleBeginTime.value, + 'endDate': state.cycleEndTime.value, + 'starTime': state.effectiveDateTime.value, + 'endTime': state.failureDateTime.value + }); + if (result != null && result.isNotEmpty) { + state.weekdaysList.value = result['validityValue']; + state.cycleBeginTime.value = result['starDate']; + state.cycleEndTime.value = result['endDate']; + state.effectiveDateTime.value = result['starTime']; + state.failureDateTime.value = result['endTime']; + } + }))), + ], + ); + } + + Widget keyBottomWidget() { + return Column( + children: [ + // Obx(() => Visibility( + // visible: CommonDataManage().currentKeyInfo.isLockOwner == 1, + // child: CommonItem( + // leftTitel: '是否为管理员'.tr, + // rightTitle: '', + // isTipsImg: false, + // isHaveRightWidget: true, + // rightWidget: + // SizedBox(width: 60.w, height: 50.h, child: _isAdmin())))), + // SizedBox(height: 10.h), + // Obx(() => CommonItem( + // leftTitel: TranslationLoader.lanKeys!.stressCard!.tr, + // rightTitle: '', + // isTipsImg: false, + // isHaveRightWidget: true, + // rightWidget: SizedBox( + // width: 60.w, height: 50.h, child: _isStressFingerprint()))), + // Container( + // color: Colors.white, + // padding: EdgeInsets.only(left: 20.w, right: 20.w, bottom: 20.h), + // child: Text( + // "${"当被胁迫要求强行开锁时,使用胁迫卡会触发报警,报警信息会推送给管理员,该功能需要锁联网。".tr}\n${"请不要将胁迫卡用于日常开锁".tr}", + // style: TextStyle( + // fontSize: 22.sp, color: AppColors.darkGrayTextColor), + // )), + SizedBox(height: 30.h), + SubmitBtn( + btnName: TranslationLoader.lanKeys!.next!.tr, + onClick: () async { + final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + if (isDemoMode == false) { + if (state.nameController.text.isEmpty) { + logic.showToast('请输入姓名'.tr); + return; + } + + logic.checkRemoteControlNameDuplicatedData(state.nameController.text); + } else { + // Get.toNamed(Routers.selectLockTypePage); + logic.showToast('演示模式'.tr); + } + }), + ], + ); + } + + // 接受者信息输入框 + Widget getTFWidget(String tfStr) { + return Container( + // color: Colors.red, + height: 65.h, + width: 300.w, + padding: EdgeInsets.only(top: 5.h), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: TextField( + //输入框一行 + maxLines: 1, + inputFormatters: [ + FilteringTextInputFormatter.deny('\n'), + LengthLimitingTextInputFormatter(50), + ], + style: TextStyle( + fontSize: 22.sp, color: AppColors.darkGrayTextColor), + controller: state.nameController, + autofocus: false, + textAlign: TextAlign.end, + decoration: InputDecoration( + //输入里面输入文字内边距设置 + // contentPadding: const EdgeInsets.only(top: 12.0, bottom: 8.0), + hintText: tfStr, + hintStyle: TextStyle(fontSize: 22.sp), + focusedBorder: const OutlineInputBorder( + borderSide: + BorderSide(width: 0, color: Colors.transparent)), + disabledBorder: const OutlineInputBorder( + borderSide: + BorderSide(width: 0, color: Colors.transparent)), + enabledBorder: const OutlineInputBorder( + borderSide: + BorderSide(width: 0, color: Colors.transparent)), + border: const OutlineInputBorder( + borderSide: + BorderSide(width: 0, color: Colors.transparent)), + contentPadding: const EdgeInsets.symmetric(vertical: 0), + ), + ), + ), + SizedBox( + width: 10.w, + ), + ], + ), + ); + } + + //isStressFingerprint false:不是胁迫指纹 ture:胁迫指纹 + CupertinoSwitch _isStressFingerprint() { + return CupertinoSwitch( + activeColor: CupertinoColors.activeBlue, + trackColor: CupertinoColors.systemGrey5, + thumbColor: CupertinoColors.white, + value: state.isStressFingerprint.value, + onChanged: (bool value) { + state.isStressFingerprint.value = value; + }, + ); + } + + // 是否为管理员 + CupertinoSwitch _isAdmin() { + return CupertinoSwitch( + activeColor: CupertinoColors.activeBlue, + trackColor: CupertinoColors.systemGrey5, + thumbColor: CupertinoColors.white, + value: state.isAdministrator.value, + onChanged: (bool value) { + state.isAdministrator.value = value; + }, + ); + } + + final List _itemTabs = [ + ItemView(title: TranslationLoader.lanKeys!.permanent!.tr, selectType: '0'), + ItemView(title: TranslationLoader.lanKeys!.timeLimit!.tr, selectType: '1'), + ItemView( + title: TranslationLoader.lanKeys!.circulation!.tr, selectType: '2'), + ]; + + + TabBar _tabBar() { + return TabBar( + controller: state.tabController, + onTap: (int index) { + FocusScope.of(context).requestFocus(FocusNode()); + }, + tabs: _itemTabs.map(_tab).toList(), + isScrollable: true, + indicatorColor: Colors.red, + unselectedLabelColor: Colors.black, + unselectedLabelStyle: TextStyle( + color: AppColors.mainColor, + fontSize: 24.sp, + ), + automaticIndicatorColorAdjustment: true, + labelColor: AppColors.mainColor, + labelStyle: TextStyle( + color: AppColors.mainColor, + fontSize: 24.sp, + fontWeight: FontWeight.w600), + indicator: CustomUnderlineTabIndicator( + borderSide: BorderSide(color: AppColors.mainColor, width: 4.w), + strokeCap: StrokeCap.round, + width: 30.w), + ); + } + + Tab _tab(ItemView item) { + return Tab( + child: SizedBox( + width: 1.sw / 5, + child: Text(item.title, textAlign: TextAlign.center))); + } + + Widget _pageWidget() { + return Expanded( + child: TabBarView( + controller: state.tabController, + children: _itemTabs + .map((ItemView item) => Obx(indexChangeWidget)) + .toList(), + ), + ); + } +} + +class ItemView { + const ItemView({required this.title, required this.selectType}); + + final String title; + final String selectType; +} diff --git a/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_state.dart b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_state.dart new file mode 100755 index 00000000..90cbbf5d --- /dev/null +++ b/lib/main/lockDetail/remoteControl/addRemoteControlType/addRemoteControlType_state.dart @@ -0,0 +1,32 @@ + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../../tools/dateTool.dart'; + +class AddRemoteControlTypeState{ + AddRemoteControlTypeState() { + Map map = Get.arguments; + if(map['lockId']!=null){ + lockId.value = map['lockId']; + } + } + + final RxInt lockId = 0.obs; + final RxString selectType = '0'.obs;// 0永久 1显示 2循环 + final RxBool isStressFingerprint = false.obs; + final RxBool isAdministrator = false.obs;// 是否是管理员 + + RxString timeLimitBeginTime = DateTool().dateToYMDHNString(DateTime.now().millisecondsSinceEpoch.toString()).obs;// 限时开始时间 + RxString timeLimitEndTime = DateTool().dateToYMDHNString(DateTime.now().millisecondsSinceEpoch.toString()).obs;// 限时结束时间 + RxString cycleBeginTime = ''.obs;// 循环开始时间 + RxString cycleEndTime = ''.obs;// 循环结束时间 + RxString effectiveDateTime = ''.obs;// 生效时间 + RxString failureDateTime = ''.obs;// 失效时间 + RxList weekdaysList = [].obs; + RxString fromTypeTwoStaffName = ''.obs; // 从添加员工进入 传入员工名字 + + final TextEditingController nameController = TextEditingController(); + late TabController tabController; + +} \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_logic.dart b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_logic.dart new file mode 100644 index 00000000..d40339c5 --- /dev/null +++ b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_logic.dart @@ -0,0 +1,226 @@ + +import 'dart:async'; + +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:get/get.dart'; +import 'package:star_lock/tools/baseGetXController.dart'; + +import '../../../../blue/blue_manage.dart'; +import '../../../../blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; +import '../../../../blue/io_reply.dart'; +import '../../../../blue/io_tool/io_tool.dart'; +import '../../../../blue/io_tool/manager_event_bus.dart'; +import '../../../../blue/sender_manage.dart'; +import '../../../../login/login/entity/LoginEntity.dart'; +import '../../../../network/api_repository.dart'; +import '../../../../tools/dateTool.dart'; +import '../../../../tools/eventBusEventManage.dart'; +import '../../../../tools/pickers/time_picker/time_utils.dart'; +import '../../../../tools/storage.dart'; +import 'remoteControlDetail_state.dart'; + +class RemoteControlDetailLogic extends BaseGetXController { + RemoteControlDetailState state = RemoteControlDetailState(); + + List get weekDayStr { + return state.weekDay.map((e) => TimeUtils.translateWeekday(e)).toList(); + } + + // 监听设备返回的数据 + late StreamSubscription _replySubscription; + + void _initReplySubscription() { + _replySubscription = + EventBusManager().eventBus!.on().listen((Reply reply) async { + // 添加卡片开始(重置锁里面所有卡) + if ((reply is SenderAddRemoteControlWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) { + _replyAddRemoteControlBegin(reply); + } + }); + } + + // 添加卡片开始(此处用作删除卡片) + Future _replyAddRemoteControlBegin(Reply reply) async { + final int status = reply.data[2]; + + switch (status) { + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + dismissEasyLoading(); + if (state.isDeletRemoteControl.value == true) { + deletRemoteControlData(); + } else { + editRemoteControlData(); + } + break; + case 0x06: + //无权限 + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); + + final List? signKey = + await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID: state.keyId.value.toString(), + userID: await Storage.getUid(), + remoteControlNo: int.parse(state.typeNumber.value), + useCountLimit: 0xffff, + operate: state.isDeletRemoteControl.value ? 2 : 1, + // 0:注册 1:修改 2:删除 3:删除全部 + // isAdmin: state.isAdministrator.value == true ? 1 : 0, + // isForce: state.isStressCard.value == true ? 1 : 0, // 是否是胁迫 + isAdmin: 0, + isForce: 0, // 是否是胁迫 + isRound: state.keyType.value == 4 ? 1 : 0, // 是否是循环 + weekRound: DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: int.parse(state.startDate.value) ~/ 1000, + endDate: int.parse(state.endDate.value) ~/ 1000, + startTime: DateTool().dateToHNString(state.starTime.value), + endTime: DateTool().dateToHNString(state.endTime.value), + needAuthor: 1, + signKey: signKeyDataList, + privateKey: getPrivateKeyList, + token: token, + isBeforeAddUser: false); + break; + default: + //失败 + break; + } + } + + // 添加卡片 + Future senderAddICCard() async { + if (state.sureBtnState.value == 1) { + return; + } + state.sureBtnState.value = 1; + + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + state.sureBtnState.value = 0; + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected) { + final List? signKey = + await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID: state.keyId.value.toString(), + userID: await Storage.getUid(), + remoteControlNo: int.parse(state.typeNumber.value), + useCountLimit: 0xffff, + operate: state.isDeletRemoteControl.value ? 2 : 1, + // 0:注册 1:修改 2:删除 3:删除全部 + // isAdmin: state.isAdministrator.value == true ? 1 : 0, + // isForce: state.isStressCard.value == true ? 1 : 0, // 是否是胁迫 + isAdmin: 0, + isForce: 0, // 是否是胁迫 + isRound: state.keyType.value == 4 ? 1 : 0, // 是否是循环 + weekRound: DateTool().accordingTheCycleIntoTheCorrespondingNumber(state.weekDay.value), // 周循环 + startDate: int.parse(state.startDate.value) ~/ 1000, + endDate: int.parse(state.endDate.value) ~/ 1000, + startTime: DateTool().dateToHNString(state.starTime.value), + endTime: DateTool().dateToHNString(state.endTime.value), + needAuthor: 1, + signKey: signKeyDataList, + privateKey: getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false); + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + state.sureBtnState.value = 0; + if (state.ifCurrentScreen.value == true) { + showBlueConnetctToast(); + } + } + }); + } + + // 编辑遥控 + Future editRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.editRemoteControlData( + lockId: state.fingerprintItemData.value.lockId ?? 0, + remoteId: state.keyId.value, + startDate: int.parse(state.startDate.value), + endDate: int.parse(state.endDate.value), + startTime: int.parse(state.starTime.value), + endTime: int.parse(state.endTime.value), + remoteType: state.keyType.value, + weekDay: state.weekDay.value, + remoteName: state.changeNameController.text, + addType: 1, + isCoerced: 2, + remoteRight: 2, + ); + if (entity.errorCode!.codeIsSuccessful) { + state.fingerprintItemData.value.cardName = state.changeNameController.text; + // state.fingerprintItemData.value.isCoerced = state.isStressCard.value ? 1 : 2; + // state.fingerprintItemData.value.cardRight = state.isAdministrator.value ? 1 : 0; + showToast('修改成功'.tr, something: () { + eventBus.fire(OtherTypeRefreshListEvent()); + }); + } + } + + // 删除遥控 + Future deletRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.deletRemoteControlData( + remoteId: state.fingerprintItemData.value.remoteId ?? 0, + ); + if (entity.errorCode!.codeIsSuccessful) { + showToast('删除成功'.tr, something: () { + Get.back(result: 'addScuess'); + }); + } + } + + String getKeyTypeShowDateTime() { + String useDateStr = ''; + if (state.keyType.value == 1) { + useDateStr = '永久'.tr; + } else if (state.keyType.value == 2) { + useDateStr = + '${DateTool().dateToYMDHNString(state.startDate.value)}\n${DateTool().dateToYMDHNString(state.endDate.value)}'; + } else if (state.keyType.value == 4) { + useDateStr = + '${DateTool().dateToYMDString(state.startDate.value)}\n${DateTool().dateToYMDString(state.endDate.value)}'; + } + return useDateStr; + } + + @override + void onReady() { + super.onReady(); + + _initReplySubscription(); + } + + @override + void onClose() { + super.onClose(); + + _replySubscription.cancel(); + } +} \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_page.dart b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_page.dart index e69de29b..dcc396db 100755 --- a/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_page.dart +++ b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_page.dart @@ -0,0 +1,354 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; + +import '../../../../appRouters.dart'; +import '../../../../app_settings/app_colors.dart'; +import '../../../../tools/appRouteObserver.dart'; +import '../../../../tools/commonItem.dart'; +import '../../../../tools/dateTool.dart'; +import '../../../../tools/showTipView.dart'; +import '../../../../tools/submitBtn.dart'; +import '../../../../tools/titleAppBar.dart'; +import '../../../../translations/trans_lib.dart'; +import '../../../lockMian/lockMain/lockMain_logic.dart'; +import 'remoteControlDetail_logic.dart'; +import 'remoteControlDetail_state.dart'; + +class RemoteControlDetailPage extends StatefulWidget { + const RemoteControlDetailPage({Key? key}) : super(key: key); + + @override + State createState() => _RemoteControlDetailPageState(); +} + +class _RemoteControlDetailPageState extends State with RouteAware { + final RemoteControlDetailLogic logic = Get.put(RemoteControlDetailLogic()); + final RemoteControlDetailState state = Get.find().state; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.mainBackgroundColor, + appBar: TitleAppBar( + barTitle: '遥控详情'.tr, + haveBack: true, + backgroundColor: AppColors.mainColor, + ), + body: ListView( + children: [ + Obx(() => CommonItem( + leftTitel: '遥控号', + rightTitle: state.typeNumber.value, + isHaveDirection: false, + isHaveLine: true)), + Obx(() => lockDataListItem( + TranslationLoader.lanKeys!.name!.tr, state.typeName.value, + () { + ShowTipView().showTFViewAlertDialog( + state.changeNameController, + '${TranslationLoader.lanKeys!.amend!.tr}${TranslationLoader.lanKeys!.name!.tr}', + '', () { + if (state.changeNameController.text.isEmpty) { + logic.showToast('请输入姓名'.tr); + return; + } + Get.back(); + state.typeName.value = state.changeNameController.text; + logic.editRemoteControlData(); + }, inputFormatters: [ + FilteringTextInputFormatter.deny('\n'), + LengthLimitingTextInputFormatter(50), + ]); + })), + Obx(() => Visibility( + visible: state.keyType.value == 4 || + state.keyType.value == 2 || + state.keyType.value == 1, + child: CommonItem( + leftTitel: TranslationLoader.lanKeys!.periodValidity!.tr, + allHeight: 70.h, + rightTitle: logic.getKeyTypeShowDateTime(), + isHaveDirection: true, + isHaveLine: true, + action: () async { + if (state.keyType.value == 2 || state.keyType.value == 1) { + // 限时 + var data = await Get.toNamed( + Routers.otherTypeKeyChangeDatePage, + arguments: { + 'pushType': 2, + 'fingerprintItemData': + state.fingerprintItemData.value, + }); + if (data != null) { + setState(() { + state.startDate.value = data['beginTimeTimestamp']; + state.endDate.value = data['endTimeTimestamp']; + state.keyType.value = 2; + }); + } + } else if (state.keyType.value == 4) { + // 循环 + var data = await Get.toNamed( + Routers.otherTypeKeyChangeValidityDatePage, + arguments: { + 'pushType': 2, + 'fingerprintItemData': + state.fingerprintItemData.value, + }); + if (data != null) { + setState(() { + state.startDate.value = data['starDate']; + state.endDate.value = data['endDate']; + state.starTime.value = data['starTime']; + state.endTime.value = data['endTime']; + state.weekDay.value = data['weekDay']; + }); + } + } + }))), + Obx(() => Visibility( + visible: state.keyType.value == 4, + child: Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.effectiveDay!.tr, + rightTitle: logic.weekDayStr.join(','), + isHaveDirection: true, + isHaveLine: true, + action: () async { + var data = await Get.toNamed( + Routers.otherTypeKeyChangeValidityDatePage, + arguments: { + 'pushType': 0, + 'fingerprintItemData': + state.fingerprintItemData.value, + }); + if (data != null) { + setState(() { + state.startDate.value = data['starDate']; + state.endDate.value = data['endDate']; + state.starTime.value = data['starTime']; + state.endTime.value = data['endTime']; + state.weekDay.value = data['weekDay']; + }); + } + })))), + Obx(() => Visibility( + visible: state.keyType.value == 4, + child: Obx(() => CommonItem( + leftTitel: '有效时间'.tr, + rightTitle: + '${DateTool().dateToHNString(state.starTime.value)}-${DateTool().dateToHNString(state.endTime.value)}', + isHaveDirection: true, + action: () async { + var data = await Get.toNamed( + Routers.otherTypeKeyChangeValidityDatePage, + arguments: { + 'pushType': 0, + 'fingerprintItemData': + state.fingerprintItemData.value, + }); + if (data != null) { + setState(() { + state.startDate.value = data['starDate']; + state.endDate.value = data['endDate']; + state.starTime.value = data['starTime']; + state.endTime.value = data['endTime']; + state.weekDay.value = data['validityValue']; + }); + } + })))), + Container(height: 10.h), + Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.additive!.tr, + isHaveLine: true, + rightTitle: state.adder.value, + )), + Obx(() => CommonItem( + leftTitel: TranslationLoader.lanKeys!.addTime!.tr, + rightTitle: DateTool() + .dateToYMDHNString(state.addTime.value.toString()), + )), + SizedBox(height: 10.h), + // Obx(() => CommonItem( + // leftTitel: TranslationLoader.lanKeys!.stressCard!.tr, + // rightTitle: '', + // isTipsImg: false, + // isHaveLine: true, + // isHaveRightWidget: true, + // rightWidget: SizedBox( + // width: 60.w, height: 50.h, child: _isStressFingerprint()))), + // Obx(() => CommonItem( + // leftTitel: '是否为管理员'.tr, + // rightTitle: '', + // isTipsImg: false, + // isHaveRightWidget: true, + // rightWidget: + // SizedBox(width: 60.w, height: 50.h, child: _isAdmin()))), + // Container(height: 10.h), + CommonItem( + leftTitel: TranslationLoader.lanKeys!.operatingRecord!.tr, + rightTitle: '', + isHaveDirection: true, + action: () { + Get.toNamed(Routers.lockOperatingRecordPage, + arguments: { + 'type': 2, + 'id': state.fingerprintItemData.value.cardId.toString(), + 'recordName': state.fingerprintItemData.value.cardName + }); + }), + // SizedBox(height: 40.h), + // addControlsBtn(type), + SizedBox(height: 30.h), + SubmitBtn( + btnName: TranslationLoader.lanKeys!.delete!.tr, + isDelete: true, + borderRadius: 20.w, + margin: EdgeInsets.only( + left: 30.w, right: 30.w, top: 30.w, bottom: 30.w), + padding: EdgeInsets.only(top: 25.w, bottom: 25.w), + onClick: () async { + final bool isNetWork = + await LockMainLogic.to()?.judgeTheNetwork() ?? false; + if (!isNetWork) { + return; + } + + ShowTipView().showIosTipWithContentDialog('确定要删除吗?'.tr, + () async { + state.isDeletRemoteControl.value = true; + logic.senderAddICCard(); + }); + }), + ], + ), + ); + } + + Widget lockDataListItem( + String leftTitle, String conentStr, Function()? action) { + return GestureDetector( + onTap: action, + child: Container( + // height: 70.h, + padding: + EdgeInsets.only(left: 20.w, right: 10.w, top: 15.h, bottom: 15.h), + decoration: BoxDecoration( + color: Colors.white, + border: Border( + bottom: BorderSide( + color: AppColors.greyLineColor, // 设置边框颜色 + width: 2.0.h, // 设置边框宽度 + ), + )), + child: Row( + children: [ + Text(leftTitle, style: TextStyle(fontSize: 22.sp)), + SizedBox(width: 10.w), + Expanded( + child: Text(conentStr, + textAlign: TextAlign.end, + style: TextStyle( + fontSize: 22.sp, + ))), + SizedBox(width: 10.w), + Image.asset( + 'images/icon_right_grey.png', + width: 12.w, + height: 21.w, + ) + ], + ), + ), + ); + } + + //isStressFingerprint false:不是胁迫指纹 ture:胁迫指纹 + // CupertinoSwitch _isStressFingerprint() { + // return CupertinoSwitch( + // activeColor: CupertinoColors.activeBlue, + // trackColor: CupertinoColors.systemGrey5, + // thumbColor: CupertinoColors.white, + // value: state.isStressCard.value, + // onChanged: (bool value) { + // setState(() { + // state.isStressCard.value = value; + // state.isDeletCard.value = false; + // logic.senderAddICCard(); + // }); + // }, + // ); + // } + + // 是否为管理员 + // CupertinoSwitch _isAdmin() { + // return CupertinoSwitch( + // activeColor: CupertinoColors.activeBlue, + // trackColor: CupertinoColors.systemGrey5, + // thumbColor: CupertinoColors.white, + // value: state.isAdministrator.value, + // onChanged: (bool value) { + // // state.isAdministrator.value = value; + // // state.isDeletCard.value = false; + // // logic.senderAddICCard(); + // }, + // ); + // } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + /// 路由订阅 + AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(context)!); + } + + @override + void dispose() { + /// 取消路由订阅 + AppRouteObserver().routeObserver.unsubscribe(this); + super.dispose(); + } + + /// 从上级界面进入 当前界面即将出现 + @override + void didPush() { + super.didPush(); + state.ifCurrentScreen.value = true; + } + + /// 返回上一个界面 当前界面即将消失 + @override + void didPop() { + super.didPop(); + logic.cancelBlueConnetctToastTimer(); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } + state.ifCurrentScreen.value = false; + state.sureBtnState.value = 0; + } + + /// 从下级返回 当前界面即将出现 + @override + void didPopNext() { + super.didPopNext(); + state.ifCurrentScreen.value = true; + } + + /// 进入下级界面 当前界面即将消失 + @override + void didPushNext() { + super.didPushNext(); + logic.cancelBlueConnetctToastTimer(); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } + state.ifCurrentScreen.value = false; + state.sureBtnState.value = 0; + } +} diff --git a/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_state.dart b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_state.dart new file mode 100644 index 00000000..78b5fa73 --- /dev/null +++ b/lib/main/lockDetail/remoteControl/remoteControlDetail/remoteControlDetail_state.dart @@ -0,0 +1,49 @@ + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../fingerprint/fingerprintList/fingerprintListData_entity.dart'; + +class RemoteControlDetailState{ + RemoteControlDetailState() { + Map map = Get.arguments; + if (map['fingerprintItemData'] != null) { + fingerprintItemData.value = map['fingerprintItemData']; + keyId.value = fingerprintItemData.value.remoteId!; + typeNumber.value = fingerprintItemData.value.remoteNumber!; + typeName.value = fingerprintItemData.value.remoteName!; + changeNameController.text = typeName.value; + startDate.value = fingerprintItemData.value.startDate!.toString(); + endDate.value = fingerprintItemData.value.endDate!.toString(); + starTime.value = fingerprintItemData.value.startDate!.toString(); + endTime.value = fingerprintItemData.value.endDate!.toString(); + keyType.value = fingerprintItemData.value.remoteType!; + adder.value = fingerprintItemData.value.senderUsername!; + addTime.value = fingerprintItemData.value.createDate!; + // isStressCard.value = fingerprintItemData.value.isCoerced! == 1; + weekDay.value = fingerprintItemData.value.weekDay!; + // isAdministrator.value = fingerprintItemData.value.remoteRight! == 1; + } + } + final Rx fingerprintItemData = FingerprintItemData().obs; + + final TextEditingController changeNameController = TextEditingController(); + + RxBool ifCurrentScreen = true.obs; // 是否是当前界面,用于判断是否需要针对当前界面进行展示 + RxInt sureBtnState = 0.obs; // 0普通状态(可用) 1连接中(不可用) + RxBool isDeletRemoteControl = true.obs; // 是否删除卡 + // RxBool isStressCard = false.obs; // 是否胁迫 + // RxBool isAdministrator = false.obs; // 是否为管理员 + + final RxString typeNumber = ''.obs; // 指纹号 + final RxString typeName = ''.obs; // 指纹名字 + RxString startDate = ''.obs; // 开始时间 + RxString endDate = ''.obs; // 结束时间 + RxString starTime = ''.obs; // 生效时间 + RxString endTime = ''.obs; // 失效时间 + final RxInt keyType = 0.obs; // 永久:1;限时2,单次3,循环:4 + RxList weekDay = [].obs; // 有效日 + RxString adder = ''.obs; // 添加者 + RxInt addTime = 0.obs; // 添加时间 + RxInt keyId = 0.obs; // 卡id +} \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_logic.dart b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_logic.dart index ecb67297..e531054f 100755 --- a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_logic.dart +++ b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_logic.dart @@ -1,20 +1,253 @@ +import 'dart:async'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:get/get.dart'; import 'package:star_lock/tools/baseGetXController.dart'; +import '../../../../blue/blue_manage.dart'; +import '../../../../blue/io_protocol/io_addRemoteControlWithTimeCycleCoercion.dart'; +import '../../../../blue/io_reply.dart'; +import '../../../../blue/io_tool/io_tool.dart'; +import '../../../../blue/io_tool/manager_event_bus.dart'; +import '../../../../blue/sender_manage.dart'; +import '../../../../login/login/entity/LoginEntity.dart'; +import '../../../../network/api_repository.dart'; +import '../../../../tools/dateTool.dart'; +import '../../../../tools/eventBusEventManage.dart'; +import '../../../../tools/storage.dart'; +import '../../fingerprint/fingerprintList/fingerprintListData_entity.dart'; import 'remoteControlList_state.dart'; class RemoteControlListLogic extends BaseGetXController{ RemoteControlListState state = RemoteControlListState(); + // 获取解析后的数据 + late StreamSubscription _replySubscription; + void _initReplySubscription() { + _replySubscription = + EventBusManager().eventBus!.on().listen((Reply reply) { + // 添加卡片开始(重置锁里面所有卡) + if ((reply is SenderAddRemoteControlWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) { + _replyAddRemoteControlBegin(reply); + } + }); + } + + // 添加卡片开始(此处用作删除卡片) + Future _replyAddRemoteControlBegin(Reply reply) async { + final int status = reply.data[2]; + + switch (status) { + case 0x00: + //成功 + cancelBlueConnetctToastTimer(); + if(state.isDeletAll == true){ + resetRemoteControlData(); + }else{ + deletRemoteControlData(); + } + break; + case 0x06: + //无权限 + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); + + final List? signKey = + await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List token = reply.data.sublist(5, 9); + final List saveStrList = changeIntListToStringList(token); + Storage.setStringList(saveBlueToken, saveStrList); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID: state.deletKeyID, + userID: (await Storage.getUid())!, + remoteControlNo: state.deletRemoteControlNo, + useCountLimit: 0xffff, + operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin: 0, + isForce: 0, // 是否是胁迫 + isRound: 0, // 是否是循环 + weekRound: 0, // 周循环 + startDate: 0x11223344, + endDate: 0x11223344, + startTime: '0', + endTime: '0', + needAuthor: 1, + signKey: signKeyDataList, + privateKey: getPrivateKeyList, + token: token, + isBeforeAddUser: false); + break; + default: + //失败 + break; + } + } + + // 删除卡片 + Future senderAddRemoteControl() async { + showEasyLoading(); + showBlueConnetctToastTimer(action: () { + dismissEasyLoading(); + }); + BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async { + if (deviceConnectionState == BluetoothConnectionState.connected) { + final List? signKey = + await Storage.getStringList(saveBlueSignKey); + final List signKeyDataList = changeStringListToIntList(signKey!); + + final List? privateKey = + await Storage.getStringList(saveBluePrivateKey); + final List getPrivateKeyList = + changeStringListToIntList(privateKey!); + + final List? token = await Storage.getStringList(saveBlueToken); + final List getTokenList = changeStringListToIntList(token!); + + IoSenderManage.senderAddRemoteControlWithTimeCycleCoercionCommand( + keyID: state.deletKeyID, + userID: (await Storage.getUid())!, + remoteControlNo: state.deletRemoteControlNo, + useCountLimit: 0xffff, + operate: state.isDeletAll == true ? 3 : 2, // 0:注册 1:修改 2:删除 3:删除全部 + isAdmin: 0, + isForce: 0, // 是否是胁迫 + isRound: 0, // 是否是循环 + weekRound: 0, // 周循环 + startDate: 0x11223344, + endDate: 0x11223344, + startTime: '0', + endTime: '0', + needAuthor: 1, + signKey: signKeyDataList, + privateKey: getPrivateKeyList, + token: getTokenList, + isBeforeAddUser: false); + } else if (deviceConnectionState == + BluetoothConnectionState.disconnected) { + dismissEasyLoading(); + cancelBlueConnetctToastTimer(); + if (state.ifCurrentScreen.value == true) { + showBlueConnetctToast(); + } + } + }); + } + + // 获取IC卡列表 + Future getRemoteControlListData( + {required bool isRefresh}) async { + // 如果是下拉刷新,清空已有数据 + if (isRefresh) { + state.fingerprintItemListData.clear(); + pageNo = 1; + } + final FingerprintListDataEntity entity = await ApiRepository.to.getRemoteControlListData( + lockId: state.lockId.value.toString(), + pageNo: pageNo.toString(), + pageSize: pageSize, + searchStr: state.searchController.text, + ); + if (entity.errorCode!.codeIsSuccessful) { + // 更新数据列表 + state.fingerprintItemListData.addAll(entity.data!.list!); + // 更新页码 + pageNo++; + } + return entity; + } + + // 删除所有IC卡 + Future resetRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.resetRemoteControlData( + lockId: state.lockId.value + ); + if (entity.errorCode!.codeIsSuccessful) { + showToast('重置成功'.tr, something: () { + getRemoteControlListData(isRefresh: true); + }); + } + } + + // 删除遥控 + Future deletRemoteControlData() async { + final LoginEntity entity = await ApiRepository.to.deletRemoteControlData( + remoteId: int.parse(state.deletKeyID), + ); + if (entity.errorCode!.codeIsSuccessful) { + showToast('删除成功'.tr, something: () { + Get.back(result: 'addScuess'); + }); + } + } + + // 监听修改完详情之后刷新列表 + late StreamSubscription _teamEvent; + void _initRefreshAction() { + _teamEvent = eventBus.on().listen((OtherTypeRefreshListEvent event) { + getRemoteControlListData(isRefresh: true); + }); + } + + String getKeyType(FingerprintItemData fingerprintItemData) { + String keyTypeStr = ''; // + if (fingerprintItemData.remoteStatus == 1) { + if (fingerprintItemData.startDate! > + DateTime.now().millisecondsSinceEpoch) { + keyTypeStr = '未生效'.tr; + } + } else if (fingerprintItemData.remoteStatus == 2) { + keyTypeStr = '已失效'.tr; + } + return keyTypeStr; + } + + String getKeyDateType(FingerprintItemData fingerprintItemData) { + String keyDateTypeStr = ''; // 永久:1;限时2,单次3,循环:4 + if (fingerprintItemData.remoteType! == 1) { + keyDateTypeStr = + '${DateTool().dateToYMDHNString((fingerprintItemData.createDate ?? 0).toString())} 永久'; + } else if (fingerprintItemData.remoteType! == 2) { + keyDateTypeStr = + '${DateTool().dateToYMDHNString(fingerprintItemData.startDate.toString())} - ${DateTool().dateToYMDHNString(fingerprintItemData.endDate.toString())} 限时'; + } else if (fingerprintItemData.remoteType! == 4) { + keyDateTypeStr = + '${DateTool().dateToYMDString(fingerprintItemData.startDate.toString())}-${DateTool().dateToYMDString(fingerprintItemData.endDate.toString())} 循环'; + } + return keyDateTypeStr; + } + @override - void onInit() { + Future onReady() async { + super.onReady(); + + // 获取是否是演示模式 演示模式不获取接口 + final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + if (isDemoMode == false) { + _initReplySubscription(); + + _initRefreshAction(); + } + } + + @override + Future onInit() async { super.onInit(); } @override - void onClose() { + Future onClose() async { super.onClose(); - } + final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + if (isDemoMode == false) { + _replySubscription.cancel(); + _teamEvent.cancel(); + } + } } \ No newline at end of file diff --git a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_page.dart b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_page.dart index 6ab01f50..15991d6a 100755 --- a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_page.dart +++ b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_page.dart @@ -1,21 +1,25 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:get/get.dart'; import 'package:star_lock/main/lockMian/lockMain/lockMain_logic.dart'; import '../../../../appRouters.dart'; import '../../../../app_settings/app_colors.dart'; -import '../../../../tools/commonDataManage.dart'; +import '../../../../tools/EasyRefreshTool.dart'; +import '../../../../tools/appRouteObserver.dart'; import '../../../../tools/keySearchWidget.dart'; import '../../../../tools/noData.dart'; -import '../../../../tools/showIosTipView.dart'; +import '../../../../tools/showTipView.dart'; import '../../../../tools/storage.dart'; import '../../../../tools/submitBtn.dart'; import '../../../../tools/titleAppBar.dart'; import '../../../../translations/trans_lib.dart'; +import '../../fingerprint/fingerprintList/fingerprintListData_entity.dart'; import 'remoteControlList_logic.dart'; +import 'remoteControlList_state.dart'; class RemoteControlListPage extends StatefulWidget { const RemoteControlListPage({Key? key}) : super(key: key); @@ -24,185 +28,238 @@ class RemoteControlListPage extends StatefulWidget { State createState() => _RemoteControlListPageState(); } -class _RemoteControlListPageState extends State { - final logic = Get.put(RemoteControlListLogic()); - final state = Get.find().state; +class _RemoteControlListPageState extends State with RouteAware { + final RemoteControlListLogic logic = Get.put(RemoteControlListLogic()); + final RemoteControlListState state = Get.find().state; + + Future getHttpData({required bool isRefresh}) async { + final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + if (isDemoMode == false) { + logic.getRemoteControlListData(isRefresh: isRefresh).then((FingerprintListDataEntity value) { + if (mounted) { + setState(() {}); + } + }); + } + } + + @override + void initState() { + super.initState(); + + getHttpData(isRefresh: true); + } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.mainBackgroundColor, + resizeToAvoidBottomInset: false, appBar: TitleAppBar( - barTitle: TranslationLoader.lanKeys!.remoteControl!.tr, + barTitle: '遥控', haveBack: true, backgroundColor: AppColors.mainColor, - actionsList: [ + actionsList: [ TextButton( child: Text( TranslationLoader.lanKeys!.reset!.tr, style: TextStyle(color: Colors.white, fontSize: 24.sp), ), onPressed: () async { - var isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); + final bool? isDemoMode = await Storage.getBool(ifIsDemoModeOrNot); if (isDemoMode == false) { final bool isNetWork = await LockMainLogic.to()?.judgeTheNetwork() ?? false; if (!isNetWork) { return; } - showDeletAlertDialog(context); + + ShowTipView().showIosTipWithContentDialog( + '重置后,该锁的遥控都将被删除哦,确认要重置吗?'.tr, () async { + state.isDeletAll = true; + state.deletKeyID = '0'; + state.deletRemoteControlNo = 0; + logic.senderAddRemoteControl(); + }); + // showDeletAlertDialog(context); } else { - // Get.toNamed(Routers.selectLockTypePage); - logic.showToast("演示模式"); + logic.showToast('演示模式'.tr); } }, ), ], ), - body: Column( - children: [ - KeySearchWidget( - editingController: state.searchController, - onSubmittedAction: () { - - }, - ), - SizedBox( - height: 20.h, - ), - Expanded(child: _buildMainUI()), - AddBottomWhiteBtn( - btnName: - '${TranslationLoader.lanKeys!.add!.tr}${TranslationLoader.lanKeys!.remoteControl!.tr}', - onClick: () async { - var data = - await Get.toNamed(Routers.addRemoteControlPage, arguments: { - "lockId": CommonDataManage().currentLockSetInfoData.lockId, - "fromType": 1 // 1从添加钥匙列表进入 2从考勤添加员工入口进入 - }); - if (data != null) { - // 遥控添加 - - } - }, - ), - SizedBox( - height: 64.h, - ) - ], - ), - ); - } - - // String getAppBarTitle(int type) { - // String title = ""; - // switch (type) { - // case 0: - // title = TranslationLoader.lanKeys!.card!.tr; - // break; - // case 1: - // title = TranslationLoader.lanKeys!.fingerprint!.tr; - // break; - // case 2: - // title = TranslationLoader.lanKeys!.remoteControl!.tr; - // break; - // case 3: - // title = TranslationLoader.lanKeys!.face!.tr; - // break; - // default: - // break; - // } - // return title; - // } - - Widget _buildMainUI() { - String typeImgName = 'images/icon_card.png'; - - return NoData(noDataHeight: 1.sh - ScreenUtil().statusBarHeight - ScreenUtil().bottomBarHeight - 190.h - 64.h); - } - - Widget _buildDeleteBtn(String idStr) { - return GestureDetector( - onTap: () { - // 省略: 弹出是否删除的确认对话框。 - showIosTipViewDialog(context, idStr); - }, - child: Container( - width: 60, - color: const Color(0xFFF20101), - alignment: Alignment.center, - child: const Text( - '删除', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.white, - height: 1, - ), + body: EasyRefreshTool( + onRefresh: () { + getHttpData(isRefresh: true); + }, + onLoad: () { + getHttpData(isRefresh: false); + }, + child: Column( + children: [ + KeySearchWidget( + editingController: state.searchController, + onSubmittedAction: () { + getHttpData(isRefresh: true); + }, + ), + SizedBox(height: 20.h), + Expanded(child: _buildMainUI()), + AddBottomWhiteBtn( + btnName: + '添加遥控', + onClick: () async { + final data = await Get.toNamed(Routers.addRemoteControlTypePage, + arguments: { + 'lockId': state.lockId.value, + 'fromType': 1 // 1从添加钥匙列表进入 2从考勤添加员工入口进入 + }); + if (data != null) { + getHttpData(isRefresh: true); + } + }, + ), + SizedBox( + height: 64.h, + ) + ], ), ), ); } - void showIosTipViewDialog(BuildContext context, String keyId) { - showDialog( - context: context, - builder: (BuildContext context) { - return ShowIosTipView( - title: "提示", - tipTitle: "确定要删除吗?", - sureClick: () async { - // 遥控删除 + Widget _buildMainUI() { + return Obx(() => state.fingerprintItemListData.value.isNotEmpty + ? SlidableAutoCloseBehavior( + child: ListView.separated( + itemCount: state.fingerprintItemListData.value.length, + itemBuilder: (BuildContext c, int index) { + final FingerprintItemData fingerprintItemData = + state.fingerprintItemListData.value[index]; + return Slidable( + key: ValueKey(fingerprintItemData.fingerprintId), + endActionPane: ActionPane( + extentRatio: 0.2, + motion: const ScrollMotion(), + children: [ + SlidableAction( + onPressed: (BuildContext context) async { + final bool isNetWork = + await LockMainLogic.to()?.judgeTheNetwork() ?? + false; + if (!isNetWork) { + return; + } - }, - cancelClick: () { - Get.back(); - }, - ); - }, - ); + ShowTipView().showIosTipWithContentDialog( + '确定要删除吗?'.tr, () async { + state.isDeletAll = false; + state.deletKeyID = fingerprintItemData.remoteId.toString(); + state.deletRemoteControlNo = int.parse(fingerprintItemData.remoteNumber!); + logic.senderAddRemoteControl(); + }); + }, + backgroundColor: Colors.red, + foregroundColor: Colors.white, + label: '删除'.tr, + padding: EdgeInsets.only(left: 5.w, right: 5.w), + ), + ], + ), + child: _keyItem( + 'images/icon_remoteControl.png', + fingerprintItemData.remoteName!, + logic.getKeyType(fingerprintItemData), + logic.getKeyDateType(fingerprintItemData), () async { + final data = await Get.toNamed(Routers.remoteControlDetailPage, + arguments: { + 'fingerprintItemData': fingerprintItemData, + }); + if (data != null) { + getHttpData(isRefresh: true); + } + }), + ); + }, + separatorBuilder: (BuildContext context, int index) { + return const Divider( + height: 1, + color: AppColors.greyLineColor, + ); + }, + ), + ) + : NoData( + noDataHeight: 1.sh - + ScreenUtil().statusBarHeight - + ScreenUtil().bottomBarHeight - + 190.h - + 64.h)); } - Widget _keyItem(String lockTypeIcon, String lockTypeTitle, String showTime, - Function() action) { + Widget _keyItem(String lockTypeIcon, String lockTypeTitle, + String ifInvalidation, String showTime, Function() action) { return GestureDetector( onTap: action, child: Container( height: 90.h, // margin: EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w), color: Colors.white, - // decoration: BoxDecoration( - // color: Colors.white, - // // borderRadius: BorderRadius.circular(10.w), - // ), child: Row( - children: [ + children: [ SizedBox(width: 30.w), - Image.asset(lockTypeIcon, width: 60.w, height: 60.w), + Container( + width: 60.w, + height: 60.w, + padding: EdgeInsets.all(20.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.w), + color: AppColors.mainColor, + ), + child: Image.asset(lockTypeIcon, ) + ), SizedBox(width: 20.w), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: [ Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text(lockTypeTitle, - style: TextStyle( - fontSize: 24.sp, color: AppColors.blackColor)), + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: 1.sw - 110.w - 100.w, + child: Row( + children: [ + Flexible( + child: Text(lockTypeTitle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 24.sp, + color: AppColors.blackColor)), + ), + ], + ), + ), + Text(ifInvalidation, + style: TextStyle(fontSize: 22.sp, color: Colors.red)), + SizedBox(width: 10.w), ], ), SizedBox(height: 5.h), - Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text(showTime, + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Flexible( + child: Text(showTime, + maxLines: 1, + overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 18.sp, color: AppColors.placeholderTextColor)), - ], - ), + ), + ], ), SizedBox(width: 20.h), ], @@ -215,32 +272,54 @@ class _RemoteControlListPageState extends State { ); } - void showDeletAlertDialog( - BuildContext context, - ) { - showCupertinoDialog( - context: context, - builder: (context) { - return CupertinoAlertDialog( - title: const Text("提示"), - content: const Text('重置后遥控信息都会清除哦,确认要重置吗?'), - actions: [ - CupertinoDialogAction( - child: Text(TranslationLoader.lanKeys!.cancel!.tr), - onPressed: () { - Navigator.pop(context); - }, - ), - CupertinoDialogAction( - child: Text(TranslationLoader.lanKeys!.sure!.tr), - onPressed: () { - // 遥控重置 + @override + void didChangeDependencies() { + super.didChangeDependencies(); - }, - ), - ], - ); - }, - ); + /// 路由订阅 + AppRouteObserver().routeObserver.subscribe(this, ModalRoute.of(context)!); + } + + @override + void dispose() { + /// 取消路由订阅 + AppRouteObserver().routeObserver.unsubscribe(this); + super.dispose(); + } + + /// 从上级界面进入 当前界面即将出现 + @override + void didPush() { + super.didPush(); + state.ifCurrentScreen.value = true; + } + + /// 返回上一个界面 当前界面即将消失 + @override + void didPop() { + super.didPop(); + logic.cancelBlueConnetctToastTimer(); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } + state.ifCurrentScreen.value = false; + } + + /// 从下级返回 当前界面即将出现 + @override + void didPopNext() { + super.didPopNext(); + state.ifCurrentScreen.value = true; + } + + /// 进入下级界面 当前界面即将消失 + @override + void didPushNext() { + super.didPushNext(); + logic.cancelBlueConnetctToastTimer(); + if (EasyLoading.isShow) { + EasyLoading.dismiss(animation: true); + } + state.ifCurrentScreen.value = false; } } diff --git a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_state.dart b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_state.dart index 42112431..8806bc0f 100755 --- a/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_state.dart +++ b/lib/main/lockDetail/remoteControl/remoteControlList/remoteControlList_state.dart @@ -1,7 +1,24 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../fingerprint/fingerprintList/fingerprintListData_entity.dart'; class RemoteControlListState{ + RemoteControlListState() { + Map map = Get.arguments; + lockId.value = map['lockId']; + } + final RxInt lockId = 0.obs; + + // 因为删除跟添加指纹用的同一个协议 所以这里用做判断 + bool isDeletAll = false; + String deletKeyID = '0'; + int deletRemoteControlNo = 0; + + final RxList fingerprintItemListData = [].obs; final TextEditingController searchController = TextEditingController(); + RxBool ifCurrentScreen = true.obs;// 是否是当前界面,用于判断是否需要针对当前界面进行展示 + } \ No newline at end of file diff --git a/lib/main/lockMian/lockList/lockList_logic.dart b/lib/main/lockMian/lockList/lockList_logic.dart index 57247151..0ce3d0d1 100755 --- a/lib/main/lockMian/lockList/lockList_logic.dart +++ b/lib/main/lockMian/lockList/lockList_logic.dart @@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:get/get.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import '../../../blue/blue_manage.dart'; @@ -31,6 +32,7 @@ class LockListLogic extends BaseGetXController { LockListInfoGroupEntity? entity; List get groupDataList { + final List list = _groupDataList.map((GroupList e) => e.copy()).toList(); if (state.searchStr.value != '' && state.showSearch.value) { @@ -39,6 +41,7 @@ class LockListLogic extends BaseGetXController { !(element.lockAlias?.contains(state.searchStr.value) ?? true)); }); } + AppLog.log('lockList!.length:${_groupDataList[0].lockList!.length}'); return list; } @@ -49,6 +52,7 @@ class LockListLogic extends BaseGetXController { _groupDataList = []; } _groupDataList.addAll(entity.groupList!); + AppLog.log('_groupDataList[0].lockList!.length:${_groupDataList[0].lockList!.length}'); update(); } @@ -315,6 +319,7 @@ class LockListLogic extends BaseGetXController { @override void onInit() { super.onInit(); + AppLog.log('onInit调用了 setLockListInfoGroupEntity'); setLockListInfoGroupEntity(entity!); } diff --git a/lib/main/lockMian/lockList/lockList_xhj_page.dart b/lib/main/lockMian/lockList/lockList_xhj_page.dart index 47d6fce9..d9a24696 100755 --- a/lib/main/lockMian/lockList/lockList_xhj_page.dart +++ b/lib/main/lockMian/lockList/lockList_xhj_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:get/get.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/main/lockMian/lockList/lockList_state.dart'; import '../../../appRouters.dart'; @@ -125,8 +126,9 @@ class _LockListXHJPageState extends State with RouteAware { ), Expanded( child: Obx(() { + AppLog.log('logic.groupDataList[0].lockList!.length:${logic.groupDataList[0].lockList!.length}'); return ListView.builder( - itemCount: logic.groupDataList.length, + itemCount: logic.groupDataList.length ?? 0, itemBuilder: (BuildContext context, int index) { final GroupList itemData = logic.groupDataList[index]; return _buildLockExpandedList(context, index, itemData); @@ -143,10 +145,9 @@ class _LockListXHJPageState extends State with RouteAware { } //设备多层级列表 - Widget _buildLockExpandedList( - BuildContext context, int index, GroupList itemData) { - final List lockItemList = - itemData.lockList ?? []; + Widget _buildLockExpandedList(BuildContext context, int index, GroupList itemData) { + final List lockItemList = itemData.lockList ?? []; + AppLog.log('lockItemList[0].lockAlias:${lockItemList[0].lockAlias}'); final List list = forItems(lockItemList); return LockListGroupView( onTap: () { diff --git a/lib/main/lockMian/lockMain/lockMain_logic.dart b/lib/main/lockMian/lockMain/lockMain_logic.dart index 32852a77..3add80b7 100755 --- a/lib/main/lockMian/lockMain/lockMain_logic.dart +++ b/lib/main/lockMian/lockMain/lockMain_logic.dart @@ -36,6 +36,7 @@ class LockMainLogic extends BaseGetXController { isUnShowLoading: isUnShowLoading, ); if (entity.errorCode!.codeIsSuccessful) { + AppLog.log('请求列表调用 loadMainDataLogic'); await loadMainDataLogic(entity.data!); } return entity; @@ -58,15 +59,18 @@ class LockMainLogic extends BaseGetXController { state.dataLength.value = 2; } state.lockListInfoGroupEntity.value = entity; + AppLog.log('entity:$entity state.lockListInfoGroupEntity.value.groupList!.length:${state.lockListInfoGroupEntity.value.groupList![0].lockList!.length}'); //检测控制器是否存在 if (Get.isRegistered()) { //设置控制器数据并刷新 + AppLog.log('检测控制器是否存 调用了 setLockListInfoGroupEntity'); Get.find().setLockListInfoGroupEntity(entity); } else { //延迟加载 Future.delayed(200.milliseconds, () { if (Get.isRegistered()) { //设置控制器数据并刷新 + AppLog.log('检测控制器是否存 延迟调用了 setLockListInfoGroupEntity'); Get.find().setLockListInfoGroupEntity(entity); } }); @@ -157,6 +161,7 @@ class LockMainLogic extends BaseGetXController { //刷新锁设备 Future flushedStarLockInfo(int keyId, int lockId) async { + AppLog.log('刷新锁设备: keyId:$keyId lockId:$lockId'); final LockListInfoEntity entity = await ApiRepository.to.getStarLockInfo( keyId: keyId, ); @@ -201,6 +206,7 @@ class LockMainLogic extends BaseGetXController { state.lockListInfoGroupEntity.value.groupList!.insert(0, list); } state.lockListInfoGroupEntity.refresh(); + AppLog.log('刷新调用 loadMainDataLogic'); loadMainDataLogic(state.lockListInfoGroupEntity.value); } update(); @@ -214,6 +220,7 @@ class LockMainLogic extends BaseGetXController { ?.removeWhere((LockListInfoItemEntity lock) => lock.keyId == keyId); }); state.lockListInfoGroupEntity.refresh(); + AppLog.log('删除调用 loadMainDataLogic'); loadMainDataLogic(state.lockListInfoGroupEntity.value); if (Get.isRegistered()) { final bool isKey = diff --git a/lib/mine/message/messageList/messageList_xhj_page.dart b/lib/mine/message/messageList/messageList_xhj_page.dart index ecd5d31f..ace80c85 100755 --- a/lib/mine/message/messageList/messageList_xhj_page.dart +++ b/lib/mine/message/messageList/messageList_xhj_page.dart @@ -31,7 +31,9 @@ class _MessageListXHJPageState extends State void getHttpData() { logic.messageListDataRequest().then((MessageListEntity value) { - setState(() {}); + if (mounted) { + setState(() {}); + } }); } diff --git a/lib/network/api.dart b/lib/network/api.dart index 65b57f2c..53c9e081 100755 --- a/lib/network/api.dart +++ b/lib/network/api.dart @@ -129,6 +129,15 @@ abstract class Api { final String checkCardNameURL = '/identityCard/checkIdentityCardName'; // 校验卡名字是否存在 + final String getRemoteControlListURL = '/remote/list'; // 遥控列表 + final String addRemoteControlURL = '/remote/add'; // 添加遥控 + final String updateRemoteUserNoURL = '/remote/updateRemoteUserNo'; // 更新遥控用户序号 + final String editRemoteControlURL = '/remote/update'; // 编辑遥控 + final String deleteRemoteControlURL = '/remote/delete'; // 删除遥控 + final String clearRemoteControlURL = '/remote/clear'; // 清空遥控 + final String checkRemoteControlNameURL = + '/remote/checkRemoteName'; // 校验遥控名字是否存在 + final String transferLockListURL = '/room/listForTransfer'; // 转移锁锁列表 final String transferLockConfirmURL = '/room/transferLockConfirm'; // 转移智能锁确认 final String transferLockURL = '/room/transfer'; // 转移智能锁 diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart index d576225e..c0df687b 100755 --- a/lib/network/api_provider.dart +++ b/lib/network/api_provider.dart @@ -1462,9 +1462,119 @@ class ApiProvider extends BaseProvider { post(checkCardNameURL.toUrl, jsonEncode({'lockId': lockId, 'cardName': cardName})); + // 获取遥控列表 + Future getRemoteControlListData( + String lockId, String pageNo, String pageSize, String searchStr) => + post( + getRemoteControlListURL.toUrl, + jsonEncode({ + 'lockId': lockId, + 'pageNo': pageNo, + 'pageSize': pageSize, + 'searchStr': searchStr, + })); + + // 添加遥控 + Future addRemoteControlData( + String lockId, + String remoteName, + String remoteNumber, + int remoteType, + int startDate, + int endDate, + int addType, + List weekDay, + int startTime, + int endTime, + int remoteRight, + // String mac, + // int electricQuantity, + // List firmwareInfo + ) => + post( + addRemoteControlURL.toUrl, + jsonEncode({ + 'lockId': lockId, + 'remoteName': remoteName, + 'remoteNumber': remoteNumber, + 'remoteType': remoteType, + 'startDate': startDate, + 'endDate': endDate, + 'addType': addType, + 'weekDay': weekDay, + 'startTime': startTime, + 'endTime': endTime, + 'remoteRight': remoteRight, + // 'mac': '', + // 'electricQuantity': electricQuantity, + // 'firmwareInfo': firmwareInfo, + })); + + // 更新遥控序号 + Future updateRemoteUserNoLoadData( + int lockId, int remoteId, String remoteUserNo) => + post( + updateRemoteUserNoURL.toUrl, + jsonEncode( + {'lockId': lockId, 'remoteId': remoteId, 'remoteUserNo': remoteUserNo})); + + // 编辑遥控 + Future editRemoteControlData( + int lockId, + int remoteId, + int startDate, + int endDate, + int startTime, + int endTime, + int remoteType, + List weekDay, + String remoteName, + int addType, + int isCoerced, + int remoteRight, + ) => + post( + editRemoteControlURL.toUrl, + jsonEncode({ + 'lockId': lockId, + 'remoteId': remoteId, + 'startDate': startDate, + 'endDate': endDate, + 'startTime': startTime, + 'endTime': endTime, + 'remoteType': remoteType, + 'weekDay': weekDay, + 'remoteName': remoteName, + 'addType': addType, + 'isCoerced': isCoerced, + 'remoteRight': remoteRight + })); + + // 删除遥控 + Future deletRemoteControlData(int remoteId) => + post( + deleteRemoteControlURL.toUrl, + jsonEncode({ + 'remoteId': remoteId, + })); + + // 删除遥控 + Future resetRemoteControlData(int lockId) => + post( + clearRemoteControlURL.toUrl, + jsonEncode({ + 'lockId': lockId, + })); + + // 校验遥控名字是否重复 + Future checkRemoteControlNameDuplicatedData( + String lockId, String remoteName) => + post(checkRemoteControlNameURL.toUrl, + jsonEncode({'lockId': lockId, 'remoteName': remoteName})); + // 获取转移锁锁列表 Future getTransferLockListData(String searchStr) => - post(transferLockListURL.toUrl, jsonEncode({"searchStr": searchStr})); + post(transferLockListURL.toUrl, jsonEncode({'searchStr': searchStr})); // 转移智能锁确认 Future transferLockConfirmInfoData( diff --git a/lib/network/api_repository.dart b/lib/network/api_repository.dart index 6fc0036f..43f72529 100755 --- a/lib/network/api_repository.dart +++ b/lib/network/api_repository.dart @@ -43,6 +43,7 @@ import 'package:star_lock/mine/valueAddedServices/valueAddedServicesSMSTemplate/ import 'package:star_lock/mine/valueAddedServices/valueAddedServicesSMSTemplate/valueAddedServicesListSMSTemplate/customSMSTemplateList_entity.dart'; import 'package:star_lock/tools/aliyunRealNameAuth/realNameVertify_entity.dart'; import 'package:star_lock/tools/aliyunRealNameAuth/serviceAuthResult_entity.dart'; + import '../common/safetyVerification/entity/CheckSafetyVerificationEntity.dart'; import '../common/safetyVerification/entity/SafetyVerificationEntity.dart'; import '../login/login/entity/LoginEntity.dart'; @@ -59,15 +60,16 @@ import '../main/lockDetail/checkingIn/checkingInStaff/checkingInSetAddStaff/chec import '../main/lockDetail/checkingIn/checkingInStaff/checkingInSetStaffList/checkingInStaffList_entity.dart'; import '../main/lockDetail/fingerprint/addFingerprint/addFingerprint_entity.dart'; import '../main/lockDetail/fingerprint/fingerprintList/fingerprintListData_entity.dart'; +import '../main/lockDetail/lockDetail/lockNetToken_entity.dart'; import '../main/lockDetail/lockOperatingRecord/keyOperationRecord_entity.dart'; +import '../main/lockDetail/lockOperatingRecord/lockOperatingRecordGetLastRecordTime_entity.dart'; import '../main/lockDetail/lockSet/basicInformation/uploadElectricQuantity/uploadElectricQuantity_entity.dart'; import '../main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifiEntity.dart'; import '../main/lockDetail/lockSet/lockSet/checkingInInfoData_entity.dart'; import '../main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart'; import '../main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart'; -import '../main/lockDetail/lockDetail/lockNetToken_entity.dart'; -import '../main/lockDetail/lockOperatingRecord/lockOperatingRecordGetLastRecordTime_entity.dart'; import '../main/lockDetail/messageWarn/msgNotification/openDoorNotify/openDoorNotify_entity.dart'; +import '../main/lockDetail/remoteControl/addRemoteControl/addRemoteControl_entity.dart'; import '../main/lockMian/entity/lockListInfo_entity.dart'; import '../mine/addLock/saveLock/entity/SaveLockEntity.dart'; import '../mine/message/messageList/messageList_entity.dart'; @@ -1647,6 +1649,107 @@ class ApiRepository { return LoginEntity.fromJson(res.body); } + // 获取遥控列表 + Future getRemoteControlListData({ + required String lockId, + required String pageNo, + required String pageSize, + required String searchStr, + }) async { + final res = await apiProvider.getRemoteControlListData( + lockId, pageNo, pageSize, searchStr); + return FingerprintListDataEntity.fromJson(res.body); + } + + // 添加遥控 + Future addRemoteControlData({ + required String lockId, + required String remoteName, + required String remoteNumber, + required int remoteType, + required int startDate, + required int endDate, + required int addType, + required List weekDay, + required int startTime, + required int endTime, + required int remoteRight, + }) async { + final res = await apiProvider.addRemoteControlData( + lockId, + remoteName, + remoteNumber, + remoteType, + startDate, + endDate, + addType, + weekDay, + startTime, + endTime, + remoteRight); + return AddRemoteControlEntity.fromJson(res.body); + } + + // 更新ICCard用户序号 + Future updateRemoteUserNoLoadData( + {required int lockId, + required int remoteId, + required String remoteUserNo}) async { + final res = await apiProvider.updateRemoteUserNoLoadData( + lockId, remoteId, remoteUserNo); + return LoginEntity.fromJson(res.body); + } + + // 编辑遥控 + Future editRemoteControlData({ + required int lockId, + required int remoteId, + required int startDate, + required int endDate, + required int startTime, + required int endTime, + required int remoteType, + required List weekDay, + required String remoteName, + required int addType, + required int isCoerced, + required int remoteRight, + }) async { + final res = await apiProvider.editRemoteControlData( + lockId, + remoteId, + startDate, + endDate, + startTime, + endTime, + remoteType, + weekDay, + remoteName, + addType, + isCoerced, + remoteRight); + return LoginEntity.fromJson(res.body); + } + + // 删除遥控 + Future deletRemoteControlData({required int remoteId}) async { + final res = await apiProvider.deletRemoteControlData(remoteId); + return LoginEntity.fromJson(res.body); + } + + // 重置遥控 + Future resetRemoteControlData({required int lockId}) async { + final res = await apiProvider.resetRemoteControlData(lockId); + return LoginEntity.fromJson(res.body); + } + + // 校验遥控名字是否重复 + Future checkRemoteControlNameDuplicatedData( + {required String lockId, required String remoteName}) async { + final res = await apiProvider.checkRemoteControlNameDuplicatedData(lockId, remoteName); + return LoginEntity.fromJson(res.body); + } + // 退出登录 Future userLogout({required String deviceld}) async { final res = await apiProvider.userLogout(deviceld);