更新分享

This commit is contained in:
wyf
2025-04-28 15:37:58 +08:00
parent 850c34b408
commit eae7a2284d
116 changed files with 12143 additions and 3017 deletions

View File

@@ -33,13 +33,15 @@
<!-- &lt;!&ndash;读取外部存储权限&ndash;&gt;-->
<!-- <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

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

BIN
assets/img/body_black.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

BIN
assets/img/body_white.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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

View 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

File diff suppressed because one or more lines are too long

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View 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

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 B

BIN
assets/img/signal1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

BIN
assets/img/signal2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

BIN
assets/img/signal3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

BIN
assets/img/signal4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

BIN
assets/img/wifi1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

BIN
assets/img/wifi2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 B

BIN
assets/img/wifi3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

BIN
assets/img/wifi4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

View File

@@ -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配置",
"分享设备":"分享设备",
"消息设置":"消息设置",
"设备分享":"设备分享",
"请输入对方手机号或邮箱":"请输入对方手机号或邮箱",
"微信好友一键分享":"微信好友一键分享",
"发送邀请":"发送邀请",
"要分享的设备":"要分享的设备",
"主设备":"主设备:",
"从设备":"从设备:",
"邀请成功":"邀请成功!",
"邀请失败":"邀请失败!",
"请输入手机号或者邮箱":"请输入手机号或者邮箱",
"请输入正确的手机号或者邮箱":"请输入正确的手机号或者邮箱",
"体征消息":"体征消息",
"系统消息":"系统消息"
}

View File

@@ -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>

View 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";
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View File

@@ -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) {

View 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,
),
),
),
),
],
),
),
),
),
);
}
}

View 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)),
),
),
);
}
}

View File

@@ -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,
),
),
],
),
),
);

View File

@@ -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),
),
),
),
],
),
),
],
),
),
),

View File

@@ -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,
),
);
}

View 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 控制
),
),
),
);
}
}

View 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,
),
),
),
),
),
),
);
}
}

View 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
View 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;
}

View File

@@ -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) {}
}

View File

@@ -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,
};

View 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
}
}

View 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,
};

View 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);
}
}
}

View 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,
};

View 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);
}
}
}

View 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,
};

View 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();
}
}

View 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,
};

View File

@@ -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);
}
}

View File

@@ -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,
};

View 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
}
}

View 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,
};

View File

@@ -1,14 +1,20 @@
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:ef/ef.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:vbvs_app/common/color/ServiceConstant.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/model/api_response.dart';
part 'person_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class PersonModel {
int read = 1;
DateTime? birthday;
double? weight;
int? read = 1;
// String? name;
PersonModel();
@@ -30,5 +36,152 @@ class PersonModel {
class PersonController extends GetControllerEx<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
}
}

View File

@@ -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(),
};

View File

@@ -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;
}
}

View File

@@ -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
View File

@@ -0,0 +1,9 @@
enum BindType {
active(1, '主动绑定'),
share(2, '分享绑定');
final int code;
final String description;
const BindType(this.code, this.description);
}

View File

@@ -0,0 +1,9 @@
enum LoginStatus {
LOGIN(1, '已登录'),
NOT_LOGIN(2, '未登录');
final int code;
final String description;
const LoginStatus(this.code, this.description);
}

View File

@@ -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()),
]));
});
}

View File

@@ -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地址

View 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);
}

View 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,
};

View File

@@ -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?,
);
}
}

View File

@@ -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) =>

View File

@@ -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,
};

View 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,
),
),
],
),
),
);
}
}

File diff suppressed because it is too large Load Diff

View 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)),
),
),
),
);
}
}

View 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)),
),
),
),
),
),
),
),
),
);
}
}

View 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,
),
],
),
),
),
),
),
),
),
);
}
}

View 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,
),
),
],
),
),
],
),
),
],
),
),
);
}
}

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,
)),
),
),
],
),
),
),
);
},
);
}

View 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)),
),
),
),
),
],
),
),
),
),
),
),
),
);
}
}

View File

@@ -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,
),

View 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,
),
),
],
),
),
);
}
}

View File

@@ -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);
void initWifiStatusAndWifiList() {
if (lisObj != null) {
lisObj!.cancel();
}
lisObj = blueteethBindController.currentDevice!.statusStream
.listen((onData) async {
if (onData.status == BleEventType.recvLineLog) {
final line = onData.val;
print("[bleee]:" + line);
}
if (onData.status == BleEventType.ready) {
showLoadingDialog(context, title: "获取wifi列表中...".tr);
bool wifiStatus =
await getWifiStatus(blueteethBindController.currentDevice!);
blueteethBindController.wifiStatus.value = wifiStatus == true ? 1 : 0;
if (wifiStatus) {
Map connect_wifiInfo =
await getDeviceWifiStatus(blueteethBindController.currentDevice!);
if (connect_wifiInfo != null) {
blueteethBindController.connect_wifi.value = connect_wifiInfo;
}
}
List wifiList =
await getWifiList(blueteethBindController.currentDevice!);
if (wifiList.length > 0) {
Navigator.pop(context);
TopSlideNotification.show(
context,
text: "获取wifi列表成功".tr,
textColor: themeController.currentColor.sc2,
);
blueteethBindController.wifiList.value = wifiList;
blueteethBindController.updateAll();
} else {
Navigator.pop(context);
TopSlideNotification.show(
context,
text: "获取wifi列表失败".tr,
textColor: themeController.currentColor.sc9,
);
}
}
});
}
initWifiList() async {
try {
// var device = widget.bluetoothDevice;
// String log = "";
// Function logAdd = (l) {
// log += l;
// };
var wifiList = await getWifiList(blueteethBindController.currentDevice!);
print(wifiList);
if (wifiList.length > 0) {
blueteethBindController.wifiList.value = wifiList;
blueteethBindController.updateAll();
}
return wifiList;
} catch (e) {
print(e);
TopSlideNotification.show(
context,
text: "获取wifi列表失败".tr,
textColor: themeController.currentColor.sc9,
);
}
}
checkIsCalibration() {
// if (controller.model.bindArr[0] == "" ||
// controller.model.bindArr[0] == null) {
// return;
// }
// if (controller.model.bindArr[2] == "" ||
// controller.model.bindArr[2] == null) {
// return;
// }
// if (controller.model.connectedWifiName == "" ||
// controller.model.connectedWifiName == null) {
// return;
// }
showCustomConfirmAndCancelDialog(context, "是否进行设备校准?", confirmName: "去校准")
.then((d) async {
// if (d == "confirm") {
// await Get.offAndToNamed("/calibration", arguments: [
// controller.model.bindArr[1],
// controller.model.bindArr[2]
// ]);
// Get.find<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",
),
);
}
}
}

View File

@@ -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) {

View File

@@ -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) {

View 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,
),
),
),
);
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More