更新分享
@@ -33,13 +33,15 @@
|
||||
<!-- <!–读取外部存储权限–>-->
|
||||
<!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />-->
|
||||
<!--相机权限-->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
<application
|
||||
android:usesCleartextTraffic="true"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="太和e护">
|
||||
android:label="太和e护"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
|
||||
BIN
assets/img/avatar.png
Normal file
|
After Width: | Height: | Size: 862 B |
BIN
assets/img/body_black.gif
Normal file
|
After Width: | Height: | Size: 814 KiB |
BIN
assets/img/body_white.gif
Normal file
|
After Width: | Height: | Size: 505 KiB |
1
assets/img/icon/arrow_down.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.63 7.32"><defs><style>.cls-1{fill:#fff;}</style></defs><title>资源 249 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M6.82,7.32a.51.51,0,0,1-.36-.15L.15.85a.48.48,0,0,1,0-.7.48.48,0,0,1,.7,0l6,6,6-6a.48.48,0,0,1,.7,0,.48.48,0,0,1,0,.7L7.17,7.17A.5.5,0,0,1,6.82,7.32Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 404 B |
21
assets/img/icon/bed_status.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34.46 29.97">
|
||||
<title>资源 239</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<line x1="3.59" y1="28.97" x2="32.17" y2="28.97"
|
||||
stroke="#85f5ff"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
fill="none" />
|
||||
<path d="M14.41,8.67a1,1,0,0,1-.71-.29l-6-6-6,6a1,1,0,0,1-1.42,0A1,1,0,0,1,.29,7L7,.29a1,1,0,0,1,1.41,0L15.11,7a1,1,0,0,1-.7,1.71Z"
|
||||
fill="#85f5ff" />
|
||||
<path d="M7.7,19.3a1,1,0,0,1-1-1V1a1,1,0,1,1,2,0V18.3A1,1,0,0,1,7.7,19.3Z"
|
||||
fill="#85f5ff" />
|
||||
<path d="M26.75,19.3a1,1,0,0,1-.7-.29l-6.71-6.67a1,1,0,0,1,0-1.42,1,1,0,0,1,1.41,0l6,6,6-6a1,1,0,1,1,1.41,1.42L27.46,19A1,1,0,0,1,26.75,19.3Z"
|
||||
fill="#85f5ff" />
|
||||
<path d="M26.75,19.3a1,1,0,0,1-1-1V1a1,1,0,0,1,2,0V18.3A1,1,0,0,1,26.75,19.3Z"
|
||||
fill="#85f5ff" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 988 B |
11
assets/img/icon/bodymotion.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27.29 33.07">
|
||||
<title>资源 238</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path d="M1,33.07a1,1,0,0,1-.71-1.71l7-7.06-7-7.06a1,1,0,0,1,0-1.41l7-7.06-7-7.06A1,1,0,0,1,.3.29,1,1,0,0,1,1.71.3L9.43,8.06a1,1,0,0,1,0,1.41l-7,7.06,7,7.07a1,1,0,0,1,0,1.41L1.71,32.77A1,1,0,0,1,1,33.07Z"
|
||||
fill="#64c961"/>
|
||||
<path d="M26.29,33.07a1,1,0,0,1-.71-.3L17.85,25a1,1,0,0,1,0-1.41l7-7.07-7-7.06a1,1,0,0,1,0-1.41L25.58.3A1,1,0,0,1,27,.29a1,1,0,0,1,0,1.42L20,8.77l7,7.06a1,1,0,0,1,0,1.41L20,24.3l7,7.06a1,1,0,0,1-.71,1.71Z"
|
||||
fill="#64c961"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 685 B |
13
assets/img/icon/breathe.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 27.98">
|
||||
<title>资源 241</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path d="M23.9,8.63a11.1,11.1,0,0,1-8-3.61C9.17-1.69,2,4.77,1.68,5.05A1,1,0,1,1,.32,3.58c.09-.08,8.92-8,17,0,6.71,6.71,13.9.25,14.2,0a1,1,0,1,1,1.36,1.47C32.8,5.1,28.88,8.63,23.9,8.63Z"
|
||||
fill="#85f5ff"/>
|
||||
<path d="M23.9,18.3a11.13,11.13,0,0,1-8-3.6C9.17,8,2,14.45,1.68,14.72a1,1,0,0,1-1.42-.05,1,1,0,0,1,.06-1.42c.09-.08,8.92-8,17,0,6.71,6.72,13.9.25,14.2,0a1,1,0,1,1,1.36,1.47C32.8,14.78,28.88,18.3,23.9,18.3Z"
|
||||
fill="#85f5ff"/>
|
||||
<path d="M23.9,28a11.1,11.1,0,0,1-8-3.61h0c-6.69-6.69-13.9-.25-14.2,0a1,1,0,0,1-1.42-.06,1,1,0,0,1,.06-1.41c.09-.08,8.92-8,17,0h0c6.71,6.71,13.9.25,14.2,0a1,1,0,1,1,1.36,1.47C32.8,24.45,28.88,28,23.9,28Z"
|
||||
fill="#85f5ff"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 907 B |
15
assets/img/icon/breathe_pause.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 33.18 33.11">
|
||||
<title>资源 237</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path d="M23.9,11.19a11.13,11.13,0,0,1-8-3.6C9.17.87,2,7.34,1.68,7.62A1,1,0,0,1,.26,7.56,1,1,0,0,1,.32,6.14c.09-.08,8.92-8,17,0,6.71,6.72,13.9.25,14.2,0a1,1,0,0,1,1.36,1.48C32.8,7.67,28.88,11.19,23.9,11.19Z"
|
||||
fill="#ff7159"/>
|
||||
<path d="M23.9,20.87a11.1,11.1,0,0,1-8-3.61C9.17,10.55,2,17,1.68,17.29A1,1,0,1,1,.32,15.82c.09-.08,8.92-8,17,0,6.71,6.71,13.9.25,14.2,0a1,1,0,1,1,1.36,1.47C32.8,17.34,28.88,20.87,23.9,20.87Z"
|
||||
fill="#ff7159"/>
|
||||
<path d="M23.9,30.54a11.13,11.13,0,0,1-8-3.6h0C9.19,20.25,2,26.69,1.68,27a1,1,0,0,1-1.42-.06,1,1,0,0,1,.06-1.42c.09-.08,8.92-8,17,0h0c6.71,6.72,13.9.25,14.2,0A1,1,0,0,1,32.86,27C32.8,27,28.88,30.54,23.9,30.54Z"
|
||||
fill="#ff7159"/>
|
||||
<path d="M7.2,33.11A1.07,1.07,0,0,1,6.68,33a1,1,0,0,1-.34-1.38L25.13.48A1,1,0,0,1,26.5.14a1,1,0,0,1,.34,1.38L8.05,32.63A1,1,0,0,1,7.2,33.11Z"
|
||||
fill="#ff7159"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
assets/img/icon/device_issue.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26">
|
||||
<title>资源 251</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path fill="#ff7159" d="M13,0A13,13,0,1,0,26,13,13,13,0,0,0,13,0Zm0,20.8a1.33,1.33,0,1,1,1.33-1.33A1.33,1.33,0,0,1,13,20.8ZM14.44,6l-.27,9.46a1,1,0,0,1-1.12.88h-.21a1,1,0,0,1-1.11-.88L11.44,6a1.35,1.35,0,0,1,1.37-1.39h.27A1.34,1.34,0,0,1,14.44,6Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 454 B |
1
assets/img/icon/group.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27.01 23.24"><defs><style>.cls-1{fill:#fff;}</style></defs><title>资源 248 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M199.81,18.84a6.5,6.5,0,1,0-9.13-9,6.78,6.78,0,0,0-.94,1.86v0a4.07,4.07,0,0,1,1.87,1,4.54,4.54,0,0,1,.91-1.9A4.45,4.45,0,1,1,196,18.06a4.34,4.34,0,0,1-1.09-.14h0a4.27,4.27,0,0,1-1.88-1,4.52,4.52,0,0,1-.9,1.9s0,0,0,0l0,0a4.51,4.51,0,0,1-2.38,1.45l-.19.17a9.47,9.47,0,0,0-1.74,1.94l-.09.14a8,8,0,0,1,1-.07,7.15,7.15,0,0,1,1.55.16,3.24,3.24,0,0,1,.49-.49,7,7,0,0,1,1.47-1.07,6.75,6.75,0,0,1,1.06-.48,7.79,7.79,0,0,1,2.74-.5,7.87,7.87,0,0,1,7.81,7,1,1,0,0,0,1,.87,1,1,0,0,0,1-1.16,10,10,0,0,0-6-8Z" transform="translate(-178.79 -7.12)"/><path class="cls-1" d="M192.51,21.22a5.09,5.09,0,0,0,.71-.61,7.06,7.06,0,0,0,.71-.83,6.71,6.71,0,0,0,.85-1.64c0-.07.05-.14.08-.22h0a6.37,6.37,0,0,0,.29-1.92,6.48,6.48,0,0,0-6.06-6.48,6.56,6.56,0,0,0-6.81,5.19,6.5,6.5,0,0,0,2.51,6.52,9.93,9.93,0,0,0-6,8,1,1,0,0,0,1,1.15,1,1,0,0,0,1-.86,7.87,7.87,0,0,1,6.82-6.93,8.26,8.26,0,0,1,1-.07,7.06,7.06,0,0,1,1.54.16,7.87,7.87,0,0,1,6.27,6.84,1,1,0,0,0,1,.87h0a1,1,0,0,0,1-1.16,10,10,0,0,0-6-8Zm-8-6.79a4.36,4.36,0,0,1,4.41-2.87,3.51,3.51,0,0,1,.86.13h0a4.16,4.16,0,0,1,1.87,1A4.44,4.44,0,0,1,193.09,16a4.08,4.08,0,0,1-.11.93,4.5,4.5,0,0,1-.9,1.91s0,0,0,0a4.57,4.57,0,0,1-2.38,1.45,4.31,4.31,0,0,1-1,.12,4.46,4.46,0,0,1-4.18-6Z" transform="translate(-178.79 -7.12)"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
15
assets/img/icon/heart.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37.32 32.47">
|
||||
<title>资源 242</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path
|
||||
d="M18.49,4.76l.15.15.15-.15C23.61.2,29.14.09,33,3.83L33.1,4a10.08,10.08,0,0,1,.11,14.37L19.63,30.93a1.45,1.45,0,0,1-2,0L4.07,18.32l-.05,0a10.16,10.16,0,0,1,.2-14.35C8,.06,13.63.14,18.49,4.76Z"
|
||||
fill="none"
|
||||
stroke="#ff7159"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2.3"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 562 B |
8
assets/img/icon/in_bed.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.48 35.04">
|
||||
<title>资源 253</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path fill="#00c1aa" d="M7.19,6.57a3.33,3.33,0,0,0,3.39-3.28A3.33,3.33,0,0,0,7.19,0,3.26,3.26,0,0,0,3.9,3.29,3.25,3.25,0,0,0,7.19,6.57Zm5,1.09H2A1.86,1.86,0,0,0,0,8.76v9.91a1,1,0,0,0,1.07,1s1.15,0,1.15-1.07v-6s.64-1.37,1.12.07V33.94A1,1,0,0,0,4.42,35H5.57a1.08,1.08,0,0,0,1.09-1.1V24.07a.62.62,0,0,1,.63-.56s.51.2.51.58V34a1,1,0,0,0,1.11,1H10s1.12-.07,1.12-1V12.59s.4-1.26,1.11,0l0,6.07S12,19.7,13.45,19.7a1,1,0,0,0,1-1V8.75a2.46,2.46,0,0,0-2.31-1.09Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 665 B |
1
assets/img/icon/loading.json
Normal file
1
assets/img/icon/not_bed.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.48 35.04"><defs><style>.cls-1{fill:#fff;opacity:0.2;}</style></defs><title>资源 247 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M7.19,6.57a3.33,3.33,0,0,0,3.39-3.28A3.33,3.33,0,0,0,7.19,0,3.26,3.26,0,0,0,3.9,3.29,3.25,3.25,0,0,0,7.19,6.57Zm5,1.09H2A1.86,1.86,0,0,0,0,8.76v9.91a1,1,0,0,0,1.07,1s1.15,0,1.15-1.07v-6s.64-1.37,1.12.07V33.94A1,1,0,0,0,4.42,35H5.57a1.08,1.08,0,0,0,1.09-1.1V24.07a.62.62,0,0,1,.63-.56s.51.2.51.58V34a1,1,0,0,0,1.11,1H10s1.12-.07,1.12-1V12.59s.4-1.26,1.11,0l0,6.07S12,19.7,13.45,19.7a1,1,0,0,0,1-1V8.75a2.46,2.46,0,0,0-2.31-1.09Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 695 B |
1
assets/img/icon/nulldata.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54.49 68.11"><defs><style>.cls-1{opacity:0.4;}.cls-2{fill:#fff;opacity:0.6;}</style></defs><title>资源 292 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><g class="cls-1"><path class="cls-2" d="M34.05,0H6.81A6.81,6.81,0,0,0,0,6.81H0V61.3a6.81,6.81,0,0,0,6.81,6.81H47.67a6.81,6.81,0,0,0,6.82-6.81V20.43Zm-3.4,5.11L49.38,23.84H30.65V5.11Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 451 B |
1
assets/img/icon/refresh.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.95 28.97"><defs><style>.cls-1{fill:#fff;}</style></defs><title>资源 232 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M24.32,3.81V2a1.21,1.21,0,0,1,.33-.87,1.22,1.22,0,0,1,1.7,0,1.28,1.28,0,0,1,.37.87V7.54a1.31,1.31,0,0,1-1.2,1.2H19.91A1.28,1.28,0,0,1,19,8.37a1.22,1.22,0,0,1,0-1.7,1.12,1.12,0,0,1,.87-.33h3.52A12.05,12.05,0,0,0,8.76,3.9,11.85,11.85,0,0,0,3.44,9.63a12.14,12.14,0,0,0-.64,7.86A11.93,11.93,0,0,0,7.08,24a12.19,12.19,0,0,0,7.42,2.55A12,12,0,0,0,23,23.06a12.27,12.27,0,0,0,3.5-7.56,1.2,1.2,0,0,1,1.2-1.1h0A1.22,1.22,0,0,1,29,15.71,14.5,14.5,0,1,1,2.34,6.57,14.2,14.2,0,0,1,8.56,1.24a14.43,14.43,0,0,1,8.28-1,14.7,14.7,0,0,1,7.46,3.6Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 784 B |
1
assets/img/icon/share.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.06 28.06"><defs><style>.cls-1{fill:#fff;}</style></defs><title>资源 285 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M5.36,28.06A5.36,5.36,0,0,1,0,22.7V5.36A5.36,5.36,0,0,1,5.36,0H14a.81.81,0,1,1,0,1.62H5.36A3.75,3.75,0,0,0,1.62,5.36V22.7a3.75,3.75,0,0,0,3.74,3.75H22.7a3.75,3.75,0,0,0,3.75-3.75V13.88a.81.81,0,0,1,1.61,0V22.7a5.36,5.36,0,0,1-5.36,5.36Z"/><path class="cls-1" d="M13.19,15.71a.83.83,0,0,1-.58-.24.82.82,0,0,1,0-1.15L25.5,1.62H19.24a.81.81,0,1,1,0-1.62h7.88a.94.94,0,0,1,.94,1V8.82a.81.81,0,1,1-1.61,0V3L13.76,15.48A.83.83,0,0,1,13.19,15.71Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 695 B |
1
assets/img/icon/signal0.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.57 22.17"><defs><style>.cls-1{fill:#fff;opacity:0.2;}</style></defs><title>资源 246 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><rect class="cls-1" y="16.63" width="6.01" height="5.54"/><rect class="cls-1" x="7.85" y="11.09" width="6.01" height="11.09"/><rect class="cls-1" x="15.71" y="4.62" width="6.01" height="17.55"/><rect class="cls-1" x="23.56" width="6.01" height="22.17"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 495 B |
1
assets/img/icon/signal1.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.57 22.17"><defs><style>.cls-1{fill:#00c1aa;}.cls-2{fill:#fff;opacity:0.2;}</style></defs><title>资源 245 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><rect class="cls-1" y="16.63" width="6.01" height="5.54"/><rect class="cls-2" x="7.85" y="11.09" width="6.01" height="11.09"/><rect class="cls-2" x="15.71" y="4.62" width="6.01" height="17.55"/><rect class="cls-2" x="23.56" width="6.01" height="22.17"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 516 B |
1
assets/img/icon/signal2.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.57 22.17"><defs><style>.cls-1{fill:#00c1aa;}.cls-2{fill:#fff;opacity:0.2;}</style></defs><title>资源 244 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><rect class="cls-1" y="16.63" width="6.01" height="5.54"/><rect class="cls-1" x="7.85" y="11.09" width="6.01" height="11.09"/><rect class="cls-2" x="15.71" y="4.62" width="6.01" height="17.55"/><rect class="cls-2" x="23.56" width="6.01" height="22.17"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 516 B |
1
assets/img/icon/signal3.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.57 22.17"><defs><style>.cls-1{fill:#00c1aa;}.cls-2{fill:#fff;opacity:0.2;}</style></defs><title>资源 243 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><rect class="cls-1" y="16.63" width="6.01" height="5.54"/><rect class="cls-1" x="7.85" y="11.09" width="6.01" height="11.09"/><rect class="cls-1" x="15.71" y="4.62" width="6.01" height="17.55"/><rect class="cls-2" x="23.56" width="6.01" height="22.17"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 516 B |
1
assets/img/icon/singal4.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1745314396634" class="icon" viewBox="0 0 1365 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3510" xmlns:xlink="http://www.w3.org/1999/xlink" width="266.6015625" height="200"><path d="M0 767.769161h277.467989v255.769162H0zM362.416592 512h277.467989v512H362.416592zM725.29486 213.29486h277.467989v810.243463h-277.467989zM1087.711452 0h277.467989v1023.538323h-277.467989z" fill="#00C1AA" p-id="3511"></path></svg>
|
||||
|
After Width: | Height: | Size: 568 B |
19
assets/img/icon/snore.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34.71 31.32">
|
||||
<title>资源 240</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path
|
||||
d="M15.38,31.32H7.29a1,1,0,0,1-.65-1.77l7.58-6.32H5.78a1,1,0,0,1,0-2H17A1,1,0,0,1,17.62,23L10,29.32h5.34a1,1,0,0,1,0,2Z"
|
||||
fill="#8e7def"
|
||||
/>
|
||||
<path
|
||||
d="M31.91,20.47H22.78a1,1,0,0,1-.94-.66,1,1,0,0,1,.3-1.11L31,11.35H21.08a1,1,0,0,1,0-2H33.71a1,1,0,0,1,.94.65,1,1,0,0,1-.3,1.11l-8.81,7.36h6.37a1,1,0,0,1,0,2Z"
|
||||
fill="#8e7def"
|
||||
/>
|
||||
<path
|
||||
d="M16.86,15.37H3.49a1,1,0,0,1-.94-.66,1,1,0,0,1,.3-1.11L16.75,2H1A1,1,0,0,1,1,0H19.5a1,1,0,0,1,.94.66,1,1,0,0,1-.29,1.11L6.25,13.37H16.86a1,1,0,0,1,0,2Z"
|
||||
fill="#8e7def"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 796 B |
1
assets/img/icon/tips.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.73 21.73"><defs><style>.cls-1{opacity:0.6;}.cls-2{fill:#fff;}</style></defs><title>资源 236 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><g class="cls-1"><path class="cls-2" d="M10.86,14.93a.91.91,0,1,0,.91.91A.91.91,0,0,0,10.86,14.93Z"/><path class="cls-2" d="M10.86,5a.91.91,0,0,0-.91.91v7a.91.91,0,1,0,1.82,0v-7A.9.9,0,0,0,10.86,5Z"/><path class="cls-2" d="M10.86,0A10.87,10.87,0,1,0,21.73,10.86,10.87,10.87,0,0,0,10.86,0Zm9,10.86a9,9,0,1,1-9-9A9,9,0,0,1,19.9,10.86Z"/></g></g></g></svg>
|
||||
|
After Width: | Height: | Size: 589 B |
8
assets/img/icon/upgrade.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32.38 23.89">
|
||||
<title>资源 250</title>
|
||||
<g id="图层_2" data-name="图层 2">
|
||||
<g id="图层_1-2" data-name="图层 1">
|
||||
<path fill="#10cff1" d="M25.06,6.83a10.33,10.33,0,0,0-19.94,2A7.81,7.81,0,0,0,0,16.22a7.62,7.62,0,0,0,7.67,7.67h6.51V16.77H9.94a.25.25,0,0,1-.19-.39l6.08-6.76a.42.42,0,0,1,.68,0l6.09,6.76a.23.23,0,0,1-.19.39H18.2v7.12h6.5a8.66,8.66,0,0,0,7.68-8.54A8.5,8.5,0,0,0,25.06,6.83Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 487 B |
1
assets/img/icon/wifi1.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.14 28.69"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#f3f5f6;opacity:0.06;}</style></defs><title>资源 226 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M19.07,28.69a2.16,2.16,0,0,1,0-4.32,2.16,2.16,0,0,1,0,4.32Z"/><path class="cls-2" d="M25.11,21.61a1.65,1.65,0,0,1-1-.41A7.53,7.53,0,0,0,14,21.2a1.52,1.52,0,0,1-2.11-.1A1.51,1.51,0,0,1,12,19a10.53,10.53,0,0,1,14.1-.06,1.57,1.57,0,0,1-1,2.68Z"/><path class="cls-2" d="M7.57,16.16a1.41,1.41,0,0,1-1.05-.45,1.68,1.68,0,0,1-.46-1.21,1.36,1.36,0,0,1,.44-1A18.07,18.07,0,0,1,19,8.58h.12a18.4,18.4,0,0,1,12.57,4.95,1.44,1.44,0,0,1,.05,2.1,1.63,1.63,0,0,1-1.09.52h0a1.79,1.79,0,0,1-1.1-.42,15.21,15.21,0,0,0-20.86,0A1.6,1.6,0,0,1,7.57,16.16Z"/><path class="cls-2" d="M36.62,10.1a1.84,1.84,0,0,1-1.09-.41A23.44,23.44,0,0,0,19.08,3,23.75,23.75,0,0,0,2.58,9.64a1.53,1.53,0,0,1-2.18,0A1.55,1.55,0,0,1,0,8.58a1.44,1.44,0,0,1,.45-1,26.81,26.81,0,0,1,37.23-.07,1.51,1.51,0,0,1,0,2.12,1.43,1.43,0,0,1-1.08.5Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
assets/img/icon/wifi2.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.14 28.69"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#f3f5f6;opacity:0.06;}</style></defs><title>资源 227 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M19.07,28.69a2.16,2.16,0,0,1,0-4.32,2.16,2.16,0,0,1,0,4.32Zm6-7.08a1.65,1.65,0,0,1-1-.41A7.53,7.53,0,0,0,14,21.2a1.52,1.52,0,0,1-2.11-.1A1.51,1.51,0,0,1,12,19a10.53,10.53,0,0,1,14.1-.06,1.57,1.57,0,0,1-1,2.68Z"/><path class="cls-2" d="M7.57,16.16a1.41,1.41,0,0,1-1.05-.45,1.68,1.68,0,0,1-.46-1.21,1.36,1.36,0,0,1,.44-1A18.07,18.07,0,0,1,19,8.58h.12a18.4,18.4,0,0,1,12.57,4.95,1.44,1.44,0,0,1,.05,2.1,1.63,1.63,0,0,1-1.09.52h0a1.79,1.79,0,0,1-1.1-.42,15.21,15.21,0,0,0-20.86,0A1.6,1.6,0,0,1,7.57,16.16Z"/><path class="cls-2" d="M36.62,10.1a1.84,1.84,0,0,1-1.09-.41A23.44,23.44,0,0,0,19.08,3,23.75,23.75,0,0,0,2.58,9.64a1.53,1.53,0,0,1-2.18,0A1.55,1.55,0,0,1,0,8.58a1.44,1.44,0,0,1,.45-1,26.81,26.81,0,0,1,37.23-.07,1.51,1.51,0,0,1,0,2.12,1.43,1.43,0,0,1-1.08.5Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
assets/img/icon/wifi3.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.14 28.69"><defs><style>.cls-1{fill:#fff;}.cls-2,.cls-3{fill:#f3f5f6;}.cls-3{opacity:0.06;}</style></defs><title>资源 228 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M19.07,28.69a2.16,2.16,0,0,1,0-4.32,2.16,2.16,0,0,1,0,4.32Zm6-7.08a1.65,1.65,0,0,1-1-.41A7.53,7.53,0,0,0,14,21.2a1.52,1.52,0,0,1-2.11-.1A1.51,1.51,0,0,1,12,19a10.53,10.53,0,0,1,14.1-.06,1.57,1.57,0,0,1-1,2.68Z"/><path class="cls-2" d="M7.57,16.16a1.41,1.41,0,0,1-1.05-.45,1.68,1.68,0,0,1-.46-1.21,1.36,1.36,0,0,1,.44-1A18.07,18.07,0,0,1,19,8.58h.12a18.4,18.4,0,0,1,12.57,4.95,1.44,1.44,0,0,1,.05,2.1,1.63,1.63,0,0,1-1.09.52h0a1.79,1.79,0,0,1-1.1-.42,15.21,15.21,0,0,0-20.86,0A1.6,1.6,0,0,1,7.57,16.16Z"/><path class="cls-3" d="M36.62,10.1a1.84,1.84,0,0,1-1.09-.41A23.44,23.44,0,0,0,19.08,3,23.75,23.75,0,0,0,2.58,9.64a1.53,1.53,0,0,1-2.18,0A1.55,1.55,0,0,1,0,8.58a1.44,1.44,0,0,1,.45-1,26.81,26.81,0,0,1,37.23-.07,1.51,1.51,0,0,1,0,2.12,1.43,1.43,0,0,1-1.08.5Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
assets/img/icon/wifi4.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 38.14 28.69"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#f3f5f6;}</style></defs><title>资源 229 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M19.07,28.69a2.16,2.16,0,0,1,0-4.32,2.16,2.16,0,0,1,0,4.32Zm6-7.08a1.65,1.65,0,0,1-1-.41A7.53,7.53,0,0,0,14,21.2a1.52,1.52,0,0,1-2.11-.1A1.51,1.51,0,0,1,12,19a10.53,10.53,0,0,1,14.1-.06,1.57,1.57,0,0,1-1,2.68Z"/><path class="cls-2" d="M7.57,16.16a1.41,1.41,0,0,1-1.05-.45,1.68,1.68,0,0,1-.46-1.21,1.36,1.36,0,0,1,.44-1A18.07,18.07,0,0,1,19,8.58h.12a18.4,18.4,0,0,1,12.57,4.95,1.44,1.44,0,0,1,.05,2.1,1.63,1.63,0,0,1-1.09.52h0a1.79,1.79,0,0,1-1.1-.42,15.21,15.21,0,0,0-20.86,0A1.6,1.6,0,0,1,7.57,16.16Z"/><path class="cls-2" d="M36.62,10.1a1.84,1.84,0,0,1-1.09-.41A23.44,23.44,0,0,0,19.08,3,23.75,23.75,0,0,0,2.58,9.64a1.53,1.53,0,0,1-2.18,0A1.55,1.55,0,0,1,0,8.58a1.44,1.44,0,0,1,.45-1,26.81,26.81,0,0,1,37.23-.07,1.51,1.51,0,0,1,0,2.12,1.43,1.43,0,0,1-1.08.5Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/img/signal0.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
assets/img/signal1.png
Normal file
|
After Width: | Height: | Size: 214 B |
BIN
assets/img/signal2.png
Normal file
|
After Width: | Height: | Size: 240 B |
BIN
assets/img/signal3.png
Normal file
|
After Width: | Height: | Size: 239 B |
BIN
assets/img/signal4.png
Normal file
|
After Width: | Height: | Size: 223 B |
BIN
assets/img/wifi1.png
Normal file
|
After Width: | Height: | Size: 910 B |
BIN
assets/img/wifi2.png
Normal file
|
After Width: | Height: | Size: 899 B |
BIN
assets/img/wifi3.png
Normal file
|
After Width: | Height: | Size: 996 B |
BIN
assets/img/wifi4.png
Normal file
|
After Width: | Height: | Size: 898 B |
@@ -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配置",
|
||||
"分享设备":"分享设备",
|
||||
"消息设置":"消息设置",
|
||||
"设备分享":"设备分享",
|
||||
"请输入对方手机号或邮箱":"请输入对方手机号或邮箱",
|
||||
"微信好友一键分享":"微信好友一键分享",
|
||||
"发送邀请":"发送邀请",
|
||||
"要分享的设备":"要分享的设备",
|
||||
"主设备":"主设备:",
|
||||
"从设备":"从设备:",
|
||||
"邀请成功":"邀请成功!",
|
||||
"邀请失败":"邀请失败!",
|
||||
"请输入手机号或者邮箱":"请输入手机号或者邮箱",
|
||||
"请输入正确的手机号或者邮箱":"请输入正确的手机号或者邮箱",
|
||||
"体征消息":"体征消息",
|
||||
"系统消息":"系统消息"
|
||||
}
|
||||
@@ -28,6 +28,8 @@
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>需要使用相机扫码</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
|
||||
29
lib/common/color/ServiceConstant.dart
Normal file
@@ -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";
|
||||
|
||||
}
|
||||
87
lib/common/util/DailyLogUtils.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
import 'dart:io';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class DailyLogUtils {
|
||||
// 获取日志文件路径(按日期命名)
|
||||
static Future<File> _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<void> _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<void> writeLog(String content) async {
|
||||
await _writeLogWithLevel('INFO', content);
|
||||
}
|
||||
|
||||
// 写入 warning 日志
|
||||
static Future<void> writeWarning(String content) async {
|
||||
await _writeLogWithLevel('WARNING', content);
|
||||
}
|
||||
|
||||
// 写入 error 日志
|
||||
static Future<void> writeError(String content) async {
|
||||
await _writeLogWithLevel('ERROR', content);
|
||||
}
|
||||
|
||||
// 写入 debug 日志
|
||||
static Future<void> writeDebug(String content) async {
|
||||
await _writeLogWithLevel('DEBUG', content);
|
||||
}
|
||||
|
||||
// 读取当天日志
|
||||
static Future<String> readTodayLog() async {
|
||||
final file = await _getLogFile();
|
||||
return await file.readAsString();
|
||||
}
|
||||
|
||||
// 获取所有日志文件(返回 File 列表)
|
||||
static Future<List<FileSystemEntity>> listLogFiles() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final files = dir.listSync();
|
||||
return files.where((f) => f.path.endsWith('.log')).toList();
|
||||
}
|
||||
|
||||
// 清除所有日志
|
||||
static Future<void> clearAllLogs() async {
|
||||
final files = await listLogFiles();
|
||||
for (final f in files) {
|
||||
await File(f.path).delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取指定日期范围内的日志文件(包含起止日期)
|
||||
static Future<List<File>> getLogsBetween(
|
||||
DateTime fromDate, DateTime toDate) async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final logFiles = <File>[];
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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) {
|
||||
|
||||
77
lib/component/NullDataComponentWidget.dart
Normal file
@@ -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<NullDataWidget> createState() => _TestWidgetState();
|
||||
}
|
||||
|
||||
class _TestWidgetState extends State<NullDataWidget> {
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
@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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
172
lib/component/home_page/DynamicReportDetailWidget.dart
Normal file
@@ -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<SleepDateWidget> sleepDateWidgets;
|
||||
final List<SleepDataModuleWidget> 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<SleepDateWidget> 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)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<String, dynamic> data;
|
||||
|
||||
const SleepDataModuleWidget({super.key, required this.data});
|
||||
|
||||
@override
|
||||
State<SleepDataModuleWidget> createState() => _SleepDataModuleWidgetState();
|
||||
@@ -33,111 +35,137 @@ class _SleepDataModuleWidgetState extends State<SleepDataModuleWidget> {
|
||||
@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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -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<SleepDateWidget> createState() => _SleepDateWidgetState();
|
||||
@@ -19,14 +37,35 @@ class _SleepDateWidgetState extends State<SleepDateWidget> {
|
||||
@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<SleepDateWidget> {
|
||||
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<SleepDateWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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<CustomCard>
|
||||
with SingleTickerProviderStateMixin {
|
||||
double _scale = 1.0;
|
||||
final Duration _animationDuration = Duration(milliseconds: 50);
|
||||
final GlobalKey _inkKey = GlobalKey();
|
||||
final Duration _animationDuration = const Duration(milliseconds: 50);
|
||||
|
||||
Future<void> _handleTap(TapDownDetails details) async {
|
||||
setState(() {
|
||||
_scale = 0.95;
|
||||
});
|
||||
Future<void> _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<CustomCard>
|
||||
|
||||
@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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
79
lib/component/tool/SelectableTagButton.dart
Normal file
@@ -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 控制
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
130
lib/component/tool/TopSlideNotification.dart
Normal file
@@ -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<TopSlideNotification> 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<TopSlideNotification>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
late Animation<Offset> _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<Offset>(
|
||||
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<ThemeController>().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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
51
lib/component/tool/WebViewWidget.dart
Normal file
@@ -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<WebViewWidget> {
|
||||
// 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, // 启用手势返回
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
166
lib/component/tool/cmd.dart
Normal file
@@ -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 = <Map<String, dynamic>>[];
|
||||
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<bool> 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;
|
||||
}
|
||||
@@ -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<BleDeviceData>? devicelist = []; //蓝牙扫描到的设备数据列表
|
||||
List<BleDeviceData>? betDevicelist = []; //请求的
|
||||
List? blelist = []; //蓝牙扫描到的设备数据列表
|
||||
List? wifiList = [];
|
||||
@JsonKey(ignore: true)
|
||||
List<BleDeviceData>? devicelist = [];//蓝牙扫描
|
||||
@JsonKey(ignore: true)
|
||||
List<BleDeviceData>? 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<String, dynamic> json) {
|
||||
try {
|
||||
return _$BlueteethBindModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return BlueteethBindModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
return BlueteethBindModel();
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$BlueteethBindModelToJson(this);
|
||||
}
|
||||
|
||||
@@ -60,30 +60,73 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
|
||||
|
||||
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<void> updateDeviceStatus() async {
|
||||
Future<ApiResponse> 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<BlueteethBindModel> {
|
||||
}
|
||||
|
||||
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<dynamic> responseList = response.data['data'];
|
||||
|
||||
Map<String, BleDeviceData> 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<String> slaveMacsToRemove = [];
|
||||
@@ -123,17 +169,26 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<ApiResponse> bindDeviceAndMAC(BleDeviceData d) async {
|
||||
@@ -142,7 +197,7 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
|
||||
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<BlueteethBindModel> {
|
||||
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<String, dynamic> map) {}
|
||||
|
||||
Future<ApiResponse> 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<BlueteethBindModel> {
|
||||
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<String, dynamic> map) {}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,30 @@ part of 'blueteeth_bind_controller.dart';
|
||||
// **************************************************************************
|
||||
|
||||
BlueteethBindModel _$BlueteethBindModelFromJson(Map<String, dynamic> 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<dynamic>
|
||||
..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<String, dynamic> _$BlueteethBindModelToJson(BlueteethBindModel instance) =>
|
||||
<String, dynamic>{
|
||||
'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,
|
||||
};
|
||||
|
||||
256
lib/controller/device/body_device_controller.dart
Normal file
@@ -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<String, dynamic> json) {
|
||||
try {
|
||||
return _$BodyDeviceModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return BodyDeviceModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$BodyDeviceModelToJson(this);
|
||||
}
|
||||
|
||||
class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
|
||||
BodyDeviceController() {
|
||||
attr = GetModel(BodyDeviceModel()).obs;
|
||||
}
|
||||
|
||||
RxInt bindDeviceNum = 0.obs;
|
||||
RxList deviceList = [].obs;
|
||||
RxMap<String, List<dynamic>> sleepReportData = <String, List<dynamic>>{}.obs;
|
||||
|
||||
RxString keyWord = "".obs;
|
||||
|
||||
Future<ApiResponse> 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<ApiResponse> 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<ApiResponse> deleteDevice(Map<String, dynamic> 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<ApiResponse> getSleepReport() async {
|
||||
try {
|
||||
sleepReportData.value = {};
|
||||
ApiResponse<Map<String, List<dynamic>>> 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
|
||||
}
|
||||
}
|
||||
15
lib/controller/device/body_device_controller.g.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'body_device_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
BodyDeviceModel _$BodyDeviceModelFromJson(Map<String, dynamic> json) =>
|
||||
BodyDeviceModel()..type = (json['type'] as num?)?.toInt();
|
||||
|
||||
Map<String, dynamic> _$BodyDeviceModelToJson(BodyDeviceModel instance) =>
|
||||
<String, dynamic>{
|
||||
'type': instance.type,
|
||||
};
|
||||
120
lib/controller/device/device_share_controller.dart
Normal file
@@ -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<String, dynamic> json) {
|
||||
try {
|
||||
return _$DeviceShareModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return DeviceShareModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$DeviceShareModelToJson(this);
|
||||
}
|
||||
|
||||
class DeviceShareController extends GetControllerEx<DeviceShareModel> {
|
||||
DeviceShareController() {
|
||||
attr = GetModel(DeviceShareModel()).obs;
|
||||
}
|
||||
RxList deviceTypeList = [].obs;
|
||||
|
||||
RxString account = "".obs;
|
||||
RxString msg = "".obs;
|
||||
RxInt code = 0.obs;
|
||||
|
||||
Future<ApiResponse> 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<ApiResponse> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
lib/controller/device/device_share_controller.g.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'device_share_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeviceShareModel _$DeviceShareModelFromJson(Map<String, dynamic> json) =>
|
||||
DeviceShareModel()
|
||||
..name = json['name'] as String?
|
||||
..type = (json['type'] as num?)?.toInt()
|
||||
..image = json['image'] as String?;
|
||||
|
||||
Map<String, dynamic> _$DeviceShareModelToJson(DeviceShareModel instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'image': instance.image,
|
||||
};
|
||||
65
lib/controller/device/device_type_controller.dart
Normal file
@@ -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<String, dynamic> json) {
|
||||
try {
|
||||
return _$DeviceTypeModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return DeviceTypeModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$DeviceTypeModelToJson(this);
|
||||
}
|
||||
|
||||
class DeviceTypeController extends GetControllerEx<DeviceTypeModel> {
|
||||
DeviceTypeController() {
|
||||
attr = GetModel(DeviceTypeModel()).obs;
|
||||
}
|
||||
RxList deviceTypeList = [].obs;
|
||||
|
||||
Future<ApiResponse> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
lib/controller/device/device_type_controller.g.dart
Normal file
@@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'device_type_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeviceTypeModel _$DeviceTypeModelFromJson(Map<String, dynamic> json) =>
|
||||
DeviceTypeModel()
|
||||
..name = json['name'] as String?
|
||||
..type = (json['type'] as num?)?.toInt()
|
||||
..image = json['image'] as String?;
|
||||
|
||||
Map<String, dynamic> _$DeviceTypeModelToJson(DeviceTypeModel instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'type': instance.type,
|
||||
'image': instance.image,
|
||||
};
|
||||
50
lib/controller/home/home_controller.dart
Normal file
@@ -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<String, dynamic> json) {
|
||||
try {
|
||||
return _$HomeModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return HomeModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$HomeModelToJson(this);
|
||||
}
|
||||
|
||||
class HomeController extends GetControllerEx<HomeModel> {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
14
lib/controller/home/home_controller.g.dart
Normal file
@@ -0,0 +1,14 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'home_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
HomeModel _$HomeModelFromJson(Map<String, dynamic> json) =>
|
||||
HomeModel()..type = (json['type'] as num?)?.toInt();
|
||||
|
||||
Map<String, dynamic> _$HomeModelToJson(HomeModel instance) => <String, dynamic>{
|
||||
'type': instance.type,
|
||||
};
|
||||
@@ -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<LoginModel> {
|
||||
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<LoginModel> {
|
||||
|
||||
Future<ApiResponse> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@ LoginModel _$LoginModelFromJson(Map<String, dynamic> 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<String, dynamic> _$LoginModelToJson(LoginModel instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -30,4 +31,5 @@ Map<String, dynamic> _$LoginModelToJson(LoginModel instance) =>
|
||||
'forceLogin': instance.forceLogin,
|
||||
'isIos': instance.isIos,
|
||||
'isWeChatNotInstalled': instance.isWeChatNotInstalled,
|
||||
'register_agree': instance.register_agree,
|
||||
};
|
||||
|
||||
78
lib/controller/message/message_controller.dart
Normal file
@@ -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<String, dynamic> json) {
|
||||
try {
|
||||
return _$MessageModelFromJson(json);
|
||||
} catch (e) {
|
||||
// 在实际应用中,应该有更细致的异常处理策略和错误日志
|
||||
return MessageModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
|
||||
}
|
||||
}
|
||||
|
||||
// 序列化为JSON时的异常处理
|
||||
Map<String, dynamic> toJson() => _$MessageModelToJson(this);
|
||||
}
|
||||
|
||||
class MessageController extends GetControllerEx<MessageModel> {
|
||||
MessageController() {
|
||||
attr = GetModel(MessageModel()).obs;
|
||||
}
|
||||
|
||||
RxList msssageList = [].obs;
|
||||
|
||||
Future<ApiResponse> 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
|
||||
}
|
||||
}
|
||||
15
lib/controller/message/message_controller.g.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'message_controller.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
MessageModel _$MessageModelFromJson(Map<String, dynamic> json) =>
|
||||
MessageModel()..type = (json['type'] as num?)?.toInt();
|
||||
|
||||
Map<String, dynamic> _$MessageModelToJson(MessageModel instance) =>
|
||||
<String, dynamic>{
|
||||
'type': instance.type,
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -31,4 +37,151 @@ class PersonController extends GetControllerEx<PersonModel> {
|
||||
PersonController() {
|
||||
attr = GetModel(PersonModel()).obs;
|
||||
}
|
||||
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<ApiResponse> 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<ApiResponse> 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<ApiResponse> 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
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,10 @@ part of 'person_controller.dart';
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PersonModel _$PersonModelFromJson(Map<String, dynamic> json) => PersonModel()
|
||||
..read = (json['read'] as num).toInt()
|
||||
..birthday = json['birthday'] == null
|
||||
? null
|
||||
: DateTime.parse(json['birthday'] as String);
|
||||
PersonModel _$PersonModelFromJson(Map<String, dynamic> json) =>
|
||||
PersonModel()..read = (json['read'] as num?)?.toInt();
|
||||
|
||||
Map<String, dynamic> _$PersonModelToJson(PersonModel instance) =>
|
||||
<String, dynamic>{
|
||||
'read': instance.read,
|
||||
'birthday': instance.birthday?.toIso8601String(),
|
||||
};
|
||||
|
||||
@@ -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<String, dynamic> json) =>
|
||||
@@ -41,4 +54,146 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
|
||||
UserInfoController() {
|
||||
attr = GetModel(UserInfoModel()).obs;
|
||||
}
|
||||
|
||||
Future<ApiResponse> 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<ApiResponse> 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<ApiResponse> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ UserInfoModel _$UserInfoModelFromJson(Map<String, dynamic> 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<String, dynamic> _$UserInfoModelToJson(UserInfoModel instance) =>
|
||||
<String, dynamic>{
|
||||
@@ -33,4 +35,6 @@ Map<String, dynamic> _$UserInfoModelToJson(UserInfoModel instance) =>
|
||||
'appVersion': instance.appVersion,
|
||||
'img_bucket': instance.img_bucket,
|
||||
'login': instance.login,
|
||||
'deviceBindNum': instance.deviceBindNum,
|
||||
'loginPhone': instance.loginPhone,
|
||||
};
|
||||
|
||||
9
lib/enum/BindType.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
enum BindType {
|
||||
active(1, '主动绑定'),
|
||||
share(2, '分享绑定');
|
||||
|
||||
final int code;
|
||||
final String description;
|
||||
|
||||
const BindType(this.code, this.description);
|
||||
}
|
||||
9
lib/enum/LoginStatus.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
enum LoginStatus {
|
||||
LOGIN(1, '已登录'),
|
||||
NOT_LOGIN(2, '未登录');
|
||||
|
||||
final int code;
|
||||
final String description;
|
||||
|
||||
const LoginStatus(this.code, this.description);
|
||||
}
|
||||
@@ -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<void> 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<void> 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<void> initLogin() async {
|
||||
final box = GetStorage();
|
||||
UserInfoController userInfoController = Get.find();
|
||||
String? token = box.read('token');
|
||||
// print('Token: $token');
|
||||
Map<String, dynamic>? userMap = box.read('user');
|
||||
if (userMap != null) {
|
||||
UserModel user = UserModel.fromJson(userMap);
|
||||
userInfoController.model.user = user;
|
||||
try {
|
||||
Map<String, dynamic>? 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()),
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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地址
|
||||
|
||||
16
lib/model/WebSocketMessage.dart
Normal file
@@ -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<String, dynamic> json) =>
|
||||
_$WebSocketMessageFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$WebSocketMessageToJson(this);
|
||||
}
|
||||
21
lib/model/WebSocketMessage.g.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'WebSocketMessage.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
WebSocketMessage _$WebSocketMessageFromJson(Map<String, dynamic> json) =>
|
||||
WebSocketMessage(
|
||||
path: json['path'] as String,
|
||||
type: (json['type'] as num?)?.toInt(),
|
||||
data: json['data'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$WebSocketMessageToJson(WebSocketMessage instance) =>
|
||||
<String, dynamic>{
|
||||
'path': instance.path,
|
||||
'type': instance.type,
|
||||
'data': instance.data,
|
||||
};
|
||||
@@ -1,17 +1,17 @@
|
||||
|
||||
|
||||
class ApiResponse<T> {
|
||||
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<String, dynamic> json, T Function(Object?) fromJsonT) {
|
||||
return ApiResponse<T>(
|
||||
code: json['code'] as int,
|
||||
data: json['data'] != null ? fromJsonT(json['data']) : null,
|
||||
msg: json['msg'] as String?,
|
||||
total: json['total'] as int?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, dynamic> json) =>
|
||||
|
||||
@@ -9,20 +9,28 @@ part of 'user_data.dart';
|
||||
UserModel _$UserModelFromJson(Map<String, dynamic> 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<String, dynamic> _$UserModelToJson(UserModel instance) => <String, dynamic>{
|
||||
'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,
|
||||
};
|
||||
|
||||
803
lib/pages/device/BodyDeviceWidget.dart
Normal file
@@ -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<BodyDeviceWidget> createState() => _BodyDevicePageState();
|
||||
}
|
||||
|
||||
class _BodyDevicePageState extends State<BodyDeviceWidget> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
1293
lib/pages/device/component/DeviceDataComponentWidget.dart
Normal file
88
lib/pages/device/component/DeviceStatusInfoWidget.dart
Normal file
@@ -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)),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
477
lib/pages/device/device_detail.dart
Normal file
@@ -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<DeviceDetailPage> createState() => _DeviceDetailPageState();
|
||||
}
|
||||
|
||||
class _DeviceDetailPageState extends State<DeviceDetailPage> {
|
||||
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)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
505
lib/pages/device/instant_body_page.dart
Normal file
@@ -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<InstantBodyPage> createState() => _InstantBodyPageState();
|
||||
}
|
||||
|
||||
class _InstantBodyPageState extends State<InstantBodyPage> {
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
255
lib/pages/device_bind/MobileScannerTestPage.dart
Normal file
@@ -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<MobileScannerTestPage> createState() => _MobileScannerTestPageState();
|
||||
}
|
||||
|
||||
class _MobileScannerTestPageState extends State<MobileScannerTestPage>
|
||||
with TickerProviderStateMixin {
|
||||
String? scannedText;
|
||||
bool isScanning = true;
|
||||
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _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<double>(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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -200,7 +200,7 @@ class _EPageState extends State<BindDeviceSuccess> {
|
||||
'assets/img/icon/share.svg',
|
||||
width: 25.rpx,
|
||||
height: 25.rpx, // 如果 SVG 中没有固定颜色,可以这样设置
|
||||
color: themeController.currentColor.sc3,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
'绑定成功.立即分享'.tr,
|
||||
|
||||
@@ -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<BlueteethDevicePage> createState() => _EPageState();
|
||||
State<BlueteethDevicePage> createState() => _BlueteethDevicePageState();
|
||||
}
|
||||
|
||||
class _EPageState extends State<BlueteethDevicePage> {
|
||||
class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
|
||||
GlobalController globalController = Get.find();
|
||||
UserInfoController userInfoController = Get.find();
|
||||
BlueteethBindController blueteethBindController = Get.find();
|
||||
@@ -49,6 +46,8 @@ class _EPageState extends State<BlueteethDevicePage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
blueteethBindController.model.devicelist = [];
|
||||
blueteethBindController.model.betDevicelist = [];
|
||||
flutterBlue = FlutterBluePlus(); // 初始化flutterBlue实例
|
||||
_checkBluetoothPermission(); // 检查蓝牙权限
|
||||
Get.find<BlueteethBindController>().startStatusPolling();
|
||||
@@ -114,6 +113,7 @@ class _EPageState extends State<BlueteethDevicePage> {
|
||||
|
||||
// 开始扫描蓝牙设备
|
||||
void _startScanning() async {
|
||||
if (!mounted) return;
|
||||
var bluetoothState = await FlutterBluePlus.isOn;
|
||||
if (!bluetoothState && !_isDialogShowing) {
|
||||
_isDialogShowing = true;
|
||||
@@ -128,8 +128,9 @@ class _EPageState extends State<BlueteethDevicePage> {
|
||||
});
|
||||
|
||||
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<BlueteethDevicePage> {
|
||||
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<BlueteethDevicePage> {
|
||||
|
||||
// 等待扫描完成
|
||||
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<BlueteethDevicePage> {
|
||||
// 停止扫描
|
||||
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<BlueteethDevicePage> {
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
StreamSubscription<List<ScanResult>>? _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<int> 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<int> 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<BlueteethDevicePage> {
|
||||
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<BlueteethDevicePage> {
|
||||
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<BlueteethDevicePage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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<int> data) {
|
||||
|
||||
@@ -75,7 +75,7 @@ class _FancyCircleCheckboxState extends State<FancyCircleCheckbox>
|
||||
child: ScaleTransition(
|
||||
scale: _scaleAnimation,
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(8.rpx),
|
||||
margin: EdgeInsets.all(6.rpx),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: widget.fillColor,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
421
lib/pages/device_bind/device_share_page.dart
Normal file
@@ -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<DeviceSharePage> createState() => _DeviceSharePageState();
|
||||
}
|
||||
|
||||
class _DeviceSharePageState extends State<DeviceSharePage> {
|
||||
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)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<DeviceTypePage> createState() => _EPageState();
|
||||
@@ -25,10 +28,20 @@ class _EPageState extends State<DeviceTypePage> {
|
||||
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<DeviceTypePage> {
|
||||
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<DeviceTypePage> {
|
||||
}
|
||||
|
||||
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<DeviceTypePage> {
|
||||
),
|
||||
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,
|
||||
),
|
||||
|
||||
192
lib/pages/device_bind/device_type_list.dart
Normal file
@@ -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<DeviceTypeListPage> createState() => _DeviceTypeListPageState();
|
||||
}
|
||||
|
||||
class _DeviceTypeListPageState extends State<DeviceTypeListPage> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<WifiPage> {
|
||||
BlueteethBindController blueteethBindController = Get.find();
|
||||
PersonController personController = Get.find();
|
||||
ThemeController themeController = Get.find();
|
||||
var lisObj;
|
||||
// List<String> _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<WifiPage> {
|
||||
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<WifiPage> {
|
||||
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<WifiPage> {
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text(
|
||||
'可用WLAN',
|
||||
'可用WLAN'.tr,
|
||||
style: FlutterFlowTheme.of(context)
|
||||
.bodyMedium
|
||||
.override(
|
||||
@@ -203,157 +269,436 @@ class _WifiPageState extends State<WifiPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
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<WifiPage> {
|
||||
);
|
||||
}
|
||||
|
||||
getWifiList({int time = 3}) {
|
||||
// LoadingDialog.show("扫描WIFI列表中...", icon: LoadingDialogIcon.wifi);
|
||||
try {
|
||||
// var device = widget.bluetoothDevice;
|
||||
// String log = "";
|
||||
// Function logAdd = (l) {
|
||||
// log += l;
|
||||
// };
|
||||
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 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<GlobalController>().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",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<LoginPage> {
|
||||
borderRadius: AppConstants()
|
||||
.button_container_radius, // 圆角半径
|
||||
onTap: () {
|
||||
TopSlideNotification.show(context,
|
||||
text: "待开发功能".tr);
|
||||
bool agree =
|
||||
loginController.model.register_agree!;
|
||||
if (!agree) {
|
||||
|
||||
@@ -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<OtherLoginPage> {
|
||||
return InkWell(
|
||||
onTap: () async {
|
||||
try {
|
||||
await DailyLogUtils
|
||||
.writeLog(
|
||||
"点击获取验证码");
|
||||
DailyLogUtils.writeLog(
|
||||
"点击获取验证码");
|
||||
if (countdownController
|
||||
.countdown
|
||||
.value !=
|
||||
@@ -457,7 +457,8 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
.getCode(
|
||||
context);
|
||||
if (apiResponse.code !=
|
||||
HttpStatusCodes.ok) {
|
||||
HttpStatusCodes
|
||||
.ok) {
|
||||
TopSlideNotification
|
||||
.show(
|
||||
context,
|
||||
@@ -551,7 +552,7 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
|
||||
.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<OtherLoginPage> {
|
||||
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<OtherLoginPage> {
|
||||
textColor: themeController
|
||||
.currentColor.sc2,
|
||||
);
|
||||
Get.offAllNamed('/homePage');
|
||||
// Get.offAllNamed('/homePage');
|
||||
Get.offAllNamed(
|
||||
"/mianPageBottomChange");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
168
lib/pages/main_bottom/component/MessageWidgetWidget.dart
Normal file
@@ -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<MessageWidgetWidget> createState() => _MessageWidgetWidgetState();
|
||||
}
|
||||
|
||||
class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||