diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6b0b61b..24b7370 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,13 +33,15 @@ - + + android:label="太和e护" + android:enableOnBackInvokedCallback="true" + > 资源 249 \ No newline at end of file diff --git a/assets/img/icon/bed_status.svg b/assets/img/icon/bed_status.svg new file mode 100644 index 0000000..3042041 --- /dev/null +++ b/assets/img/icon/bed_status.svg @@ -0,0 +1,21 @@ + + 资源 239 + + + + + + + + + + diff --git a/assets/img/icon/bodymotion.svg b/assets/img/icon/bodymotion.svg new file mode 100644 index 0000000..202f5ce --- /dev/null +++ b/assets/img/icon/bodymotion.svg @@ -0,0 +1,11 @@ + + 资源 238 + + + + + + + diff --git a/assets/img/icon/breathe.svg b/assets/img/icon/breathe.svg new file mode 100644 index 0000000..1f0ec92 --- /dev/null +++ b/assets/img/icon/breathe.svg @@ -0,0 +1,13 @@ + + 资源 241 + + + + + + + + diff --git a/assets/img/icon/breathe_pause.svg b/assets/img/icon/breathe_pause.svg new file mode 100644 index 0000000..2a9df0f --- /dev/null +++ b/assets/img/icon/breathe_pause.svg @@ -0,0 +1,15 @@ + + 资源 237 + + + + + + + + + diff --git a/assets/img/icon/device_issue.svg b/assets/img/icon/device_issue.svg new file mode 100644 index 0000000..d19c4d9 --- /dev/null +++ b/assets/img/icon/device_issue.svg @@ -0,0 +1,8 @@ + + 资源 251 + + + + + + diff --git a/assets/img/icon/group.svg b/assets/img/icon/group.svg new file mode 100644 index 0000000..a198a9e --- /dev/null +++ b/assets/img/icon/group.svg @@ -0,0 +1 @@ +资源 248 \ No newline at end of file diff --git a/assets/img/icon/heart.svg b/assets/img/icon/heart.svg new file mode 100644 index 0000000..69e3f26 --- /dev/null +++ b/assets/img/icon/heart.svg @@ -0,0 +1,15 @@ + + 资源 242 + + + + + + diff --git a/assets/img/icon/in_bed.svg b/assets/img/icon/in_bed.svg new file mode 100644 index 0000000..d5f67a6 --- /dev/null +++ b/assets/img/icon/in_bed.svg @@ -0,0 +1,8 @@ + + 资源 253 + + + + + + diff --git a/assets/img/icon/loading.json b/assets/img/icon/loading.json new file mode 100644 index 0000000..8eb7d21 --- /dev/null +++ b/assets/img/icon/loading.json @@ -0,0 +1 @@ +{"v":"5.9.4","fr":25,"ip":0,"op":21,"w":720,"h":720,"nm":"合成 6","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"loading2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[360,264.721,0],"ix":2,"l":2},"a":{"a":0,"k":[-368.2,-431.779,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":5,"s":[0,0,100]},{"t":10,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-7.439],[7.44,0],[0,7.44],[-7.439,0]],"o":[[0,7.44],[-7.439,0],[0,-7.439],[7.44,0]],"v":[[-354.729,-431.779],[-368.2,-418.309],[-381.67,-431.779],[-368.2,-445.25]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"loading12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[411.342,279.796,0],"ix":2,"l":2},"a":{"a":0,"k":[-316.857,-416.704,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":1,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":6,"s":[0,0,100]},{"t":11,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.022,-6.258],[6.258,4.022],[-4.022,6.258],[-6.258,-4.022]],"o":[[-4.022,6.258],[-6.259,-4.022],[4.022,-6.258],[6.258,4.022]],"v":[[-305.525,-409.421],[-324.14,-405.372],[-328.189,-423.986],[-309.575,-428.036]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"loading11","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[446.384,320.236,0],"ix":2,"l":2},"a":{"a":0,"k":[-281.816,-376.264,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":2,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[0,0,100]},{"t":12,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[6.767,-3.091],[3.09,6.767],[-6.767,3.091],[-3.09,-6.767]],"o":[[-6.767,3.09],[-3.091,-6.767],[6.767,-3.09],[3.09,6.767]],"v":[[-276.22,-364.011],[-294.069,-370.668],[-287.411,-388.517],[-269.563,-381.859]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"loading10","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[453.999,373.201,0],"ix":2,"l":2},"a":{"a":0,"k":[-274.201,-323.299,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":3,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[0,0,100]},{"t":13,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[7.364,1.059],[-1.059,7.364],[-7.364,-1.059],[1.059,-7.364]],"o":[[-7.364,-1.059],[1.059,-7.364],[7.364,1.059],[-1.059,7.364]],"v":[[-276.117,-309.965],[-287.534,-325.216],[-272.283,-336.632],[-260.867,-321.382]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"loading9","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[431.77,421.876,0],"ix":2,"l":2},"a":{"a":0,"k":[-296.429,-274.624,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":4,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[0,0,100]},{"t":14,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.622,4.872],[-4.872,5.622],[-5.622,-4.872],[4.872,-5.622]],"o":[[-5.622,-4.872],[4.872,-5.622],[5.622,4.872],[-4.872,5.622]],"v":[[-305.25,-264.444],[-306.609,-283.445],[-287.608,-284.804],[-286.249,-265.803]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"loading8","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[386.755,450.805,0],"ix":2,"l":2},"a":{"a":0,"k":[-341.445,-245.695,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":5,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[0,0,100]},{"t":15,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.096,7.138],[-7.138,2.096],[-2.096,-7.138],[7.138,-2.096]],"o":[[-2.096,-7.138],[7.138,-2.096],[2.096,7.138],[-7.138,2.096]],"v":[[-354.369,-241.9],[-345.24,-258.619],[-328.52,-249.49],[-337.65,-232.77]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"loading7","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[333.245,450.805,0],"ix":2,"l":2},"a":{"a":0,"k":[-394.954,-245.695,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":6,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":11,"s":[0,0,100]},{"t":16,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-2.096,7.138],[-7.138,-2.096],[2.096,-7.138],[7.138,2.096]],"o":[[2.096,-7.138],[7.138,2.096],[-2.096,7.138],[-7.138,-2.096]],"v":[[-407.879,-249.49],[-391.159,-258.619],[-382.03,-241.9],[-398.75,-232.77]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"loading6","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[288.23,421.876,0],"ix":2,"l":2},"a":{"a":0,"k":[-439.97,-274.624,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":7,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":12,"s":[0,0,100]},{"t":17,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-5.622,4.872],[-4.872,-5.622],[5.622,-4.872],[4.872,5.622]],"o":[[5.622,-4.872],[4.872,5.622],[-5.622,4.872],[-4.872,-5.622]],"v":[[-448.791,-284.804],[-429.79,-283.445],[-431.149,-264.444],[-450.15,-265.803]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"loading5","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[266.001,373.201,0],"ix":2,"l":2},"a":{"a":0,"k":[-462.199,-323.299,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":13,"s":[0,0,100]},{"t":18,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.364,1.059],[-1.059,-7.364],[7.364,-1.059],[1.059,7.364]],"o":[[7.364,-1.059],[1.059,7.364],[-7.364,1.059],[-1.059,-7.364]],"v":[[-464.116,-336.632],[-448.866,-325.216],[-460.282,-309.965],[-475.532,-321.382]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"loading4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[273.616,320.236,0],"ix":2,"l":2},"a":{"a":0,"k":[-454.583,-376.264,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":14,"s":[0,0,100]},{"t":19,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-6.767,-3.09],[3.091,-6.767],[6.767,3.09],[-3.09,6.767]],"o":[[6.767,3.091],[-3.09,6.767],[-6.767,-3.091],[3.09,-6.767]],"v":[[-448.988,-388.517],[-442.33,-370.668],[-460.179,-364.011],[-466.836,-381.859]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"loading3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[308.658,279.796,0],"ix":2,"l":2},"a":{"a":0,"k":[-419.542,-416.704,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[115,115,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[0,0,100]},{"t":20,"s":[115,115,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.022,-6.258],[6.259,-4.022],[4.022,6.258],[-6.258,4.022]],"o":[[4.022,6.258],[-6.259,4.022],[-4.022,-6.258],[6.258,-4.022]],"v":[[-408.21,-423.986],[-412.259,-405.372],[-430.874,-409.421],[-426.825,-428.036]],"c":true},"ix":2},"nm":"路径 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.019607843831,0.019607843831,0.019607843831,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"组 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":75,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/img/icon/not_bed.svg b/assets/img/icon/not_bed.svg new file mode 100644 index 0000000..07ba46a --- /dev/null +++ b/assets/img/icon/not_bed.svg @@ -0,0 +1 @@ +资源 247 \ No newline at end of file diff --git a/assets/img/icon/nulldata.svg b/assets/img/icon/nulldata.svg new file mode 100644 index 0000000..862f3ad --- /dev/null +++ b/assets/img/icon/nulldata.svg @@ -0,0 +1 @@ +资源 292 \ No newline at end of file diff --git a/assets/img/icon/refresh.svg b/assets/img/icon/refresh.svg new file mode 100644 index 0000000..9adcd93 --- /dev/null +++ b/assets/img/icon/refresh.svg @@ -0,0 +1 @@ +资源 232 \ No newline at end of file diff --git a/assets/img/icon/share.svg b/assets/img/icon/share.svg new file mode 100644 index 0000000..60cfee1 --- /dev/null +++ b/assets/img/icon/share.svg @@ -0,0 +1 @@ +资源 285 \ No newline at end of file diff --git a/assets/img/icon/signal0.svg b/assets/img/icon/signal0.svg new file mode 100644 index 0000000..5ea3155 --- /dev/null +++ b/assets/img/icon/signal0.svg @@ -0,0 +1 @@ +资源 246 \ No newline at end of file diff --git a/assets/img/icon/signal1.svg b/assets/img/icon/signal1.svg new file mode 100644 index 0000000..ff980f9 --- /dev/null +++ b/assets/img/icon/signal1.svg @@ -0,0 +1 @@ +资源 245 \ No newline at end of file diff --git a/assets/img/icon/signal2.svg b/assets/img/icon/signal2.svg new file mode 100644 index 0000000..0d5401f --- /dev/null +++ b/assets/img/icon/signal2.svg @@ -0,0 +1 @@ +资源 244 \ No newline at end of file diff --git a/assets/img/icon/signal3.svg b/assets/img/icon/signal3.svg new file mode 100644 index 0000000..a72c553 --- /dev/null +++ b/assets/img/icon/signal3.svg @@ -0,0 +1 @@ +资源 243 \ No newline at end of file diff --git a/assets/img/icon/singal4.svg b/assets/img/icon/singal4.svg new file mode 100644 index 0000000..0211af0 --- /dev/null +++ b/assets/img/icon/singal4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/img/icon/snore.svg b/assets/img/icon/snore.svg new file mode 100644 index 0000000..dbedebd --- /dev/null +++ b/assets/img/icon/snore.svg @@ -0,0 +1,19 @@ + + 资源 240 + + + + + + + + diff --git a/assets/img/icon/tips.svg b/assets/img/icon/tips.svg new file mode 100644 index 0000000..b258c59 --- /dev/null +++ b/assets/img/icon/tips.svg @@ -0,0 +1 @@ +资源 236 \ No newline at end of file diff --git a/assets/img/icon/upgrade.svg b/assets/img/icon/upgrade.svg new file mode 100644 index 0000000..a2972ed --- /dev/null +++ b/assets/img/icon/upgrade.svg @@ -0,0 +1,8 @@ + + 资源 250 + + + + + + diff --git a/assets/img/icon/wifi1.svg b/assets/img/icon/wifi1.svg new file mode 100644 index 0000000..d9e66d3 --- /dev/null +++ b/assets/img/icon/wifi1.svg @@ -0,0 +1 @@ +资源 226 \ No newline at end of file diff --git a/assets/img/icon/wifi2.svg b/assets/img/icon/wifi2.svg new file mode 100644 index 0000000..ada184b --- /dev/null +++ b/assets/img/icon/wifi2.svg @@ -0,0 +1 @@ +资源 227 \ No newline at end of file diff --git a/assets/img/icon/wifi3.svg b/assets/img/icon/wifi3.svg new file mode 100644 index 0000000..1336275 --- /dev/null +++ b/assets/img/icon/wifi3.svg @@ -0,0 +1 @@ +资源 228 \ No newline at end of file diff --git a/assets/img/icon/wifi4.svg b/assets/img/icon/wifi4.svg new file mode 100644 index 0000000..c0ca522 --- /dev/null +++ b/assets/img/icon/wifi4.svg @@ -0,0 +1 @@ +资源 229 \ No newline at end of file diff --git a/assets/img/signal0.png b/assets/img/signal0.png new file mode 100644 index 0000000..3cb8640 Binary files /dev/null and b/assets/img/signal0.png differ diff --git a/assets/img/signal1.png b/assets/img/signal1.png new file mode 100644 index 0000000..12fbf21 Binary files /dev/null and b/assets/img/signal1.png differ diff --git a/assets/img/signal2.png b/assets/img/signal2.png new file mode 100644 index 0000000..44a9f17 Binary files /dev/null and b/assets/img/signal2.png differ diff --git a/assets/img/signal3.png b/assets/img/signal3.png new file mode 100644 index 0000000..b614204 Binary files /dev/null and b/assets/img/signal3.png differ diff --git a/assets/img/signal4.png b/assets/img/signal4.png new file mode 100644 index 0000000..3ee1f35 Binary files /dev/null and b/assets/img/signal4.png differ diff --git a/assets/img/wifi1.png b/assets/img/wifi1.png new file mode 100644 index 0000000..0e8dc50 Binary files /dev/null and b/assets/img/wifi1.png differ diff --git a/assets/img/wifi2.png b/assets/img/wifi2.png new file mode 100644 index 0000000..2697397 Binary files /dev/null and b/assets/img/wifi2.png differ diff --git a/assets/img/wifi3.png b/assets/img/wifi3.png new file mode 100644 index 0000000..aaec06b Binary files /dev/null and b/assets/img/wifi3.png differ diff --git a/assets/img/wifi4.png b/assets/img/wifi4.png new file mode 100644 index 0000000..bea4433 Binary files /dev/null and b/assets/img/wifi4.png differ diff --git a/assets/langs/zh_CN.json b/assets/langs/zh_CN.json index a8cd4f8..87e03fe 100644 --- a/assets/langs/zh_CN.json +++ b/assets/langs/zh_CN.json @@ -27,7 +27,17 @@ "设备报修": "设备报修", "操作说明": "操作说明", "关注我们": "关注我们", - "当前版本": "当前版本" + "当前版本": "当前版本", + "未命名": "未命名", + "去登录": "去登录", + "头像限制": "头像图片不能超过1MB", + "头像上传失败":"头像上传失败", + "未选择图片":"未选择图片", + "上传成功":"上传成功", + "保存失败":"保存失败", + "保存成功":"保存成功", + "昵称为空":"昵称不能为空", + "查询失败":"查询用户资料失败" }, "设备类型": { "我的设备": "我的设备", @@ -75,7 +85,12 @@ "是":"是", "否":"否", "确定绑定提示":"确定绑定该设备吗?", - "连接成功":"连接成功" + "连接成功":"连接成功", + "连接异常":"连接异常", + "连接":"连接", + "输入wifi密码":"请输入wifi密码", + "显示密码":"显示", + "不显示密码":"不显示" }, "登录页":{ "欢迎使用太和e护":"欢迎使用太和e护", @@ -122,7 +137,13 @@ "未连接":"未连接", "已连接":"已连接", "可用WLAN":"可用WLAN", - "刷新":"刷新" + "刷新":"刷新", + "密码为空":"密码不能为空", + "配网成功":"配网成功", + "配网失败":"配网失败", + "配网中":"配网中", + "需配网":"请给设备配置网络!" + }, "其他手机登录页":{ "输入内容":"输入手机号码/邮箱", @@ -153,13 +174,118 @@ "用户协议":"用户协议", "隐私协议":"隐私协议", "退出登录":"退出登录", - "注销账号":"注销账号" + "注销账号":"注销账号", + "退出成功":"退出成功", + "退出失败":"退出失败" + }, "关于我们":{ "标题":"关于我们" }, "服务器":{ "失败":"服务器内部错误,请联系管理员" - } + }, + "体征检测设备":{ + "标题":"体征监测设备", + "输入关键词":"输入关键词", + "搜索":"搜索", + "我的e护":"我的e护", + "云关爱":"云关爱", + "设备ID":"设备ID", + "更新时间":"更新时间", + "设备来源":"设备来源", + "设备状态":"设备状态", + "人员资料":"人员资料", + "实时体征":"实时体征", + "消息回看":"消息回看", + "健康报告":"健康报告", + "首页展示":"首页展示", + "设备详情":"设备详情", + "重命名":"重命名", + "删除":"删除" + }, + "设备详情":{ + "标题":"设备详情", + "MAC":"MAC", + "设备来源":"设备来源", + "型号":"型号", + "设备名称":"设备名称", + "网络状态":"网络状态", + "版本":"版本", + "更新状态":"更新状态", + "更新时间":"更新时间", + "故障状态":"故障状态" + }, + "实时体征":{ + "标题":"实时体征", + "姓名":"姓名", + "年龄":"年龄", + "设备ID":"设备ID", + "体重":"体重", + "提示":"提示:实时体征分析时,人员应躺在指定床位上且设备正常运行,保持身体静止不动状态下10~30秒左右,即可显示实时体征数据。" + }, + "待开发":{ + "提示":"功能开发中..." + }, + "扫一扫":{ + "标题":"扫一扫", + "提示":"请扫描设备二维码", + "相册":"相册", + "手电筒":"手电筒" + }, + "设备":{ + "设备列表请求失败":"设备列表请求失败", + "设备列表请求成功":"设备列表请求成功" + }, + "未命名":"未命名", + "未知时间":"-", + "设备ID":"设备ID", + "更新时间":"更新时间", + "已分享":"已分享", + "设备来源":"设备来源", + "云关爱":"云关爱", + "是否确认解绑":"是否确认解绑?", + "请求失败":"请求失败!", + "操作成功":"操作成功!", + "操作失败":"操作失败!", + "暂无数据":"暂无数据", + "请输入姓名":"请输入姓名", + "请选择生日":"请选择生日", + "请输入体重":"请输入体重", + "必须登录提示":"请先登录!", + "待开发功能":"功能开发中...", + "未知数据":"-", + "在离床":"在离床", + "体动":"体动", + "心率":"心率", + "打鼾":"打鼾", + "呼吸":"呼吸", + "呼吸暂停":"呼吸暂停", + "请保持静止":"请保持身体静止", + "健康报告":"健康报告", + "修改人员名称":"修改人员名称", + "在线":"在线", + "离线":"离线", + "有更新":"有更新", + "无更新":"无更新", + "有故障":"有故障", + "无故障":"无故障", + "人":"人", + "WIFI配置":"WIFI配置", + "分享设备":"分享设备", + "消息设置":"消息设置", + "设备分享":"设备分享", + "请输入对方手机号或邮箱":"请输入对方手机号或邮箱", + "微信好友一键分享":"微信好友一键分享", + "发送邀请":"发送邀请", + "要分享的设备":"要分享的设备", + "主设备":"主设备:", + "从设备":"从设备:", + "邀请成功":"邀请成功!", + "邀请失败":"邀请失败!", + "请输入手机号或者邮箱":"请输入手机号或者邮箱", + "请输入正确的手机号或者邮箱":"请输入正确的手机号或者邮箱", + "体征消息":"体征消息", + "系统消息":"系统消息" } \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 9835cb5..58892c1 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -28,6 +28,8 @@ LaunchScreen UIMainStoryboardFile Main + NSCameraUsageDescription + 需要使用相机扫码 UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/lib/common/color/ServiceConstant.dart b/lib/common/color/ServiceConstant.dart new file mode 100644 index 0000000..bafd189 --- /dev/null +++ b/lib/common/color/ServiceConstant.dart @@ -0,0 +1,29 @@ +class ServiceConstant { + static const String baseHost = "vsbs-test.he-info.cn";//服务地址 + static const String service_address = "http://$baseHost"; + + static String server_service = "/vsbs_app_server";//服务名称 + + static String send_code = "/api/verifycode/send";//发送验证码 + static String login = "/api/user/login";//登录 + static String get_bluetooth_device_status = "/api/device/status/info";//设备绑定状态 + static String device_bind = "/api/device/bind";//设备绑定 + static String device_type = "/api/device/type/list";//设备类型 + static String upload_file = "/api/file/info";//上传文件 + static String user_info = "/api/user/info";//更新用户资料,查询用户信息 + static String device_list = "/api/device/list";//绑定设备列表 + static String person_info = "/api/personnel/info";//用户资料 + static String sleep_report = "/api/device/sleep/data";//睡眠报告 + static String device_share = "/api/device/share";//分享设备 + static String message_list = "/api/mesasge/list";//消息列表 + static String device_show = "/api/device/bind";//更新设备绑定 + static String disease_list = "/api/personnel/disease/list";//获取疾病类型 + + + + static String logService = "$service_address/vsbs_log"; + static const String webSocketService = "wss://$baseHost/vsbs_ws_gateway/ws"; + static const String sleep_token = "HdAMjzqiYQKsmHRyEFKhfRGQ"; + static const String sleep_report_url = "https://alltoone.he-info.cn/h5/#/mattress/sleep/sleep"; + +} diff --git a/lib/common/util/Ble.dart b/lib/common/util/Ble.dart deleted file mode 100644 index ccb8e20..0000000 --- a/lib/common/util/Ble.dart +++ /dev/null @@ -1,1103 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:ef/ef.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_blue_plus/flutter_blue_plus.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:vbvs_app/common/util/MyUtils.dart'; -import 'package:vbvs_app/pages/common/selectDialog.dart'; - -String findInput = ""; -double rssichange = -90; -Function? findCall; - -int mcuMax = 500; - -String myuuid = "00000001-0000-1000-8000-00805F9B34FB"; - -int closeTime = 20000; - -Map devices = Map(); - -StreamSubscription? subscription_adapterState; - -Map connectList = - Map(); - -bool isBleStart = false; - -StreamSubscription>? onScanResultsListen; - -StreamSubscription? - streamSubscription_onConnectionStateChangedEvent; - -Timer? showToastTimer; - -List permissionInfo = [ - ["位置权限说明", "获得位置信息,连接附近的蓝牙设备与推荐附近门店"], - ["蓝牙权限说明", "搜索链接附近的蓝牙设备"], - ["附近设备权限说明", "搜索链接附近的蓝牙设备"] -]; - -bool isQuanShiDevice(name) { - return "$name".contains("S4-ZM-M94-4") || "$name".contains("S4-ZM-N94-4") || "$name".contains("MHT-SWES-D"); -} - -bool isMHTSWES(name) { - return "$name".contains("MHT-SWES-H") || "$name".contains("MHT-SWES-M") || "$name".contains("MHT-SWES-S"); -} - -bleParse() { - findCall = null; - print("bleParse 执行了"); - showToastTimer?.cancel(); - if (FlutterBluePlus.isScanningNow) { - FlutterBluePlus.stopScan(); - } -} - -start(Function fun, {Function? bleOnCall}) async { - if (isBleStart) { - var isOk = await requestBluetoothPermission(); - if (isOk == true) { - findCall = fun; - bleOnCall?.call(); - bleOnCall = null; - } - print("ble start again"); - return; - } - print("ble start"); - FlutterBluePlus.setLogLevel(LogLevel.info, color: false); - streamSubscription_onConnectionStateChangedEvent?.cancel(); - streamSubscription_onConnectionStateChangedEvent = - FlutterBluePlus.events.onConnectionStateChanged.listen((event) { - print('${event.device} ${event.connectionState}'); - }); - if (await FlutterBluePlus.isSupported == false) { - print("Bluetooth not supported by this device"); - return; - } - - // handle bluetooth on & off - // note: for iOS the initial state is typically BluetoothAdapterState.unknown - // note: if you have permissions issues you will get stuck at BluetoothAdapterState.unauthorized - subscription_adapterState?.cancel(); - int callIndex = 0; - subscription_adapterState = - FlutterBluePlus.adapterState.listen((BluetoothAdapterState state) async { - print(state); - print("蓝牙状态 $state"); - if (state == BluetoothAdapterState.on) { - showToastTimer?.cancel(); - // usually start scanning, connecting, etc - findCall = fun; - var isOk = await requestBluetoothPermission(); - if (isOk == true) { - bleOnCall?.call(); - bleOnCall = null; - } - } else { - // show an error to the user, etc - if (Platform.isIOS && - callIndex == 0 && - state == BluetoothAdapterState.unknown) { - callIndex++; - return; - } - if (Platform.isAndroid) { - showToast("请打开蓝牙开关"); - } - if (Platform.isIOS) { - showToast("请打开蓝牙开关且开启蓝牙权限"); - await showCustomConfirmAndCancelDialog( - Get.context!, "请在“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限", - confirmName: "去设置") - .then((msg) async { - if (msg == "confirm") { - openAppSettings(); - } - }); - } - isBleStart = false; - showToastTimer?.cancel(); - showToastTimer = Timer.periodic(const Duration(seconds: 5), (t) { - if (t.tick > 3) { - t.cancel(); - } - if (Platform.isAndroid) { - showToast("请打开蓝牙开关"); - } - if (Platform.isIOS) { - showToast("请打开蓝牙开关且开启蓝牙权限"); - } - }); - } - callIndex++; - }); - - // turn on bluetooth ourself if we can - // for iOS, the user controls bluetooth enable/disable - if (Platform.isAndroid && - FlutterBluePlus.adapterStateNow != BluetoothAdapterState.on) { - showPermissionInfoDialog(Get.context!, permissionInfo); - FlutterBluePlus.turnOn().then((e) { - Get.back(); - }).catchError((e) { - Get.back(); - }); - } - - var timer = null; - onScanResultsListen?.cancel(); - onScanResultsListen = FlutterBluePlus.onScanResults.listen( - (List results) { - // print(results.length); - for (ScanResult result in results) { - // if (result.device.id.toString().contains("A3:76")) { - // print("$result"); - // } - Map d = { - "updateTime": DateTime.now().millisecondsSinceEpoch, - "name": result.device.advName, - "id": result.device.remoteId.str, - "rssi": result.rssi, - "device": result.device, - "connectable": result.advertisementData.connectable - }; - Map> m_d = result.advertisementData.manufacturerData; - m_d.keys.toList().forEach((v) { - if (v == 65517 && m_d[65517]?.length != 0) { - List a = [0, 0, ...?m_d[65517]]; - advertisDataFormatter(a, d); - } else if (v == 11125 && m_d[11125]?.length == 8) { - List a = [...?m_d[11125]]; - d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()}; - } else if (m_d[v]?.length == 8 && isQuanShiDevice(d["name"])) { - List a = [...?m_d[v]]; - d['adData'] = {'deviceId': ab2str(a.sublist(2, 8)).toUpperCase()}; - } else if (m_d[v]?.length == 4 && isMHTSWES(d["name"])) { - ByteData bd = ByteData(2); - bd.setUint16(0, v, Endian.little); - List a = [bd.getUint8(0), bd.getUint8(1), ...?m_d[v]]; - d['adData'] = {'deviceId': ab2str(a).toUpperCase()}; - } else if (m_d[v]?.length == 6 && isMHTSWES(d["name"])) { - List a = [...?m_d[v]]; - d['adData'] = {'deviceId': ab2str(a).toUpperCase()}; - } - }); - devices[d['id']] = d; - // print('Device found: ${result.device.name}, ${result.device.id}'); - if (timer == null) { - timer = 1; - timer = Future.delayed(const Duration(microseconds: 300), () { - timer = null; - find(); - }); - } - } - }, - onError: (e) => print(e), - ); -} - -// Future locationCheck({bool isGetLocation = true}) async { -// // 先查看定位服务是否开启 -// bool b = await Geolocator.isLocationServiceEnabled(); -// if (b == false) { -// if (Platform.isAndroid) { -// showToast("请开启系统位置开关", closeTime: 5); -// await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关", -// confirmName: "去设置") -// .then((msg) async { -// if (msg == "confirm") { -// // await Geolocator.openLocationSettings(); -// await openGeolocatorLocationSettingsAndWait(); -// b = await Geolocator.isLocationServiceEnabled(); -// } -// }); -// } -// if (Platform.isIOS) { -// showToast("请开启系统定位服务与定位权限", closeTime: 5); -// await showCustomConfirmAndCancelDialog(Get.context!, -// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限", -// confirmName: "去设置") -// .then((msg) async { -// if (msg == "confirm") { -// // await Geolocator.openAppSettings(); -// await openGeolocatorAppSettingsAndWait(); -// b = await Geolocator.isLocationServiceEnabled(); -// } -// }); -// } -// } - -// var permission = await Geolocator.checkPermission(); -// if (permission == LocationPermission.denied || -// permission == LocationPermission.deniedForever) { -// showPermissionInfoDialog(Get.context!, [permissionInfo[0]]); -// permission = await Geolocator.requestPermission().catchError((e) { -// Get.back(); -// }); -// Get.back(); -// if (permission == LocationPermission.deniedForever) { -// if (Platform.isAndroid) { -// await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)", -// confirmName: "去设置") -// .then((msg) async { -// if (msg == "confirm") { -// // await Geolocator.openAppSettings(); -// await openGeolocatorAppSettingsAndWait(); -// } -// }); -// } -// if (Platform.isIOS) { -// await showCustomConfirmAndCancelDialog(Get.context!, -// "请在“设置-隐私与安全性-定位服务”中开启定位服务开关或者在“设置-APP”中找到对应APP开启定位服务权限", -// confirmName: "去设置") -// .then((msg) async { -// if (msg == "confirm") { -// // await Geolocator.openAppSettings(); -// await openGeolocatorAppSettingsAndWait(); -// } -// }); -// } -// } -// } - -// Position? position; -// if (isGetLocation) { -// if (b && -// permission != LocationPermission.denied && -// permission != LocationPermission.deniedForever) { -// try { -// position = await Geolocator.getCurrentPosition( -// locationSettings: -// const LocationSettings(timeLimit: Duration(seconds: 5))); -// print("$position"); -// } catch (e) { -// print("error $e"); -// } -// } -// } -// return position; -// } - -/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, -/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 -/// ------开始----- -Future openGeolocatorAppSettingsAndWait() async { - final geolocatorAppSettingsLifecycleState = - GeolocatorAppSettingsLifecycleEventHandler(); - - WidgetsBinding.instance.addObserver(geolocatorAppSettingsLifecycleState); - - // await Geolocator.openAppSettings(); - - await geolocatorAppSettingsLifecycleState.waitForResume(); - - WidgetsBinding.instance.removeObserver(geolocatorAppSettingsLifecycleState); - - print('AppSettings have been opened and user has returned'); -} - -class GeolocatorAppSettingsLifecycleEventHandler - extends WidgetsBindingObserver { - final Completer _completer = Completer(); - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed && !_completer.isCompleted) { - _completer.complete(); - } - } - - Future waitForResume() => _completer.future; -} - -/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, -/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 -/// ------结束----- - -/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, -/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 -/// ------开始----- -Future openGeolocatorLocationSettingsAndWait() async { - final geolocatorLocationSettingsLifecycleState = - GeolocatorLocationSettingsLifecycleEventHandler(); - - WidgetsBinding.instance.addObserver(geolocatorLocationSettingsLifecycleState); - - // await Geolocator.openLocationSettings(); - - await geolocatorLocationSettingsLifecycleState.waitForResume(); - - WidgetsBinding.instance - .removeObserver(geolocatorLocationSettingsLifecycleState); - - print('LocationSettings have been opened and user has returned'); -} - -class GeolocatorLocationSettingsLifecycleEventHandler - extends WidgetsBindingObserver { - final Completer _completer = Completer(); - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (state == AppLifecycleState.resumed && !_completer.isCompleted) { - _completer.complete(); - } - } - - Future waitForResume() => _completer.future; -} - -/// 使用 Completer 来等待用户返回应用,自定义的 LifecycleEventHandler 类将监视应用程序的生命周期状态, -/// 并在应用程序恢复时完成,有效地等待用户从设置屏幕返回 -/// ------结束----- - -Future requestBluetoothPermission() async { - if (Platform.isIOS) { - PermissionStatus isBleGranted = await Permission.bluetooth.request(); - print('checkBlePermissions-ios, isBleGranted=$isBleGranted'); - if (isBleGranted.isGranted) { - startBluetoothScanning(); - return true; - } else { - showToast("蓝牙开关或蓝牙权限未开启,请开启蓝牙开关与蓝牙权限", closeTime: 7); - await showCustomConfirmAndCancelDialog( - Get.context!, "请“设置-蓝牙”中打开蓝牙开关或者在“设置-APP”中找到对应APP开启蓝牙权限", - confirmName: "去设置") - .then((msg) async { - if (msg == "confirm") { - openAppSettings(); - } - }); - return false; - } - } else if (Platform.isAndroid) { - // 检查蓝牙扫描权限 - String error = ""; - bool isShowDialog = false; - if (!await Permission.bluetoothScan.isGranted) { - if (!isShowDialog) { - isShowDialog = true; - showPermissionInfoDialog(Get.context!, permissionInfo); - } - PermissionStatus status = await Permission.bluetoothScan.request(); - if (!status.isGranted) { - error += "蓝牙扫描权限未开启,请开启附近设备权限"; - } - print("蓝牙扫描 $status"); - } - - // 检查蓝牙连接权限 - if (!await Permission.bluetoothConnect.isGranted) { - if (!isShowDialog) { - isShowDialog = true; - showPermissionInfoDialog(Get.context!, permissionInfo); - } - PermissionStatus status = await Permission.bluetoothConnect.request(); - if (!status.isGranted) { - if (error.isNotEmpty) { - error += "\n"; - } - error += "蓝牙连接权限未开启,请开启蓝牙权限"; - } - print("蓝牙连接 $status"); - } - - // 检查位置权限 - if (!await Permission.location.isGranted) { - if (!isShowDialog) { - isShowDialog = true; - showPermissionInfoDialog(Get.context!, permissionInfo); - } - //检查 - PermissionStatus status = await Permission.location.request(); - print("位置权限 $status"); - if (!status.isGranted) { - await showCustomConfirmAndCancelDialog(Get.context!, "请开启位置权限(打开精确位置)", - confirmName: "去设置") - .then((msg) async { - if (msg == "confirm") { - await openGeolocatorAppSettingsAndWait(); - // await Future.delayed(const Duration(seconds: 2)); - print('Proceeding with other operations'); - status = await Permission.location.request(); - } - }); - } - if (!status.isGranted) { - if (error.isNotEmpty) { - error += "\n"; - } - error += "位置权限未开启,请开启位置权限"; - } - } - - if (isShowDialog) { - Get.back(); - } - - if (await Permission.bluetoothScan.isGranted && - await Permission.bluetoothConnect.isGranted && - await Permission.location.isGranted) { - // bool b = await Geolocator.isLocationServiceEnabled(); - bool b =false; - if (b == false) { - await showCustomConfirmAndCancelDialog(Get.context!, "请开启系统位置开关", - confirmName: "去设置") - .then((msg) async { - if (msg == "confirm") { - // bool isOpen = await Geolocator.openLocationSettings(); - await openGeolocatorLocationSettingsAndWait(); - // await Future.delayed(const Duration(seconds: 2)); - print('Proceeding with other operations'); - // b = await Geolocator.isLocationServiceEnabled(); - } - }); - } - if (b) { - isBleStart = true; - startBluetoothScanning(); - return true; - } else { - showToast("系统位置开关未开启,请开启系统位置开关", closeTime: 7); - return false; - } - } else { - Timer(Duration.zero, () async { - showToast(error, closeTime: 7); - }); - return false; - } - } else { - showToast("当前系统不支持蓝牙,无法使用此功能", closeTime: 7); - return false; - } -} - -void find() { - int len = devices.length; - String reg = findInput.toLowerCase().replaceAll(RegExp("[::]"), ""); - List list = devices.values.toList(); - for (int i = 0; i < len; i++) { - Map d = list[i]; - bool flag = d['rssi'] >= rssichange; - if (flag) { - bool a = d['name'].toString().toLowerCase().contains(reg); - a = a || - d['id'] - .toString() - .toLowerCase() - .replaceAll(RegExp("[::]"), "") - .contains(reg); - // if(d.adData && d.adData.deviceId) { - // a = a || d.adData.deviceId.toLowerCase().replace(/[::]/g, "").indexOf(reg) > -1 - // } - // if(d.adData && d.adData.version && reg) { - // a = a || (d.adData.version + "").indexOf(reg) > -1 - // } - flag = flag && a; - } - if (flag) { - flag = flag && - DateTime.now().millisecondsSinceEpoch - d['updateTime'] < closeTime; - } - if (!flag) { - d['isClose'] = true; - } else { - d['isClose'] = false; - } - } - var result = list.where((item) => item['isClose'] == false).toList(); - if (result == null) { - findCall?.call([]); - } else { - // print(result); - result.sort((a, b) { - // print("${a['rssi']},${b['rssi']}"); - return b['rssi'] - a['rssi']; - }); - if (result.length > 0) { - findCall?.call(result.where((d) => d?['adData'] != null).toList()); - } else { - findCall?.call(result); - } - } -} - -String ab2str(List buffer) { - return buffer.map((x) => x.toRadixString(16).padLeft(2, '0')).join(''); -} - -void advertisDataFormatter(var a, item) { - Map obj = {}; - try { - if (a[2] == 1) { - obj['sn'] = a[3]; - obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); - obj['b'] = a[10]; - obj['h'] = a[11]; - obj['t'] = a[12]; - item['adData'] = obj; - } else if (a[2] == 2) { - obj['sn'] = a[3]; - obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); - obj['b'] = a[10]; - obj['h'] = a[11]; - obj['t'] = a[12]; - obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; - obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; - ByteData byteData = ByteData.sublistView( - Uint8List.fromList(a.sublist(14, 18).reversed.toList())); - obj['version'] = byteData.getUint32(0); - item['adData'] = obj; - } else if (a[2] == 3) { - List otherstr = []; - obj['sn'] = a[3]; - obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); - obj['b'] = a[10]; - obj['h'] = a[11]; - obj['t'] = a[12]; - obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; - obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; - - if ((a[13] & 4) == 4) { - otherstr.add('呼吸暂停'); - } - - if ((a[13] & 8) == 8 && (a[13] & 1) == 1) { - obj['isbed'] = '在床'; - } else { - obj['isbed'] = '离床'; - } - - if ((a[13] & 16) == 16) { - otherstr.add('授权过期'); - } - - if ((a[13] & 64) == 64) { - otherstr.add('设备休眠'); - } - - obj['other'] = otherstr.join('、'); - - ByteData byteData = ByteData.sublistView( - Uint8List.fromList(a.sublist(14, 18).reversed.toList())); - obj['version'] = byteData.getUint32(0); - - ByteData qsnData = - ByteData.sublistView(Uint8List.fromList(a.sublist(17, 19))); - obj['qsn'] = qsnData.getUint16(0) * 256 + obj['sn']; - - item['adData'] = obj; - } else if (a.length > 17) { - obj['sn'] = a[3]; - obj['deviceId'] = ab2str(a.sublist(4, 10)).toUpperCase(); - obj['b'] = a[10]; - obj['h'] = a[11]; - obj['t'] = a[12]; - obj['net'] = (a[13] & 1) == 1 ? '在线' : '离线'; - obj['flag'] = (a[13] & 2) == 2 ? '异常' : '正常'; - - ByteData byteData = ByteData.sublistView( - Uint8List.fromList(a.sublist(14, 18).reversed.toList())); - obj['version'] = byteData.getUint32(0); - - item['adData'] = obj; - } - } catch (e) { - print(e); - } -} - -void startBluetoothScanning() async { - // 开始扫描附近的蓝牙设备 - if (FlutterBluePlus.isScanningNow) { - await FlutterBluePlus.stopScan(); - } - FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); -} - -getOneConnectedDeviceProp(id) { - return connectList[id]; -} - -void setOther(device, connectedDeviceProp, fun) async { - try { - List services = await device.discoverServices(); - print(services); - bool isNotify = false; - bool isWrite = false; - for (var service in services) { - if (connectedDeviceProp.connectedDevicePropType == - ConnectedDevicePropType.JunHe) { - if (service.uuid.str128.toUpperCase() != myuuid) { - continue; - } - } - // print("serviece $service"); - for (BluetoothCharacteristic element in service.characteristics) { - if (isNotify == false && element.properties.notify) { - await element.setNotifyValue(true); - print("setNotifyValue 完成"); - connectedDeviceProp.createLisetenReceive(element); - isNotify = true; - if (connectedDeviceProp.connectedDevicePropType == - ConnectedDevicePropType.JunHe) { - continue; - } - if (connectedDeviceProp.connectedDevicePropType == - ConnectedDevicePropType.MHT) { - continue; - } - } - if (isWrite == false && element.properties.write) { - connectedDeviceProp?.writeCharacteristic = element; - isWrite = true; - print("$element"); - } - } - } - if (!isWrite || !isNotify) { - if (connectedDeviceProp != null) { - disconnect(connectedDeviceProp!); - } - print("service 订阅失败 isWrite $isWrite isNotify $isNotify"); - fun['fail']?.call("service 订阅失败 isWrite $isWrite isNotify $isNotify"); - return; - } - print("service 注册完成"); - connectList[connectedDeviceProp.id] = connectedDeviceProp; - connectedDeviceProp.createListenState(); - if (connectedDeviceProp.connectedDevicePropType == - ConnectedDevicePropType.JunHe) { - connectedDeviceProp.heartbeat(); - } - print("回调成功"); - fun['success']?.call(connectedDeviceProp); - } catch (e) { - print("连接失败 执行失败回调 错误: $e"); - if (connectedDeviceProp != null) { - disconnect(connectedDeviceProp!); - } - print("连接失败 执行失败回调"); - fun['fail']?.call(e); - } -} - -// 连接设备 -void connectToDevice(fun) async { - if (fun != null) { - return; - } - BluetoothDevice device = fun.device; - ConnectedDeviceProp? connectedDeviceProp = - getOneConnectedDeviceProp(device.remoteId.str); - if (connectedDeviceProp != null) { - disconnect(connectedDeviceProp); - Future.delayed(const Duration(seconds: 1), () { - connectToDevice(fun); - }); - return; - } - try { - print("connecting"); - await device.connect(timeout: const Duration(seconds: 8)); - print("device.connect success"); - ConnectedDevicePropType connectedDevicePropType = - ConnectedDevicePropType.JunHe; - connectedDeviceProp = ConnectedDeviceProp( - connectDevice: device, - fun: fun, - connectedDevicePropType: connectedDevicePropType); - if (Platform.isAndroid) { - await device.requestMtu(mcuMax); - } - Timer(const Duration(milliseconds: 1000), () { - setOther(device, connectedDeviceProp, fun); - }); - } catch (e) { - print("连接失败 执行失败回调 错误: $e"); - if (connectedDeviceProp != null) { - disconnect(connectedDeviceProp!); - } - print("连接失败 执行失败回调"); - fun['fail']?.call(e); - } -} - -bool jsJunHe(String name) { - return "$name".contains("AITH-V2") || "$name".contains("AITH-V2") || "$name".contains("AITH-V2"); -} - - -void disconnect(ConnectedDeviceProp connectedDeviceProp) { - connectedDeviceProp.closeHeartBeat(); - connectList.remove(connectedDeviceProp.id); - connectedDeviceProp.closeConnectedDeviceProp(); -} - -void closeAll() { - findCall = null; - connectList.values.toList().forEach((element) { - disconnect(element); - }); -} - -enum ConnectedDevicePropType { JunHe, QuanShi, MHT } - -class ConnectedDeviceProp { - ConnectedDevicePropType connectedDevicePropType; - Timer? heartbeatTimer = null; - int _seq = 0; - var connectDevice; - var writeCharacteristic; - var listenState; - StreamSubscription>? lisetenReceive; - Map fun; - List receiveMethods = []; - List logList = []; - Function? logChange; - ConnectedDeviceProp( - {required this.connectDevice, - required Map this.fun, - this.connectedDevicePropType = ConnectedDevicePropType.JunHe}); - List receiveLogArr = []; - int deviceType = 2; - int encodeType = 2; - List sendArr = []; - double sendExecAverage = 100; - bool isClose = false; - - String get id { - return connectDevice.remoteId.str; - } - - int sum_ab(dv) { - ByteData sum = ByteData(1); - for (int i = 0; i < dv.buffer.lengthInBytes; i++) { - sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0)); - } - return sum.getUint8(0); - } - - void heartbeat() { - closeHeartBeat(); - heartbeatTimer = Timer.periodic(const Duration(seconds: 8), (timer) { - ByteData dv = ByteData(4); - dv.setUint8(0, 4); - dv.setUint8(2, seq); - dv.setUint8(3, 5); - dv.setUint8(1, sum_ab(dv)); - writeBle(dv); - }); - } - - closeHeartBeat() { - if (heartbeatTimer != null) { - heartbeatTimer!.cancel(); - heartbeatTimer = null; - } - } - - ByteData str2ab_oneByte(String str, {int startLength = 0}) { - Uint8List utf8str = utf8.encode(str); - int len = utf8str.length + startLength; - ByteData buf2 = ByteData.sublistView(utf8str); - ByteData buf = ByteData(len); - for (int i = startLength; i < len; i++) { - buf.setUint8(i, buf2.getUint8(i - startLength)); - } - return buf; - } - - void write3OfString(sendDate, {Function? success, Function? fail}) { - ByteData dv = str2ab_oneByte(sendDate, startLength: 4); - int len = dv.buffer.lengthInBytes; - dv.setUint8(0, len); - dv.setUint8(2, seq); - dv.setUint8(3, 8 * 16 + 3); - dv.setUint8(1, sum_ab(dv)); - writeBle(dv, success: success, fail: fail); - } - - void writeBle(ByteData d, {Function? success, Function? fail}) { - Uint8List d_ = Uint8List.view(d.buffer); - if (sendArr.length == 0) { - write(d_, success, fail); - } - sendArr.insert(0, {"d": d_, "success": success, "fail": fail}); - } - - void write(Uint8List d, Function? success, Function? fail, {int exec = 100}) { - if (writeCharacteristic != null) { - // try { - // if (d[3] == 8 * 16 + 3) { - // print( - // "blewrite s = $sendExecAverage d = ${utf8.decode(d.sublist(4))}"); - // } else { - // print("ble last write d = ${d[3]}"); - // } - // } catch (e) { - // print("write logprint error $e"); - // } - writeCharacteristic.write(d, withoutResponse: true).then((e) { - // print("write success $e"); - if (connectedDevicePropType != ConnectedDevicePropType.JunHe) { - print("发送 $d"); - } - if (exec > 95) { - sendExecAverage = sendExecAverage + 0.5; - } - if (sendExecAverage > 99) { - sendExecAverage = 99; - } - if (sendArr.length > 0) { - sendArr.removeLast(); - Map last = sendArr.last; - write(last["d"], last["success"], last["fail"]); - } - success?.call(); - }).catchError((e) { - // print("exec = $exec , $e"); - if (exec < 0) { - print("$e"); - fail?.call(); - } - if (exec > -1 && isClose == false) { - int time = ((100.0 - sendExecAverage) * 5.0).toInt(); - if (exec < 80) { - time = (100 - exec) * 5; - sendExecAverage = exec * 1.0; - } else { - sendExecAverage = sendExecAverage - (100 - exec) * 0.1; - } - Timer(Duration(milliseconds: time), () { - write(d, success, fail, exec: exec - 1); - }); - } - }); - } - } - - void read6() { - ByteData dv = ByteData(4); - dv.setUint8(0, 4); - dv.setUint8(2, seq); - dv.setUint8(3, 6); - dv.setUint8(1, sum_ab(dv)); - writeBle(dv); - } - - addLog(String log) { - if (logList.length > 500) { - logList.removeRange(0, 50); - } - DateTime date = DateTime.now(); - String h = date.hour > 10 ? "${date.hour}" : "0${date.hour}"; - String m = date.minute > 10 ? "${date.minute}" : "0${date.minute}"; - String s = date.second > 10 ? "${date.second}" : "0${date.second}"; - logList.add({"time": "$h:$m:$s", "value": log}); - print("ble $id log: $log"); - if (logChange != null) { - logChange?.call(logList, log); - } - } - - createListenState() { - listenState = connectDevice.connectionState.listen((state) { - print('ble Device state $id $state'); - if (state == BluetoothConnectionState.disconnected) { - print('ble Device state $id disconnected'); - isClose = true; - disconnect(this); - fun['stateChange']?.call(state, this); - } - }); - } - - createLisetenReceive(BluetoothCharacteristic element) { - lisetenReceive = element.onValueReceived.listen((List value) { - if (connectedDevicePropType == ConnectedDevicePropType.JunHe) { - if (value.isEmpty) { - return; - } - bool isOk = sumCheck(value); - if (isOk) { - receiveMethods.forEach((m) { - m?.call(); - }); - yewuSwitch(value[3], value.sublist(4)); - } - } else { - print("onValueReceived $value"); - receiveLogArr.forEach((m) { - m(value); - }); - } - }); - } - - closeConnectedDeviceProp() { - isClose = true; - if (listenState != null) { - listenState?.cancel(); - } - if (lisetenReceive != null) { - lisetenReceive?.cancel(); - } - connectDevice?.disconnect(); - } - - int get seq { - int r = _seq % 256; - _seq++; - return r; - } - - bool sumCheck(List ab) { - ByteData dv = ByteData.sublistView(Uint8List.fromList(ab)); - - if (dv.getUint8(0) != ab.length) { - print("和校验失败:长度不对"); - return false; - } //和校验失败 - - if (sumList(ab) != dv.getUint8(1)) { - print("和校验失败: 校验失败"); - return false; - } - - return true; - } - - int sumList(List ab) { - ByteData dv = ByteData.sublistView(Uint8List.fromList(ab)); - ByteData sum = ByteData.sublistView(Uint8List(1)); - - sum.setUint8(0, dv.getUint8(0)); - - for (int i = 2; i < ab.length; i++) { - sum.setUint8(0, dv.getUint8(i) + sum.getUint8(0)); - } - - return sum.getUint8(0); - } - - List endLogValue = []; - Timer? endLogTimer; - - void yewuSwitch(int yewu, List abData) { - switch (yewu) { - case 7: - String error = ab2StrByType(abData); - print(error); - break; - - case 131: - List? logData; - if (abData.last != 10) { - int index = abData.lastIndexOf(10); - if (index == -1) { - index = abData.length; - endLogValue = [...endLogValue, ...abData.sublist(0, index)]; - } else { - logData = [...endLogValue, ...abData.sublist(0, index)]; - endLogValue = abData.sublist(index); - } - // if (index == -1) { - // index = abData.length; - // } - // if(endLogValue.isNotEmpty) { - // logData = [...endLogValue, ...abData.sublist(0, index)]; - // endLogValue = []; - // } else { - // logData = [...abData.sublist(0, index)]; - // } - // if(index != abData.length) { - // endLogValue = abData.sublist(index); - // } - } else { - int index = abData.length; - - if (endLogValue.isNotEmpty) { - logData = [...endLogValue, ...abData]; - } else { - logData = abData; - } - - endLogValue = []; - } - - if (endLogTimer != null) { - endLogTimer!.cancel(); - endLogTimer = null; - } - - if (endLogValue != null && endLogValue!.isNotEmpty) { - endLogTimer = Timer(Duration(milliseconds: 400), () { - String log = ab2StrByType(endLogValue!); - endLogValue = []; - addLog(log); - try { - receiveLogArr.forEach((m) { - m(log); - }); - } catch (e) { - print(e); - } - }); - } - - if (logData != null && logData.isNotEmpty) { - if (logData.length != 1 || logData[0] != 13) { - String log = ab2StrByType(logData!); - addLog(log); - try { - receiveLogArr.forEach((m) { - m(log); - }); - } catch (e) { - print(e); - } - } - } - // 处理逻辑 - break; - - case 132: - ByteData dv = ByteData.sublistView(Uint8List.fromList(abData)); - - for (int i = 0; i < abData.length;) { - int len = dv.getUint8(i); - yewuSwitch(dv.getUint8(i + 1), abData.sublist(i + 2, i + 1 + len)); - i = i + 1 + len; - } - - break; - - default: - break; - } - } - - String ab2StrByType(List abData) { - // Implement your logic for converting abData to String - String str = ""; - if (abData.isNotEmpty) { - try { - str = utf8.decode(abData); - } catch (e) { - str = "解析错误"; - } - } - return str; - } -} diff --git a/lib/common/util/DailyLogUtils.dart b/lib/common/util/DailyLogUtils.dart new file mode 100644 index 0000000..7d1d5f7 --- /dev/null +++ b/lib/common/util/DailyLogUtils.dart @@ -0,0 +1,87 @@ +import 'dart:io'; +import 'package:path_provider/path_provider.dart'; +import 'package:intl/intl.dart'; + +class DailyLogUtils { + // 获取日志文件路径(按日期命名) + static Future _getLogFile() async { + final dir = await getApplicationDocumentsDirectory(); + final date = DateFormat('yyyy-MM-dd').format(DateTime.now()); + final filePath = '${dir.path}/$date.log'; + final file = File(filePath); + if (!await file.exists()) { + await file.create(recursive: true); + } + return file; + } + + // 写入日志核心方法,带日志等级 + static Future _writeLogWithLevel(String level, String content) async { + final file = await _getLogFile(); + final now = DateTime.now(); + final time = DateFormat('HH:mm:ss').format(now); + final logLine = '[$time][$level] $content\n'; + await file.writeAsString(logLine, mode: FileMode.append); + } + + // 写入 info 日志(原 writeLog 保留) + static Future writeLog(String content) async { + await _writeLogWithLevel('INFO', content); + } + + // 写入 warning 日志 + static Future writeWarning(String content) async { + await _writeLogWithLevel('WARNING', content); + } + + // 写入 error 日志 + static Future writeError(String content) async { + await _writeLogWithLevel('ERROR', content); + } + + // 写入 debug 日志 + static Future writeDebug(String content) async { + await _writeLogWithLevel('DEBUG', content); + } + + // 读取当天日志 + static Future readTodayLog() async { + final file = await _getLogFile(); + return await file.readAsString(); + } + + // 获取所有日志文件(返回 File 列表) + static Future> listLogFiles() async { + final dir = await getApplicationDocumentsDirectory(); + final files = dir.listSync(); + return files.where((f) => f.path.endsWith('.log')).toList(); + } + + // 清除所有日志 + static Future clearAllLogs() async { + final files = await listLogFiles(); + for (final f in files) { + await File(f.path).delete(); + } + } + + /// 获取指定日期范围内的日志文件(包含起止日期) + static Future> getLogsBetween( + DateTime fromDate, DateTime toDate) async { + final dir = await getApplicationDocumentsDirectory(); + final logFiles = []; + final dateFormat = DateFormat('yyyy-MM-dd'); + + for (DateTime date = fromDate; + !date.isAfter(toDate); + date = date.add(Duration(days: 1))) { + final fileName = '${dateFormat.format(date)}.log'; + final file = File('${dir.path}/$fileName'); + if (await file.exists()) { + logFiles.add(file); + } + } + + return logFiles; + } +} diff --git a/lib/common/util/MyUtils.dart b/lib/common/util/MyUtils.dart index 6ea7faf..4d597c8 100644 --- a/lib/common/util/MyUtils.dart +++ b/lib/common/util/MyUtils.dart @@ -118,6 +118,57 @@ class MyUtils { curve: Curves.easeInOut, ); } + + static String formatBindTime(DateTime d) { + final DateFormat formatter = DateFormat('yyyy/MM/dd'); + return formatter.format(d); + } + + static DateTime? formatBirthdayTime(String? device) { + if (device == null || device.isEmpty) return null; + + try { + return DateTime.parse(device.replaceAll('/', '-')); // 替换为标准格式 + } catch (e) { + return null; // 解析失败时返回 null + } + } + + static int getAgeByDate(DateTime? formatBirthdayTime) { + if (formatBirthdayTime == null) return 0; + + final now = DateTime.now(); + int age = now.year - formatBirthdayTime.year; + + // 如果还没到今年生日,减一岁 + if (now.month < formatBirthdayTime.month || + (now.month == formatBirthdayTime.month && + now.day < formatBirthdayTime.day)) { + age--; + } + + return age; + } + + static String formatDateTimeWeek(DateTime date) { + DateTime now = DateTime.now(); + // 去除时间部分,仅比较年月日 + DateTime today = DateTime(now.year, now.month, now.day); + DateTime target = DateTime(date.year, date.month, date.day); + + if (target == today) { + return '今日'; + } + + const List weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + return weekdays[date.weekday % 7]; // Dart中星期日是7,要映射到索引0 + } + + /// 返回 MM/dd 格式 + static String formatDateTimeDay(DateTime date) { + String twoDigits(int n) => n.toString().padLeft(2, '0'); + return '${twoDigits(date.month)}/${twoDigits(date.day)}'; + } } Color stringToColor(String hexColor) { diff --git a/lib/component/NullDataComponentWidget.dart b/lib/component/NullDataComponentWidget.dart new file mode 100644 index 0000000..a69235f --- /dev/null +++ b/lib/component/NullDataComponentWidget.dart @@ -0,0 +1,77 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class NullDataWidget extends StatefulWidget { + const NullDataWidget({super.key}); + + @override + State createState() => _TestWidgetState(); +} + +class _TestWidgetState extends State { + final scaffoldKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + ThemeController themeController = Get.find(); + return GestureDetector( + child: Scaffold( + backgroundColor: Colors.transparent, + key: scaffoldKey, + body: SafeArea( + top: true, + child: Container( + decoration: BoxDecoration( + color: Colors.transparent, + ), + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 1, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: 56.rpx, + height: 69.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/nulldata.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc4, + ), + ), + ), + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height * 0.04, + constraints: BoxConstraints( + minHeight: 40, + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '暂无数据'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + useGoogleFonts: false, + fontFamily: "PingFangSC", + letterSpacing: 0.0, + fontSize: 26.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/component/home_page/DynamicReportDetailWidget.dart b/lib/component/home_page/DynamicReportDetailWidget.dart new file mode 100644 index 0000000..22a4082 --- /dev/null +++ b/lib/component/home_page/DynamicReportDetailWidget.dart @@ -0,0 +1,172 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/home_page/SleepDataModuleWidget.dart'; +import 'package:vbvs_app/component/home_page/SleepDateWidget.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class DynamicReportDetailWidget extends StatelessWidget { + final List sleepDateWidgets; + final List sleepDataModuleWidgets; + final ThemeController themeController = Get.find(); + final Map targetDevice; + + DynamicReportDetailWidget({ + required this.sleepDateWidgets, + required this.sleepDataModuleWidgets, + required this.targetDevice, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 25.rpx, 0, 25.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: + BorderRadius.circular(AppConstants().normal_container_radius), + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + _buildHeader(context), + _buildSleepDateWidgets(), + SizedBox(height: 20.rpx), + _buildSleepDataModuleWidgets(), + ], + ), + ), + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Container( + width: double.infinity, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${targetDevice['person']?['name'] == null ? '未命名'.tr : targetDevice['person']['name']}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc3, + borderRadius: 0, + padding: EdgeInsets.zero, + onTap: () { + String mac = targetDevice['mac']; + List selectedWidgets = sleepDateWidgets + .where( + (widget) => widget.isSelected == true, + ) + .toList(); + DateTime dateTime = DateTime.fromMillisecondsSinceEpoch( + int.parse(selectedWidgets[0].time!)); + String time = MyUtils.formatBindTime(dateTime); + String sleepReportUrl = + "${ServiceConstant.sleep_report_url}?mac=${mac}&token=${ServiceConstant.sleep_token}&date=${time}"; + Get.toNamed("/sleepReportPage", arguments: sleepReportUrl); + }, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '首页.报告详情'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 6.rpx, 0, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 14.rpx, + height: 14.rpx, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + ), + ], + ), + ); + } + + Widget _buildSleepDateWidgets() { + return Container( + width: double.infinity, + decoration: BoxDecoration(), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + children: sleepDateWidgets + .map((widget) => widget) + .toList() + .divide(SizedBox(width: 20.rpx)), + ), + ), + ); + } + + Widget _buildSleepDataModuleWidgets() { + // bool hasData = sleepDataModuleWidgets.any((w) { + // final data = w.data; + // return data['state'] != null && + // data['state'].toString().trim().isNotEmpty; + // }); + bool hasData = sleepDataModuleWidgets.length > 0; + + if (!hasData) { + return Container( + height: 100.rpx, + alignment: Alignment.center, + child: Text( + '暂无数据'.tr, + style: FlutterFlowTheme.of(Get.context!).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 28.rpx, + color: themeController.currentColor.sc3, + ), + ), + ); + } + + return Container( + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.max, + children: sleepDataModuleWidgets + .map((widget) => widget) + .toList() + .divide(SizedBox(width: 14.rpx)), + ), + ), + ); + } +} diff --git a/lib/component/home_page/SleepDataModuleWidget.dart b/lib/component/home_page/SleepDataModuleWidget.dart index 96bcbad..5d0cf74 100644 --- a/lib/component/home_page/SleepDataModuleWidget.dart +++ b/lib/component/home_page/SleepDataModuleWidget.dart @@ -1,14 +1,16 @@ -import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; class SleepDataModuleWidget extends StatefulWidget { - const SleepDataModuleWidget({super.key}); + final Map data; + + const SleepDataModuleWidget({super.key, required this.data}); @override State createState() => _SleepDataModuleWidgetState(); @@ -33,111 +35,137 @@ class _SleepDataModuleWidgetState extends State { @override Widget build(BuildContext context) { ThemeController themeController = Get.find(); - return Container( - width: MediaQuery.sizeOf(context).width * 0.27, - constraints: BoxConstraints( - minWidth: 200.rpx, - ), - decoration: BoxDecoration( - color: themeController.currentColor.sc5, - borderRadius: BorderRadius.circular(20.rpx), - ), - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB(18.rpx, 18.rpx, 18.rpx, 22.rpx), - child: Container( - decoration: BoxDecoration(), - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '离床次数', - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController.currentColor.sc3, + return ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: themeController.currentColor.sc3, + borderRadius: 20.rpx, + padding: EdgeInsetsDirectional.fromSTEB(18.rpx, 18.rpx, 18.rpx, 22.rpx), + onTap: () { + print('点击了离床次数卡片'); + }, + child: Container( + width: MediaQuery.sizeOf(context).width * 0.27, + constraints: BoxConstraints( + minWidth: 200.rpx, + minHeight: 161.rpx, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Text( + '${widget.data['name']}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '${widget.data['value']}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 40.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), ), - ), - // SizedBox( - // height: 21.rpx, - // ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '4', + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 10.rpx), + child: Text( + '${widget.data['unit'] ?? ''}', style: FlutterFlowTheme.of(context).bodyMedium.override( fontFamily: 'Inter', - fontSize: 40.rpx, + fontSize: AppConstants().small_text_fontSize, letterSpacing: 0.0, color: themeController.currentColor.sc3, ), ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0.rpx, 0, 0.rpx, 10.rpx), - child: Text( - '次', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: AppConstants().small_text_fontSize, - letterSpacing: 0.0, - color: themeController.currentColor.sc3, - ), - ), - ), - ], + ), + ], + ), + // Container( + // width: MediaQuery.sizeOf(context).width * 0.07, + // height: MediaQuery.sizeOf(context).height * 0.014, + // constraints: BoxConstraints( + // minWidth: 43.rpx, + // minHeight: 36.rpx, + // ), + // child: FFButtonWidget( + // onPressed: () { + // print('Button pressed ...'); + // }, + // // text: '${widget.data['level']}', + // text: '${widget.data['level']}', + // options: FFButtonOptions( + // height: 40.rpx, + // padding: EdgeInsets.zero, + // // color: themeController.currentColor.sc14, + // color: stringToColor('${widget.data['color']}'), + // textStyle: + // FlutterFlowTheme.of(context).titleSmall.override( + // fontFamily: 'Inter Tight', + // color: themeController.currentColor.sc3, + // // color: stringToColor('${widget.data['color']}'), + // letterSpacing: 0.0, + // fontSize: 15.rpx, + // ), + // elevation: 0, + // borderRadius: BorderRadius.circular(8.rpx), + // ), + // ), + // ), + + ClickableContainer( + backgroundColor: stringToColor('${widget.data['color']}'), + highlightColor: themeController.currentColor.sc3, + padding: EdgeInsets.symmetric( + horizontal: 0.rpx, + vertical: 0.rpx, ), - Container( - width: MediaQuery.sizeOf(context).width * 0.07, - height: MediaQuery.sizeOf(context).height * 0.014, + borderRadius: 8.rpx, + onTap: () { + print('Button pressed ...'); + }, + child: Container( + alignment: Alignment.center, constraints: BoxConstraints( minWidth: 43.rpx, minHeight: 36.rpx, ), - decoration: BoxDecoration(), - child: FFButtonWidget( - onPressed: () { - print('Button pressed ...'); - }, - text: '异常', - options: FFButtonOptions( - height: 40.rpx, - padding: - EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), - color: themeController.currentColor.sc14, - textStyle: - FlutterFlowTheme.of(context).titleSmall.override( - fontFamily: 'Inter Tight', - color: themeController.currentColor.sc3, - letterSpacing: 0.0, - fontSize: 15.rpx, - ), - elevation: 0, - borderRadius: BorderRadius.circular(8.rpx), - ), + child: Text( + '${widget.data['level']}', + style: FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + fontSize: 15.rpx, + ), ), ), - ], - ), - Text( - '正常值:0~2', - style: FlutterFlowTheme.of(context).bodyMedium.override( + ), + ].divide(SizedBox(width: 0.rpx)), + ), + Text( + '${widget.data['range']}', + style: FlutterFlowTheme.of(context).bodyMedium.override( fontFamily: 'Inter', fontSize: AppConstants().small_text_fontSize, letterSpacing: 0.0, - color: themeController.currentColor.sc4), - ), - ], - ), + color: themeController.currentColor.sc4, + ), + ), + ], ), ), ); diff --git a/lib/component/home_page/SleepDateWidget.dart b/lib/component/home_page/SleepDateWidget.dart index 82838bf..6c7b400 100644 --- a/lib/component/home_page/SleepDateWidget.dart +++ b/lib/component/home_page/SleepDateWidget.dart @@ -6,10 +6,28 @@ import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; class SleepDateWidget extends StatefulWidget { - const SleepDateWidget({super.key}); + final String? mac; + final String? time; // 必传:日期,例如 "07/15" + final DateTime date; // 必传:日期,例如 "07/15" + final String? score; // 可选:分数,默认为 "--" + final String? comment; // 可选:评价,默认为 "暂无".tr + final Color? textColor; // 可选:文字颜色,默认为灰色 + final bool? isSelected; // 是否选中 + + const SleepDateWidget({ + super.key, + this.mac, + this.time, + required this.date, + this.score = '--', + this.comment = '暂无', + this.textColor, + this.isSelected = false, // 新增参数,默认不选中 + }); @override State createState() => _SleepDateWidgetState(); @@ -19,14 +37,35 @@ class _SleepDateWidgetState extends State { @override Widget build(BuildContext context) { ThemeController themeController = Get.find(); + BodyDeviceController bodyDeviceController = Get.find(); + String week = MyUtils.formatDateTimeWeek(widget.date); + String day = MyUtils.formatDateTimeDay(widget.date); + + // 选中时背景色为黑色,否则为透明 + Color backgroundColor = + widget.isSelected == true ? Colors.black : Colors.transparent; + return ClickableContainer( - backgroundColor: Colors.transparent, // 原 BoxDecoration 为空 - highlightColor: - themeController.currentColor.sc3.withOpacity(0.1), // 自定义点击波纹颜色 - borderRadius: AppConstants().normal_container_radius, // 原来没设置圆角 + backgroundColor: backgroundColor, + // highlightColor: themeController.currentColor.sc3.withOpacity(0.1), + highlightColor: Colors.transparent, + borderRadius: AppConstants().normal_container_radius, padding: EdgeInsets.zero, onTap: () { - print("今日评分卡片点击"); + final mac = widget.mac; + final time = widget.time; + + if (bodyDeviceController.sleepReportData.value.containsKey(mac)) { + final list = bodyDeviceController.sleepReportData.value[mac]; + + for (var item in list!) { + item['selected'] = (item['time'] == time); + } + bodyDeviceController.sleepReportData.value = { + ...bodyDeviceController.sleepReportData.value, + }; + bodyDeviceController.updateAll(); + } }, child: Container( width: MediaQuery.sizeOf(context).width * 0.19, @@ -36,54 +75,55 @@ class _SleepDateWidgetState extends State { child: Padding( padding: EdgeInsetsDirectional.fromSTEB(10.rpx, 25.rpx, 10.rpx, 22.rpx), - child: Container( - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 14.rpx), - child: Text( - '今日', - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: 'Inter', - fontSize: AppConstants().title_text_fontSize, - letterSpacing: 0.0, - color: themeController.currentColor.sc3, - ), - ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 33.rpx), - child: Text( - '07/15', - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: 'Inter', - fontSize: 20.rpx, - letterSpacing: 0.0, - color: themeController.currentColor.sc3, - ), - ), - ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 16.rpx), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '70', - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: 'Inter', - fontSize: 48.rpx, - letterSpacing: 0.0, - color: stringToColor("#00C1AA")), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 14.rpx), + child: Text( + '${week}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: AppConstants().title_text_fontSize, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 33.rpx), + child: Text( + '${day}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 20.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 16.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + (widget.score?.isEmpty ?? true) ? '--' : widget.score!, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 48.rpx, + letterSpacing: 0.0, + color: widget.textColor ?? + themeController.currentColor.sc4), + ), + if ((widget.score?.trim().isNotEmpty ?? false)) Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 16.rpx, 0, 0.rpx), child: Text( - '分', + '分'.tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -94,40 +134,40 @@ class _SleepDateWidgetState extends State { ), ), ), - ], + ], + ), + ), + Container( + width: 0.2.rpx, + height: 2.4.rpx, + constraints: BoxConstraints( + minWidth: 123.rpx, + minHeight: 47.rpx, + ), + child: FFButtonWidget( + onPressed: () { + print('合格按钮点击'); + }, + text: (widget.comment?.trim().isEmpty ?? true) + ? '暂无'.tr + : widget.comment!, + options: FFButtonOptions( + height: 40.rpx, + padding: + EdgeInsetsDirectional.fromSTEB(16.rpx, 0, 16.rpx, 0), + iconPadding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), + color: widget.textColor ?? themeController.currentColor.sc4, + textStyle: FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + ), + elevation: 0, + borderRadius: BorderRadius.circular(50.rpx), ), ), - Container( - width: 0.2.rpx, - height: 2.4.rpx, - constraints: BoxConstraints( - minWidth: 123.rpx, - minHeight: 47.rpx, - ), - child: FFButtonWidget( - onPressed: () { - print('合格按钮点击'); - }, - text: '合格', - options: FFButtonOptions( - height: 40.rpx, - padding: - EdgeInsetsDirectional.fromSTEB(16.rpx, 0, 16.rpx, 0), - iconPadding: EdgeInsetsDirectional.fromSTEB(0, 0, 0, 0), - color: stringToColor("#00C1AA"), - textStyle: - FlutterFlowTheme.of(context).titleSmall.override( - fontFamily: 'Inter Tight', - color: themeController.currentColor.sc3, - letterSpacing: 0.0, - ), - elevation: 0, - borderRadius: BorderRadius.circular(50.rpx), - ), - ), - ), - ], - ), + ), + ], ), ), ), diff --git a/lib/component/tool/CustomCard.dart b/lib/component/tool/CustomCard.dart index 1c6975d..69b649f 100644 --- a/lib/component/tool/CustomCard.dart +++ b/lib/component/tool/CustomCard.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; class CustomCard extends StatefulWidget { final double borderRadius; // 圆角 @@ -15,7 +16,7 @@ class CustomCard extends StatefulWidget { required this.colors, required this.child, this.enableAnimation = true, // 默认启用动画效果 - this.enableGradient = true, // 默认启用渐变效果 + this.enableGradient = true, // 默认启用渐变效果 }) : super(key: key); @override @@ -25,36 +26,19 @@ class CustomCard extends StatefulWidget { class _CustomCardState extends State with SingleTickerProviderStateMixin { double _scale = 1.0; - final Duration _animationDuration = Duration(milliseconds: 50); - final GlobalKey _inkKey = GlobalKey(); + final Duration _animationDuration = const Duration(milliseconds: 50); - Future _handleTap(TapDownDetails details) async { - setState(() { - _scale = 0.95; - }); + Future _handleTap() async { + if (widget.enableAnimation) { + setState(() { + _scale = 0.95; + }); - await Future.delayed(_animationDuration); + await Future.delayed(_animationDuration); - setState(() { - _scale = 1.0; - }); - - await Future.delayed(_animationDuration); - - // 手动触发水波纹 - final RenderBox? box = - _inkKey.currentContext?.findRenderObject() as RenderBox?; - if (box != null) { - final Offset localPosition = box.globalToLocal(details.globalPosition); - InkRipple.splashFactory.create( - controller: Material.of(_inkKey.currentContext!)!, - referenceBox: box, - position: localPosition, - color: widget.colors.first.withOpacity(0.2), - containedInkWell: true, - borderRadius: BorderRadius.circular(widget.borderRadius), - textDirection: Directionality.of(context), - ); + setState(() { + _scale = 1.0; + }); } widget.onTap(); @@ -62,51 +46,44 @@ class _CustomCardState extends State @override Widget build(BuildContext context) { - final bool isGradient = widget.enableGradient && widget.colors.length > 1; // 只有启用渐变时,才使用渐变 + final bool isGradient = widget.enableGradient && widget.colors.length > 1; final Color baseColor = widget.colors.first; return Material( color: Colors.transparent, borderRadius: BorderRadius.circular(widget.borderRadius), - child: GestureDetector( - onTapDown: _handleTap, - behavior: HitTestBehavior.translucent, // 关键:让空白区域也能点击 - child: widget.enableAnimation // 判断是否启用动画 + child: InkWell( + onTap: _handleTap, + borderRadius: BorderRadius.circular(widget.borderRadius), + splashColor: widget.colors.first.withOpacity(0.2), + child: widget.enableAnimation ? AnimatedScale( scale: _scale, duration: _animationDuration, curve: Curves.easeInOut, - child: Ink( - key: _inkKey, - decoration: BoxDecoration( - color: isGradient ? null : baseColor, - gradient: isGradient - ? LinearGradient( - colors: widget.colors, - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ) - : null, - borderRadius: BorderRadius.circular(widget.borderRadius), - ), - child: widget.child, - ), + child: _buildContent(isGradient, baseColor), ) - : Ink( - key: _inkKey, - decoration: BoxDecoration( - color: isGradient ? null : baseColor, - gradient: isGradient - ? LinearGradient( - colors: widget.colors, - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ) - : null, - borderRadius: BorderRadius.circular(widget.borderRadius), - ), - child: widget.child, - ), + : _buildContent(isGradient, baseColor), + ), + ); + } + + Widget _buildContent(bool isGradient, Color baseColor) { + return Container( + decoration: BoxDecoration( + color: isGradient ? null : baseColor, + gradient: isGradient + ? LinearGradient( + colors: widget.colors, + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ) + : null, + borderRadius: BorderRadius.circular(widget.borderRadius), + ), + child: Padding( + padding: EdgeInsets.fromLTRB(0.rpx, 0.rpx, 0.rpx, 5.rpx), + child: widget.child, ), ); } diff --git a/lib/component/tool/SelectableTagButton.dart b/lib/component/tool/SelectableTagButton.dart new file mode 100644 index 0000000..1b987e5 --- /dev/null +++ b/lib/component/tool/SelectableTagButton.dart @@ -0,0 +1,79 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +import 'CustomCard.dart'; + +class SelectableTagButton extends StatelessWidget { + final String label; + final bool selected; + final VoidCallback onTap; + final double minWidth; // 最小宽度,单位:dp(会转 rpx) + final double maxWidth; // 最大宽度,单位:dp(会转 rpx) + + ThemeController themeController = Get.find(); + + SelectableTagButton({ + Key? key, + required this.label, + required this.selected, + required this.onTap, + this.minWidth = 132, // 默认最小宽度 + this.maxWidth = 500, // 默认最大宽度 + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final double minWidthRpx = minWidth.rpx; + final double maxWidthRpx = maxWidth.rpx; + final double horizontalPadding = 28.rpx * 2; // 左右各 28,总 56 + // 估算文本宽度:每个字符按 14.rpx 字号估一个宽度 + final double estimatedTextWidth = label.length * 33.rpx; + + // 总宽度 = 文本宽度 + padding + final double totalWidth = estimatedTextWidth + horizontalPadding; + + // 限制在 min 和 max 之间 + final double constrainedWidth = totalWidth.clamp(minWidthRpx, maxWidthRpx); + + return CustomCard( + onTap: onTap, + borderRadius: AppConstants().normal_container_radius, + colors: selected + ? [themeController.currentColor.sc1, themeController.currentColor.sc2] + : [Colors.transparent], + // colors: [Colors.transparent], + enableGradient: true, + child: Container( + decoration: BoxDecoration( + border: selected + ? null + : Border.all( + color: themeController.currentColor.sc4, + width: 1.rpx, + ), // 未选中时无边框 + borderRadius: BorderRadius.circular(12.0), // 如果需要圆角 + ), + padding: EdgeInsets.symmetric(horizontal: 28.rpx), + constraints: BoxConstraints( + minHeight: 61.rpx, + maxWidth: constrainedWidth, + ), + alignment: Alignment.center, + child: Text( + label, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: selected + ? themeController.currentColor.sc3 + : themeController.currentColor.sc4, + fontSize: AppConstants().normal_text_fontSize, // 字体也用 rpx 控制 + ), + ), + ), + ); + } +} diff --git a/lib/component/tool/TopSlideNotification.dart b/lib/component/tool/TopSlideNotification.dart new file mode 100644 index 0000000..ca510d6 --- /dev/null +++ b/lib/component/tool/TopSlideNotification.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:get/get.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class TopSlideNotification extends StatefulWidget { + final String text; + double? fontSize = 26.rpx; + Color? textColor; + double? slideOffset = 200; + final Duration duration; + + TopSlideNotification({ + super.key, + this.text = '操作成功!', + this.fontSize, + this.textColor, + this.slideOffset, + this.duration = const Duration(seconds: 2), + }); + + @override + State createState() => _TopSlideNotificationState(); + + /// 工具方法:调用时直接加进 Overlay 上 + static void show( + BuildContext context, { + String text = '操作成功!', + double fontSize = 16, + Color? textColor, + double slideOffset = 300.0, + Duration duration = const Duration(seconds: 2), + }) { + final overlay = Overlay.of(context); + final entry = OverlayEntry( + builder: (_) => TopSlideNotification( + text: text, + fontSize: fontSize, + textColor: textColor, + slideOffset: slideOffset, + duration: duration, + ), + ); + overlay.insert(entry); + + Future.delayed(duration + const Duration(milliseconds: 500), () { + entry.remove(); + }); + } +} + +class _TopSlideNotificationState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + SchedulerBinding.instance.addPostFrameCallback((_) async { + await _controller.forward(); + await Future.delayed(widget.duration); + await _controller.reverse(); + }); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + + final screenHeight = MediaQuery.of(context).size.height; + final offsetValue = widget.slideOffset! / screenHeight; + + _animation = Tween( + begin: const Offset(0, -1), + end: Offset(0, offsetValue), + ).animate(CurvedAnimation( + parent: _controller, + curve: Curves.easeOut, + reverseCurve: Curves.easeIn, + )); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + Color get _textColor { + return widget.textColor ?? Get.find().currentColor.sc2; + } + + @override + Widget build(BuildContext context) { + return Positioned( + top: 0, + left: 0, + right: 0, + child: SlideTransition( + position: _animation, + child: Material( + color: stringToColor("#000000").withOpacity(0.8), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0), + child: Container( + // color: Colors.red, + child: Text( + widget.text, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: widget.fontSize, + color: _textColor, + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/component/tool/WebViewWidget.dart b/lib/component/tool/WebViewWidget.dart new file mode 100644 index 0000000..25c60df --- /dev/null +++ b/lib/component/tool/WebViewWidget.dart @@ -0,0 +1,51 @@ +// import 'package:flutter/material.dart'; +// import 'package:webview_flutter/webview_flutter.dart'; + +// class WebViewWidget extends StatefulWidget { +// final String url; +// const WebViewWidget({Key? key, required this.url}) : super(key: key); + +// @override +// _WebViewWidgetState createState() => _WebViewWidgetState(); +// } + +// class _WebViewWidgetState extends State { +// late WebViewController _webViewController; + +// @override +// void initState() { +// super.initState(); +// // 初始化 WebView 控件 +// WebView.platform = SurfaceAndroidWebView(); +// } + +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// appBar: AppBar( +// title: Text('WebView'), +// ), +// body: WebView( +// initialUrl: widget.url, // 设置要打开的网页地址 +// javascriptMode: JavascriptMode.unrestricted, // 启用 JavaScript +// onWebViewCreated: (WebViewController webViewController) { +// _webViewController = webViewController; +// }, +// onPageStarted: (String url) { +// print("页面开始加载:$url"); +// }, +// onPageFinished: (String url) { +// print("页面加载完成:$url"); +// }, +// navigationDelegate: (NavigationRequest request) { +// if (request.url.startsWith('https://www.google.com/')) { +// print('拦截了URL请求: ${request.url}'); +// return NavigationDecision.prevent; // 拦截特定的请求 +// } +// return NavigationDecision.navigate; +// }, +// gestureNavigationEnabled: true, // 启用手势返回 +// ), +// ); +// } +// } diff --git a/lib/component/tool/cmd.dart b/lib/component/tool/cmd.dart new file mode 100644 index 0000000..8ad6c1e --- /dev/null +++ b/lib/component/tool/cmd.dart @@ -0,0 +1,166 @@ +//蓝牙指令 + +// wifi列表指令 +import 'package:EasyDartModule/EasyDartModule.dart' as edm; +import 'package:easydevice/src/app/thapp.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; + +getWifiList(THapp tHapp) async { + try { + edm.EasyDartModule.logger.info("发送请求网络列表指令"); + DailyLogUtils.writeLog("发送请求网络列表指令"); + List data = []; + var wifilist = await tHapp.send("wscan scan", true, (log) { + print("[bles]${log.log}"); + if (log.log.contains("SCAN RESULT OVER!")) { + final wifiList = >[]; + final items = log.log.split('[wifi]: SCAN RESULT ITEM:'); + final reg = + RegExp(r'SSID=([^\t\r\n]+)\s+RSSI=(-?\d+)\s*,\s*auth\s*=\s*(\d+)'); + + for (var item in items) { + final match = reg.firstMatch(item); + if (match != null) { + wifiList.add({ + 'ssid': match.group(1), + 'rssi': int.parse(match.group(2)!), + 'auth': int.parse(match.group(3)!), + }); + } + } + + print('解析得到 Wi-Fi 列表: $wifiList'); + if (wifiList.length != 0) { + log.result = wifiList; + data = wifiList; + log.over = true; + return true; + } + } + + return false; + }, 10); + + return wifilist; + } catch (e) { + print(e); + } + return []; +} + +getWifiStatus(THapp tHapp) async { + edm.EasyDartModule.logger.info("发送请求设备网络状态指令"); + DailyLogUtils.writeLog("发送请求设备网络状态指令"); + var result = await tHapp.send( + "wl show", + true, + (ss) { + var log = ss.log; + final match = RegExp(r'status=([^\s]+)').firstMatch(log); + final status = match?.group(1); + if (status != null) { + print('提取到的 status: $status'); + if (status == 'connect') { + ss.result = true; + ss.over = true; + return true; + } else { + ss.result = false; + ss.over = true; + return false; + } + } else { + return false; + } + }, + ); + return result; +} + +Future sendWifiSetting(wifiItem, String password, THapp tHapp) async { + try { + edm.EasyDartModule.logger.info("发送wifi配置指令"); + DailyLogUtils.writeLog("发送wifi配置指令->"); + String cmd = "vtouch save update -a -i .wifi.sta.auth=${wifiItem['auth']} " + ".wifi.sta.ssid=${wifiItem['ssid']} .wifi.sta.pwd=$password"; + final success = await tHapp.send(cmd, true, (log) { + if (log.log.contains("update parm is successful")) { + print("[wifi456]:" + log.log); + edm.EasyDartModule.logger.info("WiFi配置成功-》log:$log"); + DailyLogUtils.writeLog("WiFi配置成功->log:$log"); + log.result = true; + log.over = true; + return true; + } + return false; + }, 10); + + if (!success) { + edm.EasyDartModule.logger.error("WiFi配置超时或失败"); + DailyLogUtils.writeLog("WiFi配置超时或失败"); + } + return success; + } catch (e) { + edm.EasyDartModule.logger.error("发送wifi配置指令异常: ${e.toString()}"); + DailyLogUtils.writeLog("发送wifi配置指令异常-> ${e.toString()}"); + return false; + } +} + +getDeviceWifiStatus(THapp tHapp) async { + edm.EasyDartModule.logger.info("发送请求设备已配置网络状态指令"); + DailyLogUtils.writeLog("发送请求设备已配置网络状态指令"); + + var result = await tHapp.send( + "at+system info", + true, + (ss) { + var log = ss.log; + + // 匹配设备状态 + final statusMatch = RegExp(r'status=([^\s]+)').firstMatch(log); + final status = statusMatch?.group(1); + if (status != null) { + print('提取到的 status: $status'); + + // 如果设备连接状态是 "connect",继续检测 + if (status == 'connect') { + ss.result = true; + ss.over = true; + + // 匹配 Wi-Fi 连接信息 + final wifiInfoMatch = RegExp( + r'WIFI CONNECTED INFO:SSID=([^\s]+),RSSI=([-0-9]+),AUTH=([0-9]+),CH=([0-9]+),BSSID=([A-F0-9]+)') + .firstMatch(log); + if (wifiInfoMatch != null) { + final ssid = wifiInfoMatch.group(1); + final rssi = wifiInfoMatch.group(2); + final auth = wifiInfoMatch.group(3); + final ch = wifiInfoMatch.group(4); + final bssid = wifiInfoMatch.group(5); + + // 打印并返回 Wi-Fi 信息 + print( + 'Wi-Fi 信息: SSID=$ssid, RSSI=$rssi, AUTH=$auth, CH=$ch, BSSID=$bssid'); + + // 停止监听并返回信息 + ss.result = { + 'ssid': ssid, + 'rssi': rssi, + 'auth': auth, + 'ch': ch, + 'bssid': bssid, + }; + ss.over = true; + return ss.result; + } + } + } + + // 未找到状态或Wi-Fi信息时,返回 false + return false; + }, + ); + + return result; +} diff --git a/lib/controller/device/blueteeth_bind_controller.dart b/lib/controller/device/blueteeth_bind_controller.dart index 903ed2c..eeada89 100644 --- a/lib/controller/device/blueteeth_bind_controller.dart +++ b/lib/controller/device/blueteeth_bind_controller.dart @@ -2,54 +2,54 @@ import 'dart:async'; import 'dart:convert'; import 'package:EasyDartModule/EasyDartModule.dart'; -import 'package:easydevice/src/ble_device.dart'; +import 'package:easydevice/easydevice.dart'; import 'package:ef/ef.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:vbvs_app/common/color/ServiceConstant.dart'; import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/DailyLogUtils.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/model/BleDeviceData.dart'; import 'package:vbvs_app/model/api_response.dart'; -part 'blueteeth_bind_controller.g.dart'; // 由json_serializable自动生成的部分 +part 'blueteeth_bind_controller.g.dart'; @JsonSerializable() class BlueteethBindModel { - int? read = 1; //是否不再提示教程 0 不再提示 1 需要提示 - double? singal = -70; //扫描信号强度 + int? read = 1; + double? singal = -70; - List? devicelist = []; //蓝牙扫描到的设备数据列表 - List? betDevicelist = []; //请求的 - List? blelist = []; //蓝牙扫描到的设备数据列表 - List? wifiList = []; + @JsonKey(ignore: true) + List? devicelist = [];//蓝牙扫描 + @JsonKey(ignore: true) + List? betDevicelist = [];//网络状态 + @JsonKey(ignore: true) + List? blelist = []; List bindArr = ["", "", ""]; - String connectedWifiName = ""; - int connectedRssi = 0; - String deviceName = ""; bool? deviceIndex0 = true; bool? deviceIndex1 = false; bool? deviceIndex2 = false; - BlueteethBindModel(); + bool wifiPassShow = false; + String? wifiPass; - // 从JSON反序列化时的异常处理 + BlueteethBindModel(); factory BlueteethBindModel.fromJson(Map json) { try { return _$BlueteethBindModelFromJson(json); } catch (e) { - // 在实际应用中,应该有更细致的异常处理策略和错误日志 - return BlueteethBindModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + return BlueteethBindModel(); } } - // 序列化为JSON时的异常处理 Map toJson() => _$BlueteethBindModelToJson(this); } @@ -60,30 +60,73 @@ class BlueteethBindController extends GetControllerEx { Timer? _statusTimer; - BLEDevice? currentDevice; + THapp? currentDevice; + RxInt wifiStatus = 0.obs; + RxList wifiList = [].obs; + RxMap connect_wifi = {}.obs; + RxString scanMac = "".obs; + + // 安全展示 TopSlideNotification + void safeShowNotification(String msg) { + try { + final ctx = Get.context; + if (ctx != null && ctx.mounted) { + TopSlideNotification.show( + ctx, + text: msg, + textColor: themeController.currentColor.sc9, + ); + } else { + print("TopSlideNotification 未显示:context 不可用或未挂载"); + } + } catch (e, stack) { + // print("TopSlideNotification 显示异常: $e\n$stack"); + } + } // 启动每10秒获取设备状态 void startStatusPolling() { + updateDeviceStatus().then((res) { + if (res.code == HttpStatusCodes.ok) { + updateAll(); + } else { + safeShowNotification(res.msg ?? "获取设备状态异常"); + EasyDartModule.logger.info("获取设备状态异常: $res"); + DailyLogUtils.writeLog("获取设备状态异常: $res"); + } + }); + if (_statusTimer == null) { _statusTimer = Timer.periodic(Duration(seconds: 10), (timer) { - updateDeviceStatus(); + updateDeviceStatus().then((res) { + if (res.code == HttpStatusCodes.ok) { + updateAll(); + } else { + safeShowNotification(res.msg ?? "获取设备状态异常"); + EasyDartModule.logger.info("获取设备状态异常: $res"); + DailyLogUtils.writeLog("获取设备状态异常: $res"); + } + }).catchError((e, stack) { + print("updateDeviceStatus 执行异常: $e\n$stack"); + safeShowNotification("设备状态请求失败"); + EasyDartModule.logger.info("设备状态异常: $e"); + DailyLogUtils.writeLog("设备状态异常: $e"); + }); }); } } - // 停止轮询 void stopStatusPolling() { _statusTimer?.cancel(); _statusTimer = null; } - // 你的已有方法 - Future updateDeviceStatus() async { + Future updateDeviceStatus() async { try { String serviceAddress = ServiceConstant.service_address; String serviceName = ServiceConstant.server_service; String serviceApi = ServiceConstant.get_bluetooth_device_status; - String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; if (model.devicelist != null && model.devicelist!.isNotEmpty) { final macParams = model.devicelist! @@ -97,13 +140,16 @@ class BlueteethBindController extends GetControllerEx { } var response = await EasyDartModule.dio.get(queryUrl); + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) return res; if (response.data['data'] != null && response.data['data'] is List) { List responseList = response.data['data']; - Map deviceMap = { - for (var d in model.devicelist!) - if (d.mac != null) d.mac!: d + for (var d in model.devicelist!) if (d.mac != null) d.mac!: d }; List slaveMacsToRemove = []; @@ -123,17 +169,26 @@ class BlueteethBindController extends GetControllerEx { } } } + model.devicelist! .removeWhere((device) => slaveMacsToRemove.contains(device.mac)); + model.betDevicelist = model.devicelist!; + } else { + model.betDevicelist = []; } + print("获取设备状态成功"); updateAll(); + return res; } } catch (e) { print("获取设备状态异常: $e"); EasyDartModule.logger.info("获取设备状态异常: $e"); DailyLogUtils.writeLog("获取设备状态异常: $e"); + return ApiResponse(code: -1, msg: "请求失败".tr); } + + return ApiResponse(code: -1, msg: "未知错误".tr); } Future bindDeviceAndMAC(BleDeviceData d) async { @@ -142,7 +197,7 @@ class BlueteethBindController extends GetControllerEx { String serviceAddress = ServiceConstant.service_address; String serviceName = ServiceConstant.server_service; String serviceApi = ServiceConstant.device_bind; - String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; var data = { "deviceType": 1, "mac": d.mac, @@ -154,10 +209,55 @@ class BlueteethBindController extends GetControllerEx { response.data is String ? jsonDecode(response.data) : response.data; ApiResponse res = ApiResponse.fromJson(responseData, (object) => object); - MyUtils.formatResponse(apiResponse, "蓝牙绑定.绑定成功".tr, "蓝牙绑定.绑定成功".tr); + MyUtils.formatResponse(res, "蓝牙绑定.绑定成功".tr, "蓝牙绑定.绑定成功".tr); + if (res.code == HttpStatusCodes.ok) { + PersonController personController = Get.find(); + personController.currentPersonId.value = res.data['id']; + return res; + } else { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } catch (e) { + EasyDartModule.logger.info("蓝牙绑定.绑定异常: $e"); + DailyLogUtils.writeLog("蓝牙绑定.绑定异常: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); + } + + @override + void onClose() { + stopStatusPolling(); + super.onClose(); + } + + bindDevice(Map map) {} + + Future bindDeviceByScan(String mac) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "蓝牙绑定.绑定失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_bind; + String queryUrl = "$serviceAddress$serviceName$serviceApi"; + var data = { + "deviceType": 1, + "mac": mac, + }; + var response = + await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "蓝牙绑定.绑定成功".tr, "蓝牙绑定.绑定成功".tr); if (res.code == HttpStatusCodes.ok) { return res; } + apiResponse.msg = res.msg ?? apiResponse.msg; } else { return ApiResponse(code: -1, msg: "服务器.失败".tr); } @@ -166,14 +266,6 @@ class BlueteethBindController extends GetControllerEx { EasyDartModule.logger.info("蓝牙绑定.绑定异常: $e"); DailyLogUtils.writeLog("蓝牙绑定.绑定异常: $e"); } - return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + return ApiResponse(code: -1, msg: "未知错误".tr); } - - @override - void onClose() { - stopStatusPolling(); // 控制器销毁时停止轮询 - super.onClose(); - } - - bindDevice(Map map) {} } diff --git a/lib/controller/device/blueteeth_bind_controller.g.dart b/lib/controller/device/blueteeth_bind_controller.g.dart index fb8ecf3..f4beb3b 100644 --- a/lib/controller/device/blueteeth_bind_controller.g.dart +++ b/lib/controller/device/blueteeth_bind_controller.g.dart @@ -7,9 +7,30 @@ part of 'blueteeth_bind_controller.dart'; // ************************************************************************** BlueteethBindModel _$BlueteethBindModelFromJson(Map json) => - BlueteethBindModel()..read = (json['read'] as num).toInt(); + BlueteethBindModel() + ..read = (json['read'] as num?)?.toInt() + ..singal = (json['singal'] as num?)?.toDouble() + ..bindArr = json['bindArr'] as List + ..connectedWifiName = json['connectedWifiName'] as String + ..connectedRssi = (json['connectedRssi'] as num).toInt() + ..deviceName = json['deviceName'] as String + ..deviceIndex0 = json['deviceIndex0'] as bool? + ..deviceIndex1 = json['deviceIndex1'] as bool? + ..deviceIndex2 = json['deviceIndex2'] as bool? + ..wifiPassShow = json['wifiPassShow'] as bool + ..wifiPass = json['wifiPass'] as String?; Map _$BlueteethBindModelToJson(BlueteethBindModel instance) => { 'read': instance.read, + 'singal': instance.singal, + 'bindArr': instance.bindArr, + 'connectedWifiName': instance.connectedWifiName, + 'connectedRssi': instance.connectedRssi, + 'deviceName': instance.deviceName, + 'deviceIndex0': instance.deviceIndex0, + 'deviceIndex1': instance.deviceIndex1, + 'deviceIndex2': instance.deviceIndex2, + 'wifiPassShow': instance.wifiPassShow, + 'wifiPass': instance.wifiPass, }; diff --git a/lib/controller/device/body_device_controller.dart b/lib/controller/device/body_device_controller.dart new file mode 100644 index 0000000..7d78f8f --- /dev/null +++ b/lib/controller/device/body_device_controller.dart @@ -0,0 +1,256 @@ +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:ef/ef.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +part 'body_device_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class BodyDeviceModel { + int? type = 1; //设备类型 1:我的e护 2.云关爱 + + BodyDeviceModel(); + + // 从JSON反序列化时的异常处理 + + factory BodyDeviceModel.fromJson(Map json) { + try { + return _$BodyDeviceModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return BodyDeviceModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$BodyDeviceModelToJson(this); +} + +class BodyDeviceController extends GetControllerEx { + BodyDeviceController() { + attr = GetModel(BodyDeviceModel()).obs; + } + + RxInt bindDeviceNum = 0.obs; + RxList deviceList = [].obs; + RxMap> sleepReportData = >{}.obs; + + RxString keyWord = "".obs; + + Future getDeviceNum() async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备.设备列表请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_list; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?bindNum=1"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "设备.设备列表请求成功".tr, "设备.设备列表请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + bindDeviceNum.value = res.total!; + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("设备请求列表: $e"); + DailyLogUtils.writeLog("设备请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future getDeviceList({String? key}) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备.设备列表请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_list; + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?bindType=${model.type}${key != null ? '&key=$key' : ''}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "设备.设备列表请求成功".tr, "设备.设备列表请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + // bindDeviceNum.value = res.total!; + deviceList.value = res.data!; + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("设备请求列表: $e"); + DailyLogUtils.writeLog("设备请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future deleteDevice(Map device) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_bind; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + final data = { + "mac": device['mac'], + }; + var response = + await EasyDartModule.dio.delete(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("解绑设备: $e"); + DailyLogUtils.writeLog("解绑设备: $e"); + } finally { + EasyDartModule.logger.info("用户操作:解绑设备"); + DailyLogUtils.writeLog("用户操作:解绑设备"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future getSleepReport() async { + try { + sleepReportData.value = {}; + ApiResponse>> apiResponse = ApiResponse( + code: -1, + msg: "请求失败".tr, + ); + + if (deviceList.value.isEmpty) { + return ApiResponse( + code: HttpStatusCodes.ok, + msg: "请求成功".tr, + ); + } + + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.sleep_report; + + for (var device in deviceList.value) { + String mac = device['mac'] ?? ""; + if (mac.isEmpty) continue; + + sleepReportData[mac] = []; // 初始化当前设备的数据列表 + + String queryUrl = + "$serviceAddress$serviceName$serviceApi?mac=$mac&time=${DateTime.now().millisecondsSinceEpoch}"; + + try { + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = response.data is String + ? jsonDecode(response.data) + : response.data; + + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + + if (res.code == HttpStatusCodes.ok && res.data != null) { + // 确保数据是一个列表 + if (res.data is List) { + sleepReportData[mac] = List.from(res.data); + } else { + sleepReportData[mac] = [res.data]; + } + } + } + } catch (e) { + EasyDartModule.logger.warning("请求设备 $mac 的睡眠数据失败: $e"); + } + } + if (sleepReportData.value.isNotEmpty) { + // 遍历 Map 中的每一个键值对 + sleepReportData.value.forEach((key, report) { + // 确保 report 列表不为空 + if (report.isNotEmpty) { + // 获取该列表的最后一个元素 + var lastElement = report.last; + + // 给最后一个元素添加 selected 属性 + lastElement['selected'] = true; // 假设每个元素是一个 Map 类型 + } + }); + } + + updateAll(); + + return ApiResponse( + code: HttpStatusCodes.ok, + msg: "请求成功".tr, + data: sleepReportData, + ); + } catch (e) { + EasyDartModule.logger.info("设备请求列表异常: $e"); + DailyLogUtils.writeLog("设备请求列表异常: $e"); + return ApiResponse(code: -1, msg: "未知错误".tr, data: {}); + } + } + + updateDeviceShow(device) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "操作失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_show; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "id": device['_id'], + "show": !device['show'], + }; + var response = + await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "操作成功".tr, "操作失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("更新显示异常: $e"); + DailyLogUtils.writeLog("更新显示异常: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } +} diff --git a/lib/controller/device/body_device_controller.g.dart b/lib/controller/device/body_device_controller.g.dart new file mode 100644 index 0000000..77e05a2 --- /dev/null +++ b/lib/controller/device/body_device_controller.g.dart @@ -0,0 +1,15 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'body_device_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +BodyDeviceModel _$BodyDeviceModelFromJson(Map json) => + BodyDeviceModel()..type = (json['type'] as num?)?.toInt(); + +Map _$BodyDeviceModelToJson(BodyDeviceModel instance) => + { + 'type': instance.type, + }; diff --git a/lib/controller/device/device_share_controller.dart b/lib/controller/device/device_share_controller.dart new file mode 100644 index 0000000..2dd64c9 --- /dev/null +++ b/lib/controller/device/device_share_controller.dart @@ -0,0 +1,120 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +part 'device_share_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class DeviceShareModel { + String? _id; // 设备类型 + String? name; // 设备类型 + int? type; // 设备类型 + String? image; // 设备类型 + DeviceShareModel(); + + // 从JSON反序列化时的异常处理 + + factory DeviceShareModel.fromJson(Map json) { + try { + return _$DeviceShareModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return DeviceShareModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$DeviceShareModelToJson(this); +} + +class DeviceShareController extends GetControllerEx { + DeviceShareController() { + attr = GetModel(DeviceShareModel()).obs; + } + RxList deviceTypeList = [].obs; + + RxString account = "".obs; + RxString msg = "".obs; + RxInt code = 0.obs; + + Future getDeviceType() async { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备类型.请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_type; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = apiResponse.msg; + } + } else { + deviceTypeList.value = res.data; + } + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } + + Future shareDevice(String mac) async { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + EasyDartModule.logger.info("分享设备"); + DailyLogUtils.writeLog("分享设备"); + try { + if (account.value == null || account.value.isEmpty) { + apiResponse.msg = "请输入手机号或者邮箱".tr; + return apiResponse; + } + if (!MyUtils.isValidPhoneNumber(account.value) && + !MyUtils.isValidEmail(account.value)) { + apiResponse.msg = '请输入正确的手机号或者邮箱'.tr; + return apiResponse; + } + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_share; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var data = {"type": 1, "userName": account.value, "mac": mac}; + var response = + await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = apiResponse.msg; + } + } else { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = "操作成功".tr; + } + } + msg.value = res.msg!; + code.value = res.code!; + updateAll(); + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } catch (e) { + EasyDartModule.logger.info("分享设备失败:${e.toString()}"); + DailyLogUtils.writeLog("分享设备失败:${e.toString()}"); + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } +} diff --git a/lib/controller/device/device_share_controller.g.dart b/lib/controller/device/device_share_controller.g.dart new file mode 100644 index 0000000..6b00644 --- /dev/null +++ b/lib/controller/device/device_share_controller.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'device_share_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DeviceShareModel _$DeviceShareModelFromJson(Map json) => + DeviceShareModel() + ..name = json['name'] as String? + ..type = (json['type'] as num?)?.toInt() + ..image = json['image'] as String?; + +Map _$DeviceShareModelToJson(DeviceShareModel instance) => + { + 'name': instance.name, + 'type': instance.type, + 'image': instance.image, + }; diff --git a/lib/controller/device/device_type_controller.dart b/lib/controller/device/device_type_controller.dart new file mode 100644 index 0000000..8d80aa4 --- /dev/null +++ b/lib/controller/device/device_type_controller.dart @@ -0,0 +1,65 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +part 'device_type_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class DeviceTypeModel { + String? _id; // 设备类型 + String? name; // 设备类型 + int? type; // 设备类型 + String? image; // 设备类型 + DeviceTypeModel(); + + // 从JSON反序列化时的异常处理 + + factory DeviceTypeModel.fromJson(Map json) { + try { + return _$DeviceTypeModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return DeviceTypeModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$DeviceTypeModelToJson(this); +} + +class DeviceTypeController extends GetControllerEx { + DeviceTypeController() { + attr = GetModel(DeviceTypeModel()).obs; + } + RxList deviceTypeList = [].obs; + + Future getDeviceType() async { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备类型.请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.device_type; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = apiResponse.msg; + } + } else { + deviceTypeList.value = res.data; + } + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } +} diff --git a/lib/controller/device/device_type_controller.g.dart b/lib/controller/device/device_type_controller.g.dart new file mode 100644 index 0000000..da9e572 --- /dev/null +++ b/lib/controller/device/device_type_controller.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'device_type_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +DeviceTypeModel _$DeviceTypeModelFromJson(Map json) => + DeviceTypeModel() + ..name = json['name'] as String? + ..type = (json['type'] as num?)?.toInt() + ..image = json['image'] as String?; + +Map _$DeviceTypeModelToJson(DeviceTypeModel instance) => + { + 'name': instance.name, + 'type': instance.type, + 'image': instance.image, + }; diff --git a/lib/controller/home/home_controller.dart b/lib/controller/home/home_controller.dart new file mode 100644 index 0000000..dc920bc --- /dev/null +++ b/lib/controller/home/home_controller.dart @@ -0,0 +1,50 @@ +import 'package:ef/ef.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +part 'home_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class HomeModel { + int? type = 1; //我的e护 2.云关爱 + + HomeModel(); + + // 从JSON反序列化时的异常处理 + + factory HomeModel.fromJson(Map json) { + try { + return _$HomeModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return HomeModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$HomeModelToJson(this); +} + +class HomeController extends GetControllerEx { + + UserInfoController userInfoController = Get.find(); + ThemeController themeController = Get.find(); + BodyDeviceController deviceController = Get.find(); + @override + void onInit() { + super.onInit(); + } + + HomeController() { + attr = GetModel(HomeModel()).obs; + } + + RxInt sleepNum = 0.obs; + + getSleepReport() { + sleepNum.value = 5; + updateAll(); + } +} diff --git a/lib/controller/home/home_controller.g.dart b/lib/controller/home/home_controller.g.dart new file mode 100644 index 0000000..27f037c --- /dev/null +++ b/lib/controller/home/home_controller.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'home_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +HomeModel _$HomeModelFromJson(Map json) => + HomeModel()..type = (json['type'] as num?)?.toInt(); + +Map _$HomeModelToJson(HomeModel instance) => { + 'type': instance.type, + }; diff --git a/lib/controller/login/login_controller.dart b/lib/controller/login/login_controller.dart index c5fccfe..5486e5c 100644 --- a/lib/controller/login/login_controller.dart +++ b/lib/controller/login/login_controller.dart @@ -12,6 +12,7 @@ import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/model/user_data.dart'; part 'login_controller.g.dart'; @JsonSerializable() @@ -84,10 +85,12 @@ class LoginController extends GetControllerEx { if (res.code == HttpStatusCodes.ok) { UserInfoController userInfoController = Get.find(); userInfoController.model.login = 1; + userInfoController.model.user = UserModel.fromJson(res.data); String token = response.headers['token']!.first; EasyDartModule.dio.token = token; final box = GetStorage(); - box.write('token', userInfoController.model.token); // 存储 token + box.write('token', token); // 存储 token + box.write('user', userInfoController.model.user!.toJson()); // 存储用户信息 } return res; } else { @@ -97,43 +100,48 @@ class LoginController extends GetControllerEx { Future getCode(BuildContext context) async { ApiResponse apiResponse = ApiResponse(code: -1, msg: "其他手机登录页.发送失败".tr); - if (model.register_agree == null || model.register_agree != true) { - apiResponse.msg = "登录页.未同意协议".tr; - return apiResponse; - } - if (model.phone == null || model.phone!.isEmpty) { - apiResponse.msg = "其他手机登录页.请输入手机号".tr; - return apiResponse; - } - if (!MyUtils.isValidPhoneNumber(model.phone!) && - !MyUtils.isValidEmail(model.phone!)) { - apiResponse.msg = '其他手机登录页.不正确手机号'.tr; - return apiResponse; - } - String serviceAddress = ServiceConstant.service_address; - String serviceName = ServiceConstant.server_service; - String serviceApi = ServiceConstant.send_code; - String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; - var data = { - "userName": model.phone, - }; - var response = - await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data)); - if (response != null) { - var responseData = - response.data is String ? jsonDecode(response.data) : response.data; - ApiResponse res = ApiResponse.fromJson(responseData, (object) => object); - if (res.code != HttpStatusCodes.ok) { - if (res.msg == null || res.msg!.isEmpty) { - res.msg = apiResponse.msg; - } - } else { - if (res.msg == null || res.msg!.isEmpty) { - res.msg = "其他手机登录页.发送成功".tr; - } + try { + if (model.register_agree == null || model.register_agree != true) { + apiResponse.msg = "登录页.未同意协议".tr; + return apiResponse; } - return res; - } else { + if (model.phone == null || model.phone!.isEmpty) { + apiResponse.msg = "其他手机登录页.请输入手机号".tr; + return apiResponse; + } + if (!MyUtils.isValidPhoneNumber(model.phone!) && + !MyUtils.isValidEmail(model.phone!)) { + apiResponse.msg = '其他手机登录页.不正确手机号'.tr; + return apiResponse; + } + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.send_code; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var data = { + "userName": model.phone, + }; + var response = + await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + if (res.code != HttpStatusCodes.ok) { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = apiResponse.msg; + } + } else { + if (res.msg == null || res.msg!.isEmpty) { + res.msg = "其他手机登录页.发送成功".tr; + } + } + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } catch (e) { return ApiResponse(code: -1, msg: "服务器.失败".tr); } } diff --git a/lib/controller/login/login_controller.g.dart b/lib/controller/login/login_controller.g.dart index 05c1dc7..179e10a 100644 --- a/lib/controller/login/login_controller.g.dart +++ b/lib/controller/login/login_controller.g.dart @@ -16,7 +16,8 @@ LoginModel _$LoginModelFromJson(Map json) => LoginModel() ..showPd = json['showPd'] as bool? ..forceLogin = (json['forceLogin'] as num?)?.toInt() ..isIos = json['isIos'] as bool? - ..isWeChatNotInstalled = json['isWeChatNotInstalled'] as bool?; + ..isWeChatNotInstalled = json['isWeChatNotInstalled'] as bool? + ..register_agree = json['register_agree'] as bool?; Map _$LoginModelToJson(LoginModel instance) => { @@ -30,4 +31,5 @@ Map _$LoginModelToJson(LoginModel instance) => 'forceLogin': instance.forceLogin, 'isIos': instance.isIos, 'isWeChatNotInstalled': instance.isWeChatNotInstalled, + 'register_agree': instance.register_agree, }; diff --git a/lib/controller/message/message_controller.dart b/lib/controller/message/message_controller.dart new file mode 100644 index 0000000..7c3218c --- /dev/null +++ b/lib/controller/message/message_controller.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:ef/ef.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +part 'message_controller.g.dart'; // 由json_serializable自动生成的部分 + +@JsonSerializable() +class MessageModel { + int? type = 1; //设备类型 1:体征消息 2.系统消息 + + MessageModel(); + + // 从JSON反序列化时的异常处理 + + factory MessageModel.fromJson(Map json) { + try { + return _$MessageModelFromJson(json); + } catch (e) { + // 在实际应用中,应该有更细致的异常处理策略和错误日志 + return MessageModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例 + } + } + + // 序列化为JSON时的异常处理 + Map toJson() => _$MessageModelToJson(this); +} + +class MessageController extends GetControllerEx { + MessageController() { + attr = GetModel(MessageModel()).obs; + } + + RxList msssageList = [].obs; + + Future getMessageList({String? key}) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.message_list; + String messageType = "app_system"; + if (model.type == 1) { + messageType = "app_body"; + } else { + messageType = "app_system"; + } + String queryUrl = + "${serviceAddress}${serviceName}${serviceApi}?type=${messageType}'}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("设备请求列表: $e"); + DailyLogUtils.writeLog("设备请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } +} diff --git a/lib/controller/message/message_controller.g.dart b/lib/controller/message/message_controller.g.dart new file mode 100644 index 0000000..d4657cf --- /dev/null +++ b/lib/controller/message/message_controller.g.dart @@ -0,0 +1,15 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'message_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +MessageModel _$MessageModelFromJson(Map json) => + MessageModel()..type = (json['type'] as num?)?.toInt(); + +Map _$MessageModelToJson(MessageModel instance) => + { + 'type': instance.type, + }; diff --git a/lib/controller/person/person_controller.dart b/lib/controller/person/person_controller.dart index 8e6c2f0..1621bc4 100644 --- a/lib/controller/person/person_controller.dart +++ b/lib/controller/person/person_controller.dart @@ -1,14 +1,20 @@ +import 'dart:convert'; + +import 'package:EasyDartModule/EasyDartModule.dart'; import 'package:ef/ef.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/model/api_response.dart'; part 'person_controller.g.dart'; // 由json_serializable自动生成的部分 @JsonSerializable() class PersonModel { - int read = 1; - - DateTime? birthday; - double? weight; + int? read = 1; + // String? name; PersonModel(); @@ -30,5 +36,152 @@ class PersonModel { class PersonController extends GetControllerEx { PersonController() { attr = GetModel(PersonModel()).obs; - } -} \ No newline at end of file + } + RxList selectedDiseaseIds = [].obs; + // RxList diseaseList = [ + // {'id': 1, 'name': '高血压'}, + // {'id': 2, 'name': '糖尿病'}, + // {'id': 3, 'name': '冠心病'}, + // {'id': 4, 'name': '哮喘'}, + // {'id': 5, 'name': '脑卒中'}, + // {'id': 6, 'name': '慢性肾病'}, + // {'id': 7, 'name': '慢阻肺'}, + // {'id': 8, 'name': '类风湿关节炎'}, + // {'id': 9, 'name': '类风湿关节炎类风湿关节炎'}, + // {'id': 10, 'name': '类风湿关节炎类风湿关节炎类风湿关节炎'}, + // ].obs; + + RxString currentPersonId = "".obs; + RxString name = "".obs; + RxInt gender = 1.obs; + RxString birthday = "".obs; + RxInt weight = 65.obs; + DateTime? dateTime; //选择时间 + RxList diseaseList = [].obs; + + //保存人员资料 + // void savePersonData() { + // print("id->" + currentPersonId.value); + // print("name->" + name.value); + // print("gender->${gender.value}"); + // print("生日->${birthday.value}"); + // print("体重->${weight.value}"); + // print("慢病->${selectedDiseaseIds.value}"); + // } + + Future savePersonData() async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "蓝牙绑定.绑定失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.person_info; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + if (name.value.isEmpty) { + apiResponse.msg = "请输入姓名".tr; + return apiResponse; + } + if (birthday.value.isEmpty) { + apiResponse.msg = "请选择生日".tr; + return apiResponse; + } + if (weight.value == 0) { + apiResponse.msg = "请输入体重".tr; + return apiResponse; + } + + var data = { + "id": currentPersonId.value, + "name": name.value, + "gender": gender.value, + "birthday": birthday.value, + "weight": weight.value, + "disease": selectedDiseaseIds.value, + }; + var response = + await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "操作成功".tr, "操作失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("保存人员资料异常: $e"); + DailyLogUtils.writeLog("保存人员资料异常: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future updatePersonName(person, deviceId) async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.person_info; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + if (name.value.isEmpty) { + apiResponse.msg = "请输入姓名".tr; + return apiResponse; + } + person['name'] = name.value; + person['id'] = deviceId; + var response = + await EasyDartModule.dio.put(queryUrl, data: jsonEncode(person)); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "操作成功".tr, "操作失败".tr); + if (res.code == HttpStatusCodes.ok) { + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("保存人员资料异常: $e"); + DailyLogUtils.writeLog("保存人员资料异常: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } + + Future getDiseaseData() async { + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.disease_list; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr); + if (res.code == HttpStatusCodes.ok) { + // bindDeviceNum.value = res.total!; + diseaseList.value = res.data!; + updateAll(); + return res; + } + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + return apiResponse; + } catch (e) { + EasyDartModule.logger.info("疾病请求列表: $e"); + DailyLogUtils.writeLog("疾病请求列表: $e"); + } + return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement + } +} diff --git a/lib/controller/person/person_controller.g.dart b/lib/controller/person/person_controller.g.dart index 004da7e..a67b81d 100644 --- a/lib/controller/person/person_controller.g.dart +++ b/lib/controller/person/person_controller.g.dart @@ -6,14 +6,10 @@ part of 'person_controller.dart'; // JsonSerializableGenerator // ************************************************************************** -PersonModel _$PersonModelFromJson(Map json) => PersonModel() - ..read = (json['read'] as num).toInt() - ..birthday = json['birthday'] == null - ? null - : DateTime.parse(json['birthday'] as String); +PersonModel _$PersonModelFromJson(Map json) => + PersonModel()..read = (json['read'] as num?)?.toInt(); Map _$PersonModelToJson(PersonModel instance) => { 'read': instance.read, - 'birthday': instance.birthday?.toIso8601String(), }; diff --git a/lib/controller/user_info_controller.dart b/lib/controller/user_info_controller.dart index 091cea0..d19766e 100644 --- a/lib/controller/user_info_controller.dart +++ b/lib/controller/user_info_controller.dart @@ -1,5 +1,17 @@ +import 'package:EasyDartModule/EasyDartModule.dart'; +import 'package:dio/dio.dart' as dio; import 'package:ef/ef.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:get_storage/get_storage.dart'; +import 'package:img_picker/img_picker.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/home/home_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/model/user_data.dart'; part 'user_info_controller.g.dart'; @@ -24,11 +36,12 @@ class UserInfoModel { String? img_bucket = 'user'; int? login = 0; //0未登录 1 登录 - - int? deviceBindNum = 0; //绑定设备数量 - int? loginPhone = 0;//0 本机号码 1其他手机号 + int? loginPhone = 0; //0 本机号码 1其他手机号 + + // String? nickname; //修改昵称 + // String? avatar; //修改头像地址 UserInfoModel(); static UserInfoModel fromJson(Map json) => @@ -41,4 +54,146 @@ class UserInfoController extends GetControllerEx { UserInfoController() { attr = GetModel(UserInfoModel()).obs; } + + Future uploadImg() async { + EasyDartModule.logger.info("请求上传图片"); + DailyLogUtils.writeLog("请求上传图片"); + final ImagePicker picker = ImagePicker(); + final XFile? image = await picker.pickImage(source: ImageSource.gallery); + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "我的.头像上传失败".tr); + if (image != null) { + int fileSize = await image.length(); // 获取图片大小,单位为字节 + if (fileSize > 1048576) { + apiResponse.msg = "我的.头像限制".tr; + return apiResponse; + } + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.upload_file; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var formData = dio.FormData.fromMap({ + "type": 1, + "file": await dio.MultipartFile.fromFile( + image.path, // 确保 image 是 File 类型 + filename: image.path.split('/').last, + ), + }); + var response = await EasyDartModule.dio.post(queryUrl, data: formData); + if (response != null) { + var responseData = response.data is String + ? jsonDecode(response.data) + : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(apiResponse, "我的.上传成功".tr, "我的.头像上传失败".tr); + model.user!.tmpHead = res.data['path']; + updateAll(); + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } else { + apiResponse.msg = "我的.未选择图片".tr; + return apiResponse; + } + } catch (e) { + EasyDartModule.logger.error("上传图片失败->$e"); + DailyLogUtils.writeError("上传图片失败->$e"); + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } + + //更新用户信息 + Future updateData() async { + EasyDartModule.logger.info("更新用户资料"); + DailyLogUtils.writeLog("更新用户资料"); + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "我的.保存失败".tr); + UserModel user = model.user!; + if (user.tmpNickName == null || user.tmpNickName!.isEmpty) { + apiResponse.msg = "我的.昵称为空".tr; + return apiResponse; + } + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_info; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + final data = { + "nickName": user.tmpNickName, + if (user.tmpHead != null && user.tmpHead!.isNotEmpty) + "avatar": user.tmpHead, + }; + + var response = + await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data)); + if (apiResponse != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(res, "我的.保存成功".tr, "我的.保存失败".tr); + return res; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } catch (e) { + EasyDartModule.logger.info("更新用户资料失败->$e"); + DailyLogUtils.writeLog("更新用户资料失败->$e"); + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } + + //查询用户信息 + Future getUserInfo() async { + EasyDartModule.logger.info("查询用户资料"); + DailyLogUtils.writeLog("查询用户资料"); + try { + ApiResponse apiResponse = ApiResponse(code: -1, msg: "我的.查询失败".tr); + String serviceAddress = ServiceConstant.service_address; + String serviceName = ServiceConstant.server_service; + String serviceApi = ServiceConstant.user_info; + String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"; + var response = await EasyDartModule.dio.get(queryUrl); + if (response != null) { + var responseData = + response.data is String ? jsonDecode(response.data) : response.data; + ApiResponse res = + ApiResponse.fromJson(responseData, (object) => object); + MyUtils.formatResponse(apiResponse, "我的.保存成功".tr, "我的.保存失败".tr); + if (res.code == HttpStatusCodes.ok) { + UserInfoController userInfoController = Get.find(); + userInfoController.model.user = UserModel.fromJson(res.data); + final box = GetStorage(); + box.write('user', userInfoController.model.user!.toJson()); // 存储用户信息 + userInfoController.updateAll(); + } + return apiResponse; + } else { + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } catch (e) { + EasyDartModule.logger.info("更新用户资料失败->$e"); + DailyLogUtils.writeLog("更新用户资料失败->$e"); + return ApiResponse(code: -1, msg: "服务器.失败".tr); + } + } + + ApiResponse logOut() { + ApiResponse apiResponse = ApiResponse(code: 1, msg: "设置页.退出成功".tr); + EasyDartModule.logger.info("退出登录"); + DailyLogUtils.writeLog("退出登录"); + model.login = 0; + EasyDartModule.dio.token = null; + final box = GetStorage(); + box.remove("user"); + box.remove("token"); + BodyDeviceController bodyDeviceController = Get.find(); + bodyDeviceController.bindDeviceNum.value = 0; + HomeController homeController = Get.find(); + homeController.sleepNum.value = 0; + + bodyDeviceController.sleepReportData.value = {}; + return apiResponse; + } } diff --git a/lib/controller/user_info_controller.g.dart b/lib/controller/user_info_controller.g.dart index 2243d6a..3c95bf2 100644 --- a/lib/controller/user_info_controller.g.dart +++ b/lib/controller/user_info_controller.g.dart @@ -19,7 +19,9 @@ UserInfoModel _$UserInfoModelFromJson(Map json) => ..deviceModel = json['deviceModel'] as String? ..appVersion = json['appVersion'] as String? ..img_bucket = json['img_bucket'] as String? - ..login = (json['login'] as num?)?.toInt(); + ..login = (json['login'] as num?)?.toInt() + ..deviceBindNum = (json['deviceBindNum'] as num?)?.toInt() + ..loginPhone = (json['loginPhone'] as num?)?.toInt(); Map _$UserInfoModelToJson(UserInfoModel instance) => { @@ -33,4 +35,6 @@ Map _$UserInfoModelToJson(UserInfoModel instance) => 'appVersion': instance.appVersion, 'img_bucket': instance.img_bucket, 'login': instance.login, + 'deviceBindNum': instance.deviceBindNum, + 'loginPhone': instance.loginPhone, }; diff --git a/lib/enum/BindType.dart b/lib/enum/BindType.dart new file mode 100644 index 0000000..b2838ed --- /dev/null +++ b/lib/enum/BindType.dart @@ -0,0 +1,9 @@ +enum BindType { + active(1, '主动绑定'), + share(2, '分享绑定'); + + final int code; + final String description; + + const BindType(this.code, this.description); +} diff --git a/lib/enum/LoginStatus.dart b/lib/enum/LoginStatus.dart new file mode 100644 index 0000000..a325261 --- /dev/null +++ b/lib/enum/LoginStatus.dart @@ -0,0 +1,9 @@ +enum LoginStatus { + LOGIN(1, '已登录'), + NOT_LOGIN(2, '未登录'); + + final int code; + final String description; + + const LoginStatus(this.code, this.description); +} diff --git a/lib/main.dart b/lib/main.dart index be07cf9..4ac1984 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,34 +6,39 @@ import 'package:EasyDartModule/base/websocket/WebSocket.dart'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get_storage/get_storage.dart'; import 'package:localstorage/localstorage.dart'; +import 'package:syncfusion_localizations/syncfusion_localizations.dart'; import 'package:vbvs_app/common/color/ServiceConstant.dart'; import 'package:vbvs_app/common/util/CommonVariables.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/device/device_share_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/home/home_controller.dart'; import 'package:vbvs_app/controller/login/login_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; +import 'package:vbvs_app/controller/message/message_controller.dart'; import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/time/countdown_controller.dart'; import 'package:vbvs_app/language/AppLanguage.dart'; import 'package:vbvs_app/model/CustomThemeColor.dart'; import 'package:vbvs_app/model/user_data.dart'; + import 'controller/user_info_controller.dart'; import 'routers/routers.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:syncfusion_localizations/syncfusion_localizations.dart'; - Future main() async { WidgetsFlutterBinding.ensureInitialized(); await AppLanguage().loadLanguage("zh_CN"); WidgetsFlutterBinding.ensureInitialized(); // ApiService.init(); - // await GetStorage.init(); + await GetStorage.init(); // 初始化登录 await initLocalStorage(); initEasyDartModule(); @@ -54,37 +59,37 @@ Future main() async { } void initEasyDartModule() { - //初始化 - EasyDartModule.init( - loggerConfig: - LoggerConfig(host: ServiceConstant.logService, serviceName: "web"), - webSocketConfig: - WebSocketConfig(ServiceConstant.webSocketService, (data) { - // 接收到服务消息 - var json = jsonDecode(data); - if (json["path"] != null) { - var call = CommonVariables.callMap[json["path"]]; - if (call != null) { - try { - call(json["data"]); - } catch (e) { - print(e); + try { + EasyDartModule.init( + loggerConfig: + LoggerConfig(host: ServiceConstant.logService, serviceName: "web"), + webSocketConfig: + WebSocketConfig(ServiceConstant.webSocketService, (data) { + // 接收到服务消息 + var json = jsonDecode(data); + if (json["path"] != null) { + var call = CommonVariables.callMap[json["path"]]; + if (call != null) { + try { + call(json["data"]); + } catch (e) { + print(e); + } + } else { + print("未找到当前路径: ${json["path"]} 回调函数"); } - } else { - print("未找到当前路径: ${json["path"]} 回调函数"); } - } - // print(data); - }, onOpen: () { - //连接建立完毕 - // EasyDartModule.websocket - // .sendData(jsonEncode({"path": "/aa/bb", "type": 1})); - })); - EasyDartModule.dio.token = localStorage.getItem('token'); - // document.onContextMenu.listen((event) { - // event.preventDefault(); - // }); - EasyDartModule.dio.token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiI2ODAxY2JmMzY5YjJhODQ5MWQwMDAwMDAiLCJ0aWQiOiI2N2Y1ZDk2ZTI2ZWYzMTA0NjMwMDAwMDAiLCJsZXZlbCI6NSwiaWF0IjoxNzQ0OTQ4MjExfQ._XXG3WzEHuOzWdj01NXJxLJpxe3SU20XQqShBZUHCUU"; + // print(data); + }, onOpen: () { + // 连接建立完毕 + // EasyDartModule.websocket + // .sendData(jsonEncode({"path": "/aa/bb", "type": 1})); + print("object"); + })); + } catch (e) { + print(e); + } + //初始化 } initLog() {} @@ -103,12 +108,16 @@ Future initLogin() async { final box = GetStorage(); UserInfoController userInfoController = Get.find(); String? token = box.read('token'); - // print('Token: $token'); - Map? userMap = box.read('user'); - if (userMap != null) { - UserModel user = UserModel.fromJson(userMap); - userInfoController.model.user = user; + try { + Map? userMap = box.read('user'); + if (userMap != null) { + UserModel user = UserModel.fromJson(userMap); + userInfoController.model.user = user; + } + } catch (e) { + print(e); } + if (token != null) { userInfoController.model.login = 1; // 根据token去请求 @@ -199,13 +208,18 @@ class MyApp extends StatelessWidget { initialRoute: "/mianPageBottomChange", onGenerateRoute: onGenerateRoute, initialBinding: BindingsBuilder(() => [ - Get.lazyPut(() => UserInfoController()), + // Get.lazyPut(() => UserInfoController()), Get.put(GlobalController()), Get.lazyPut(() => MainPageController()), Get.lazyPut(() => BlueteethBindController()), Get.lazyPut(() => PersonController()), Get.lazyPut(() => CountdownController()), Get.lazyPut(() => LoginController()), + Get.lazyPut(() => DeviceTypeController()), + Get.lazyPut(() => BodyDeviceController()), + Get.lazyPut(() => HomeController()), + Get.lazyPut(() => DeviceShareController()), + Get.lazyPut(() => MessageController()), ])); }); } diff --git a/lib/model/BleDeviceData.dart b/lib/model/BleDeviceData.dart index 94b292d..5ae3c60 100644 --- a/lib/model/BleDeviceData.dart +++ b/lib/model/BleDeviceData.dart @@ -8,7 +8,7 @@ class BleDeviceData { final int flag; // 设备属性 final int version; // 软件版本 final int qsn; // 广播帧序列号高16位 - bool? bind = true; // 设备状态 + bool? bind = false; // 设备状态 String? name; //设备名称 int? rssi; String? mac; //mac地址 diff --git a/lib/model/WebSocketMessage.dart b/lib/model/WebSocketMessage.dart new file mode 100644 index 0000000..67ed567 --- /dev/null +++ b/lib/model/WebSocketMessage.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'WebSocketMessage.g.dart'; + +@JsonSerializable() +class WebSocketMessage { + String path; + int? type; + dynamic data; + + WebSocketMessage({required this.path, this.type, this.data}); + + static WebSocketMessage fromJson(Map json) => + _$WebSocketMessageFromJson(json); + Map toJson() => _$WebSocketMessageToJson(this); +} diff --git a/lib/model/WebSocketMessage.g.dart b/lib/model/WebSocketMessage.g.dart new file mode 100644 index 0000000..ca9147d --- /dev/null +++ b/lib/model/WebSocketMessage.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'WebSocketMessage.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +WebSocketMessage _$WebSocketMessageFromJson(Map json) => + WebSocketMessage( + path: json['path'] as String, + type: (json['type'] as num?)?.toInt(), + data: json['data'], + ); + +Map _$WebSocketMessageToJson(WebSocketMessage instance) => + { + 'path': instance.path, + 'type': instance.type, + 'data': instance.data, + }; diff --git a/lib/model/api_response.dart b/lib/model/api_response.dart index 1950041..3fcf6af 100644 --- a/lib/model/api_response.dart +++ b/lib/model/api_response.dart @@ -1,17 +1,17 @@ - - class ApiResponse { int? code; T? data; String? msg; + int? total; - ApiResponse({required this.code, this.data, this.msg}); + ApiResponse({required this.code, this.data, this.msg, this.total}); factory ApiResponse.fromJson( Map json, T Function(Object?) fromJsonT) { return ApiResponse( code: json['code'] as int, data: json['data'] != null ? fromJsonT(json['data']) : null, msg: json['msg'] as String?, + total: json['total'] as int?, ); } } diff --git a/lib/model/user_data.dart b/lib/model/user_data.dart index 7c4280a..192cb6f 100644 --- a/lib/model/user_data.dart +++ b/lib/model/user_data.dart @@ -6,12 +6,16 @@ part 'user_data.g.dart'; class UserModel { String? uid; String? userName; - String? nickName; - String? tel; + String? nick_name; + String? phone; String? exp1; - String? head; + String? avatar; String? tmpHead; String? tmpNickName; + int? deleted; + String? status; + int? created_at; + String? email; UserModel(); static UserModel fromJson(Map json) => diff --git a/lib/model/user_data.g.dart b/lib/model/user_data.g.dart index 40d6732..7d25e07 100644 --- a/lib/model/user_data.g.dart +++ b/lib/model/user_data.g.dart @@ -9,20 +9,28 @@ part of 'user_data.dart'; UserModel _$UserModelFromJson(Map json) => UserModel() ..uid = json['uid'] as String? ..userName = json['userName'] as String? - ..nickName = json['nickName'] as String? - ..tel = json['tel'] as String? + ..nick_name = json['nick_name'] as String? + ..phone = json['phone'] as String? ..exp1 = json['exp1'] as String? - ..head = json['head'] as String? + ..avatar = json['avatar'] as String? ..tmpHead = json['tmpHead'] as String? - ..tmpNickName = json['tmpNickName'] as String?; + ..tmpNickName = json['tmpNickName'] as String? + ..deleted = (json['deleted'] as num?)?.toInt() + ..status = json['status'] as String? + ..created_at = (json['created_at'] as num?)?.toInt() + ..email = json['email'] as String?; Map _$UserModelToJson(UserModel instance) => { 'uid': instance.uid, 'userName': instance.userName, - 'nickName': instance.nickName, - 'tel': instance.tel, + 'nick_name': instance.nick_name, + 'phone': instance.phone, 'exp1': instance.exp1, - 'head': instance.head, + 'avatar': instance.avatar, 'tmpHead': instance.tmpHead, 'tmpNickName': instance.tmpNickName, + 'deleted': instance.deleted, + 'status': instance.status, + 'created_at': instance.created_at, + 'email': instance.email, }; diff --git a/lib/pages/device/BodyDeviceWidget.dart b/lib/pages/device/BodyDeviceWidget.dart new file mode 100644 index 0000000..e076cec --- /dev/null +++ b/lib/pages/device/BodyDeviceWidget.dart @@ -0,0 +1,803 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/NullDataComponentWidget.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/pages/device/component/DeviceDataComponentWidget.dart'; + +class BodyDeviceWidget extends StatefulWidget { + const BodyDeviceWidget({super.key}); + + @override + State createState() => _BodyDevicePageState(); +} + +class _BodyDevicePageState extends State { + final ThemeController themeController = Get.find(); + final BodyDeviceController bodyDeviceController = Get.find(); + final GlobalKey addIconKey = GlobalKey(); + OverlayEntry? _popupEntry; + + void _showPopup() { + final renderBox = + addIconKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final position = renderBox.localToGlobal(Offset.zero); + final size = renderBox.size; + double popupWidth = 190.rpx; + + // 移除之前的弹窗 + _popupEntry?.remove(); + + // 创建新的 OverlayEntry + _popupEntry = OverlayEntry( + builder: (context) => Stack( + children: [ + // 半透明背景,点击后关闭弹窗 + ModalBarrier( + dismissible: true, + color: Colors.transparent, + onDismiss: _hidePopup, + ), + + // 弹窗内容 + Positioned( + top: position.dy + size.height + 26.rpx, + left: position.dx + size.width - popupWidth - 40.rpx, + child: Material( + color: Colors.transparent, + child: Container( + width: popupWidth, + padding: EdgeInsets.all(20.rpx), + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(12.rpx), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + blurRadius: 12.rpx, + spreadRadius: 2.rpx, + offset: Offset(0, 6.rpx), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 11.rpx), + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 10.rpx), + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc16.withOpacity(0.1), + borderRadius: 0.rpx, + onTap: () { + print('点击扫一扫'); + _hidePopup(); + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: themeController.currentColor.sc2, + ); + }, + child: Container( + width: double.infinity, + child: Center( + child: Text( + '扫一扫'.tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + ), + SizedBox(height: 35.rpx), + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 10.rpx), + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc16.withOpacity(0.1), + borderRadius: 0.rpx, + onTap: () { + _hidePopup(); + Get.toNamed("/deviceType"); + }, + child: Container( + width: double.infinity, + child: Center( + child: Text( + '蓝牙绑定'.tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + ), + SizedBox(height: 13.rpx), + ], + ), + ), + ), + ), + ], + ), + ); + + // 插入新的 OverlayEntry + Overlay.of(context)!.insert(_popupEntry!); + } + + void _hidePopup() { + _popupEntry?.remove(); + _popupEntry = null; + } + + @override + void initState() { + bodyDeviceController.keyWord.value = ""; + super.initState(); + bodyDeviceController.getDeviceList().then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodysize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + // width: bodysize.maxWidth, + // height: bodysize.maxHeight * 1, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '体征检测设备.标题'.tr, + style: TextStyle( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + // child: returnIconButtom, + child: returnIconButtomAddCallback(() { + bodyDeviceController.getDeviceNum(); + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + }), + ), + Positioned( + right: 20.rpx, + child: ClickableContainer( + key: addIconKey, + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc16, + padding: EdgeInsets.all(8.rpx), + onTap: () { + // 点击图标时,展示弹窗 + if (_popupEntry == null) { + _showPopup(); + } else { + _hidePopup(); + } + }, + child: SvgPicture.asset( + 'assets/img/icon/add.svg', + width: 39.rpx, + height: 39.rpx, + color: themeController.currentColor.sc16, + ), + ), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: GestureDetector( + onTap: _hidePopup, // 点击空白处自动关闭弹窗 + child: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 0, 0.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 90.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 15.rpx, 30.rpx, 15.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Obx(() { + // return ClickableContainer( + // backgroundColor: + // Colors.transparent, // 或者你想设置的背景色 + // highlightColor: themeController + // .currentColor.sc3, // 点击涟漪颜色 + // borderRadius: 8.rpx, // 自定义圆角 + // padding: EdgeInsets.all( + // 0), // 外部已经排版,这里不用加内边距 + // onTap: () async { + // // 点击事件处理逻辑 + // bodyDeviceController.model.type = 1; + // await bodyDeviceController + // .getDeviceList(); + // bodyDeviceController.updateAll(); + // }, + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '体征检测设备.我的e护'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // color: + // bodyDeviceController + // .model + // .type == + // 2 + // ? themeController + // .currentColor + // .sc3 + // : themeController + // .currentColor + // .sc2, + // ), + // ), + // SizedBox( + // width: 100.rpx, + // child: Divider( + // height: 1.rpx, + // thickness: 2.rpx, + // color: bodyDeviceController + // .model.type == + // 2 + // ? Colors.transparent + // : themeController + // .currentColor.sc2, + // ), + // ), + // ].divide(SizedBox(height: 10.rpx)), + // ), + // ); + // }), + // Obx(() { + // return ClickableContainer( + // backgroundColor: Colors.transparent, + // highlightColor: + // themeController.currentColor.sc3, + // borderRadius: 8.rpx, + // padding: EdgeInsets.all(0), + // onTap: () async { + // // 这里写你的点击逻辑 + // bodyDeviceController.model.type = 2; + // await bodyDeviceController + // .getDeviceList(); + // bodyDeviceController.updateAll(); + // }, + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '体征检测设备.云关爱'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: + // bodyDeviceController + // .model + // .type == + // 1 + // ? themeController + // .currentColor + // .sc3 + // : themeController + // .currentColor + // .sc2, + // ), + // ), + // SizedBox( + // width: 100.rpx, + // child: Divider( + // height: 1.rpx, + // thickness: 2.rpx, + // color: bodyDeviceController + // .model.type == + // 1 + // ? Colors.transparent + // : themeController + // .currentColor.sc2, + // ), + // ), + // ].divide(SizedBox(height: 10.rpx)), + // ), + // ); + // }), + // ].divide(SizedBox(width: 60.rpx)), + // ), + + Stack( + alignment: Alignment.bottomLeft, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Obx(() { + return ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc3, + borderRadius: 8.rpx, + padding: EdgeInsets.all(0), + onTap: () async { + bodyDeviceController.model.type = + 1; + await bodyDeviceController + .getDeviceList(); + bodyDeviceController.updateAll(); + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: + 160.rpx, // 固定宽度为 160.rpx + alignment: + Alignment.center, // 文字居中 + child: Text( + '体征检测设备.我的e护'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: bodyDeviceController + .model + .type == + 2 + ? themeController + .currentColor + .sc3 + : themeController + .currentColor + .sc2, + ), + ), + ), + SizedBox(height: 10.rpx), + ], + ), + ); + }), + Obx(() { + return ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc3, + borderRadius: 8.rpx, + padding: EdgeInsets.all(0), + onTap: () async { + bodyDeviceController.model.type = + 2; + await bodyDeviceController + .getDeviceList(); + bodyDeviceController.updateAll(); + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: + 160.rpx, // 固定宽度为 160.rpx + alignment: + Alignment.center, // 文字居中 + child: Text( + '体征检测设备.云关爱'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: bodyDeviceController + .model + .type == + 1 + ? themeController + .currentColor + .sc3 + : themeController + .currentColor + .sc2, + ), + ), + ), + SizedBox(height: 10.rpx), + ], + ), + ); + }), + ], + ), + Obx(() { + // 横线宽度固定为 160.rpx + double lineWidth = 160.rpx; + + return AnimatedPositioned( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + bottom: 0, + left: bodyDeviceController.model.type == + 1 + ? 0 + : 160.rpx, // 第二个按钮横线从 160.rpx 开始 + child: Container( + width: lineWidth, // 横线宽度固定为 160.rpx + height: 4.rpx, + decoration: BoxDecoration( + color: themeController + .currentColor.sc2, + borderRadius: + BorderRadius.circular(2.rpx), + ), + ), + ); + }), + ], + ), + + Container( + width: + MediaQuery.sizeOf(context).width * 0.38, + constraints: BoxConstraints( + minWidth: 285.rpx, + ), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0, 20.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Container( + height: 80.rpx, + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + onChanged: (value) { + bodyDeviceController + .keyWord.value = value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + contentPadding: + EdgeInsets.fromLTRB( + 12.rpx, 0, 0.rpx, 0), + isDense: true, + labelStyle: + FlutterFlowTheme.of( + context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: '体征检测设备.输入关键词'.tr, + hintStyle: FlutterFlowTheme + .of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor + .sc4), + enabledBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: false, + fillColor: + FlutterFlowTheme.of( + context) + .secondaryBackground, + ), + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + cursorColor: + FlutterFlowTheme.of(context) + .primaryText, + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 26.rpx, 0, 0, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + height: 40.rpx, + child: VerticalDivider( + thickness: 2.rpx, + color: themeController + .currentColor.sc2, + ), + ), + // Text( + // '体征检测设备.搜索'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: AppConstants() + // .normal_text_fontSize, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc2, + // ), + // ), + ClickableContainer( + backgroundColor: + Colors.transparent, + highlightColor: themeController + .currentColor.sc5, + borderRadius: 6.rpx, + padding: EdgeInsets.zero, + onTap: () async { + await bodyDeviceController + .getDeviceList( + key: + bodyDeviceController + .keyWord + .value); + bodyDeviceController + .updateAll(); + }, + child: Text( + '体征检测设备.搜索'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + color: themeController + .currentColor.sc2, + ), + ), + ), + ].divide(SizedBox(width: 14.rpx)), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + Obx(() { + final isEmpty = + bodyDeviceController.deviceList.value.isEmpty; + + return isEmpty + ? Expanded( + child: NullDataWidget(), + ) + : Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 26.rpx, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: bodyDeviceController + .deviceList.value + .map((device) => + DeviceDataComponentWidget( + device: device)) + .toList() + .divide(SizedBox(height: 25.rpx)), + ), + ), + ); + }), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: TextStyle( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device/component/DeviceDataComponentWidget.dart b/lib/pages/device/component/DeviceDataComponentWidget.dart new file mode 100644 index 0000000..fdd8983 --- /dev/null +++ b/lib/pages/device/component/DeviceDataComponentWidget.dart @@ -0,0 +1,1293 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/ServiceConstant.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/enum/BindType.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; + +class DeviceDataComponentWidget extends StatefulWidget { + final Map device; + + const DeviceDataComponentWidget({super.key, required this.device}); + + @override + State createState() => + _DeviceDataComponentWidgetState(); +} + +class _DeviceDataComponentWidgetState extends State { + final GlobalKey _arrowKey = GlobalKey(); + OverlayEntry? _popupEntry; + BodyDeviceController bodyDeviceController = Get.find(); + PersonController personController = Get.find(); + + @override + void dispose() { + _popupEntry?.remove(); + super.dispose(); + } + + void _showPopup() { + final RenderBox? renderBox = + _arrowKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final position = renderBox.localToGlobal(Offset.zero); + final size = renderBox.size; + final screenHeight = MediaQuery.of(context).size.height; + final popupHeight = 160.rpx; + final popupWidth = 190.rpx; + final paddingOffset = 10.rpx; + + bool showAbove = (position.dy + size.height + popupHeight > screenHeight); + + _popupEntry?.remove(); + if (widget.device['bind_type'] == BindType.active.code) { + _popupEntry = OverlayEntry( + builder: (context) => GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + }, + child: Stack( + children: [ + Positioned( + top: showAbove + ? position.dy - popupHeight - paddingOffset + : position.dy + size.height + paddingOffset, + left: position.dx + size.width - popupWidth, + child: Material( + color: Colors.transparent, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: 300.rpx, // 限制最大宽度 + ), + child: IntrinsicWidth( + // 自适应内容宽度 + child: Container( + padding: EdgeInsets.all(20.rpx), + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(12.rpx), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 12.rpx, + spreadRadius: 1.rpx, + offset: Offset(0, 6.rpx), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 首页展示 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () async { + ApiResponse apiResponse = + await bodyDeviceController + .updateDeviceShow(widget.device); + _popupEntry?.remove(); + _popupEntry = null; + if (apiResponse.code == HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + } else { + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: + themeController.currentColor.sc9, + ); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, // 这里!行居中 + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.device['show'] != null && + widget.device['show'] == true) + SizedBox( + width: 24.rpx, + height: 24.rpx, + child: Icon( + Icons.check, // 使用Flutter内置的√图标 + size: 24.rpx, + color: + themeController.currentColor.sc2, + ), + ), + if (widget.device['show'] == null || + widget.device['show'] != true) + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.首页展示".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: (widget.device['show'] != null && + widget.device['show'] == true) + ? themeController.currentColor.sc2 + : themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 设备详情 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceDetail", + arguments: widget.device); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // 空占位 + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.设备详情".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // wifi配置 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceDetail", + arguments: widget.device); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // 空占位 + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "WIFI配置".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + //分享设备 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceSharePage", + arguments: widget.device); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // 空占位 + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "分享设备".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + //消息设置 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceDetail", + arguments: widget.device); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // 空占位 + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "消息设置".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 重命名 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + personController.currentPersonId.value = + widget.device['_id']; + personController.name.value = ""; + showConfirmDialog( + context, + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 41.rpx, 0.rpx, 0), + child: Container( + height: 80.rpx, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius.circular(12.rpx), + ), + child: Align( + alignment: + AlignmentDirectional(-1, 0), + child: TextFormField( + onChanged: (value) { + personController.name.value = + value; + }, + autofocus: false, + decoration: InputDecoration( + isDense: true, + hintText: '请输入人员名称'.tr, + hintStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: Colors.black, + ), + cursorColor: + FlutterFlowTheme.of(context) + .primaryText, + ), + ), + ), + ), + '修改人员名称'.tr, + onConfirm: () async { + ApiResponse response = + await personController + .updatePersonName( + widget.device['person'], + widget.device['_id'], + ); + if (response.code == HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + } + }, + onCancel: () { + print('用户点击了取消'); + }, + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.重命名".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 删除 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController + .currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + showConfirmDialog( + context, + Container(), + "是否确认解绑".tr, + onConfirm: () async { + ApiResponse apiResponse = + await bodyDeviceController + .deleteDevice(widget.device); + if (apiResponse.code == + HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + TopSlideNotification.show( + context, + text: apiResponse.msg!, + ); + } else { + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: + themeController.currentColor.sc9, + ); + } + }, + onCancel: () {}, + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.删除".tr, + style: TextStyle( + fontSize: + AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + )), + ], + ), + ), + ); + } + if (widget.device['bind_type'] == BindType.share.code) { + _popupEntry = OverlayEntry( + builder: (context) => GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + }, + child: Stack( + children: [ + Positioned( + top: showAbove + ? position.dy - popupHeight - paddingOffset + : position.dy + size.height + paddingOffset, + left: position.dx + size.width - popupWidth, + child: Material( + color: Colors.transparent, + child: Container( + width: popupWidth, + padding: EdgeInsets.all(20.rpx), + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(12.rpx), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.2), + blurRadius: 12.rpx, + spreadRadius: 1.rpx, + offset: Offset(0, 6.rpx), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 首页展示 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () async { + ApiResponse apiResponse = await bodyDeviceController + .updateDeviceShow(widget.device); + _popupEntry?.remove(); + _popupEntry = null; + if (apiResponse.code == HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + } else { + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, // 这里!行居中 + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.device['show'] != null && + widget.device['show'] == true) + SizedBox( + width: 24.rpx, + height: 24.rpx, + child: SvgPicture.asset( + 'assets/img/icon/tick.svg', + fit: BoxFit.contain, + color: themeController.currentColor.sc3, + ), + ), + if (widget.device['show'] == null || + widget.device['show'] != true) + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.首页展示".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 设备详情 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceDetail", + arguments: widget.device); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // 空占位 + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.设备详情".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 重命名 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + personController.currentPersonId.value = + widget.device['_id']; + personController.name.value = ""; + showConfirmDialog( + context, + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 41.rpx, 0.rpx, 0), + child: Container( + height: 80.rpx, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12.rpx), + ), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: TextFormField( + onChanged: (value) { + personController.name.value = value; + }, + autofocus: false, + decoration: InputDecoration( + isDense: true, + hintText: '请输入人员名称'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: Colors.black, + ), + cursorColor: FlutterFlowTheme.of(context) + .primaryText, + ), + ), + ), + ), + '修改人员名称'.tr, + onConfirm: () async { + ApiResponse response = + await personController.updatePersonName( + widget.device['person'], + widget.device['_id'], + ); + if (response.code == HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + } + }, + onCancel: () { + print('用户点击了取消'); + }, + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.重命名".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + + // 删除 + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc16 + .withOpacity(0.1), + borderRadius: 8.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + showConfirmDialog( + context, + Container(), + "是否确认解绑".tr, + onConfirm: () async { + ApiResponse apiResponse = + await bodyDeviceController + .deleteDevice(widget.device); + if (apiResponse.code == HttpStatusCodes.ok) { + bodyDeviceController.getDeviceList(); + TopSlideNotification.show( + context, + text: apiResponse.msg!, + ); + } else { + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } + }, + onCancel: () {}, + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(width: 24.rpx, height: 24.rpx), + SizedBox(width: 10.rpx), + Text( + "体征检测设备.删除".tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } + + Overlay.of(context)?.insert(_popupEntry!); + } + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular(20.rpx), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Container( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + // 'TH689564522DL', + '${widget.device['person']['name'] ?? '未命名'.tr}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ClickableContainer( + key: _arrowKey, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 16.rpx, 14.rpx, 16.rpx), + backgroundColor: Colors.transparent, + highlightColor: Colors.black.withOpacity(0.1), + borderRadius: 8.rpx, + onTap: _showPopup, + child: Container( + width: 15.rpx, + height: 8.rpx, + child: SvgPicture.asset( + 'assets/img/icon/arrow_down.svg', + fit: BoxFit.cover, + color: Colors.white, + ), + ), + ), + ], + ), + SizedBox(height: 40.rpx), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 100.rpx, + constraints: BoxConstraints( + minWidth: 105.rpx, + ), + decoration: BoxDecoration(), + child: Text( + '设备ID'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + Text( + '${widget.device['_id']}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + SizedBox(height: 40.rpx), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 100.rpx, + constraints: BoxConstraints( + minWidth: 105.rpx, + ), + decoration: BoxDecoration(), + child: Text( + '更新时间'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + Text( + '${widget.device['update_time'] != null ? MyUtils.formatDateTime(widget.device['update_time']) : '未知时间'.tr}', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + SizedBox(height: 40.rpx), + if (widget.device['bind_type'] == BindType.active.code) + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 100.rpx, + constraints: BoxConstraints( + minWidth: 105.rpx, + ), + decoration: BoxDecoration(), + child: Text( + '已分享'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + Text( + '${widget.device['shareNum']}' + "人".tr, + style: + FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + // Icon( + // Icons.arrow_back, + // color: FlutterFlowTheme.of(context).primaryText, + // size: 24.rpx, + // ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 14.rpx, 0), + child: Container( + width: 28.rpx, + height: 28.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/group.svg', + fit: BoxFit.cover, + // color: stringToColor("#333333"), //固定 + color: Colors.white, + ), + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + if (widget.device['bind_type'] == BindType.share.code) + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 100.rpx, + constraints: BoxConstraints( + minWidth: 105.rpx, + ), + decoration: BoxDecoration(), + child: Text( + '设备来源'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + Text( + '张三', + style: + FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + // Icon( + // Icons.arrow_back, + // color: FlutterFlowTheme.of(context).primaryText, + // size: 24.rpx, + // ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 14.rpx, 0), + child: Container( + width: 28.rpx, + height: 28.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + // child: SvgPicture.asset( + // 'assets/img/icon/group.svg', + // fit: BoxFit.cover, + // color: Colors.white, + // // color: stringToColor("#333333"), //固定 + // ), + child: Text( + "云关爱".tr, + style: TextStyle( + color: themeController.currentColor.sc4, + fontSize: AppConstants().normal_text_fontSize, + ), + ), + ), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + SizedBox(height: 40.rpx), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 100.rpx, + constraints: BoxConstraints( + minWidth: 105.rpx, + ), + decoration: BoxDecoration(), + child: Text( + '设备状态', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + if (widget.device['status']['wifi'] != null && + widget.device['status']['wifi'] != -1) + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: 25.rpx, + height: 25.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/signal${widget.device['status']['wifi']}.svg', + fit: BoxFit.fill, + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: 16.rpx, + height: 36.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: widget.device['status']['inBed'] == 0 + ? SvgPicture.asset( + 'assets/img/icon/not_bed.svg', + fit: BoxFit.fill, + ) + : SvgPicture.asset( + 'assets/img/icon/in_bed.svg', + fit: BoxFit.fill, + ), + ), + ), + if (widget.device['status']['failure'] != 0) + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: 27.rpx, + height: 27.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/device_issue.svg', + fit: BoxFit.cover, + ), + ), + ), + if (widget.device['status']['upgrade'] != 0) + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 0.rpx, 0, 0), + child: Container( + width: 34.rpx, + height: 24.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/upgrade.svg', + fit: BoxFit.cover, + // color: themeController.currentColor.sc3, //固定 + ), + ), + ), + //todo 缺少一个设备离线的图标 + ].divide(SizedBox(width: 50.rpx)), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + Divider( + thickness: 2, + color: themeController.currentColor.sc4, + ), + SizedBox(height: 5.rpx), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + // personController.currentPersonId = widget.device + if (widget.device['person'] != null) { + personController.currentPersonId.value = + widget.device['_id']; + personController.name.value = + widget.device['person']['name']; + personController.gender.value = + widget.device['person']['gender'] ?? 1; + personController.weight.value = + widget.device['person']['weight'] ?? 0; + personController.selectedDiseaseIds.value = + widget.device['person']['disease'] ?? []; + personController.birthday.value = + widget.device['person']['birthday'] ?? ''; + personController.dateTime = MyUtils.formatBirthdayTime( + widget.device['person']['birthday']); + } + // Get.toNamed("/personPage"); + Get.toNamed("/updatePersonPage"); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0.rpx), + child: Container( + width: MediaQuery.sizeOf(context).width * 0.19, + height: MediaQuery.sizeOf(context).height * 0.0037, + constraints: BoxConstraints( + minWidth: 143.rpx, + minHeight: 61.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "体征检测设备.人员资料".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + Get.toNamed("/instantBodyPage", arguments: widget.device); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.19, + height: MediaQuery.sizeOf(context).height * 0.0037, + constraints: BoxConstraints( + minWidth: 143.rpx, + minHeight: 61.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "体征检测设备.实时体征".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + TopSlideNotification.show(context, text: "待开发功能".tr); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.19, + height: MediaQuery.sizeOf(context).height * 0.0037, + constraints: BoxConstraints( + minWidth: 143.rpx, + minHeight: 61.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "体征检测设备.消息回看".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + String mac = widget.device['mac']; + String sleepReportUrl = + "${ServiceConstant.sleep_report_url}?mac=${mac}&token=${ServiceConstant.sleep_token}"; + Get.toNamed("/sleepReportPage", + arguments: sleepReportUrl); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.19, + height: MediaQuery.sizeOf(context).height * 0.0037, + constraints: BoxConstraints( + minWidth: 143.rpx, + minHeight: 61.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "体征检测设备.健康报告".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ], + ), + ].divide(SizedBox(height: 0.rpx)), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device/component/DeviceStatusInfoWidget.dart b/lib/pages/device/component/DeviceStatusInfoWidget.dart new file mode 100644 index 0000000..0673389 --- /dev/null +++ b/lib/pages/device/component/DeviceStatusInfoWidget.dart @@ -0,0 +1,88 @@ +import 'package:ef/base/widget/flutterflow/FlutterFlowTheme.dart'; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class DeviceStatusInfoWidget extends StatelessWidget { + final String title; // 标题,如“在床” + final String iconAsset; // SVG 路径,如 'assets/icons/bed.svg' + final String value; // 显示内容,如“在离床” + ThemeController themeController = Get.find(); + + DeviceStatusInfoWidget({ + super.key, + required this.title, + required this.iconAsset, + required this.value, + }); + + @override + Widget build(BuildContext context) { + return ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: themeController.currentColor.sc5, + borderRadius: AppConstants().normal_container_radius, + padding: EdgeInsets.zero, + onTap: () { + print('点击了 $title 模块'); + }, + child: Container( + width: MediaQuery.sizeOf(context).width * 0.32, + constraints: BoxConstraints( + minWidth: 201.rpx, + minHeight: 182.rpx, + ), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(20.rpx, 29.rpx, 20.rpx, 39.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SvgPicture.asset( + iconAsset, + width: 38.rpx, + height: 38.rpx, + // colorFilter: ColorFilter.mode( + // FlutterFlowTheme.of(context).primaryText, + // BlendMode.srcIn, + // ), + ), + Text( + value, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 48.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ] + .divide(SizedBox(width: 18.rpx)) + .addToStart(SizedBox(width: 31.rpx)), + ), + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(height: 39.rpx)), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device/device_detail.dart b/lib/pages/device/device_detail.dart new file mode 100644 index 0000000..5733c32 --- /dev/null +++ b/lib/pages/device/device_detail.dart @@ -0,0 +1,477 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:qr_flutter/qr_flutter.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +class DeviceDetailPage extends StatefulWidget { + var device; + DeviceDetailPage({super.key, required this.device}); + + @override + State createState() => _DeviceDetailPageState(); +} + +class _DeviceDetailPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0.rpx, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '设备详情.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0.rpx, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0.rpx, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 26.rpx, 30.rpx, 0.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular( + AppConstants().normal_container_radius), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 50.rpx, 0.rpx, 0.rpx, 0.rpx), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.267, + height: MediaQuery.sizeOf(context).width * 0.267, + constraints: BoxConstraints( + minWidth: 200.rpx, + minHeight: 200.rpx, + ), + decoration: BoxDecoration(), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.设备名称'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.MAC'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.型号'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.版本'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.网络状态'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.故障状态'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.更新状态'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '设备详情.更新时间'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc4, + ), + ), + ), + ), + ] + .divide(SizedBox(height: 34.rpx)) + .addToStart(SizedBox(height: 92.rpx)) + .addToEnd(SizedBox(height: 97.rpx)), + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.267, + height: MediaQuery.sizeOf(context).width * 0.267, + constraints: BoxConstraints( + minWidth: 200.rpx, + minHeight: 200.rpx, + ), + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context) + .secondaryBackground, + ), + // child: ClipRRect( + // borderRadius: BorderRadius.circular(8.rpx), + // child: Image.network( + // 'https://picsum.photos/seed/851/600', + // width: 200.rpx, + // height: 200.rpx, + // fit: BoxFit.cover, + // ), + // ), + child: QrImageView( + data: '1234567890', + version: QrVersions.auto, + size: 200.0.rpx, + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + 'A35968956', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['person']['name'] ?? '未命名'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['mac'] ?? '-'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '-', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '-', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['status']['status'] == 1 ? '在线'.tr : '离线'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['status']['failure'] == 1 ? '有故障'.tr : '无故障'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['status']['upgrade'] == 1 ? '有更新'.tr : '无更新'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Container( + height: 50.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1.rpx, 0.rpx), + child: Text( + '${widget.device['update_time'] ?? '-'.tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.rpx, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + ] + .divide(SizedBox(height: 34.rpx)) + .addToStart(SizedBox(height: 92.rpx)) + .addToEnd(SizedBox(height: 97.rpx)), + ), + ].divide(SizedBox(width: 34.rpx)), + ), + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device/instant_body_page.dart b/lib/pages/device/instant_body_page.dart new file mode 100644 index 0000000..8e9e4c3 --- /dev/null +++ b/lib/pages/device/instant_body_page.dart @@ -0,0 +1,505 @@ +import 'dart:async'; + +import 'package:EasyDartModule/EasyDartModule.dart' as edm; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/CommonVariables.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/WebSocketMessage.dart'; +import 'package:vbvs_app/pages/device/component/DeviceStatusInfoWidget.dart'; + +class InstantBodyPage extends StatefulWidget { + var personInfo; + InstantBodyPage({super.key, required this.personInfo}); + + @override + State createState() => _InstantBodyPageState(); +} + +class _InstantBodyPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); + + int maxBodyMotion = 1; + String breathState = "否"; + String inBed = "离床".tr; + String onlineState = "离线".tr; + Timer? _onlineTimer; // 添加 Timer 引用 + int bodyMotion = 0; + int breathrate = 0; + String snores = "否".tr; + int heartrate = 0; + + @override + void initState() { + edm.EasyDartModule.websocket.sendData(jsonEncode(WebSocketMessage( + path: "/vsbs/web/rt/marttress", + type: 1, + data: {"mac": widget.personInfo['mac']}))); + _startOnlineTimer(); // 初始化时启动定时器 + super.initState(); + } + + @override + void dispose() { + _onlineTimer?.cancel(); // 取消定时器,防止内存泄漏 + edm.EasyDartModule.websocket.sendData( + jsonEncode(WebSocketMessage(path: "/vsbs/web/rt/marttress", type: 2))); + super.dispose(); + } + + void _startOnlineTimer() { + _onlineTimer?.cancel(); // 取消之前的定时器 + _onlineTimer = Timer.periodic(Duration(seconds: 30), (timer) { + if (mounted) { + setState(() { + onlineState = "离线".tr; // 30 秒内没有接收到数据,设置为离线 + }); + } + }); + } + + @override + Widget build(BuildContext context) { + Map device = widget.personInfo; + CommonVariables.callMap["/vsbs/web/rt/marttress"] = (data) { + inBed = data["inBed"]; + // 心率 呼吸 体动 呼吸暂停 + if ("离床" == inBed) { + breathState = "否"; + data["breathRate"] = 0; + data["heartRate"] = 0; + data["bodyMotion"] = 0; + } else { + breathState = data["breathState"]; + bodyMotion = data['bodyMotion']; + breathrate = data["breathRate"]; + heartrate = data['heartRate']; + snores = data['snores']; + } + + if (mounted) { + setState(() { + onlineState = "在线".tr; // 接收到数据,设置为在线 + }); + } + + _startOnlineTimer(); // 重置定时器 + }; + + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0.rpx, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + RichText( + text: TextSpan( + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + children: [ + TextSpan( + text: '实时体征.标题'.tr, + ), + TextSpan( + text: "(${onlineState})", + style: TextStyle( + color: onlineState == '在线' + ? themeController.currentColor.sc2 + : themeController + .currentColor.sc9, // 👈 单独设置颜色 + ), + ), + ], + ), + ), + Positioned( + left: 0.rpx, + child: returnIconButtom, + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 29.rpx, 0.rpx, 0.rpx), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 120.rpx), + child: ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc5, // 或你希望的点击水波纹颜色 + borderRadius: AppConstants() + .normal_container_radius, // 如果你想加圆角可以设置 eg. 12.rpx + padding: EdgeInsets.zero, + onTap: () { + print('点击了体征卡片'); + }, + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Text( + '实时体征.姓名'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + Text( + '实时体征.年龄'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '${device['person']['name'] ?? '未命名'.tr}', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Text( + '${MyUtils.getAgeByDate(MyUtils.formatBirthdayTime(device['person']['birthday'])) ?? '未知数据'.tr}', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + ] + .divide(SizedBox(width: 33.rpx)) + .addToStart(SizedBox(width: 37.rpx)), + ), + ] + .addToStart(SizedBox(height: 36.rpx)) + .addToEnd(SizedBox(height: 36.rpx)), + ), + ), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: + CrossAxisAlignment.end, + children: [ + Text( + '实时体征.设备ID'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + Text( + '实时体征.体重'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + // '${device['_id']??'未知数据'.tr}', + "D11250300003", + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + Text( + '${device['person']['weight'] ?? '未知数据'.tr}kg', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + ), + ].divide(SizedBox(height: 34.rpx)), + ), + ] + .divide(SizedBox(width: 33.rpx)) + .addToStart(SizedBox(width: 37.rpx)), + ), + ] + .addToStart(SizedBox(height: 36.rpx)) + .addToEnd(SizedBox(height: 36.rpx)), + ), + ), + ], + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 66.rpx, 0, 66.rpx, 0), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'assets/img/body_black.gif'), // 本地图片 + fit: BoxFit.cover, + ), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + DeviceStatusInfoWidget( + title: "在离床".tr, + iconAsset: "assets/img/icon/bed_status.svg", + value: inBed, + ), + DeviceStatusInfoWidget( + title: "体动".tr, + iconAsset: "assets/img/icon/bodymotion.svg", + value: "${bodyMotion}" ?? "未知数据".tr, + ), + ], + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + DeviceStatusInfoWidget( + title: "心率".tr, + iconAsset: "assets/img/icon/heart.svg", + value: "${heartrate}", + ), + DeviceStatusInfoWidget( + title: "打鼾".tr, + iconAsset: "assets/img/icon/snore.svg", + value: '${snores}'.tr, + ), + ], + ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + DeviceStatusInfoWidget( + title: "呼吸".tr, + iconAsset: "assets/img/icon/breathe.svg", + value: '${breathrate}', + ), + DeviceStatusInfoWidget( + title: "呼吸暂停".tr, + iconAsset: + "assets/img/icon/breathe_pause.svg", + value: '${breathState}', + ), + ], + ), + ].divide(SizedBox(height: 49.rpx)), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 67.rpx, 0.rpx, 0.rpx), + child: Container( + height: 40.rpx, + child: Text( + bodyMotion >= maxBodyMotion ? '请保持静止'.tr : "", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc9, + ), + ), + ), + ), + SizedBox( + height: 207.rpx, + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 可自定义背景色 + highlightColor: Colors.white, // 点击涟漪颜色 + borderRadius: 16.rpx, // 圆角大小,可按需调整 + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 0.rpx, 30.rpx, 0.rpx), + onTap: () {}, + child: Container( + padding: EdgeInsetsDirectional.fromSTEB( + 26.rpx, 26.rpx, 26.rpx, 26.rpx), + decoration: BoxDecoration( + // color: FlutterFlowTheme.of(context) + // .primaryBackground + // .withOpacity(0.6), // 半透明背景 + borderRadius: BorderRadius.circular(16.rpx), + border: Border.all( + // 设置边框颜色和宽度 + color: themeController.currentColor.sc4, // 边框颜色 + width: 2.rpx, // 边框宽度 + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Container( + width: 23.rpx, + height: 23.rpx, + // width: double.infinity, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/tips.svg', + fit: BoxFit.cover, + color: themeController.currentColor.sc3, + ), + ), + ), + Expanded( + child: Text( + '实时体征.提示'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + ].divide(SizedBox(width: 23.rpx)), + ), + ), + ), + SizedBox( + height: 26.rpx, + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/MobileScannerTestPage.dart b/lib/pages/device_bind/MobileScannerTestPage.dart new file mode 100644 index 0000000..f6d4dbf --- /dev/null +++ b/lib/pages/device_bind/MobileScannerTestPage.dart @@ -0,0 +1,255 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:img_picker/img_picker.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; + +class MobileScannerTestPage extends StatefulWidget { + const MobileScannerTestPage({Key? key}) : super(key: key); + + @override + State createState() => _MobileScannerTestPageState(); +} + +class _MobileScannerTestPageState extends State + with TickerProviderStateMixin { + String? scannedText; + bool isScanning = true; + + late AnimationController _controller; + late Animation _animation; + late MobileScannerController _scannerController; + BlueteethBindController blueteethBindController = Get.find(); + + final double scanAreaSize = 480.rpx; + + @override + void initState() { + super.initState(); + _scannerController = MobileScannerController(); + _controller = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + )..repeat(reverse: true); + + _animation = Tween(begin: 0, end: 1).animate(CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + )); + } + + @override + void dispose() { + _controller.dispose(); + _scannerController.dispose(); + super.dispose(); + } + + void _onDetect(BarcodeCapture capture) { + if (!isScanning) return; + + final Barcode? barcode = capture.barcodes.first; + final String? value = barcode?.rawValue; + + if (value != null) { + setState(() { + scannedText = value; + isScanning = false; + if (scannedText != null && scannedText!.isNotEmpty) { + blueteethBindController.scanMac.value = scannedText!; + showConfirmDialog( + context, + Container(), + '蓝牙绑定.确定绑定提示'.tr, + onConfirm: () async { + ApiResponse response = + await blueteethBindController.bindDeviceByScan(scannedText!); + if (response.code == HttpStatusCodes.ok) { + TopSlideNotification.show( + context, + text: "蓝牙绑定.连接成功".tr, + textColor: themeController.currentColor.sc2, + ); + } else { + TopSlideNotification.show( + context, + text: response.msg ?? "蓝牙绑定.连接异常".tr, + textColor: themeController.currentColor.sc9, + ); + } + }, + onCancel: () { + print('用户点击了取消'); + // 执行取消后的处理逻辑 + }, + ); + } + }); + + Future.delayed(const Duration(seconds: 2), () { + setState(() { + isScanning = true; + }); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '扫一扫.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + centerTitle: false, + ), + body: Container( + child: Column( + children: [ + Expanded( + child: Stack( + children: [ + MobileScanner( + controller: _scannerController, + onDetect: _onDetect, + ), + Align( + alignment: Alignment.topCenter, // 使扫描框位于顶部居中 + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.only(top: 219.rpx), // 向上移动扫描框 + child: SizedBox( + width: scanAreaSize, + height: scanAreaSize, + child: Stack( + children: [ + Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + ), + ), + AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Positioned( + top: scanAreaSize * _animation.value, + left: 0, + right: 0, + child: Container( + height: 2, + color: themeController.currentColor.sc2, + ), + ); + }, + ), + ], + ), + ), + ), + SizedBox(height: 31.rpx), + Text( + '扫一扫.提示'.tr, + style: TextStyle( + color: themeController.currentColor.sc2, + fontSize: 26.rpx, + ), + ), + ], + ), + ), + ], + ), + ), + Padding( + padding: EdgeInsets.fromLTRB(0, 0, 0, 83.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () async { + final picker = ImagePicker(); + final pickedFile = + await picker.pickImage(source: ImageSource.gallery); + if (pickedFile != null) { + final bytes = await pickedFile.readAsBytes(); + final image = await decodeImageFromList(bytes); + } + }, + child: Column( + children: [ + Icon(Icons.photo, + color: themeController.currentColor.sc2, + size: 60.rpx), + SizedBox(height: 10.rpx), + Text( + '扫一扫.相册'.tr, + style: TextStyle( + color: themeController.currentColor.sc2, + fontSize: 24.rpx, + ), + ), + ], + ), + ), + SizedBox(width: 80.rpx), + GestureDetector( + onTap: () { + _scannerController.toggleTorch(); + }, + child: Column( + children: [ + Icon(Icons.flashlight_on, + color: themeController.currentColor.sc2, + size: 60.rpx), + SizedBox(height: 10.rpx), + Text( + '扫一扫.手电筒'.tr, + style: TextStyle( + color: themeController.currentColor.sc2, + fontSize: 24.rpx, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/bind_device_success.dart b/lib/pages/device_bind/bind_device_success.dart index 779e492..d82088e 100644 --- a/lib/pages/device_bind/bind_device_success.dart +++ b/lib/pages/device_bind/bind_device_success.dart @@ -200,7 +200,7 @@ class _EPageState extends State { 'assets/img/icon/share.svg', width: 25.rpx, height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - color: themeController.currentColor.sc3, + color: Colors.white, ), Text( '绑定成功.立即分享'.tr, diff --git a/lib/pages/device_bind/blueteeth_device_page.dart b/lib/pages/device_bind/blueteeth_device_page.dart index 1de07b6..c86a07b 100644 --- a/lib/pages/device_bind/blueteeth_device_page.dart +++ b/lib/pages/device_bind/blueteeth_device_page.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:ef/ef.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_svg/svg.dart'; @@ -14,19 +13,17 @@ import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; import 'package:vbvs_app/model/BleDeviceData.dart'; -import 'package:vbvs_app/pages/common/selectDialog.dart'; import 'package:vbvs_app/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart'; -import 'package:vbvs_app/common/util/Ble.dart' as ble; class BlueteethDevicePage extends StatefulWidget { int tid = -1; BlueteethDevicePage({super.key, this.tid = -1}); @override - State createState() => _EPageState(); + State createState() => _BlueteethDevicePageState(); } -class _EPageState extends State { +class _BlueteethDevicePageState extends State { GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); BlueteethBindController blueteethBindController = Get.find(); @@ -49,6 +46,8 @@ class _EPageState extends State { @override void initState() { super.initState(); + blueteethBindController.model.devicelist = []; + blueteethBindController.model.betDevicelist = []; flutterBlue = FlutterBluePlus(); // 初始化flutterBlue实例 _checkBluetoothPermission(); // 检查蓝牙权限 Get.find().startStatusPolling(); @@ -114,6 +113,7 @@ class _EPageState extends State { // 开始扫描蓝牙设备 void _startScanning() async { + if (!mounted) return; var bluetoothState = await FlutterBluePlus.isOn; if (!bluetoothState && !_isDialogShowing) { _isDialogShowing = true; @@ -128,8 +128,9 @@ class _EPageState extends State { }); await FlutterBluePlus.startScan(timeout: Duration(seconds: 10)); + // await FlutterBluePlus.startScan(timeout: Duration(minutes: 30)); - FlutterBluePlus.scanResults.listen((results) { + _scanSubscription = FlutterBluePlus.scanResults.listen((results) { final signalThreshold = blueteethBindController.model.singal!; final filteredResults = results .where((r) => @@ -149,6 +150,9 @@ class _EPageState extends State { deviceData.rssi = r.rssi; deviceData.mac = deviceData.deviceId.replaceAll(':', ''); parsedDeviceList.add(deviceData); + if (deviceData.mac!.toLowerCase() == 'b43a45c3dfa0') { + print('匹配设备数据: ${deviceData.mac}-->sn:${deviceData.sn}'); + } } catch (e) { print("设备数据解析失败: $e"); } @@ -188,13 +192,15 @@ class _EPageState extends State { // 等待扫描完成 await Future.delayed(Duration(seconds: 10)); + // await Future.delayed(Duration(minutes: 30)); await FlutterBluePlus.stopScan(); - setState(() { - isScanning = false; - }); - - print("扫描完成"); + if (mounted) { + setState(() { + isScanning = false; + }); + } + // print("扫描完成"); } } @@ -210,10 +216,13 @@ class _EPageState extends State { // 停止扫描 void _stopScanning() { if (isScanning) { - FlutterBluePlus.stopScan(); // 停止扫描 - setState(() { - isScanning = false; // 更新扫描状态 - }); + FlutterBluePlus.stopScan(); + _scanSubscription?.cancel(); // 取消订阅 + if (mounted) { + setState(() { + isScanning = false; + }); + } } } @@ -222,396 +231,15 @@ class _EPageState extends State { _timer?.cancel(); } + StreamSubscription>? _scanSubscription; // 添加扫描订阅变量 @override void dispose() { - super.dispose(); _stopPeriodicScan(); // 停止定时扫描 - _stopScanning(); // 确保离开页面时停止扫描 - } - - connectToDevice(device, {int time = 5}) { - ble.connectToDevice( - { - "device": device, - 'success': (ble.ConnectedDeviceProp deviceProp) { - if (deviceProp.connectedDevicePropType == - ble.ConnectedDevicePropType.JunHe) { - currentConnectedDeviceProp = deviceProp; - deviceProp.write3OfString("blog enable"); - deviceProp.write3OfString("blog rlmax=128"); - Timer(const Duration(microseconds: 100), () async { - String log = ""; - Function logAdd = (l) { - log += l; - }; - deviceProp.receiveLogArr.add(logAdd); - deviceProp.encodeType = 1; - deviceProp.deviceType = 1; - Timer.periodic(const Duration(milliseconds: 300), (timer) async { - if (timer.tick > 20) { - ble.disconnect(currentConnectedDeviceProp); - failSelectDialog(); - timer.cancel(); - } - if (log.contains("GB2312") || log.contains("UTF-8")) { - timer.cancel(); - if (log.contains('CHARSET:UTF-8')) { - deviceProp.encodeType = 2; - } - if (log.contains('TARGET:ESPXX')) { - deviceProp.deviceType = 2; - } - log = ""; - bool isSuccess = false; - for (var i = 0; i < 4; i++) { - deviceProp.write3OfString("at+system info"); - await Future.delayed(const Duration(milliseconds: 400)); - RegExp regExp = RegExp(r"Target Mac:(\S*)"); - RegExpMatch? regExpMatch = regExp.firstMatch(log); - if (regExpMatch != null && regExpMatch.group(1) != null) { - String? mac = regExpMatch.group(1); - if (mac?.length == 12 && mac != "000000000000") { - bindArr[2] = "$mac".toUpperCase(); - } - isSuccess = true; - break; - } - } - deviceProp.receiveLogArr.remove(logAdd); - print("$bindArr"); - RegExp regExp = RegExp( - r"WIFI CONNECTED INFO:SSID=([^\t\n]*)\s*,RSSI=(\S*)\s*,"); - RegExpMatch? regExpMatch = regExp.firstMatch(log); - if (regExpMatch != null && log.contains("Status=connect")) { - blueteethBindController.model.connectedWifiName = - regExpMatch.group(1) ?? ""; - if (int.tryParse("${regExpMatch.group(2)}") != null) { - blueteethBindController.model.connectedRssi = - int.parse("${regExpMatch.group(2)}"); - } - blueteethBindController.updateAll(); - } - ble.bleParse(); - if (bindArr[0] != null && - bindArr[0] != "" && - bindArr[1] != "") { - setState(() { - currentMsg = "绑定中..."; - }); - blueteethBindController.bindDevice({ - "tid": widget.tid, - "name": blueteethBindController.model.deviceName, - "mac": bindArr[0], - "macA": bindArr[1], - "macB": bindArr[2] - }).then((d) { - blueteethBindController.model.bindArr = bindArr; - globalController.getDeviceList(); - LoadingDialog.hide(); - showCustomConfirmDialog(context, "设备添加成功!", - btnName: "打开WIFI配置", - icon: ConfirmDialogIcon.success) - .then((d) { - if (d == "confirm") { - Get.offAndToNamed("/wifi", arguments: deviceProp); - } - }); - }).catchError((d) { - print("$d"); - currentMsg = "绑定失败: ${d.message}"; - ble.disconnect(currentConnectedDeviceProp); - failSelectDialog(title: "${d.message}"); - }); - } else { - LoadingDialog.hide(); - Get.offAndToNamed("/wifi", arguments: deviceProp); - } - } else { - deviceProp.read6(); - } - }); - }); - } else if (deviceProp.connectedDevicePropType == - ble.ConnectedDevicePropType.QuanShi) { - List receive = []; - Function fun = (d) { - receive.add(d); - }; - deviceProp.receiveLogArr.add(fun); - List head = [ - 255, - 255, - 255, - 255, - 1, - 0, - 12, - 17, - ]; - Timer.periodic(const Duration(seconds: 1), (timer) { - if (timer.tick > 20) { - timer.cancel(); - currentMsg = "错误:未能获取到MAC"; - failSelectDialog(); - } - deviceProp.write( - Uint8List.fromList([ - 0xFF, - 0xFF, - 0xFF, - 0xFF, - 0x01, - 0x00, - 0x0C, - 0x0B, - 0x0F, - 0x23, - 0x04 - ]), - null, - null); - if (receive.length > 0) { - receive.forEach((data) { - if (data.length != 17) { - return; - } - bool r = true; - for (var i = 0; i < head.length; i++) { - if (head[i] != data[i]) { - r = false; - } - } - if (r == false) { - return; - } - bindArr[1] = ble.ab2str(data.sublist(9, 15)).toUpperCase(); - timer.cancel(); - deviceProp.receiveLogArr.remove(fun); - blueteethBindController.model.deviceName = - deviceProp.connectDevice?.advName; - ble.disconnect(deviceProp); - toFindJunhe(); - }); - } - }); - } else { - List receive = []; - Function fun = (d) { - receive.add(d); - }; - deviceProp.receiveLogArr.add(fun); - List head = [255, 255, 255, 255, 0x00, 0x08, 0x40, 0x01]; - Timer.periodic(const Duration(seconds: 1), (timer) { - if (timer.tick > 20) { - timer.cancel(); - currentMsg = "错误:未能获取到MAC"; - failSelectDialog(); - } - deviceProp.write( - Uint8List.fromList([ - 255, - 255, - 255, - 255, - 0x00, - 0x03, - 0x40, - 0x01, - 0x01, - 0x00, - 0x45, - 0xfd - ]), - null, - null); - if (receive.length > 0) { - receive.forEach((data) { - if (data.length != 17) { - return; - } - bool r = true; - for (var i = 0; i < head.length; i++) { - if (head[i] != data[i]) { - r = false; - } - } - if (r == false) { - return; - } - - bindArr[1] = ble.ab2str(data.sublist(8, 14)).toUpperCase(); - print("$bindArr"); - timer.cancel(); - deviceProp.receiveLogArr.remove(fun); - blueteethBindController.model.deviceName = - deviceProp.connectDevice?.advName; - ble.disconnect(deviceProp); - toFindJunhe(); - }); - } - }); - } - }, - 'fail': (e) { - print(e); - if (time > 0) { - connectToDevice(device, time: time - 1); - } else { - currentMsg = "蓝牙无法连接上设备"; - failSelectDialog(title: currentMsg); - } - } - }, - ); - } - - isBind() { - return !(blueteethBindController.model.bindArr[1]?.length == 12); - } - - failSelectDialog({String title = ""}) { - LoadingDialog.hide(); - setState(() {}); - showCustomConfirmAndCancelDialog( - context, title == "" ? (isBind() ? "绑定失败" : "连接失败") : title, - confirmName: "重试", cancelName: "返回") - .then((d) { - if (d == "confirm") { - if (connectDeviceCurrent != null) { - ble.bleParse(); - ble.start((List d) { - setState(() { - bleDevice = d; - }); - }, bleOnCall: () { - LoadingDialog.show("连接中...\n靠近设备2米内", - icon: - isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); - setState(() { - currentMsg = "连接设备中..."; - }); - connectToDevice(connectDeviceCurrent); - }); - } else { - bleExec(); - } - } else if (d == "cancel") { - Get.back(); - } - }); - } - - bleExec() { - ble.bleParse(); - connectTimer?.cancel(); - int index = 0; - bool isCloseLoadingDialog = false; - isFind = false; - blueteethBindController.model.bindArr = bindArrBackup; - bindArr = ["", "", ""]; - ble.start((List d) { - setState(() { - bleDevice = d; - }); - if (isBind()) { - if (isCloseLoadingDialog == false && - bleDevice.indexWhere((item) { - if (widget.tid == 1) { - return ble.isQuanShiDevice(item["name"]); - } else { - return ble.isMHTSWES(item["name"]); - } - }) != - -1) { - isCloseLoadingDialog = true; - LoadingDialog.hide(); - } - } - }, bleOnCall: () { - if (isBind()) { - LoadingDialog.show("搜索蓝牙设备中...\n请打开蓝牙开关、定位开关\n与设备距离在2米内", - icon: isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); - Timer.periodic(const Duration(seconds: 1), (t) { - if (t.tick > 15) { - t.cancel(); - isCloseLoadingDialog = true; - LoadingDialog.hide(); - showCustomConfirmAndCancelDialog(context, "未发现设备", - confirmName: "重试", cancelName: "返回") - .then((d) { - if (d == "confirm") { - bleExec(); - } else if (d == "cancel") { - Get.back(); - } - }); - } else { - if (isCloseLoadingDialog == true) { - t.cancel(); - } - } - }); - return; - } - LoadingDialog.show( - "${isBind() ? "绑定中...\n与设备距离在2米内" : "连接中...\n请打开蓝牙开关、定位开关\n与设备距离在2米内"}", - icon: isBind() ? LoadingDialogIcon.ble : LoadingDialogIcon.wifi); - connectTimer = Timer.periodic(const Duration(seconds: 1), (t) { - index++; - if (index > 15) { - connectTimer = null; - t.cancel(); - failSelectDialog(); - } - var d = bleDevice; - if (d != null && d.length > 0) { - if (isBind()) { - var deviceble = d.firstWhere((item) { - bool isFF = false; - if (widget.tid == 1) { - isFF = ble.isQuanShiDevice(item["name"]); - } else { - isFF = ble.isMHTSWES(item["name"]); - } - if (isFF) { - isFF = globalController.model.deviceList.indexWhere( - (d) => d["mac"] == item["adData"]["deviceId"]) == - -1 - ? true - : false; - } - return isFF; - }, orElse: () => null); - if (!isFind && deviceble != null) { - print("quanshidevice"); - isFind = true; - setState(() { - currentMsg = "连接设备中..."; - }); - t.cancel(); - connectToDevice(deviceble["device"]); - bindArr[0] = deviceble["adData"]["deviceId"]; - } - } else { - var deviceble = d.firstWhere( - (item) => - item["adData"]["deviceId"] == - blueteethBindController.model.bindArr[1], - orElse: () => null); - if (!isFind && deviceble != null) { - print("junhedevice"); - isFind = true; - t.cancel(); - setState(() { - currentMsg = "连接设备中..."; - }); - connectToDevice(deviceble["device"]); - bindArr[1] = deviceble["adData"]["deviceId"]; - } - } - } - }); - }); + _stopScanning(); // 停止扫描 + _scanSubscription?.cancel(); // 取消扫描订阅 + connectTimer?.cancel(); // 取消连接定时器 + blueteethBindController.stopStatusPolling(); // 停止状态轮询 + super.dispose(); } @override @@ -679,7 +307,7 @@ class _EPageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 0, 30.rpx, 0, 30.rpx), child: Text( - '蓝牙绑定.扫描蓝牙设备中…'.tr, + '蓝牙绑定.扫描'.tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -917,42 +545,48 @@ class _EPageState extends State { child: Padding( padding: EdgeInsetsDirectional.fromSTEB(19.rpx, 0, 0, 0), - child: Text( - '匹配出的外围设备(${blueteethBindController.model.devicelist!.length})', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController.currentColor.sc3, - ), - ), + child: Obx(() { + return Text( + '匹配出的外围设备(${blueteethBindController.model.betDevicelist!.length})', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ); + }), ), ), ), Obx(() { - return Expanded( - child: Container( - width: double.infinity, - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.max, - children: [ - ...blueteethBindController.model.blelist! - .map((device) => - SingleBlueteethDeviceCompoentWidget( - // device: device, - bleDevice: device, - )) - .toList() - .divide(SizedBox(height: 30.rpx)) - .addToEnd(SizedBox(height: 30.rpx)), - ], + if (blueteethBindController + .model.betDevicelist!.isNotEmpty) { + return Expanded( + child: Container( + width: double.infinity, + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + ...blueteethBindController.model.blelist! + .map((device) => + SingleBlueteethDeviceCompoentWidget( + // device: device, + bleDevice: device, + )) + .toList() + .divide(SizedBox(height: 30.rpx)) + .addToEnd(SizedBox(height: 30.rpx)), + ], + ), ), ), - ), - ); + ); + } + return Container(); }), ].divide(SizedBox(height: 30.rpx)), ), @@ -979,38 +613,6 @@ class _EPageState extends State { ), ); } - - toFindJunhe() { - bool isSuccess = false; - int i = 0; - Timer.periodic(const Duration(seconds: 1), (t) async { - i++; - if (isSuccess) { - return; - } - if (i > 8) { - if (!isSuccess) { - currentMsg = "错误:未找到关联设备"; - failSelectDialog(title: "绑定失败:未找到关联设备"); - } - t.cancel(); - return; - } - bleDevice.forEach((item) { - if (isSuccess) { - return; - } - if (item['adData']['deviceId'] == bindArr[1]) { - isSuccess = true; - t.cancel(); - setState(() { - currentMsg = "寻找关联设备中..."; - }); - connectToDevice(item["device"]); - } - }); - }); - } } BleDeviceData parseBleData(List data) { diff --git a/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart b/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart index 94e1310..1797ec3 100644 --- a/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart +++ b/lib/pages/device_bind/componnet/FancyCircleCheckbox.dart @@ -75,7 +75,7 @@ class _FancyCircleCheckboxState extends State child: ScaleTransition( scale: _scaleAnimation, child: Container( - margin: EdgeInsets.all(8.rpx), + margin: EdgeInsets.all(6.rpx), decoration: BoxDecoration( shape: BoxShape.circle, color: widget.fillColor, diff --git a/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart index 61915e3..0f08bec 100644 --- a/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart +++ b/lib/pages/device_bind/componnet/SingleBlueteethDeviceCompoentWidget.dart @@ -41,7 +41,7 @@ class _SingleBlueteethDeviceCompoentWidgetState deviceData.rssi = widget.bleDevice.rssi; deviceData.mac = deviceData.deviceId.replaceAll(':', ''); BleDeviceData device = deviceData; - device = blueteethBindController.model.devicelist!.firstWhere( + device = blueteethBindController.model.betDevicelist!.firstWhere( (d) => d.mac == device.mac, orElse: () => device, ); @@ -64,15 +64,12 @@ class _SingleBlueteethDeviceCompoentWidgetState onConfirm: () async { ApiResponse response = await blueteethBindController.bindDeviceAndMAC(device); + TopSlideNotification.show(context, text: response.msg!); if (response.code == HttpStatusCodes.ok) { showLoadingDialog(context); // 显示 loading - BLEDevice bledevice = - BLEDevice(device: widget.bleDevice.device); - var res1 = bledevice.isConnected; - print("res1: $res1"); + THapp bledevice = THapp(device: widget.bleDevice.device); await bledevice.device.connect(); var res2 = bledevice.isConnected; - print("res2: $res2"); if (res2) { Navigator.pop(context); TopSlideNotification.show( @@ -94,7 +91,7 @@ class _SingleBlueteethDeviceCompoentWidgetState } else { TopSlideNotification.show( context, - text: "蓝牙绑定.连接异常".tr, + text: response.msg ?? "蓝牙绑定.连接异常".tr, textColor: themeController.currentColor.sc9, ); } @@ -104,6 +101,7 @@ class _SingleBlueteethDeviceCompoentWidgetState // 执行取消后的处理逻辑 }, ); + } } catch (e) { Navigator.pop(context); diff --git a/lib/pages/device_bind/componnet/bind_dialog.dart b/lib/pages/device_bind/componnet/bind_dialog.dart index 9082fd0..aacdf3c 100644 --- a/lib/pages/device_bind/componnet/bind_dialog.dart +++ b/lib/pages/device_bind/componnet/bind_dialog.dart @@ -1,11 +1,14 @@ import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:loading_indicator/loading_indicator.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; import 'package:vbvs_app/component/tool/FrostedDialog.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/model/BleDeviceData.dart'; @@ -248,7 +251,7 @@ void showHaveBindDialog(BuildContext context) { ); } -void showLoadingDialog(BuildContext context) { +void showLoadingDialog(BuildContext context, {String? title}) { ThemeController themeController = Get.find(); showDialog( context: context, @@ -310,7 +313,7 @@ void showLoadingDialog(BuildContext context) { child: RichText( text: TextSpan(children: [ TextSpan( - text: "连接中...".tr, + text: title ?? "连接中...".tr, style: TextStyle( color: themeController.currentColor.sc3, fontSize: AppConstants().normal_text_fontSize, @@ -433,6 +436,30 @@ void showConfirmDialog( mainAxisSize: MainAxisSize.min, children: [ // 标题 + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: + themeController.currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + }, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 33.rpx, 0, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/close.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: themeController.currentColor.sc3, + ), + ), + ), + ], + ), Align( alignment: AlignmentDirectional(0, 0), child: Padding( @@ -457,9 +484,10 @@ void showConfirmDialog( children: [ CustomCard( borderRadius: AppConstants().button_container_radius, - onTap: () { - Get.back(); + onTap: () async { onConfirm(); + // await Future.delayed(Duration(milliseconds: 300)); + Get.back(); }, colors: [ themeController.currentColor.sc1, @@ -542,3 +570,141 @@ void showConfirmDialog( }, ); } + +void showWifiDialog( + BuildContext context, + Widget widget, + String title, { + required VoidCallback onConfirm, +}) { + ThemeController themeController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + + showDialog( + context: context, + barrierDismissible: true, + barrierColor: Colors.black.withOpacity(0.5), // 背景模糊色 + builder: (BuildContext context) { + return FrostedDialog( + blurSigma: 3.0, + child: Container( + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(20.0), + ), + padding: EdgeInsetsDirectional.fromSTEB(64.rpx, 0, 64.rpx, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: MediaQuery.sizeOf(context).height * 0.656, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: + themeController.currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets.zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + Get.back(); + }, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 33.rpx, 0, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/close.svg', + width: 25.rpx, + height: 25.rpx, // 如果 SVG 中没有固定颜色,使用 color 设置 + color: themeController.currentColor.sc3, + ), + ), + ), + ], + ), + + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0.rpx, 40.rpx, 0, 0), + child: Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + widget, + Padding( + padding: EdgeInsetsDirectional.fromSTEB(0, 58.rpx, 0, 60.rpx), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard( + borderRadius: AppConstants().button_container_radius, + onTap: () { + if (blueteethBindController.model.wifiPass == null || + blueteethBindController.model.wifiPass!.isEmpty) { + TopSlideNotification.show( + context, + text: "wifi页.密码为空".tr, + textColor: themeController.currentColor.sc9, + ); + } else { + Get.back(); + onConfirm(); + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.115, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 160.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "蓝牙绑定.连接".tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ].divide(SizedBox( + width: 70.rpx, + )), + ), + ), + ], + ), + ), + ), + ); + }, + ); +} diff --git a/lib/pages/device_bind/device_share_page.dart b/lib/pages/device_bind/device_share_page.dart new file mode 100644 index 0000000..d8a31e8 --- /dev/null +++ b/lib/pages/device_bind/device_share_page.dart @@ -0,0 +1,421 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/device_share_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/FancyCircleCheckbox.dart'; + +class DeviceSharePage extends StatefulWidget { + var device; + DeviceSharePage({super.key, required this.device}); + + @override + State createState() => _DeviceSharePageState(); +} + +class _DeviceSharePageState extends State { + DeviceShareController deviceShareController = Get.find(); + @override + void initState() { + deviceShareController.msg = "".obs; + deviceShareController.code = 0.obs; + deviceShareController.account = "".obs; + super.initState(); + } + + @override + Widget build(BuildContext context) { + var device = widget.device; + RxBool flag1 = true.obs; + RxBool flag2 = false.obs; + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '设备分享'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(47.rpx, 0, 47.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 28.rpx, 0, 0), + child: Container( + width: double.infinity, + child: Text( + '要分享的设备'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 62.rpx, 0, 62.rpx), + child: Container( + width: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Obx(() => FancyCircleCheckbox( + borderColor: + themeController.currentColor.sc3, + fillColor: + themeController.currentColor.sc2, + value: flag1.value, + onChanged: (value) { + flag1.value = !flag1.value; + deviceShareController.updateAll(); + }, + )), + Text( + '主设备'.tr + + "${device['person']?['name'] == null ? '未命名'.tr : device['person']['name']}", + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: + themeController.currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 20.rpx)), + ), + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Obx(() => FancyCircleCheckbox( + // borderColor: + // themeController.currentColor.sc3, + // fillColor: + // themeController.currentColor.sc2, + // value: true, + // onChanged: (value) {}, + // )), + // Text( + // '主设备:蓝盈盈(A9876451)', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // color: + // themeController.currentColor.sc3, + // ), + // ), + // ].divide(SizedBox(width: 20.rpx)), + // ), + ].divide(SizedBox(height: 64.rpx)), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 58.rpx, 0, 0), + child: Container( + width: MediaQuery.sizeOf(context).width * 0.66, + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all( + color: Color(0xFFF3EDED), + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: TextFormField( + // controller: _model.textController1, + // focusNode: _model.textFieldFocusNode1, + initialValue: deviceShareController.account.value, + onChanged: (Value) { + deviceShareController.account.value = Value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + hintText: '请输入对方手机号或邮箱'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + filled: true, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + // validator: _model.textController1Validator + // .asValidator(context), + ), + ), + ), + ), + Obx(() { + return Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 15.rpx, 0, 0), + child: Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 30.rpx, + ), + child: deviceShareController.code != 0 + ? Align( + alignment: AlignmentDirectional(0, 0), + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + children: [ + TextSpan( + text: + '${deviceShareController.code == 1 ? "邀请成功".tr : "邀请失败".tr}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: deviceShareController + .code == + 1 + ? themeController + .currentColor.sc2 + : themeController + .currentColor.sc9, + ), + ), + TextSpan( + text: + '${deviceShareController.msg}', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc9, + ), + ), + ], + ), + ), + ) + : Container(), + ), + ); + }), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 58.rpx, 0, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () async { + ApiResponse apiResponse = + await deviceShareController + .shareDevice(device['mac']); + if (apiResponse.code == HttpStatusCodes.ok) { + TopSlideNotification.show(context, + text: apiResponse.msg!); + } else { + TopSlideNotification.show(context, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9); + } + }, + colors: [ + // 渐变色 + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.214, + height: MediaQuery.sizeOf(context).height * 0.037, + constraints: BoxConstraints( + minWidth: 160.rpx, + minHeight: 60.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '发送邀请'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, // 自定义字体大小 + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 379.rpx, 0, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () { + TopSlideNotification.show(context, + text: "待开发功能".tr); + }, + colors: [ + // 渐变色 + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: MediaQuery.sizeOf(context).width * 0.66, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '微信好友一键分享'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, // 自定义字体大小 + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/device_type.dart b/lib/pages/device_bind/device_type.dart index 19ae67c..d1f39cd 100644 --- a/lib/pages/device_bind/device_type.dart +++ b/lib/pages/device_bind/device_type.dart @@ -3,18 +3,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; import 'package:vbvs_app/component/tool/FrostedDialog.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; class DeviceTypePage extends StatefulWidget { - int type = 0;//0绑定设备 1.查询我的设备 - DeviceTypePage({super.key,this.type = 0}); + int type = 0; //0绑定设备 1.查询我的设备 + DeviceTypePage({super.key, this.type = 0}); @override State createState() => _EPageState(); @@ -25,10 +28,20 @@ class _EPageState extends State { UserInfoController userInfoController = Get.find(); BlueteethBindController blueteethBindController = Get.find(); ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); @override void initState() { super.initState(); + deviceTypeController.getDeviceType().then((response) { + if (response.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: response.msg ?? "服务器.失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + }); // 延迟到 build 完成后执行弹窗逻辑 WidgetsBinding.instance.addPostFrameCallback((_) { if (blueteethBindController.model.read == 1 && widget.type == 0) { @@ -349,28 +362,29 @@ class _EPageState extends State { child: Column( mainAxisSize: MainAxisSize.max, children: [ - _buildDeviceCard( - context, - title: '设备类型.体征监测设备'.tr, - imageUrl: 'assets/img/device.png', - type: '1', - ), - _buildDeviceCard( - context, - title: '设备类型.智能床/床垫'.tr, - imageUrl: 'assets/img/bed.png', - type: '2', - ), - _buildDeviceCard( - context, - title: '设备类型.摄像头'.tr, - imageUrl: 'assets/img/camera.png', - type: '3', - ), - ] - .divide(SizedBox(height: 26.rpx)) - .addToStart(SizedBox(height: 26.rpx)) - .addToEnd(SizedBox(height: 26.rpx)), + // 使用 Obx 来监听 deviceTypeList 的变化 + Obx(() { + return Column( + children: [ + SizedBox(height: 26.rpx), // 开始的间隔 + ...deviceTypeController.deviceTypeList.value + .map((device) { + return Padding( + padding: EdgeInsets.only( + bottom: 26.rpx), // 添加每个设备之间的间隔 + child: _buildDeviceCard( + context, + title: device['name'], // 这里假设 device 是一个 Map + imageUrl: device['image'], + type: device['type'], + ), + ); + }).toList(), + SizedBox(height: 26.rpx), // 结束的间隔 + ], + ); + }), + ], ), ), ), @@ -382,16 +396,20 @@ class _EPageState extends State { } Widget _buildDeviceCard(BuildContext context, - {required String title, required String imageUrl, required String type}) { + {required String title, required String imageUrl, required double type}) { return CustomCard( borderRadius: 20.rpx, // 圆角大小 onTap: () { if (type != null) { - if (type == '1') { + if (type == 1) { Get.toNamed("/blueteethDevice"); } - if (type == '2') { - Get.toNamed("/wifiPage"); + if (type == 2) { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: themeController.currentColor.sc2, + ); } } }, @@ -418,8 +436,14 @@ class _EPageState extends State { ), ClipRRect( borderRadius: BorderRadius.circular(8.rpx), - child: Image.asset( + // child: Image.asset( + // imageUrl, + // width: 212.rpx, + // height: 168.rpx, + // ), + child: Image.network( imageUrl, + // fit: BoxFit.cover, width: 212.rpx, height: 168.rpx, ), diff --git a/lib/pages/device_bind/device_type_list.dart b/lib/pages/device_bind/device_type_list.dart new file mode 100644 index 0000000..445342a --- /dev/null +++ b/lib/pages/device_bind/device_type_list.dart @@ -0,0 +1,192 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +class DeviceTypeListPage extends StatefulWidget { + DeviceTypeListPage({super.key}); + + @override + State createState() => _DeviceTypeListPageState(); +} + +class _DeviceTypeListPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); + + @override + void initState() { + super.initState(); + deviceTypeController.getDeviceType().then((response) { + if (response.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: response.msg ?? "服务器.失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '设备列表', + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + // 使用 Obx 来监听 deviceTypeList 的变化 + Obx(() { + return Column( + children: [ + SizedBox(height: 26.rpx), // 开始的间隔 + ...deviceTypeController.deviceTypeList.value + .map((device) { + return Padding( + padding: EdgeInsets.only( + bottom: 26.rpx), // 添加每个设备之间的间隔 + child: _buildDeviceCard( + context, + title: device['name'], // 这里假设 device 是一个 Map + imageUrl: device['image'], + type: device['type'], + ), + ); + }).toList(), + SizedBox(height: 26.rpx), // 结束的间隔 + ], + ); + }), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required double type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == 1) { + Get.toNamed("/bodyDevice"); + } + if (type == 2) { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: themeController.currentColor.sc2, + ); + } + } + }, + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + // child: Image.asset( + // imageUrl, + // width: 212.rpx, + // height: 168.rpx, + // ), + child: Image.network( + imageUrl, + // fit: BoxFit.cover, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/device_bind/wifi_page.dart b/lib/pages/device_bind/wifi_page.dart index 0fa20df..46b76ab 100644 --- a/lib/pages/device_bind/wifi_page.dart +++ b/lib/pages/device_bind/wifi_page.dart @@ -1,23 +1,24 @@ -import 'dart:async'; - import 'package:easydevice/easydevice.dart'; import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/component/tool/cmd.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; -import 'package:vbvs_app/pages/common/selectDialog.dart'; +import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart'; class WifiPage extends StatefulWidget { - BLEDevice bleDevice; - WifiPage({super.key, required this.bleDevice}); + WifiPage({super.key}); // WifiPage({super.key}); @override @@ -30,18 +31,29 @@ class _WifiPageState extends State { BlueteethBindController blueteethBindController = Get.find(); PersonController personController = Get.find(); ThemeController themeController = Get.find(); + var lisObj; + // List _logBuffer = []; + // bool _isCollecting = false; @override void initState() { super.initState(); - // connectedDeviceProp = widget.connectedDeviceProp; - Timer(const Duration(microseconds: 100), () { - getWifiList(); - }); + blueteethBindController.wifiList = [].obs; + initWifiStatusAndWifiList(); + } + + @override + void dispose() { + super.dispose(); + if (lisObj != null) { + lisObj.cancel(); + } + blueteethBindController.currentDevice!.disconnect(); } @override Widget build(BuildContext context) { + print("object"); return LayoutBuilder( builder: (context, bodySize) => GestureDetector( onTap: () => FocusScope.of(context).unfocus(), @@ -87,11 +99,19 @@ class _WifiPageState extends State { child: CustomCard( borderRadius: 20.rpx, onTap: () async { - Get.offAllNamed("/bindDeviceSuccess"); + if (blueteethBindController.wifiStatus.value != 1) { + TopSlideNotification.show( + context, + text: "wifi页.需配网".tr, + textColor: themeController.currentColor.sc9, + ); + } + Get.toNamed("/personPage"); + // Get.toNamed("/bindDeviceSuccess"); }, colors: [ - themeController.currentColor.sc1, - themeController.currentColor.sc2, + themeController.currentColor.sc1, + themeController.currentColor.sc2, ], child: Container( width: 100.rpx, @@ -132,41 +152,87 @@ class _WifiPageState extends State { padding: EdgeInsetsDirectional.fromSTEB(0, 30.rpx, 0, 0), child: Container( - width: double.infinity, - decoration: BoxDecoration( - color: Color(0xFF242835), - borderRadius: BorderRadius.circular(20.rpx), - ), - child: Align( - alignment: AlignmentDirectional(0, 0), - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 30.rpx, 30.rpx, 30.rpx), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "wifi页.WLAN".tr, - style: TextStyle( - color: themeController.currentColor.sc3, - fontSize: - AppConstants().title_text_fontSize, - ), - ), - Text( - "wifi页.未连接".tr, - style: TextStyle( - color: themeController.currentColor.sc3, - fontSize: - AppConstants().normal_text_fontSize, - ), - ), - ], - ), + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular(20.rpx), ), - ), - ), + child: Column( + children: [ + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + "wifi页.WLAN".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + ), + ), + Obx(() { + return Text( + blueteethBindController + .wifiStatus.value == + 0 + ? "wifi页.未连接".tr + : "wifi页.已连接".tr, + style: TextStyle( + color: blueteethBindController + .wifiStatus.value == + 0 + ? themeController + .currentColor.sc4 + : themeController + .currentColor.sc2, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ); + }), + ], + ), + ), + ), + Obx(() { + if (blueteethBindController + .connect_wifi.value == + null || + blueteethBindController + .connect_wifi.value.isEmpty) { + return Container(); + } else { + return Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + blueteethBindController + .connect_wifi.value['ssid'] ?? + '未命名'.tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + ), + ), + getWifiIconByRsso( + blueteethBindController + .connect_wifi.value), + ], + ); + } + }) + ], + )), ), Padding( padding: EdgeInsetsDirectional.fromSTEB( @@ -190,7 +256,7 @@ class _WifiPageState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - '可用WLAN', + '可用WLAN'.tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -203,157 +269,436 @@ class _WifiPageState extends State { ), ], ), - Column( - mainAxisSize: MainAxisSize.max, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '6503', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, + // Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // '6503', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc3, + // ), + // ), + // Icon( + // Icons.wifi_outlined, + // size: 30.rpx, + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // '6503', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc3, + // ), + // ), + // Icon( + // Icons.wifi_outlined, + // size: 30.rpx, + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // '6503', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc3, + // ), + // ), + // Icon( + // Icons.wifi_outlined, + // size: 30.rpx, + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // '6503', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc3, + // ), + // ), + // Icon( + // Icons.wifi_outlined, + // size: 30.rpx, + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Text( + // '6503', + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // fontSize: 30.rpx, + // letterSpacing: 0.0, + // color: themeController + // .currentColor.sc3, + // ), + // ), + // Icon( + // Icons.wifi_outlined, + // size: 30.rpx, + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // ].divide(SizedBox(height: 67.rpx)), + // ), + Obx(() { + final sortedList = [ + ...blueteethBindController.wifiList.value + ]..sort((a, b) => b['rssi'] + .compareTo(a['rssi'])); // 按 rssi 倒序 + + return Column( + mainAxisSize: MainAxisSize.max, + children: sortedList + .map((wifiItem) => ClickableContainer( + backgroundColor: + Colors.transparent, + highlightColor: themeController + .currentColor.sc3, + padding: EdgeInsets.symmetric( + vertical: 0.rpx, + horizontal: 20.rpx), + borderRadius: 16.rpx, + onTap: () { + showWifiDialog( + context, + Padding( + padding: + EdgeInsetsDirectional + .fromSTEB( + 0.rpx, + 41.rpx, + 0.rpx, + 0), + child: Container( + height: 80.rpx, + decoration: + BoxDecoration( + color: Colors.white, + borderRadius: + BorderRadius + .circular(12 + .rpx), // 加圆角 + ), + child: Align( + alignment: + AlignmentDirectional( + -1, 0), + child: Obx(() { + return TextFormField( + onChanged: + (value) { + blueteethBindController + .model + .wifiPass = + value; + }, + autofocus: + false, + obscureText: + blueteethBindController + .model + .wifiPassShow, + decoration: + InputDecoration( + isDense: + true, + labelStyle: FlutterFlowTheme.of( + context) + .labelMedium + .override( + fontFamily: + 'Inter', + fontSize: + 26.rpx, + letterSpacing: + 0.0, + ), + hintText: + '蓝牙绑定.输入wifi密码' + .tr, + hintStyle: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: + 'Inter', + fontSize: 26 + .rpx, + letterSpacing: + 0.0, + color: themeController + .currentColor + .sc4), + enabledBorder: + OutlineInputBorder( + borderSide: + BorderSide( + color: Color( + 0x00000000), + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: + OutlineInputBorder( + borderSide: + BorderSide( + color: Color( + 0x00000000), + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: + OutlineInputBorder( + borderSide: + BorderSide( + color: FlutterFlowTheme.of(context) + .error, + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: + BorderSide( + color: FlutterFlowTheme.of(context) + .error, + width: 1 + .rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: + false, + fillColor: FlutterFlowTheme.of( + context) + .secondaryBackground, + suffixIcon: + InkWell( + onTap: + () { + blueteethBindController + .model + .wifiPassShow = !blueteethBindController.model.wifiPassShow; + blueteethBindController + .updateAll(); + }, + focusNode: + FocusNode( + skipTraversal: true), + child: + Icon( + blueteethBindController.model.wifiPassShow + ? Icons.visibility_outlined + : Icons.visibility_off_outlined, + size: 30 + .rpx, + ), + ), + ), + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: + 'Inter', + fontSize: 26 + .rpx, + letterSpacing: + 0.0, + color: Colors + .black), + cursorColor: + FlutterFlowTheme.of( + context) + .primaryText, + ); + })), + ), + ), + wifiItem['ssid'] ?? + '未命名'.tr, + onConfirm: () async { + showLoadingDialog( + context); // 显示 loading + bool flag = await sendWifiSetting( + wifiItem, + blueteethBindController + .model.wifiPass!, + blueteethBindController + .currentDevice!); + Navigator.pop(context); + if (flag) { + TopSlideNotification.show( + context, + text: "wifi页.配网成功".tr, + textColor: + themeController + .currentColor + .sc2, + ); + blueteethBindController + .wifiStatus.value = 1; + blueteethBindController + .updateAll(); + } else { + TopSlideNotification.show( + context, + text: "wifi页.配网失败".tr, + textColor: + themeController + .currentColor + .sc9, + ); + blueteethBindController + .wifiStatus.value = 0; + blueteethBindController + .updateAll(); + } + }); + }, + child: Row( + mainAxisSize: + MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Text( + wifiItem['ssid'] ?? '', + style: FlutterFlowTheme + .of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: + themeController + .currentColor + .sc3, + ), + ), + getWifiIconByRsso(wifiItem), + ], ), + )) + .toList() + .divide(SizedBox(height: 67.rpx)), + ); + }), + + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: Colors.white, + padding: EdgeInsets.symmetric( + horizontal: 20.rpx, vertical: 10.rpx), + borderRadius: 20.rpx, + onTap: () async { + print("点击刷新"); + await initWifiList(); + TopSlideNotification.show( + context, + text: "获取wifi列表成功".tr, + textColor: + themeController.currentColor.sc2, + ); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + Container( + width: 25.rpx, + height: 25.rpx, + decoration: BoxDecoration(), + child: SvgPicture.asset( + 'assets/img/icon/refresh.svg', + fit: BoxFit.cover, + color: Colors.white, // 图标固定白色 ), - Icon( - Icons.wifi_outlined, - size: 30.rpx, - color: themeController - .currentColor.sc3, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '6503', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Icon( - Icons.wifi_outlined, - size: 30.rpx, - color: themeController - .currentColor.sc3, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '6503', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Icon( - Icons.wifi_outlined, - size: 30.rpx, - color: themeController - .currentColor.sc3, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '6503', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Icon( - Icons.wifi_outlined, - size: 30.rpx, - color: themeController - .currentColor.sc3, - ), - ], - ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '6503', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Icon( - Icons.wifi_outlined, - size: 30.rpx, - color: themeController - .currentColor.sc3, - ), - ], - ), - ].divide(SizedBox(height: 67.rpx)), - ), - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.arrow_back, - color: themeController.currentColor.sc3, - size: 30.rpx, - ), - Text( - '刷新', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( + ), + Text( + '刷新', + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( fontFamily: 'Inter', fontSize: 30.rpx, letterSpacing: 0.0, color: themeController - .currentColor.sc3), - ), - ].divide(SizedBox(width: 26.rpx)), + .currentColor.sc3, + ), + ), + ].divide(SizedBox(width: 26.rpx)), + ), ), ].divide(SizedBox(height: 65.rpx)), ), @@ -419,42 +764,143 @@ class _WifiPageState extends State { ); } - getWifiList({int time = 3}) { - // LoadingDialog.show("扫描WIFI列表中...", icon: LoadingDialogIcon.wifi); + void initWifiStatusAndWifiList() { + if (lisObj != null) { + lisObj!.cancel(); + } + lisObj = blueteethBindController.currentDevice!.statusStream + .listen((onData) async { + if (onData.status == BleEventType.recvLineLog) { + final line = onData.val; + print("[bleee]:" + line); + } + if (onData.status == BleEventType.ready) { + showLoadingDialog(context, title: "获取wifi列表中...".tr); + bool wifiStatus = + await getWifiStatus(blueteethBindController.currentDevice!); + blueteethBindController.wifiStatus.value = wifiStatus == true ? 1 : 0; + if (wifiStatus) { + Map connect_wifiInfo = + await getDeviceWifiStatus(blueteethBindController.currentDevice!); + if (connect_wifiInfo != null) { + blueteethBindController.connect_wifi.value = connect_wifiInfo; + } + } + List wifiList = + await getWifiList(blueteethBindController.currentDevice!); + if (wifiList.length > 0) { + Navigator.pop(context); + TopSlideNotification.show( + context, + text: "获取wifi列表成功".tr, + textColor: themeController.currentColor.sc2, + ); + blueteethBindController.wifiList.value = wifiList; + blueteethBindController.updateAll(); + } else { + Navigator.pop(context); + TopSlideNotification.show( + context, + text: "获取wifi列表失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + } + }); + } + + initWifiList() async { try { - // var device = widget.bluetoothDevice; - // String log = ""; - // Function logAdd = (l) { - // log += l; - // }; - + var wifiList = await getWifiList(blueteethBindController.currentDevice!); + print(wifiList); + if (wifiList.length > 0) { + blueteethBindController.wifiList.value = wifiList; + blueteethBindController.updateAll(); + } + return wifiList; } catch (e) { print(e); + TopSlideNotification.show( + context, + text: "获取wifi列表失败".tr, + textColor: themeController.currentColor.sc9, + ); } } - checkIsCalibration() { - // if (controller.model.bindArr[0] == "" || - // controller.model.bindArr[0] == null) { - // return; - // } - // if (controller.model.bindArr[2] == "" || - // controller.model.bindArr[2] == null) { - // return; - // } - // if (controller.model.connectedWifiName == "" || - // controller.model.connectedWifiName == null) { - // return; - // } - showCustomConfirmAndCancelDialog(context, "是否进行设备校准?", confirmName: "去校准") - .then((d) async { - // if (d == "confirm") { - // await Get.offAndToNamed("/calibration", arguments: [ - // controller.model.bindArr[1], - // controller.model.bindArr[2] - // ]); - // Get.find().getDeviceList(); - // } - }); + getWifiIconByRsso(wifiItem) { + if (wifiItem['rssi'] >= -30) { + // return SvgPicture.asset( + // 'assets/img/icon/wifi4.svg', + // width: 25.rpx, + // height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController.currentColor.sc3, + // ); + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/wifi4.png", + ), + ); + } else if (wifiItem['rssi'] >= -45) { + // return SvgPicture.asset( + // 'assets/img/icon/wifi3.svg', + // width: 25.rpx, + // height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController.currentColor.sc3, + // ); + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/wifi3.png", + ), + ); + } else if (wifiItem['rssi'] >= -60) { + // return SvgPicture.asset( + // 'assets/img/icon/wifi2.svg', + // width: 25.rpx, + // height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController.currentColor.sc3, + // ); + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/wifi3.png", + ), + ); + } else { + // return SvgPicture.asset( + // 'assets/img/icon/wifi1.svg', + // width: 25.rpx, + // height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController.currentColor.sc3, + // ); + return Container( + width: 40.rpx, + height: 40.rpx, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: Image.asset( + "assets/img/wifi1.png", + ), + ); + } } } diff --git a/lib/pages/login/login.dart b/lib/pages/login/login.dart index 8799a49..0e5e419 100644 --- a/lib/pages/login/login.dart +++ b/lib/pages/login/login.dart @@ -7,6 +7,7 @@ import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/login/login_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; @@ -168,6 +169,8 @@ class _EPageState extends State { borderRadius: AppConstants() .button_container_radius, // 圆角半径 onTap: () { + TopSlideNotification.show(context, + text: "待开发功能".tr); bool agree = loginController.model.register_agree!; if (!agree) { diff --git a/lib/pages/login/other_login.dart b/lib/pages/login/other_login.dart index a9a25a3..b22da89 100644 --- a/lib/pages/login/other_login.dart +++ b/lib/pages/login/other_login.dart @@ -5,6 +5,7 @@ import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:get_storage/get_storage.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/DailyLogUtils.dart'; @@ -442,9 +443,8 @@ class _OtherLoginPageState extends State { return InkWell( onTap: () async { try { - await DailyLogUtils - .writeLog( - "点击获取验证码"); + DailyLogUtils.writeLog( + "点击获取验证码"); if (countdownController .countdown .value != @@ -457,7 +457,8 @@ class _OtherLoginPageState extends State { .getCode( context); if (apiResponse.code != - HttpStatusCodes.ok) { + HttpStatusCodes + .ok) { TopSlideNotification .show( context, @@ -551,7 +552,7 @@ class _OtherLoginPageState extends State { .button_container_radius, // 圆角半径 onTap: () async { try { - await DailyLogUtils.writeLog("点击登录"); + DailyLogUtils.writeLog("点击登录"); bool agree = loginController.model.register_agree!; if (!agree) { @@ -565,7 +566,8 @@ class _OtherLoginPageState extends State { ApiResponse apiResponse = await loginController .login(context); - if (apiResponse.code != HttpStatusCodes.ok) { + if (apiResponse.code != + HttpStatusCodes.ok) { TopSlideNotification.show( context, text: apiResponse.msg!, @@ -580,7 +582,9 @@ class _OtherLoginPageState extends State { textColor: themeController .currentColor.sc2, ); - Get.offAllNamed('/homePage'); + // Get.offAllNamed('/homePage'); + Get.offAllNamed( + "/mianPageBottomChange"); } } } catch (e) { diff --git a/lib/pages/main_bottom/component/MessageWidgetWidget.dart b/lib/pages/main_bottom/component/MessageWidgetWidget.dart new file mode 100644 index 0000000..bc9368a --- /dev/null +++ b/lib/pages/main_bottom/component/MessageWidgetWidget.dart @@ -0,0 +1,168 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; + +class MessageWidgetWidget extends StatefulWidget { + const MessageWidgetWidget({super.key}); + + @override + State createState() => _MessageWidgetWidgetState(); +} + +class _MessageWidgetWidgetState extends State { + ThemeController themeController = Get.find(); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + ClickableContainer( + backgroundColor: themeController.currentColor.sc5, + highlightColor: themeController.currentColor.sc3, + borderRadius: 20.rpx, + padding: + EdgeInsetsDirectional.fromSTEB(31.rpx, 33.rpx, 0.rpx, 33.rpx), + onTap: () {}, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: double.infinity, + constraints: BoxConstraints( + minHeight: 66.rpx, + ), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Text( + '实时监测结果通知'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 30.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + constraints: BoxConstraints( + minWidth: 30.rpx, + maxWidth: 120.rpx, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildInfoItem(context, '设备ID'), + _buildInfoItem(context, '使用人员'), + _buildInfoItem(context, '消息类型'), + _buildInfoItem(context, '检测数值'), + _buildInfoItem(context, '发生时间'), + ], + ), + ), + Container( + constraints: BoxConstraints( + minWidth: 30.rpx, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildValueItem(context, '设备ID'), + _buildValueItem(context, '使用人员'), + _buildValueItem(context, '消息类型'), + _buildValueItem(context, '检测数值'), + _buildValueItem(context, '发生时间'), + ], + ), + ), + ].divide(SizedBox(width: 35.rpx)), + ), + ], + ), + ), + Positioned( + bottom: 46.rpx, + right: 20.rpx, + child: Container( + width: 123.rpx, + height: 47.rpx, + child: CustomCard( + borderRadius: AppConstants().button_container_radius, // 直角 + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2 + ], // 单色背景 + enableAnimation: true, // 有点击缩放动画 + enableGradient: false, // 不用渐变 + onTap: () { + // 点击处理逻辑 + print('处理按钮点击了'); + }, + child: Center( + child: Text( + '处理'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: Colors.white, + ), + ), + ), + ), + ), + ), + ], + ); + } + + Widget _buildInfoItem(BuildContext context, String label) { + return Container( + constraints: BoxConstraints( + minHeight: 62.rpx, + ), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Text( + label.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc4, + ), + ), + ), + ); + } + + Widget _buildValueItem(BuildContext context, String value) { + return Container( + constraints: BoxConstraints( + minHeight: 62.rpx, + ), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Text( + value, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + ), + ), + ); + } +} diff --git a/lib/pages/main_bottom/home_page.dart b/lib/pages/main_bottom/home_page.dart index d1baddb..14ec1aa 100644 --- a/lib/pages/main_bottom/home_page.dart +++ b/lib/pages/main_bottom/home_page.dart @@ -3,15 +3,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/NullDataComponentWidget.dart'; +import 'package:vbvs_app/component/home_page/DynamicReportDetailWidget.dart'; import 'package:vbvs_app/component/home_page/SleepDataModuleWidget.dart'; import 'package:vbvs_app/component/home_page/SleepDateWidget.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/home/home_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/enum/LoginStatus.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -24,11 +31,172 @@ class _HomePageState extends State { GlobalController globalController = Get.find(); UserInfoController userInfoController = Get.find(); ThemeController themeController = Get.find(); + BodyDeviceController deviceController = Get.find(); + HomeController homeController = Get.find(); + + final GlobalKey addIconKey = GlobalKey(); + OverlayEntry? _popupEntry; + + void _showPopup() { + final renderBox = + addIconKey.currentContext?.findRenderObject() as RenderBox?; + if (renderBox == null) return; + + final position = renderBox.localToGlobal(Offset.zero); + final size = renderBox.size; + double popupWidth = 190.rpx; // 弹窗宽度(可以改) + + _popupEntry?.remove(); // 清除旧弹窗 + _popupEntry = OverlayEntry( + builder: (context) => Stack( + children: [ + // 空白区域点击关闭 + Positioned.fill( + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + }, + child: Container(), // 透明区域,必须加上确保能响应点击 + ), + ), + + // 弹窗本体 + Positioned( + top: position.dy + size.height + 26.rpx, + left: position.dx + size.width - popupWidth - 40.rpx, + child: Material( + color: Colors.transparent, + child: Container( + width: popupWidth, + padding: EdgeInsets.all(20.rpx), + decoration: BoxDecoration( + color: themeController.currentColor.sc17, + borderRadius: BorderRadius.circular(12.rpx), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.5), + blurRadius: 12.rpx, + spreadRadius: 2.rpx, + offset: Offset(0, 6.rpx), + ), + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 11.rpx), + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 10.rpx), + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc16.withOpacity(0.1), + borderRadius: 0.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + TopSlideNotification.show( + context, + text: "待开发功能".tr, + ); + }, + child: Container( + width: double.infinity, + child: Center( + child: Text( + '扫一扫'.tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + ), + SizedBox(height: 35.rpx), + ClickableContainer( + padding: EdgeInsets.symmetric(vertical: 10.rpx), + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc16.withOpacity(0.1), + borderRadius: 0.rpx, + onTap: () { + _popupEntry?.remove(); + _popupEntry = null; + Get.toNamed("/deviceType"); + }, + child: Container( + width: double.infinity, + child: Center( + child: Text( + '蓝牙绑定'.tr, + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3, + ), + ), + ), + ), + ), + SizedBox(height: 13.rpx), + ], + ), + ), + ), + ), + ], + ), + ); + + Overlay.of(context)!.insert(_popupEntry!); + } + + void _hidePopup() { + _popupEntry?.remove(); + _popupEntry = null; + } + + @override + initState() { + super.initState(); + if (userInfoController.model.login == 1) { + homeController.getSleepReport(); + deviceController.getDeviceNum().then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } + }); + deviceController.getDeviceList().then((apiResponse) { + if (apiResponse.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: themeController.currentColor.sc9, + ); + } else { + //请求睡眠报告 + deviceController.getSleepReport(); + } + }); + } + } + @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, bodySize) => GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), + onTap: () { + FocusScope.of(context).unfocus(); + if (_popupEntry != null) { + _hidePopup(); + } + }, child: Container( decoration: BoxDecoration( image: DecorationImage( @@ -49,348 +217,464 @@ class _HomePageState extends State { 47.rpx, AppConstants().main_left_right_padding, 0), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - //用户信息 - padding: EdgeInsetsDirectional.fromSTEB( - AppConstants().content_left_right_padding, - 0, - AppConstants().content_left_right_padding, - 0), - child: Container( - width: double.infinity, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Obx(() { - return Visibility( - visible: - userInfoController.model.login == 0, - child: CustomCard( - borderRadius: 20.rpx, - onTap: () async { - Get.toNamed("/loginPage"); - }, - colors: [ - themeController.currentColor.sc1, - themeController.currentColor.sc2, - ], - child: Container( - width: 100.rpx, - height: 60.rpx, - alignment: Alignment.center, - padding: EdgeInsetsDirectional.fromSTEB( - 16.rpx, 0, 16.rpx, 0), - child: Text( - '首页.登录'.tr, - style: FlutterFlowTheme.of(context) - .titleSmall - .override( - fontFamily: 'Inter Tight', - color: themeController - .currentColor.sc19, - letterSpacing: 0.0, + child: Column( + children: [ + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + //用户信息 + padding: EdgeInsetsDirectional.fromSTEB( + AppConstants().content_left_right_padding, + 0, + AppConstants().content_left_right_padding, + 0), + child: Container( + width: double.infinity, + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + if (userInfoController.model.login == 0) + Obx(() { + return Visibility( + visible: + userInfoController.model.login == + 0, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + Get.toNamed("/loginPage"); + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional + .fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '首页.登录'.tr, + style: + FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: + 'Inter Tight', + color: themeController + .currentColor + .sc19, + letterSpacing: 0.0, + ), ), - ), + ), + ), + ); + }), + if (userInfoController.model.login == 1) + Obx(() { + return Visibility( + visible: + userInfoController.model.login == + 1, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + Text( + userInfoController.model.user! + .nick_name ?? + '未命名'.tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ), + Text( + "嘉兴 晴", + style: TextStyle( + color: themeController + .currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ), + ], + ), + ); + }), + // SvgPicture.asset( + // 'assets/img/icon/add.svg', + // width: 39.rpx, + // height: 39.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // //todo 颜色 + // color: themeController.currentColor.sc16, + // ), + ClickableContainer( + key: addIconKey, + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc16, + padding: EdgeInsets.all(8.rpx), + onTap: () { + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: themeController + .currentColor.sc9, + ); + _hidePopup(); + Get.toNamed("/loginPage"); + } else { + // 点击图标时,展示弹窗 + if (_popupEntry == null) { + _showPopup(); + } else { + _hidePopup(); + } + } + }, + child: SvgPicture.asset( + 'assets/img/icon/add.svg', + width: 39.rpx, + height: 39.rpx, + color: + themeController.currentColor.sc16, ), ), - ); - }), - Obx(() { - return Visibility( - visible: - userInfoController.model.login == 1, - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, + ], + ), + ), + ), + Padding( + //绑定数量 + padding: EdgeInsetsDirectional.fromSTEB( + 19.rpx, 34.rpx, 0, 21.rpx), + child: ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + onTap: () { + print('点击了容器'); + if (userInfoController.model.login == + LoginStatus.LOGIN.code) { + Get.toNamed("/bodyDevice"); + } else { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } + }, + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 10.rpx, 0, 10.rpx), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Container( + child: Row( + mainAxisSize: MainAxisSize.min, children: [ Text( - "飞行的鱼", - style: TextStyle( - color: themeController - .currentColor.sc3, - ), + '首页.已关联体征监测设备'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + //todo 颜色 + color: themeController + .currentColor.sc3, + ), ), - Text( - "嘉兴 晴", - style: TextStyle( - color: themeController - .currentColor.sc3, - ), - ), - ], - ), - ); - }), - SvgPicture.asset( - 'assets/img/icon/add.svg', - width: 39.rpx, - height: 39.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - //todo 颜色 - color: themeController.currentColor.sc16, - ), - ], - ), - ), - ), - Padding( - //绑定数量 - padding: EdgeInsetsDirectional.fromSTEB( - 19.rpx, 34.rpx, 0, 21.rpx), - child: ClickableContainer( - backgroundColor: Colors.transparent, // 容器背景色 - highlightColor: - themeController.currentColor.sc21, // 点击时的背景色 - onTap: () { - print('点击了容器'); - }, - padding: EdgeInsetsDirectional.fromSTEB( - 0.rpx, 10.rpx, 0, 10.rpx), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - '首页.已关联体征监测设备'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: AppConstants() - .title_text_fontSize, - letterSpacing: 0.0, - //todo 颜色 - color: themeController - .currentColor.sc3, - ), - ), - Text( - '0', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: AppConstants() - .title_text_fontSize, - letterSpacing: 0.0, - color: themeController - .currentColor.sc8, - ), - ), - ].divide(SizedBox( - width: 6.rpx, - )), - )), - Obx(() { - return Visibility( - visible: userInfoController - .model.deviceBindNum! > - 0, - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 0.rpx, 8.rpx, 0.rpx), - child: SvgPicture.asset( - 'assets/img/icon/arrow_right.svg', - width: 14.rpx, - height: 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - color: themeController.currentColor.sc3, - ), - ), - ); - }), - ], - ), - ), - ), - Obx(() { - return Visibility( - visible: userInfoController.model.login == 0, - child: Container( - //未绑定布局 - width: MediaQuery.sizeOf(context).width, - height: MediaQuery.sizeOf(context).height * 0.277, - constraints: BoxConstraints( - minWidth: 690.rpx, - minHeight: 450.rpx, - ), - decoration: BoxDecoration( - color: themeController.currentColor.sc5, - borderRadius: BorderRadius.circular( - AppConstants() - .normal_container_radius), // 圆角半径 - ), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CustomCard( - borderRadius: AppConstants() - .button_container_radius, // 圆角半径 - onTap: () { - Get.toNamed("/personPage"); - }, - colors: [ - // 渐变色 - themeController.currentColor.sc1, - themeController.currentColor.sc2, - ], - - child: Container( - width: MediaQuery.sizeOf(context).width * - 0.66, - height: - MediaQuery.sizeOf(context).height * - 0.055, - constraints: BoxConstraints( - minWidth: 500.rpx, - minHeight: 90.rpx, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/img/icon/scan.svg', - width: 25.rpx, - height: 25.rpx, // SVG 的固定大小 - color: themeController - .currentColor.sc16, // 颜色设置 - ), - Text( - '首页.扫一扫绑定'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - color: themeController - .currentColor.sc19, - fontFamily: 'Inter', - fontSize: AppConstants() - .normal_text_fontSize, - letterSpacing: 0.0, - ), - ), - ].divide(SizedBox(width: 17.rpx)), - ), - ), - ), - CustomCard( - borderRadius: AppConstants() - .button_container_radius, // 圆角半径 - onTap: () { - print('Button pressed ...'); - Get.toNamed("/deviceType"); - }, - colors: [ - //todo 颜色 - themeController.currentColor.sc1, - themeController.currentColor.sc2, - ], // 渐变色是同一个色,也可以根据需要调整 - - child: Container( - width: MediaQuery.sizeOf(context).width * - 0.66, - height: - MediaQuery.sizeOf(context).height * - 0.055, - constraints: BoxConstraints( - minWidth: 500.rpx, - minHeight: 90.rpx, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SvgPicture.asset( - 'assets/img/icon/bluetooth.svg', - width: 25.rpx, - height: - 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - //todo 颜色 - color: themeController - .currentColor.sc16, - ), - Text( - '首页.蓝牙绑定'.tr, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - //todo 颜色 - color: themeController - .currentColor.sc19, - fontFamily: 'Inter', - fontSize: AppConstants() - .normal_text_fontSize, - letterSpacing: 0.0, - ), - ), - ].divide(SizedBox( - width: 17.rpx, - )), - ), - ), - ) - ].divide(SizedBox( - height: 60.rpx, - )), - ), - ), - ); - }), - Obx(() { - return Visibility( - visible: userInfoController.model.login == 0, - child: Padding( - //未绑定标语 - padding: EdgeInsetsDirectional.fromSTEB( - 0, 26.rpx, 0, 0), - child: Container( - width: MediaQuery.sizeOf(context).width, - decoration: BoxDecoration( - color: themeController.currentColor.sc6, - borderRadius: BorderRadius.circular( - AppConstants() - .normal_container_radius), // 圆角半径 - ), - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 25.rpx, 25.rpx, 25.rpx, 25.rpx), - child: Row( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0.rpx, 5.rpx, 0.rpx, 0.rpx), - child: SvgPicture.asset( - 'assets/img/icon/sound.svg', - width: 30.rpx, - height: - 30.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - color: stringToColor("#FF9F66"), //固定 - ), - ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Text( - '首页.提示标题'.tr, + Obx(() { + return Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 4.rpx, 0.rpx, 0.rpx), + child: Text( + "${deviceController.bindDeviceNum.value}", style: FlutterFlowTheme.of(context) .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: themeController + .currentColor.sc8, + ), + ), + ); + }), + ].divide(SizedBox( + width: 6.rpx, + )), + )), + Obx(() { + return Visibility( + visible: userInfoController + .model.deviceBindNum! >= + 0, + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0, 0.rpx, 8.rpx, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 14.rpx, + height: + 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ), + ); + }), + ], + ), + ), + ), + Obx(() { + return Visibility( + visible: + deviceController.bindDeviceNum.value == 0, + child: Container( + //未绑定布局 + width: MediaQuery.sizeOf(context).width, + height: + MediaQuery.sizeOf(context).height * 0.277, + constraints: BoxConstraints( + minWidth: 690.rpx, + minHeight: 450.rpx, + ), + decoration: BoxDecoration( + color: themeController.currentColor.sc5, + borderRadius: BorderRadius.circular( + AppConstants() + .normal_container_radius), // 圆角半径 + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CustomCard( + borderRadius: AppConstants() + .button_container_radius, // 圆角半径 + onTap: () { + // Get.toNamed("/qrView"); + if (userInfoController.model.login == + LoginStatus.LOGIN.code) { + // Get.toNamed("/deviceType"); + TopSlideNotification.show( + context, + text: "待开发功能".tr, + ); + } else { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: themeController + .currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } + }, + colors: [ + // 渐变色 + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + + child: Container( + width: + MediaQuery.sizeOf(context).width * + 0.66, + height: MediaQuery.sizeOf(context) + .height * + 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/img/icon/scan.svg', + width: 25.rpx, + height: 25.rpx, // SVG 的固定大小 + color: themeController + .currentColor.sc16, // 颜色设置 + ), + Text( + '首页.扫一扫绑定'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController + .currentColor + .sc19, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 17.rpx)), + ), + ), + ), + CustomCard( + borderRadius: AppConstants() + .button_container_radius, // 圆角半径 + onTap: () { + if (userInfoController.model.login == + LoginStatus.LOGIN.code) { + Get.toNamed("/deviceType"); + } else { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: themeController + .currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } + }, + colors: [ + //todo 颜色 + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], // 渐变色是同一个色,也可以根据需要调整 + + child: Container( + width: + MediaQuery.sizeOf(context).width * + 0.66, + height: MediaQuery.sizeOf(context) + .height * + 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + SvgPicture.asset( + 'assets/img/icon/bluetooth.svg', + width: 25.rpx, + height: 25 + .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + //todo 颜色 + color: themeController + .currentColor.sc16, + ), + Text( + '首页.蓝牙绑定'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + //todo 颜色 + color: themeController + .currentColor + .sc19, + fontFamily: 'Inter', + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ) + ].divide(SizedBox( + height: 60.rpx, + )), + ), + ), + ); + }), + Obx(() { + return Visibility( + visible: userInfoController.model.login == 0 || + deviceController.bindDeviceNum.value == 0, + child: Padding( + //未绑定标语 + padding: EdgeInsetsDirectional.fromSTEB( + 0, 26.rpx, 0, 0), + child: Container( + width: MediaQuery.sizeOf(context).width, + decoration: BoxDecoration( + color: themeController.currentColor.sc6, + borderRadius: BorderRadius.circular( + AppConstants() + .normal_container_radius), // 圆角半径 + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 25.rpx, 25.rpx, 25.rpx, 25.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Padding( + padding: + EdgeInsetsDirectional.fromSTEB( + 0.rpx, 5.rpx, 0.rpx, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/sound.svg', + width: 30.rpx, + height: 30 + .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: + stringToColor("#FF9F66"), //固定 + ), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + '首页.提示标题'.tr, + style: FlutterFlowTheme.of( + context) + .bodyMedium .override( fontFamily: 'Inter', fontSize: AppConstants() @@ -401,11 +685,11 @@ class _HomePageState extends State { color: stringToColor( "#916D46"), //固定 ), - ), - Text( - '首页.提示内容1'.tr, - style: - FlutterFlowTheme.of(context) + ), + Text( + '首页.提示内容1'.tr, + style: FlutterFlowTheme.of( + context) .bodyMedium .override( fontFamily: 'Inter', @@ -416,11 +700,11 @@ class _HomePageState extends State { color: stringToColor( "#916D46"), //固定 ), - ), - Text( - '首页.提示内容2'.tr, - style: - FlutterFlowTheme.of(context) + ), + Text( + '首页.提示内容2'.tr, + style: FlutterFlowTheme.of( + context) .bodyMedium .override( fontFamily: 'Inter', @@ -430,11 +714,11 @@ class _HomePageState extends State { color: stringToColor( "#916D46"), //固定 ), - ), - Text( - '首页.提示内容3'.tr, - style: - FlutterFlowTheme.of(context) + ), + Text( + '首页.提示内容3'.tr, + style: FlutterFlowTheme.of( + context) .bodyMedium .override( fontFamily: 'Inter', @@ -445,234 +729,155 @@ class _HomePageState extends State { color: stringToColor( "#916D46"), //固定 ), + ), + ].divide(SizedBox( + height: AppConstants() + .text_padding_up_dowm_p)), ), - ].divide(SizedBox( - height: AppConstants() - .text_padding_up_dowm_p)), - ), - ) - ].divide(SizedBox(width: 20.rpx)), - ), - ), - ), - ), - ); - }), - Obx(() { - return Visibility( - visible: userInfoController.model.login == 1, - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 26.rpx, 0, 0), - child: Container( - width: bodySize.maxWidth, - height: bodySize.maxHeight * 0.107, - constraints: BoxConstraints( - minHeight: 240.rpx, - ), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - ClickableContainer( - backgroundColor: - themeController.currentColor.sc5, - highlightColor: - themeController.currentColor.sc3, - borderRadius: AppConstants() - .normal_container_radius, - padding: EdgeInsets - .zero, // 原始Container没有padding - onTap: () { - // 点击逻辑放这里 - }, - child: Container( - width: bodySize.maxWidth * 0.445, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - SizedBox(height: 32.rpx), - Container( - width: 120.rpx, - height: 120.rpx, - child: Image.asset( - "assets/img/netlove.png", - fit: BoxFit.cover, - ), - ), - Text( - "首页.我的e护".tr, - style: TextStyle( - color: themeController - .currentColor.sc3, - ), - ), - SizedBox(height: 32.rpx), - ], - ), + ) + ].divide(SizedBox(width: 20.rpx)), ), ), - ClickableContainer( - backgroundColor: - themeController.currentColor.sc5, - highlightColor: - themeController.currentColor.sc3, - borderRadius: AppConstants() - .normal_container_radius, - padding: EdgeInsets - .zero, // 原本的Container没有 padding,这里设置为 zero - onTap: () { - // TODO: 替换为你需要的点击事件逻辑 - print("云关爱 被点击"); - }, - child: SizedBox( - width: bodySize.maxWidth * 0.445, - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - children: [ - Container( - width: 120.rpx, - height: 120.rpx, - child: Image.asset( - "assets/img/mye.png", - fit: BoxFit.cover, - ), - ), - Text( - "首页.云关爱".tr, - style: TextStyle( - color: themeController - .currentColor.sc3, - ), - ), - ] - .addToStart( - SizedBox(height: 32.rpx)) - .addToEnd( - SizedBox(height: 32.rpx)), - ), - ), - ), - ], - ), - ), - ), - ); - }), - // Generated code for this Container Widget... - Obx(() { - return Visibility( - visible: userInfoController.model.login == 1, - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 0, 25.rpx, 0, 25.rpx), - child: Container( - width: double.infinity, - decoration: BoxDecoration( - color: themeController.currentColor.sc5, - borderRadius: BorderRadius.circular( - AppConstants().normal_container_radius), ), - child: Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 30.rpx, 30.rpx, 30.rpx, 30.rpx), - child: Column( - mainAxisSize: MainAxisSize.max, + ), + ); + }), + Obx(() { + return Visibility( + visible: userInfoController.model.login == 1 && + deviceController.bindDeviceNum.value != 0, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 26.rpx, 0, 0), + child: Container( + width: bodySize.maxWidth, + height: bodySize.maxHeight * 0.107, + constraints: BoxConstraints( + minHeight: 240.rpx, + ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ - Container( - width: double.infinity, - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '次卧/1201/李小北', - style: - FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 30.rpx, - letterSpacing: 0.0, + ClickableContainer( + backgroundColor: + themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc3, + borderRadius: AppConstants() + .normal_container_radius, + padding: EdgeInsets + .zero, // 原始Container没有padding + onTap: () { + // BodyDeviceController + // bodyDeviceController = Get.find(); + // bodyDeviceController.model.type = 1; + // Get.toNamed("/bodyDevice"); + homeController.model.type = 1; + deviceController.model.type = 1; + deviceController.getDeviceList(); + homeController.updateAll(); + }, + child: Container( + decoration: BoxDecoration( + border: + homeController.model.type == 1 + ? Border.all( color: themeController - .currentColor.sc3, - ), - ), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Text( - '首页.报告详情'.tr, - style: FlutterFlowTheme.of( - context) - .bodyMedium - .override( - fontFamily: 'Inter', - fontSize: 26.rpx, - letterSpacing: 0.0, - color: themeController - .currentColor.sc3, - ), - ), - Padding( - padding: - EdgeInsetsDirectional - .fromSTEB(0, 6.rpx, - 0, 0.rpx), - child: SvgPicture.asset( - 'assets/img/icon/arrow_right.svg', - width: 14.rpx, - height: 14 - .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - color: themeController - .currentColor.sc3, - ), - ), - ].divide( - SizedBox(width: 22.rpx)), - ), - ], - ), - ), - Container( - width: double.infinity, - decoration: BoxDecoration(), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisSize: MainAxisSize.max, + .currentColor + .sc2, // 边框颜色 + width: 1, // 边框宽度 + ) + : null, + borderRadius: + BorderRadius.circular( + 12), // 可选:圆角 + ), + width: bodySize.maxWidth * 0.445, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, children: [ - SleepDateWidget(), - SleepDateWidget(), - SleepDateWidget(), - SleepDateWidget(), - SleepDateWidget(), - SleepDateWidget(), - ].divide(SizedBox( - width: 20.rpx, - )), + SizedBox(height: 32.rpx), + Container( + width: 120.rpx, + height: 120.rpx, + child: Image.asset( + "assets/img/netlove.png", + fit: BoxFit.cover, + ), + ), + Text( + "首页.我的e护".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + ), + ), + SizedBox(height: 32.rpx), + ], ), ), ), - Container( - width: double.infinity, - decoration: BoxDecoration(), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - mainAxisSize: MainAxisSize.max, + ClickableContainer( + backgroundColor: + themeController.currentColor.sc5, + highlightColor: + themeController.currentColor.sc3, + borderRadius: AppConstants() + .normal_container_radius, + padding: EdgeInsets + .zero, // 原本的Container没有 padding,这里设置为 zero + onTap: () { + // BodyDeviceController + // bodyDeviceController = Get.find(); + // bodyDeviceController.model.type = 2; + // Get.toNamed("/bodyDevice"); + homeController.model.type = 2; + deviceController.model.type = 2; + deviceController.getDeviceList(); + homeController.updateAll(); + }, + child: Container( + decoration: BoxDecoration( + border: + homeController.model.type == 2 + ? Border.all( + color: themeController + .currentColor + .sc2, // 边框颜色 + width: 1, // 边框宽度 + ) + : null, + borderRadius: + BorderRadius.circular( + 12), // 可选:圆角 + ), + width: bodySize.maxWidth * 0.445, + child: Column( + mainAxisAlignment: + MainAxisAlignment.center, children: [ - SleepDataModuleWidget(), - SleepDataModuleWidget(), - SleepDataModuleWidget(), - SleepDataModuleWidget(), - SleepDataModuleWidget(), - ].divide(SizedBox( - width: - 14.rpx)), // ✅ 这里加了 .rpx + Container( + width: 120.rpx, + height: 120.rpx, + child: Image.asset( + "assets/img/mye.png", + fit: BoxFit.cover, + ), + ), + Text( + "首页.云关爱".tr, + style: TextStyle( + color: themeController + .currentColor.sc3, + ), + ), + ] + .addToStart( + SizedBox(height: 32.rpx)) + .addToEnd( + SizedBox(height: 32.rpx)), ), ), ), @@ -680,10 +885,107 @@ class _HomePageState extends State { ), ), ), - )); - }), - ], - ), + ); + }), + ], + ), + ), + Obx(() { + Map> reportData = + deviceController.sleepReportData.value; + var deviceList = deviceController.deviceList.value + .where((device) => device['show'] == true) + .toList(); + + if (deviceList.isEmpty) { + return Expanded(child: NullDataWidget()); + } + if (reportData.isEmpty) { + return Expanded(child: NullDataWidget()); + } + + List macList = reportData.keys.toList(); + macList = macList + .where( + (mac) => deviceList + .any((device) => device['mac'] == mac), + ) + .toList(); + + // ⛔️防止 macList 长度和 deviceList.length 不对应导致崩溃 + if (macList.length != deviceList.length) { + return Expanded( + child: Center(child: CircularProgressIndicator()), + ); + } + + return Expanded( + child: SingleChildScrollView( + child: Column( + children: List.generate( + deviceList.length, + (i) { + String mac = macList[i]; + List dailyDataList = + reportData[mac]!; + Map? targetDevice = + deviceList.firstWhereOrNull( + (device) => device['mac'] == mac, + ); + List stateModule = []; + + return DynamicReportDetailWidget( + targetDevice: targetDevice!, + sleepDateWidgets: List.generate( + dailyDataList.length, + (j) { + var dayData = dailyDataList[j]; + DateTime date = + DateTime.fromMillisecondsSinceEpoch( + dayData['time'] is String + ? int.parse(dayData['time']) + : dayData['time'], + ); + if (dayData['selected'] != null && + dayData['selected'] == true && + dayData['state'] != null) { + stateModule = dayData['state']; + } + return SleepDateWidget( + mac: mac, + time: dayData['time'], + date: date, + score: dayData['score']?['socre'] + ?.toString() ?? + '', + comment: dayData['score']?['name'], + textColor: dayData['score'] + ?['color'] == + null + ? null + : stringToColor( + dayData['score']?['color']), + isSelected: dayData['selected'], + ); + }, + ), + sleepDataModuleWidgets: + stateModule.isNotEmpty + ? List.generate( + stateModule.length, + (j) => SleepDataModuleWidget( + data: stateModule[j], + ), + ) + : [], + ); + }, + ), + ), + ), + ); + }), + ], ), ), ), diff --git a/lib/pages/main_bottom/main_page_bottom_change.dart b/lib/pages/main_bottom/main_page_bottom_change.dart index c3f6b83..e053a49 100644 --- a/lib/pages/main_bottom/main_page_bottom_change.dart +++ b/lib/pages/main_bottom/main_page_bottom_change.dart @@ -7,9 +7,12 @@ import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/enum/LoginStatus.dart'; import 'package:vbvs_app/pages/main_bottom/e_page.dart'; import 'package:vbvs_app/pages/main_bottom/home_page.dart'; import 'package:vbvs_app/pages/main_bottom/message_page.dart'; @@ -33,7 +36,7 @@ class MainPageBottomChange extends GetView { width: size, height: size, ), - ), + ), activeIcon: Padding( padding: EdgeInsets.only(bottom: 6.rpx), child: isEmpty @@ -105,15 +108,40 @@ class MainPageBottomChange extends GetView { type: BottomNavigationBarType.fixed, currentIndex: controller.model.currentIndex, onTap: (index) { - Future.delayed(const Duration(milliseconds: 500), () { - if (controller.model.currentIndex != 1) { + Future.delayed(const Duration(milliseconds: 100), () { + UserInfoController userInfoController = Get.find(); + bool isLoggedIn = userInfoController.model.login == + LoginStatus.LOGIN.code; + + // 需要登录的页面 + if ((index == 1 || index == 2) && !isLoggedIn) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: themeController.currentColor.sc9, + ); + Future.delayed(Duration(milliseconds: 50), () { + if (Get.currentRoute == '/ePage' || + Get.currentRoute == '/messagePage') { + Get.back(); + } + Future.delayed(Duration(milliseconds: 100), () { + Get.toNamed("/loginPage"); + }); + }); + return; + } + + // 已登录 或 index 是不需要登录的页面,正常跳转 + if (controller.model.currentIndex != index) { globalController.model.hideBottomNavigationBar = false; globalController.updateAll(); } + + controller.model.currentIndex = index; + controller.updateAll(); }); - controller.model.currentIndex = index; - controller.updateAll(); }, items: [ getBottomNavigationBarItem("assets/img/menu/home.svg", diff --git a/lib/pages/main_bottom/message_page.dart b/lib/pages/main_bottom/message_page.dart index be6324d..83c7038 100644 --- a/lib/pages/main_bottom/message_page.dart +++ b/lib/pages/main_bottom/message_page.dart @@ -1,7 +1,15 @@ import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; -import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; -import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:flutter/services.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/message/message_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/pages/main_bottom/component/MessageWidgetWidget.dart'; class MessagePage extends StatefulWidget { const MessagePage({super.key}); @@ -11,17 +19,209 @@ class MessagePage extends StatefulWidget { } class _MessagePageState extends State { - GlobalController globalController = Get.find(); - UserInfoController userInfoController = Get.find(); + ThemeController themeController = Get.find(); + MessageController messageController = Get.find(); + + @override + void initState() { + messageController.getMessageList().then((response) { + if (response.code != HttpStatusCodes.ok) { + TopSlideNotification.show( + Get.context!, + text: response.msg ?? "服务器.失败".tr, + textColor: themeController.currentColor.sc9, + ); + } + }); + } + @override Widget build(BuildContext context) { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarColor: Colors.transparent, // 这里设置你希望的颜色 + statusBarIconBrightness: Brightness.light, // 状态栏图标的亮度 + )); return LayoutBuilder( builder: (context, boxConstraints) => GestureDetector( onTap: () => FocusScope.of(context).unfocus(), - child: Scaffold( - body: SafeArea( - top: true, - child: Text("消息"), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + toolbarHeight: 140.rpx, + titleSpacing: 0, + title: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, + 0.rpx, + 0.rpx, + 0.rpx, + ), + child: Container( + width: double.infinity, + height: 140.rpx, // 👈 明确告诉 Flutter 高度 + child: Column( + children: [ + SizedBox(height: 40.rpx), // 上边距 + Expanded( + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Obx(() { + return ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc3, + borderRadius: 8.rpx, + padding: EdgeInsets.all(0), + onTap: () async { + messageController.model.type = 1; + await messageController.getMessageList(); + messageController.updateAll(); + }, + child: Container( + width: 160.rpx, + alignment: Alignment.center, + child: Text( + '体征消息'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: + messageController + .model.type == + 2 + ? themeController + .currentColor.sc3 + : themeController + .currentColor.sc2, + ), + ), + ), + ); + }), + Obx(() { + return ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: + themeController.currentColor.sc3, + borderRadius: 8.rpx, + padding: EdgeInsets.all(0), + onTap: () async { + messageController.model.type = 2; + await messageController.getMessageList(); + messageController.updateAll(); + }, + child: Container( + width: 160.rpx, + alignment: Alignment.center, + child: Text( + '系统消息'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + color: + messageController + .model.type == + 1 + ? themeController + .currentColor.sc3 + : themeController + .currentColor.sc2, + ), + ), + ), + ); + }), + ], + ), + Obx(() { + double lineWidth = 160.rpx; + return AnimatedPositioned( + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + bottom: 0, // 👈 现在 Stack 够大,线能放得下 + left: messageController.model.type == 1 + ? 0 + : 160.rpx, + child: Container( + width: lineWidth, + height: 4.rpx, + decoration: BoxDecoration( + color: themeController.currentColor.sc2, + borderRadius: BorderRadius.circular(2.rpx), + ), + ), + ); + }), + ], + ), + ), + SizedBox(height: 17.rpx), // 上边距 + ], + ), + ), + ), + actions: [], + centerTitle: false, + ), + backgroundColor: Colors.transparent, + body: SafeArea( + top: true, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, + 0.rpx, + 30.rpx, + 0.rpx, + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + MessageWidgetWidget(), + MessageWidgetWidget(), + MessageWidgetWidget(), + ] + .divide(SizedBox(height: 30.rpx)) + .addToStart(SizedBox(height: 30.rpx)) + .addToEnd(SizedBox(height: 30.rpx)), + ), + ), + ), + ), + ), + ], + ), + ), ), ), ), diff --git a/lib/pages/main_bottom/mine_page.dart b/lib/pages/main_bottom/mine_page.dart index cec344a..b73bfca 100644 --- a/lib/pages/main_bottom/mine_page.dart +++ b/lib/pages/main_bottom/mine_page.dart @@ -8,9 +8,11 @@ import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/ClickableContainer.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/enum/LoginStatus.dart'; class MinePage extends StatefulWidget { const MinePage({super.key}); @@ -30,6 +32,7 @@ class _MinePageState extends State { statusBarColor: Colors.transparent, // 这里设置你希望的颜色 statusBarIconBrightness: Brightness.light, // 状态栏图标的亮度 )); + int login = userInfoController.model.login!; return LayoutBuilder( builder: (context, bodySize) => GestureDetector( onTap: () => FocusScope.of(context).unfocus(), @@ -75,18 +78,6 @@ class _MinePageState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.end, children: [ - SvgPicture.asset( - 'assets/img/icon/earphone.svg', - width: 29.rpx, - height: 29.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - color: themeController.currentColor.sc3, - ), - // SvgPicture.asset( - // 'assets/img/icon/setting.svg', - // width: 29.rpx, - // height: 29.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 - // color: themeController.currentColor.sc3, - // ), ClickableContainer( backgroundColor: Colors.transparent, // 容器背景色 @@ -95,7 +86,53 @@ class _MinePageState extends State { padding: EdgeInsets .zero, // 这里去掉外部的 padding,避免影响点击范围 onTap: () { - Get.toNamed("/settingPage"); + if (userInfoController.model.login == + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "待开发功能".tr, + ); + } else { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } + }, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0.rpx), + child: SvgPicture.asset( + 'assets/img/icon/earphone.svg', + width: 29.rpx, + height: 29.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController.currentColor.sc3, + ), + ), + ), + ClickableContainer( + backgroundColor: + Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsets + .zero, // 这里去掉外部的 padding,避免影响点击范围 + onTap: () { + if (userInfoController.model.login == + LoginStatus.LOGIN.code) { + Get.toNamed("/settingPage"); + } else { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } }, child: Padding( padding: EdgeInsetsDirectional.fromSTEB( @@ -130,10 +167,25 @@ class _MinePageState extends State { decoration: BoxDecoration( shape: BoxShape.circle, ), - child: Image.network( - 'https://picsum.photos/seed/270/600', - fit: BoxFit.cover, - ), + child: login == 1 + ? (userInfoController.model.user! + .avatar == + null || + userInfoController.model + .user!.avatar!.isEmpty + ? Image.asset( + "assets/img/avatar.png", + fit: BoxFit.cover, + ) + : Image.network( + userInfoController + .model.user!.avatar!, + fit: BoxFit.cover, + )) + : Image.asset( + "assets/img/avatar.png", + fit: BoxFit.cover, + ), ), Column( mainAxisSize: MainAxisSize.max, @@ -141,7 +193,11 @@ class _MinePageState extends State { CrossAxisAlignment.start, children: [ Text( - '张天爱', + login == 1 + ? (userInfoController.model + .user!.nick_name ?? + '未命名'.tr) + : "我的.未登录".tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -154,7 +210,16 @@ class _MinePageState extends State { ), ), Text( - '账号:135****2598', + login == 1 + ? (userInfoController.model + .user!.email != + null + ? userInfoController + .model.user!.email! + : MyUtils.hidePhoneNumber( + userInfoController.model + .user!.phone!)) + : "我的.未登录".tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -173,7 +238,11 @@ class _MinePageState extends State { CustomCard( borderRadius: 0, onTap: () { - Get.toNamed("/updateUserPage"); + if (login == 1) { + Get.toNamed("/updateUserPage"); + } else { + Get.toNamed("/loginPage"); + } }, // colors: [themeController.currentColor.sc5], colors: [Colors.transparent], @@ -181,7 +250,9 @@ class _MinePageState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - '我的.个人信息'.tr, + login == 1 + ? '我的.个人信息'.tr + : '我的.去登录'.tr, style: FlutterFlowTheme.of(context) .bodyMedium .override( @@ -239,7 +310,20 @@ class _MinePageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 40.rpx, 0.rpx, 40.rpx, 0.rpx), onTap: () { - Get.toNamed("/deviceType",arguments:1); + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } else { + Get.toNamed("/deviceTypeList"); + } }, child: Container( child: Padding( @@ -296,7 +380,25 @@ class _MinePageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 0.rpx, 0.rpx, 0.rpx, 0.rpx), onTap: () { - print('点击了容器'); + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } else { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: + themeController.currentColor.sc2, + ); + } }, child: Container( child: Padding( @@ -353,7 +455,25 @@ class _MinePageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 0.rpx, 0.rpx, 0.rpx, 0.rpx), onTap: () { - print('点击了容器'); + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } else { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: + themeController.currentColor.sc2, + ); + } }, child: Container( child: Padding( @@ -410,7 +530,25 @@ class _MinePageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 40.rpx, 0.rpx, 40.rpx, 0.rpx), onTap: () { - print('点击了容器'); + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } else { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: + themeController.currentColor.sc2, + ); + } }, child: Container( child: Padding( @@ -467,7 +605,25 @@ class _MinePageState extends State { padding: EdgeInsetsDirectional.fromSTEB( 40.rpx, 0.rpx, 40.rpx, 0.rpx), onTap: () { - print('点击了容器'); + UserInfoController userInfoController = + Get.find(); + if (userInfoController.model.login != + LoginStatus.LOGIN.code) { + TopSlideNotification.show( + context, + text: "必须登录提示".tr, + textColor: + themeController.currentColor.sc9, + ); + Get.toNamed("/loginPage"); + } else { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: + themeController.currentColor.sc2, + ); + } }, child: Container( child: Padding( @@ -508,7 +664,7 @@ class _MinePageState extends State { mainAxisSize: MainAxisSize.max, children: [ Text( - '1.0.0', + 'V1.0.2504.12', style: FlutterFlowTheme.of(context) .bodyMedium diff --git a/lib/pages/main_bottom/sleep_report_page.dart b/lib/pages/main_bottom/sleep_report_page.dart index 52afb41..6152ba9 100644 --- a/lib/pages/main_bottom/sleep_report_page.dart +++ b/lib/pages/main_bottom/sleep_report_page.dart @@ -1,31 +1,31 @@ -import 'package:ef/ef.dart'; -import 'package:flutter/material.dart'; -import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; -import 'package:vbvs_app/controller/user_info_controller.dart'; +// import 'package:ef/ef.dart'; +// import 'package:flutter/material.dart'; +// import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +// import 'package:vbvs_app/controller/user_info_controller.dart'; -class SleepReportPage extends StatefulWidget { - const SleepReportPage({super.key}); +// class SleepReportPage extends StatefulWidget { +// const SleepReportPage({super.key}); - @override - State createState() => _SleepReportPageState(); -} +// @override +// State createState() => _SleepReportPageState(); +// } -class _SleepReportPageState extends State { - GlobalController globalController = Get.find(); - UserInfoController userInfoController = Get.find(); - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, boxConstraints) => GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - child: Scaffold( +// class _SleepReportPageState extends State { +// GlobalController globalController = Get.find(); +// UserInfoController userInfoController = Get.find(); +// @override +// Widget build(BuildContext context) { +// return LayoutBuilder( +// builder: (context, boxConstraints) => GestureDetector( +// onTap: () => FocusScope.of(context).unfocus(), +// child: Scaffold( - body: SafeArea( - top: true, - child: Text("睡眠报告"), - ), - ), - ), - ); - } -} +// body: SafeArea( +// top: true, +// child: Text("睡眠报告"), +// ), +// ), +// ), +// ); +// } +// } diff --git a/lib/pages/person/person_page.dart b/lib/pages/person/person_page.dart index 97bd137..db5b22b 100644 --- a/lib/pages/person/person_page.dart +++ b/lib/pages/person/person_page.dart @@ -1,15 +1,20 @@ import 'package:ef/ef.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutterflow_ui/flutterflow_ui.dart'; import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; import 'package:vbvs_app/common/util/FitTool.dart'; import 'package:vbvs_app/common/util/MyUtils.dart'; import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/SelectableTagButton.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; import 'package:vbvs_app/controller/person/person_controller.dart'; import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; import 'package:vbvs_app/pages/person/select_time.dart'; class PersonPage extends StatefulWidget { @@ -29,6 +34,12 @@ class _EPageState extends State { @override void initState() { super.initState(); + personController.selectedDiseaseIds.value = []; + personController.name.value = ''; + personController.gender.value = 1; + personController.birthday.value = ""; + personController.weight.value = 65; + personController.dateTime = null; } @override @@ -45,7 +56,7 @@ class _EPageState extends State { ), child: Scaffold( backgroundColor: Colors.transparent, // 加上这一行 - appBar: AppBar( + appBar: AppBar( backgroundColor: themeController.currentColor.sc17, automaticallyImplyLeading: false, iconTheme: IconThemeData( @@ -80,7 +91,17 @@ class _EPageState extends State { child: CustomCard( borderRadius: 20.rpx, onTap: () async { - Get.offAllNamed("/bindDeviceSuccess"); + ApiResponse apiRespons = + await personController.savePersonData(); + if (apiRespons.code == HttpStatusCodes.ok) { + TopSlideNotification.show(context, + text: apiRespons.msg!); + Get.offAllNamed("/bindDeviceSuccess"); + } else { + TopSlideNotification.show(context, + text: apiRespons.msg!, + textColor: themeController.currentColor.sc9); + } }, colors: [ themeController.currentColor.sc1, @@ -138,6 +159,10 @@ class _EPageState extends State { child: TextFormField( // controller: _model.textController1, // focusNode: _model.textFieldFocusNode1, + initialValue: personController.name.value, + onChanged: (Value) { + personController.name.value = Value; + }, autofocus: false, obscureText: false, decoration: InputDecoration( @@ -218,69 +243,201 @@ class _EPageState extends State { mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ - Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: 90.rpx, - height: 90.rpx, - decoration: BoxDecoration(), - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - shape: BoxShape.circle, - ), - child: Image.asset( - "assets/img/man.png", - fit: BoxFit.cover, - ), - ), - ), - Text( - '男', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - color: themeController - .currentColor.sc3, - fontSize: 26.rpx, - letterSpacing: 0.0, + // Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 90.rpx, + // height: 90.rpx, + // decoration: BoxDecoration(), + // child: Container( + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // ), + // child: ColorFiltered( + // colorFilter: ColorFilter.mode( + // Colors.grey.withOpacity( + // 0.6), // 这里控制灰色度的强度 + // BlendMode.saturation, // 将图像变成灰度 + // ), + // child: Image.asset( + // "assets/img/man.png", + // fit: BoxFit.cover, + // ), + // ), + // ), + // ), + // Text( + // '男'.tr, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(height: 14.rpx)), + // ), + // Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 90.rpx, + // height: 90.rpx, + // decoration: BoxDecoration(), + // child: Container( + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // ), + // child: Image.asset( + // "assets/img/woman.png", + // fit: BoxFit.cover, + // ), + // ), + // ), + // Text( + // '女'.tr, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(height: 14.rpx)), + // ), + + Obx( + () { + bool isMaleGreyed = + personController.gender.value == + 0; // gender == 0 时男生部分变灰 + return GestureDetector( + onTap: () { + personController.gender.value = + 1; // 点击时将 gender 设置为 1(女生部分被选中) + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ColorFiltered( + colorFilter: isMaleGreyed + ? ColorFilter.mode( + Colors.grey + .withOpacity(0.6), + BlendMode.saturation) + : ColorFilter.mode( + Colors.transparent, + BlendMode.saturation), + child: Image.asset( + "assets/img/man.png", + fit: BoxFit.cover, + ), + ), + ), ), - ), - ].divide(SizedBox(height: 14.rpx)), + Text( + '男'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: isMaleGreyed + ? themeController + .currentColor + .sc4 + : themeController + .currentColor + .sc3, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + ); + }, ), - Column( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - width: 90.rpx, - height: 90.rpx, - decoration: BoxDecoration(), - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - shape: BoxShape.circle, - ), - child: Image.asset( - "assets/img/woman.png", - fit: BoxFit.cover, - ), - ), - ), - Text( - '女', - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - color: themeController - .currentColor.sc3, - fontSize: 26.rpx, - letterSpacing: 0.0, + + // 女性部分 + Obx( + () { + bool isFemaleGreyed = + personController.gender.value == + 1; // gender == 1 时女生部分变灰 + return GestureDetector( + onTap: () { + personController.gender.value = + 0; // 点击时将 gender 设置为 0(男生部分被选中) + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ColorFiltered( + colorFilter: isFemaleGreyed + ? ColorFilter.mode( + Colors.grey + .withOpacity(0.6), + BlendMode.saturation) + : ColorFilter.mode( + Colors.transparent, + BlendMode.saturation), + child: Image.asset( + "assets/img/woman.png", + fit: BoxFit.cover, + ), + ), + ), ), - ), - ].divide(SizedBox(height: 14.rpx)), + Text( + '女'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: isFemaleGreyed + ? themeController + .currentColor + .sc4 + : themeController + .currentColor + .sc3, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + ); + }, ), ].divide(SizedBox(width: 170.rpx)), ), @@ -303,10 +460,12 @@ class _EPageState extends State { Future.delayed(Duration(milliseconds: 250), () { showDateSelectionDialog( context, - checkDate: personController.model.birthday ?? + checkDate: personController.dateTime ?? DateTime.now(), checkChange: (DateTime d) { - personController.model.birthday = d; + personController.birthday.value = + MyUtils.formatBindTime(d); + personController.dateTime = d; personController.updateAll(); }, ); @@ -314,17 +473,16 @@ class _EPageState extends State { }, child: Center( child: Text( - personController.model.birthday != null - ? DateFormat("yyyy年MM月dd日").format( - personController.model.birthday!) + personController.dateTime != null + ? DateFormat("yyyy年MM月dd日") + .format(personController.dateTime!) : '人员资料.生日输入提示'.tr, textAlign: TextAlign.right, style: FlutterFlowTheme.of(context) .bodyMedium .override( fontFamily: 'Readex Pro', - color: personController.model.birthday != - null + color: personController.dateTime != null ? themeController.currentColor.sc3 : themeController.currentColor.sc4, fontSize: @@ -350,75 +508,103 @@ class _EPageState extends State { ), child: Align( alignment: AlignmentDirectional(0, 0), - child: TextFormField( - // controller: _model.textController3, - // focusNode: _model.textFieldFocusNode3, - autofocus: false, - obscureText: false, - decoration: InputDecoration( - fillColor: Colors.transparent, - isDense: true, - labelStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Inter', - letterSpacing: 0.0, + child: Stack( + alignment: Alignment.center, + children: [ + // 实际输入框(输入逻辑保留) + TextFormField( + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + initialValue: + personController.weight.value.toString(), + onChanged: (value) { + personController.weight.value = + int.tryParse(value) ?? 0; + }, + autofocus: false, + obscureText: false, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Colors.transparent, // 隐藏输入文字 + letterSpacing: 0.0, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + hintText: '人员资料.体重输入提示'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + color: + themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), ), - hintText: '人员资料.体重输入提示'.tr, - hintStyle: FlutterFlowTheme.of(context) - .labelMedium - .override( - fontFamily: 'Inter', - color: personController.model.weight != - null - ? themeController.currentColor.sc3 - : themeController.currentColor.sc4, - fontSize: - AppConstants().normal_text_fontSize, - letterSpacing: 0.0, + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0x00000000), - width: 1.rpx, + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + filled: true, ), - borderRadius: BorderRadius.circular(8.rpx), ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: Color(0x00000000), - width: 1.rpx, - ), - borderRadius: BorderRadius.circular(8.rpx), - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, - width: 1.rpx, - ), - borderRadius: BorderRadius.circular(8.rpx), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, - width: 1.rpx, - ), - borderRadius: BorderRadius.circular(8.rpx), - ), - filled: true, - ), - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: 'Inter', - letterSpacing: 0.0, - color: themeController.currentColor.sc3, - ), - textAlign: TextAlign.center, - cursorColor: - FlutterFlowTheme.of(context).primaryText, - // validator: _model.textController3Validator - // .asValidator(context), + Obx(() { + final weight = personController.weight.value; + return weight == 0 + ? const SizedBox.shrink() // 不显示任何内容 + : Text( + '${weight}kg', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ); + }), + ], ), ), ), @@ -445,14 +631,38 @@ class _EPageState extends State { ), ), ), - Padding( - padding: EdgeInsetsDirectional.fromSTEB( - 70.rpx, 70.rpx, 70.rpx, 0), - child: Container( - width: double.infinity, - decoration: BoxDecoration(), - ), - ), + Obx(() { + final selectedIds = personController.selectedDiseaseIds; + final diseases = personController.diseaseList; + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 70.rpx, 70.rpx, 0), + child: Wrap( + spacing: 20.rpx, + runSpacing: 20.rpx, + children: diseases.map((disease) { + final id = disease['_id']; + final name = disease['disease_type_name']; + final isSelected = selectedIds.contains(id); + return SelectableTagButton( + label: name, + selected: isSelected, + onTap: () { + if (isSelected) { + selectedIds.remove(id); + } else { + selectedIds.add(id); + } + personController.model.read = 0; + personController.updateAll(); + //切换语言 + // Get.updateLocale(Locale("en", "us")); + }, + ); + }).toList(), + ), + ); + }), Padding( padding: EdgeInsetsDirectional.fromSTEB(0, 152.rpx, 0, 0), diff --git a/lib/pages/person/update_person_page.dart b/lib/pages/person/update_person_page.dart new file mode 100644 index 0000000..7a0ec3f --- /dev/null +++ b/lib/pages/person/update_person_page.dart @@ -0,0 +1,774 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/SelectableTagButton.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/body_device_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; +import 'package:vbvs_app/pages/person/select_time.dart'; + +class UpdatePersonPage extends StatefulWidget { + const UpdatePersonPage({super.key}); + + @override + State createState() => _UpdatePageState(); +} + +class _UpdatePageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + PersonController personController = Get.find(); + BodyDeviceController bodyDeviceController = Get.find(); + ThemeController themeController = Get.find(); + + @override + void initState() { + super.initState(); + personController.getDiseaseData().then((apiResponse) { + TopSlideNotification.show( + Get.context!, + text: apiResponse.msg!, + textColor: apiResponse.code != HttpStatusCodes.ok + ? themeController.currentColor.sc9 + : themeController.currentColor.sc2, + ); + }); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '人员资料.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtomAddCallback(() { + bodyDeviceController.getDeviceList(); + bodyDeviceController.updateAll(); + }), + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + ApiResponse apiRespons = + await personController.savePersonData(); + if (apiRespons.code == HttpStatusCodes.ok) { + TopSlideNotification.show(context, + text: apiRespons.msg!); + } else { + TopSlideNotification.show(context, + text: apiRespons.msg!, + textColor: themeController.currentColor.sc9); + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0, 16.rpx, 0), + child: Text( + '人员资料.保存'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 141.rpx, 70.rpx, 0), + child: Container( + width: double.infinity, + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all( + color: Color(0xFFF3EDED), + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: TextFormField( + // controller: _model.textController1, + // focusNode: _model.textFieldFocusNode1, + initialValue: personController.name.value, + onChanged: (Value) { + personController.name.value = Value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + labelStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + hintText: '人员资料.名字输入提示'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: BorderRadius.circular(8.rpx), + ), + filled: true, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + letterSpacing: 0.0, + color: themeController.currentColor.sc3, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + // validator: _model.textController1Validator + // .asValidator(context), + ), + ), + ), + ), + Align( + alignment: AlignmentDirectional(0, 0), + child: Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 90.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(-1, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 90.rpx, + // height: 90.rpx, + // decoration: BoxDecoration(), + // child: Container( + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // ), + // child: ColorFiltered( + // colorFilter: ColorFilter.mode( + // Colors.grey.withOpacity( + // 0.6), // 这里控制灰色度的强度 + // BlendMode.saturation, // 将图像变成灰度 + // ), + // child: Image.asset( + // "assets/img/man.png", + // fit: BoxFit.cover, + // ), + // ), + // ), + // ), + // Text( + // '男'.tr, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(height: 14.rpx)), + // ), + // Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Container( + // width: 90.rpx, + // height: 90.rpx, + // decoration: BoxDecoration(), + // child: Container( + // clipBehavior: Clip.antiAlias, + // decoration: BoxDecoration( + // shape: BoxShape.circle, + // ), + // child: Image.asset( + // "assets/img/woman.png", + // fit: BoxFit.cover, + // ), + // ), + // ), + // Text( + // '女'.tr, + // style: FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(height: 14.rpx)), + // ), + + Obx( + () { + bool isMaleGreyed = + personController.gender.value == + 0; // gender == 0 时男生部分变灰 + return GestureDetector( + onTap: () { + personController.gender.value = + 1; // 点击时将 gender 设置为 1(女生部分被选中) + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ColorFiltered( + colorFilter: isMaleGreyed + ? ColorFilter.mode( + Colors.grey + .withOpacity(0.6), + BlendMode.saturation) + : ColorFilter.mode( + Colors.transparent, + BlendMode.saturation), + child: Image.asset( + "assets/img/man.png", + fit: BoxFit.cover, + ), + ), + ), + ), + Text( + '男'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: isMaleGreyed + ? themeController + .currentColor + .sc4 + : themeController + .currentColor + .sc3, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + ); + }, + ), + + // 女性部分 + Obx( + () { + bool isFemaleGreyed = + personController.gender.value == + 1; // gender == 1 时女生部分变灰 + return GestureDetector( + onTap: () { + personController.gender.value = + 0; // 点击时将 gender 设置为 0(男生部分被选中) + }, + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 90.rpx, + height: 90.rpx, + decoration: BoxDecoration(), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: ColorFiltered( + colorFilter: isFemaleGreyed + ? ColorFilter.mode( + Colors.grey + .withOpacity(0.6), + BlendMode.saturation) + : ColorFilter.mode( + Colors.transparent, + BlendMode.saturation), + child: Image.asset( + "assets/img/woman.png", + fit: BoxFit.cover, + ), + ), + ), + ), + Text( + '女'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: isFemaleGreyed + ? themeController + .currentColor + .sc4 + : themeController + .currentColor + .sc3, + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(height: 14.rpx)), + ), + ); + }, + ), + ].divide(SizedBox(width: 170.rpx)), + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 50.rpx, 70.rpx, 0), + child: Container( + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all(color: Color(0xFFF3EDED)), + ), + child: InkWell( + onTap: () { + FocusScope.of(context).requestFocus(FocusNode()); + Future.delayed(Duration(milliseconds: 250), () { + showDateSelectionDialog( + context, + checkDate: personController.dateTime ?? + DateTime.now(), + checkChange: (DateTime d) { + personController.birthday.value = + MyUtils.formatBindTime(d); + personController.dateTime = d; + personController.updateAll(); + }, + ); + }); + }, + child: Center( + child: Text( + personController.dateTime != null + ? DateFormat("yyyy年MM月dd日") + .format(personController.dateTime!) + : '人员资料.生日输入提示'.tr, + textAlign: TextAlign.right, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Readex Pro', + color: personController.dateTime != null + ? themeController.currentColor.sc3 + : themeController.currentColor.sc4, + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0, + ), + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 18.rpx, 70.rpx, 0), + child: Container( + width: double.infinity, + height: 100.rpx, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50.rpx), + border: Border.all( + color: Color(0xFFF3EDED), + ), + ), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Stack( + alignment: Alignment.center, + children: [ + // 实际输入框(输入逻辑保留) + TextFormField( + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + initialValue: + personController.weight.value.toString(), + onChanged: (value) { + personController.weight.value = + int.tryParse(value) ?? 0; + }, + autofocus: false, + obscureText: false, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Colors.transparent, // 隐藏输入文字 + letterSpacing: 0.0, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context).primaryText, + decoration: InputDecoration( + fillColor: Colors.transparent, + isDense: true, + hintText: '人员资料.体重输入提示'.tr, + hintStyle: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + color: + themeController.currentColor.sc4, + fontSize: AppConstants() + .normal_text_fontSize, + letterSpacing: 0.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: + FlutterFlowTheme.of(context).error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular(8.rpx), + ), + filled: true, + ), + ), + Obx(() { + final weight = personController.weight.value; + return weight == 0 + ? const SizedBox.shrink() // 不显示任何内容 + : Text( + '${weight}kg', + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .normal_text_fontSize, + ), + ); + }), + ], + ), + ), + ), + ), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 117.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: Text( + '人员资料.疾病标题'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFF3F4F5), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + Obx(() { + final selectedIds = personController.selectedDiseaseIds; + final diseases = personController.diseaseList; + return Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 70.rpx, 70.rpx, 70.rpx, 0), + child: Wrap( + spacing: 20.rpx, + runSpacing: 20.rpx, + children: diseases.map((disease) { + final id = disease['_id']; + final name = disease['disease_type_name']; + final isSelected = selectedIds.contains(id); + return SelectableTagButton( + label: name, + selected: isSelected, + onTap: () { + if (isSelected) { + selectedIds.remove(id); + } else { + selectedIds.add(id); + } + personController.model.read = 0; + personController.updateAll(); + //切换语言 + // Get.updateLocale(Locale("en", "us")); + }, + ); + }).toList(), + ), + ); + }), + Padding( + padding: + EdgeInsetsDirectional.fromSTEB(0, 152.rpx, 0, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.rpx), + border: Border.all( + color: Color(0xFFE9E3E3), + ), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 30.rpx, 30.rpx, 30.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0, 8.rpx, 0, 0), + child: Icon( + Icons.arrow_back, + color: Color(0xFFE4EBF0), + size: 24.rpx, + ), + ), + Expanded( + child: Text( + '人员资料.提示'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: Color(0xFFEEF3F8), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + ), + ].divide(SizedBox(width: 23.rpx)), + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/sleep_report/sleep_report_page.dart b/lib/pages/sleep_report/sleep_report_page.dart new file mode 100644 index 0000000..ee7b0a6 --- /dev/null +++ b/lib/pages/sleep_report/sleep_report_page.dart @@ -0,0 +1,190 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/component/tool/WebViewWidget.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/device/device_type_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; + +class SleepReportPage extends StatefulWidget { + var sleepUri; + SleepReportPage({super.key, required this.sleepUri}); + + @override + State createState() => _SleepReportPageState(); +} + +class _SleepReportPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + DeviceTypeController deviceTypeController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + // backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + iconTheme: IconThemeData(color: themeController.currentColor.sc3), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '健康报告'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + + body: SafeArea( + top: true, + child: InAppWebView( + key: UniqueKey(), + initialUrlRequest: URLRequest(url: WebUri(widget.sleepUri)), + ), + // child: WebViewWidget(url: "${widget.sleepUri}"), + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + // child: SingleChildScrollView( + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // // 使用 Obx 来监听 deviceTypeList 的变化 + // Obx(() { + // return Column( + // children: [ + // SizedBox(height: 26.rpx), // 开始的间隔 + // ...deviceTypeController.deviceTypeList.value + // .map((device) { + // return Padding( + // padding: EdgeInsets.only( + // bottom: 26.rpx), // 添加每个设备之间的间隔 + // child: _buildDeviceCard( + // context, + // title: device['name'], // 这里假设 device 是一个 Map + // imageUrl: device['image'], + // type: device['type'], + // ), + // ); + // }).toList(), + // SizedBox(height: 26.rpx), // 结束的间隔 + // ], + // ); + // }), + // ], + // ), + // ), + // ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required double type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == 1) { + Get.toNamed("/bodyDevice"); + } + if (type == 2) { + TopSlideNotification.show( + context, + text: "待开发.提示".tr, + textColor: themeController.currentColor.sc2, + ); + } + } + }, + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + // child: Image.asset( + // imageUrl, + // width: 212.rpx, + // height: 168.rpx, + // ), + child: Image.network( + imageUrl, + // fit: BoxFit.cover, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/user/about_us_page.dart b/lib/pages/user/about_us_page.dart new file mode 100644 index 0000000..7db6550 --- /dev/null +++ b/lib/pages/user/about_us_page.dart @@ -0,0 +1,470 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +// import 'package:easydevice/easydevice.dart'; + + +class AboutUsPage extends StatefulWidget { + const AboutUsPage({super.key}); + + @override + State createState() => _AboutUsPageState(); +} + +class _AboutUsPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '关于我们.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + // Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 30.rpx, 25.rpx, 30.rpx, 0), + // child: Container( + // width: double.infinity, + // decoration: BoxDecoration( + // color: Color(0xFF242835), + // borderRadius: BorderRadius.circular( + // AppConstants().normal_container_radius), + // ), + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 0, 0.rpx, 0), + // child: Column( + // mainAxisSize: MainAxisSize.max, + // children: [ + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: themeController + // .currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsetsDirectional.fromSTEB( + // 40.rpx, 0.rpx, 40.rpx, 0.rpx), + // onTap: () { + // print('点击了容器'); + // }, + // child: Container( + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 20.rpx, 0.rpx, 20.rpx), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '设置页.主题模式'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 22.rpx)), + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '深色', + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: + // Color(0xFFD9E3EB), + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // SvgPicture.asset( + // 'assets/img/icon/arrow_right.svg', + // width: 8.rpx, + // height: 15 + // .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController + // .currentColor.sc3, + // ), + // ].divide(SizedBox(width: 28.rpx)), + // ), + // ], + // ), + // ), + // ), + // ), + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: themeController + // .currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsetsDirectional.fromSTEB( + // 40.rpx, 0.rpx, 40.rpx, 0.rpx), + // onTap: () { + // print('点击了容器'); + // }, + // child: Container( + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 20.rpx, 0.rpx, 20.rpx), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '设置页.选择语言'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 22.rpx)), + // ), + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '中文', + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: + // Color(0xFFD9E3EB), + // fontSize: 26.rpx, + // letterSpacing: 0.0, + // ), + // ), + // SvgPicture.asset( + // 'assets/img/icon/arrow_right.svg', + // width: 8.rpx, + // height: 15 + // .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController + // .currentColor.sc3, + // ), + // ].divide(SizedBox(width: 28.rpx)), + // ), + // ], + // ), + // ), + // ), + // ), + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: themeController + // .currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 0.rpx, 0.rpx, 0.rpx), + // onTap: () { + // print('点击了容器'); + // }, + // child: Container( + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 40.rpx, 20.rpx, 40.rpx, 20.rpx), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '设置页.关于我们'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 22.rpx)), + // ), + // SvgPicture.asset( + // 'assets/img/icon/arrow_right.svg', + // width: 8.rpx, + // height: + // 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // ), + // ), + // ), + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: themeController + // .currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsetsDirectional.fromSTEB( + // 40.rpx, 0.rpx, 40.rpx, 0.rpx), + // onTap: () { + // print('点击了容器'); + // }, + // child: Container( + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 20.rpx, 0.rpx, 20.rpx), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '设置页.用户协议'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 22.rpx)), + // ), + // SvgPicture.asset( + // 'assets/img/icon/arrow_right.svg', + // width: 8.rpx, + // height: + // 15.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // ), + // ), + // ), + // ClickableContainer( + // backgroundColor: Colors.transparent, // 容器背景色 + // highlightColor: themeController + // .currentColor.sc21, // 点击时的背景色 + // padding: EdgeInsetsDirectional.fromSTEB( + // 0.rpx, 0.rpx, 0.rpx, 0.rpx), + // onTap: () { + // print('点击了容器'); + // }, + // child: Container( + // child: Padding( + // padding: EdgeInsetsDirectional.fromSTEB( + // 40.rpx, 20.rpx, 40.rpx, 20.rpx), + // child: Row( + // mainAxisSize: MainAxisSize.max, + // mainAxisAlignment: + // MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.max, + // children: [ + // Text( + // '设置页.隐私协议'.tr, + // style: + // FlutterFlowTheme.of(context) + // .bodyMedium + // .override( + // fontFamily: 'Inter', + // color: themeController + // .currentColor.sc3, + // fontSize: AppConstants() + // .title_text_fontSize, + // letterSpacing: 0.0, + // ), + // ), + // ].divide(SizedBox(width: 22.rpx)), + // ), + // SvgPicture.asset( + // 'assets/img/icon/arrow_right.svg', + // width: 8.rpx, + // height: + // 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + // color: themeController + // .currentColor.sc3, + // ), + // ], + // ), + // ), + // ), + // ), + // ] + // .divide(SizedBox(height: 0.rpx)) + // .addToStart(SizedBox(height: 30.rpx)) + // .addToEnd(SizedBox(height: 30.rpx)), + // ), + // ), + // ), + // ), + SizedBox( + height: 30.rpx, + ), + Text( + "企业简介\n\n\n嘉兴太和信息技术有限公司成立于2013年,是一家以传感技术、室内定位技术和人工智能技术为基础的国家高新技术企业,AI非接触生命体征传感器、高精度室内外一体定位平台、AI视频分析系统、射频消融等技术成果,目前已经拥有30多类知识产权证书,多项专利技术处于行业领先水平。\n\n\n我司研发的“非接触式生命体征传感器”是一款基于BCG信号原理,通过检测人体心脏搏动引起的微小振动的传感器系统。传感器系统通过将人体微弱的心跳、呼吸信号转换未电信号,进行相关生命体征分析。该传感器可为用户提供高灵敏度和精确度检测结构,适用于需要非接触式、高分辨率的监测场景。该系统的硬件、软件及生产维护均由我司自主开发和管理,拥有完全自主知识产权,并已申请多项国家专利,可依据用户需求定制个性化方案。\n\n\n该产品置于床垫下方使用,全程完全无感。采集的体征数据通过睡眠健康管理平台实时显示用户的健康状态,并对每次的睡眠报告进行系统化归档管理,支持长期查询。一旦用户在使用过程中出现异常情况,系统可及时做出判断并反馈预警信息和建议。目前,心率监测的准确度可达97%以上,呼吸监测的准确度可达95%以上,其他生理指标的监测精度也显著优于同类产品。该产品主体材质均采用符合国家标准的环保材料,部分硅胶配件达到食品级安全标准。产品尺寸可根据需求进行定制,适用于单人床、双人床、婴儿床、椅子及枕头等多种场景。\n\n\n睡眠健康管理平台通过实时预警与远程管理,提升睡眠质量及慢病干预效率,助力养老院、月子中心、康复中心、智能寝具等行业降本增效,实现精准健康的科学管理。", + style: TextStyle( + fontSize: AppConstants().normal_text_fontSize, + color: themeController.currentColor.sc3), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/user/setting_page.dart b/lib/pages/user/setting_page.dart new file mode 100644 index 0000000..3721bf6 --- /dev/null +++ b/lib/pages/user/setting_page.dart @@ -0,0 +1,623 @@ +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/appConstants.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +class SettingPage extends StatefulWidget { + const SettingPage({super.key}); + + @override + State createState() => _SettingPageState(); +} + +class _SettingPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + ThemeController themeController = Get.find(); + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), // 本地图片 + fit: BoxFit.fill, // 填满整个 Container + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, // 加上这一行 + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + titleSpacing: 0, + // leading: returnIconButtom, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + /// 居中标题 + Text( + '设置页.标题'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + + /// 左边返回按钮 + Positioned( + left: 0, + child: returnIconButtom, + ), + ], + ), + ), + + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 30.rpx, 25.rpx, 30.rpx, 0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Color(0xFF242835), + borderRadius: BorderRadius.circular( + AppConstants().normal_container_radius), + ), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0, 0.rpx, 0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 0.rpx, 40.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 20.rpx, 0.rpx, 20.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '设置页.主题模式'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '深色', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: + Color(0xFFD9E3EB), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: 15 + .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ].divide(SizedBox(width: 28.rpx)), + ), + ], + ), + ), + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 0.rpx, 40.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 20.rpx, 0.rpx, 20.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '设置页.选择语言'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '中文', + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: + Color(0xFFD9E3EB), + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: 15 + .rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ].divide(SizedBox(width: 28.rpx)), + ), + ], + ), + ), + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + Get.toNamed("/aboutUsPage"); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 20.rpx, 40.rpx, 20.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '设置页.关于我们'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: + 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ], + ), + ), + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 0.rpx, 40.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 20.rpx, 0.rpx, 20.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '设置页.用户协议'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: + 15.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ], + ), + ), + ), + ), + ClickableContainer( + backgroundColor: Colors.transparent, // 容器背景色 + highlightColor: themeController + .currentColor.sc21, // 点击时的背景色 + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0.rpx), + onTap: () { + print('点击了容器'); + }, + child: Container( + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 40.rpx, 20.rpx, 40.rpx, 20.rpx), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Text( + '设置页.隐私协议'.tr, + style: + FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + color: themeController + .currentColor.sc3, + fontSize: AppConstants() + .title_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox(width: 22.rpx)), + ), + SvgPicture.asset( + 'assets/img/icon/arrow_right.svg', + width: 8.rpx, + height: + 14.rpx, // 如果 SVG 中没有固定颜色,可以这样设置 + color: themeController + .currentColor.sc3, + ), + ], + ), + ), + ), + ), + ] + .divide(SizedBox(height: 0.rpx)) + .addToStart(SizedBox(height: 30.rpx)) + .addToEnd(SizedBox(height: 30.rpx)), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 100.rpx, 360.rpx, 100.rpx, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () { + ApiResponse apiResponse = + userInfoController.logOut(); + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: apiResponse.code == HttpStatusCodes.ok + ? themeController.currentColor.sc2 + : themeController.currentColor.sc9, + ); + if (apiResponse.code == HttpStatusCodes.ok) { + Get.offAllNamed("/mianPageBottomChange"); + } + }, + colors: [ + themeController.currentColor.sc9, + ], // 渐变色是同一个色,也可以根据需要调整 + child: Container( + width: + // MediaQuery.sizeOf(context).width * 0.66, + bodySize.maxWidth, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '设置页.退出登录'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc3, + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 100.rpx, 20.rpx, 100.rpx, 0), + child: CustomCard( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () { + TopSlideNotification.show( + context, + text: "待开发功能".tr, + ); + }, + colors: [ + Colors.transparent, + ], // 渐变色是同一个色,也可以根据需要调整 + child: Container( + width: + // MediaQuery.sizeOf(context).width * 0.66, + bodySize.maxWidth, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '设置页.注销账号'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: themeController.currentColor.sc9, + fontFamily: 'Inter', + fontSize: + AppConstants().normal_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + width: 17.rpx, + )), + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 100.rpx, 0.rpx, 0), + child: ClickableContainer( + borderRadius: + AppConstants().button_container_radius, // 圆角半径 + onTap: () {}, + backgroundColor: + Colors.transparent, // 渐变色是同一个色,也可以根据需要调整 + highlightColor: themeController.currentColor.sc5, + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 0.rpx, 0.rpx, 0), + child: Container( + width: + // MediaQuery.sizeOf(context).width * 0.66, + bodySize.maxWidth, + height: MediaQuery.sizeOf(context).height * 0.055, + constraints: BoxConstraints( + minWidth: 500.rpx, + minHeight: 90.rpx, + ), + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'ICP备案号:浙ICP备2023000785号-1'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: + themeController.currentColor.sc4, + fontFamily: 'Inter', + fontSize: AppConstants() + .smaller_text_fontSize, + letterSpacing: 0.0, + ), + ), + Text( + 'Copyright © 202-2025 嘉兴太和信息技术有限责任公司 版权所有' + .tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + color: + themeController.currentColor.sc4, + fontFamily: 'Inter', + fontSize: AppConstants() + .smaller_text_fontSize, + letterSpacing: 0.0, + ), + ), + ].divide(SizedBox( + height: 17.rpx, + ))), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget _buildDeviceCard(BuildContext context, + {required String title, required String imageUrl, required String type}) { + return CustomCard( + borderRadius: 20.rpx, // 圆角大小 + onTap: () { + if (type != null) { + if (type == '1') { + Get.toNamed("/blueteethDevice"); + } + } + }, + + colors: [themeController.currentColor.sc17], // 背景色 + child: Container( + width: double.infinity, + height: MediaQuery.sizeOf(context).height * 0.135, + constraints: BoxConstraints( + minHeight: 220.rpx, + ), + padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Inter', + color: const Color(0xFFC2CED7), + fontSize: 30.rpx, + letterSpacing: 0.0, + ), + ), + ClipRRect( + borderRadius: BorderRadius.circular(8.rpx), + child: Image.asset( + imageUrl, + width: 212.rpx, + height: 168.rpx, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/user/update_user_page.dart b/lib/pages/user/update_user_page.dart new file mode 100644 index 0000000..f985560 --- /dev/null +++ b/lib/pages/user/update_user_page.dart @@ -0,0 +1,367 @@ +import 'dart:io'; + +import 'package:EasyDartModule/EasyDartModule.dart' as edm; +import 'package:ef/ef.dart'; +import 'package:flutter/material.dart'; +import 'package:flutterflow_ui/flutterflow_ui.dart'; +import 'package:vbvs_app/common/color/app_uri_status.dart'; +import 'package:vbvs_app/common/util/DailyLogUtils.dart'; +import 'package:vbvs_app/common/util/FitTool.dart'; +import 'package:vbvs_app/common/util/MyUtils.dart'; +import 'package:vbvs_app/component/tool/ClickableContainer.dart'; +import 'package:vbvs_app/component/tool/CustomCard.dart'; +import 'package:vbvs_app/component/tool/TopSlideNotification.dart'; +import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart'; +import 'package:vbvs_app/controller/main_bottom/global_controller.dart'; +import 'package:vbvs_app/controller/person/person_controller.dart'; +import 'package:vbvs_app/controller/theme_controller/ThemeController.dart'; +import 'package:vbvs_app/controller/user_info_controller.dart'; +import 'package:vbvs_app/model/api_response.dart'; + +class UpdateUserPage extends StatefulWidget { + const UpdateUserPage({super.key}); + + @override + State createState() => _UpdateUserPageState(); +} + +class _UpdateUserPageState extends State { + GlobalController globalController = Get.find(); + UserInfoController userInfoController = Get.find(); + BlueteethBindController blueteethBindController = Get.find(); + PersonController personController = Get.find(); + ThemeController themeController = Get.find(); + + @override + void initState() { + super.initState(); + userInfoController.model.user!.tmpHead = null; + userInfoController.model.user!.tmpNickName = null; + userInfoController.model.user!.tmpHead = + userInfoController.model.user!.avatar; + userInfoController.model.user!.tmpNickName = + userInfoController.model.user!.nick_name; + } + + @override + Widget build(BuildContext context) { + int login = userInfoController.model.login!; + return LayoutBuilder( + builder: (context, bodySize) => GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/img/bgNoImg.png'), + fit: BoxFit.fill, + ), + ), + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + backgroundColor: themeController.currentColor.sc17, + automaticallyImplyLeading: false, + iconTheme: IconThemeData( + color: themeController.currentColor.sc3, + ), + titleSpacing: 0, + title: Container( + width: double.infinity, + height: 180.rpx, + child: Stack( + alignment: Alignment.center, + children: [ + Text( + '修改资料页.个人信息'.tr, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: 'Readex Pro', + color: themeController.currentColor.sc3, + letterSpacing: 0, + fontSize: 30.rpx, + ), + ), + Positioned( + left: 0, + child: returnIconButtom, + ), + Positioned( + right: 20.rpx, + child: CustomCard( + borderRadius: 20.rpx, + onTap: () async { + ApiResponse apiResponse = + await userInfoController.updateData(); + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: apiResponse.code == HttpStatusCodes.ok + ? themeController.currentColor.sc2 + : themeController.currentColor.sc9, + ); + if (apiResponse.code == HttpStatusCodes.ok) { + userInfoController.model.user!.tmpHead = null; + userInfoController.model.user!.tmpNickName = null; + await userInfoController.getUserInfo(); + Get.back(); + } + }, + colors: [ + themeController.currentColor.sc1, + themeController.currentColor.sc2, + ], + child: Container( + width: 100.rpx, + height: 60.rpx, + alignment: Alignment.center, + padding: EdgeInsetsDirectional.fromSTEB( + 16.rpx, 0.rpx, 16.rpx, 0.rpx), + child: Text( + '修改资料页.保存'.tr, + style: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Inter Tight', + color: themeController.currentColor.sc3, + letterSpacing: 0.0, + ), + ), + ), + ), + ), + ], + ), + ), + actions: [], + centerTitle: false, + ), + body: SafeArea( + top: true, + child: Container( + width: double.infinity, + height: double.infinity, + decoration: BoxDecoration(), + child: Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 60.rpx, 0.rpx, 60.rpx, 0.rpx), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 76.rpx, 0.rpx, 0.rpx), + child: Container( + width: MediaQuery.sizeOf(context).width * 0.213, + height: MediaQuery.sizeOf(context).height * 0.098, + constraints: BoxConstraints( + minWidth: 160.rpx, + minHeight: 160.rpx, + ), + decoration: BoxDecoration(), + child: Obx(() { + return getImageWidget(context); + })), + ), + ClickableContainer( + backgroundColor: Colors.transparent, + highlightColor: themeController.currentColor.sc2, + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 44.rpx, 0.rpx, 44.rpx), + borderRadius: 0, + onTap: () async { + edm.EasyDartModule.logger + .info("${userInfoController.model.user!}点击上传头像"); + DailyLogUtils.writeLog( + "${userInfoController.model.user!}点击上传头像"); + ApiResponse apiResponse = + await userInfoController.uploadImg(); + TopSlideNotification.show( + context, + text: apiResponse.msg!, + textColor: apiResponse.code == HttpStatusCodes.ok + ? themeController.currentColor.sc2 + : themeController.currentColor.sc9, + ); + }, + child: Center( + child: Text( + '修改资料页.点击更换头像'.tr, + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController.currentColor.sc2, + ), + ), + ), + ), + Padding( + padding: EdgeInsetsDirectional.fromSTEB( + 0.rpx, 79.rpx, 0.rpx, 0.rpx), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border( + top: BorderSide( + color: Color(0xFFF8F5F5), + width: 1.rpx, + ), + bottom: BorderSide( + color: Color(0xFFF8F5F5), + width: 1.rpx, + ), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Container( + width: 100.rpx, + height: 100.rpx, + decoration: BoxDecoration(), + child: Align( + alignment: AlignmentDirectional(0, 0), + child: TextFormField( + initialValue: userInfoController + .model.user!.nick_name, + onChanged: (value) { + userInfoController.model.user! + .tmpNickName = value; + }, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + ), + hintText: '未命名'.tr, + hintStyle: + FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc4, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Color(0x00000000), + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + focusedErrorBorder: + OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of( + context) + .error, + width: 1.rpx, + ), + borderRadius: + BorderRadius.circular( + 8.rpx), + ), + filled: false, + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: 'Inter', + fontSize: 26.rpx, + letterSpacing: 0.0, + color: themeController + .currentColor.sc3, + ), + textAlign: TextAlign.center, + cursorColor: + FlutterFlowTheme.of(context) + .primaryText, + ), + ), + ), + ), + ].divide(SizedBox(width: 27.rpx)), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + ); + } + + Widget getImageWidget(BuildContext context) { + try { + UserInfoController controller = Get.find(); + var head = controller.model.user!.tmpHead; + return Container( + width: 200.rpx, + height: 200.rpx, + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + ), + child: head == null || head.isEmpty + ? Image.asset( + 'assets/img/avatar.png', + fit: BoxFit.cover, + ) + : Image.network( + head, + fit: BoxFit.cover, + ), + ), + ); + } catch (e) { + print(e); + } + return Container(); + } +} diff --git a/lib/routers/routers.dart b/lib/routers/routers.dart index 04e8522..6ccae42 100644 --- a/lib/routers/routers.dart +++ b/lib/routers/routers.dart @@ -1,7 +1,13 @@ import 'package:flutter/cupertino.dart'; +import 'package:vbvs_app/pages/device/BodyDeviceWidget.dart'; +import 'package:vbvs_app/pages/device/device_detail.dart'; +import 'package:vbvs_app/pages/device/instant_body_page.dart'; +import 'package:vbvs_app/pages/device_bind/MobileScannerTestPage.dart'; import 'package:vbvs_app/pages/device_bind/bind_device_success.dart'; import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart'; +import 'package:vbvs_app/pages/device_bind/device_share_page.dart'; import 'package:vbvs_app/pages/device_bind/device_type.dart'; +import 'package:vbvs_app/pages/device_bind/device_type_list.dart'; import 'package:vbvs_app/pages/device_bind/wifi_page.dart'; import 'package:vbvs_app/pages/login/login.dart'; import 'package:vbvs_app/pages/login/other_login.dart'; @@ -10,8 +16,9 @@ import 'package:vbvs_app/pages/main_bottom/home_page.dart'; import 'package:vbvs_app/pages/main_bottom/main_page_bottom_change.dart'; import 'package:vbvs_app/pages/main_bottom/message_page.dart'; import 'package:vbvs_app/pages/main_bottom/mine_page.dart'; -import 'package:vbvs_app/pages/main_bottom/sleep_report_page.dart'; import 'package:vbvs_app/pages/person/person_page.dart'; +import 'package:vbvs_app/pages/person/update_person_page.dart'; +import 'package:vbvs_app/pages/sleep_report/sleep_report_page.dart'; import 'package:vbvs_app/pages/user/about_us_page.dart'; import 'package:vbvs_app/pages/user/setting_page.dart'; import 'package:vbvs_app/pages/user/update_user_page.dart'; @@ -21,22 +28,28 @@ import 'package:vbvs_app/pages/user/update_user_page.dart'; var routes = { "/homePage": (contxt) => HomePage(), - "/sleepReportPage": (contxt) => SleepReportPage(), + "/sleepReportPage": (contxt,{arguments}) => SleepReportPage(sleepUri:arguments), "/ePage": (contxt) => EPage(), "/messagePage": (contxt) => MessagePage(), "/minePage": (contxt) => MinePage(), "/mianPageBottomChange": (contxt) => MainPageBottomChange(), "/loginPage": (contxt) => LoginPage(), - "/deviceType": (contxt, {arguments}) => DeviceTypePage(type: arguments??0,), + "/deviceType": (contxt, {arguments}) => DeviceTypePage(), "/blueteethDevice": (contxt) => BlueteethDevicePage(), "/personPage": (contxt) => PersonPage(), "/bindDeviceSuccess": (contxt) => BindDeviceSuccess(), - "/wifiPage": (contxt, {arguments}) => WifiPage(bleDevice: arguments), - // "/wifiPage": (contxt) => WifiPage(), + "/wifiPage": (contxt) => WifiPage(), "/otherLoginPage": (contxt) => OtherLoginPage(), "/updateUserPage": (contxt) => UpdateUserPage(), "/settingPage": (contxt) => SettingPage(), "/aboutUsPage": (contxt) => AboutUsPage(), + "/bodyDevice": (contxt) => BodyDeviceWidget(), + "/deviceTypeList": (contxt) => DeviceTypeListPage(), + "/deviceDetail": (contxt,{arguments}) => DeviceDetailPage(device:arguments), + "/instantBodyPage": (contxt,{arguments}) => InstantBodyPage(personInfo:arguments), + "/qrView": (contxt) => MobileScannerTestPage(), + "/updatePersonPage": (contxt) => UpdatePersonPage(), + "/deviceSharePage": (contxt,{arguments}) => DeviceSharePage(device:arguments), }; //2、配置onGenerateRoute 固定写法 这个方法也相当于一个中间件,这里可以做权限判断 diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index c81b8d5..eab6f5c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,8 +9,10 @@ import app_links import device_info_plus import file_picker import flutter_blue_plus +import flutter_inappwebview_macos import flutter_localization import flutter_web_auth_2 +import mobile_scanner import package_info_plus import path_provider_foundation import rive_common @@ -25,8 +27,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin")) + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin")) FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin")) + MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) RivePlugin.register(with: registry.registrar(forPlugin: "RivePlugin")) diff --git a/pubspec.lock b/pubspec.lock index 6e9c514..0c2057e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -596,6 +596,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.5.2" + flutter_barcode_scanner: + dependency: "direct main" + description: + name: flutter_barcode_scanner + sha256: a4ba37daf9933f451a5e812c753ddd045d6354e4a3280342d895b07fecaab3fa + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.0" flutter_blue_plus: dependency: "direct main" description: @@ -628,6 +636,70 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.1.0" + flutter_inappwebview: + dependency: "direct main" + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" flutter_lints: dependency: "direct dev" description: @@ -677,10 +749,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.17" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -784,10 +856,10 @@ packages: dependency: transitive description: name: google_identity_services_web - sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9" + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.3" + version: "0.3.3+1" googleapis_auth: dependency: transitive description: @@ -832,10 +904,10 @@ packages: dependency: transitive description: name: html - sha256: "9475be233c437f0e3637af55e7702cbbe5c23a68bd56e8a5fa2d426297b7c6c8" + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" url: "https://pub.flutter-io.cn" source: hosted - version: "0.15.5+1" + version: "0.15.6" http: dependency: transitive description: @@ -876,6 +948,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" + img_picker: + dependency: "direct main" + description: + name: img_picker + sha256: "289796ebc667d1031e944c82709f5c67f06a9698ba4538baf5e002f10e562c7c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.2" + img_picker_android: + dependency: transitive + description: + name: img_picker_android + sha256: "930be3e686b164676383d8775f818affa883f88191c42af983b1a781996a3589" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" + img_picker_ios: + dependency: transitive + description: + name: img_picker_ios + sha256: bde838d7a9fcafcdde6183c701a31d6b846b7b761714ed3438b3f25ac406ac55 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" + img_picker_platform_interface: + dependency: transitive + description: + name: img_picker_platform_interface + sha256: b33367f332f1ad9f7174a4e02b8932376c15a79d5d3aef34cca139832558e2bd + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.0" intl: dependency: transitive description: @@ -1076,6 +1180,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.5.7" + mobile_scanner: + dependency: "direct main" + description: + name: mobile_scanner + sha256: f536c5b8cadcf73d764bdce09c94744f06aa832264730f8971b21a60c5666826 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.0.10" mongo_dart: dependency: transitive description: @@ -1429,6 +1541,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" rational: dependency: transitive description: @@ -1915,7 +2035,7 @@ packages: source: hosted version: "3.1.4" uuid: - dependency: transitive + dependency: "direct main" description: name: uuid sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff diff --git a/pubspec.yaml b/pubspec.yaml index c5b3be2..2fad222 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,7 @@ environment: sdk: ^3.5.4 dependencies: + qr_flutter: ^4.1.0 flutter: sdk: flutter ef: @@ -38,8 +39,13 @@ dependencies: # url: http:///enchantedwyf@git.it.real.he-info.cn:90/r/easydevice.git # ref: main localstorage: ^5.0.0 - + img_picker: ^1.0.2 + uuid: ^4.5.1 + mobile_scanner: ^6.0.10 + flutter_barcode_scanner: ^2.0.0 + flutter_inappwebview: ^6.1.5 + dev_dependencies: flutter_test: sdk: flutter diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 7c21b19..7600291 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -16,6 +17,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { AppLinksPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); FlutterLocalizationPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterLocalizationPluginCApi")); PermissionHandlerWindowsPluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 0d113fb..df6b0cf 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST app_links + flutter_inappwebview_windows flutter_localization permission_handler_windows rive_common