This commit is contained in:
wyf
2025-05-13 11:59:04 +08:00
parent eae7a2284d
commit fb5c3864a3
101 changed files with 8427 additions and 1953 deletions

View File

@@ -87,7 +87,7 @@ android {
signingConfig signingConfigs.release
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "ZHMHT_v${variant.versionName}-${releaseTime()}.apk"
outputFileName = "THYH_v${variant.versionName}-${releaseTime()}.apk"
}
}
}

View File

@@ -97,6 +97,10 @@
<!-- If your application checks for inAppBrowserView launch mode support -->
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="weixin" />
</intent>
</queries>
</manifest>

BIN
assets/img/addItem.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/img/followus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 52.03"><defs><style>.cls-1{fill:#c8cbd2;opacity:0.5;}</style></defs><title>资源 347 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M54.78,52H5.22A5.2,5.2,0,0,1,0,46.83H0V11.71A5.22,5.22,0,0,1,5.22,6.5H16.31l3.25-5.2c0-.72,1.89-1.3,2.61-1.3H37.83c.72,0,2.61.58,2.61,1.3l3.25,5.2H54.78A5.21,5.21,0,0,1,60,11.7h0V46.83A5.21,5.21,0,0,1,54.78,52ZM30,11.71A16.91,16.91,0,1,0,47,28.62h0A16.94,16.94,0,0,0,30,11.71Zm0,28.61a11.71,11.71,0,1,1,11.74-11.7A11.72,11.72,0,0,1,30,40.32Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 609 B

View File

@@ -1,8 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26">
<title>资源 251</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.74 23.74">
<title>资源 342</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"/>
<path fill="#ff7159" d="M5.77,23.31a5.5,5.5,0,0,1-3.85-1.5,5.18,5.18,0,0,1,0-7.48l4.5-4.49,1.49,1.49L3.42,15.82a3.11,3.11,0,0,0,0,4.49,3.09,3.09,0,0,0,4.49,0l4.49-4.49,1.5,1.5L9.41,21.81A4.7,4.7,0,0,1,5.77,23.31Zm11.34-9-1.5-1.5,4.49-4.7A3.25,3.25,0,0,0,21,5.77a4.47,4.47,0,0,0-.86-2.35,3.22,3.22,0,0,0-2.35-.85,4.43,4.43,0,0,0-2.35.85L11.12,8.13,9.41,6.63,13.9,2.14A5.15,5.15,0,0,1,17.75.64a6.12,6.12,0,0,1,3.85,1.5A5.14,5.14,0,0,1,23.09,6,6.19,6.19,0,0,1,21.6,9.84ZM4.28,9.41,0,9,.21,6.84l4.28.43Zm.42-3-3-3,1.5-1.5,3,3ZM7.06,4.49,6.63.21,8.77,0,9.2,4.28ZM15,23.74l-.43-4.28,2.14-.21.43,4.27Zm5.56-1.93-3-3,1.5-1.5,3,3-1.5,1.5Zm3-4.92-4.27-.42.21-2.14,4.28.43Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 876 B

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,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24.33 24.33">
<title>资源 254</title>
<g id="图层_2" data-name="图层 2">
<g id="图层_1-2" data-name="图层 1">
<path fill="#00c1aa" d="M12.17,24.33A12.17,12.17,0,1,1,24.33,12.17,12.18,12.18,0,0,1,12.17,24.33ZM12.17,2A10.17,10.17,0,1,0,22.33,12.17,10.18,10.18,0,0,0,12.17,2Z"/>
<path fill="#00c1aa" d="M12.17,20.17a1,1,0,0,1-1-1V11.28a1,1,0,0,1,2,0v7.89A1,1,0,0,1,12.17,20.17Z"/>
<circle fill="#00c1aa" cx="12.17" cy="6.94" r="1.59"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 545 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37.18 37.18"><defs><style>.cls-1{fill:#fff;}</style></defs><title>资源 447 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M18.59,37.18A18.59,18.59,0,1,1,37.18,18.59,18.61,18.61,0,0,1,18.59,37.18ZM18.59,2A16.59,16.59,0,1,0,35.18,18.59,16.61,16.61,0,0,0,18.59,2Z"/><path class="cls-1" d="M26.5,23.28a1.06,1.06,0,0,1-.42-.09L18.17,19.5A1,1,0,1,1,19,17.68l7.92,3.69a1,1,0,0,1-.43,1.91Z"/><path class="cls-1" d="M18.59,19.59a1,1,0,0,1-1-1V6.43a1,1,0,0,1,2,0V18.59A1,1,0,0,1,18.59,19.59Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 615 B

View File

@@ -1 +1,8 @@
<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>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14.48 35.04">
<title>资源 247</title>
<g id="图层_2" data-name="图层 2">
<g id="图层_1-2" data-name="图层 1">
<path fill="#ffffff" opacity="0.2" 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>

Before

Width:  |  Height:  |  Size: 695 B

After

Width:  |  Height:  |  Size: 679 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.66 16.64"><defs><style>.cls-1{fill:#ff7159;}</style></defs><title>资源 222 </title><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><path class="cls-1" d="M13.05,7.2,2,.21A1.32,1.32,0,0,0,0,1.33v14a1.32,1.32,0,0,0,2,1.12l11-7A1.33,1.33,0,0,0,13.05,7.2Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
assets/img/test.pdf Normal file

Binary file not shown.

BIN
assets/img/tip_arrow.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

355
assets/langs/en_US.json Normal file
View File

@@ -0,0 +1,355 @@
{
"语言名称":"English",
"菜单": {
"首页": "Home",
"报告": "Reports",
"小e": "Little e",
"消息": "Messages",
"我的": "Me"
},
"首页": {
"登录": "Login",
"提示标题": "Dear user, please note the following before binding:",
"提示内容1": "1. Is the sensor powered on successfully, with the control box in fast green blinking state?",
"提示内容2": "2. Grant Bluetooth and location (positioning service) permissions to the APP;",
"提示内容3": "3. If using the scan function, please grant camera permission.",
"扫一扫绑定": "Scan to add new device",
"蓝牙绑定": "Search nearby devices via Bluetooth",
"已关联体征监测设备": "Bound vital sign monitoring devices",
"我的e护": "My eCare",
"云关爱": "Cloud Care",
"报告详情": "Report Details"
},
"我的": {
"个人信息": "Personal Information",
"未登录": "Not logged in",
"账号": "Account:",
"我的设备": "My Devices",
"设备报修": "Device Repair",
"操作说明": "User Manual",
"关注我们": "Follow Us",
"当前版本": "Current Version",
"未命名": "Unnamed",
"去登录": "Go to Login",
"头像限制": "Avatar image cannot exceed 5MB",
"头像上传失败": "Avatar upload failed",
"未选择图片": "No image selected",
"上传成功": "Upload successful",
"保存失败": "Save failed",
"保存成功": "Save successful",
"昵称为空": "Nickname cannot be empty",
"查询失败": "Failed to query user information"
},
"设备类型": {
"我的设备": "My Devices",
"请选择设备类型": "Please select device type",
"体征监测设备": "Vital Sign Monitoring Device",
"智能床/床垫": "Smart Bed/Mattress",
"摄像头": "Camera"
},
"绑定引导":{
"说明标题": "Device Placement Instructions:",
"说明正文": "This vital sign monitoring device is used under the mattress (note: the side with the label should face upward), preferably placed directly below the user's chest. After placement, connect to power. When the control box blinks green rapidly, you can proceed with the binding process.",
"不再提示": "Don't show again",
"跳过": "Skip"
},
"蓝牙绑定":{
"标题":"Bluetooth",
"扫描":"Scanning Bluetooth devices...",
"信号":"Minimum signal strength",
"搜索提示":"Search devices",
"搜索":"Search",
"匹配":"Matched peripheral devices",
"信号强度":"Signal strength",
"SN":"SN",
"蓝牙地址":"Bluetooth address",
"mac":"MAC",
"网络":"Network",
"在线":"Online",
"离线":"Offline",
"版本":"Version",
"默认设备名称":"Unknown device",
"传感器":"Sensor",
"可绑定":"Bindable",
"已被绑定":"Already bound",
"双人版绑定标题":"This is a dual-user device, please select",
"绑定全部":"Bind all",
"主设备":"Main device:",
"从设备":"Secondary device:",
"确定":"Confirm",
"取消":"Cancel",
"无法绑定":"Cannot bind!",
"无法绑定1":"Detected that this device",
"无法绑定2":"has been bound",
"无法绑定3":", please unbind before binding again. Contact customer service if you have questions",
"知道了":"Got it",
"是":"Yes",
"否":"No",
"确定绑定提示":"Confirm to bind this device?",
"连接成功":"Connection successful",
"连接异常":"Connection exception",
"连接":"Connect",
"输入wifi密码":"Please enter WiFi password",
"显示密码":"Show",
"不显示密码":"Hide"
},
"登录页":{
"欢迎使用太和e护":"Welcome to Taihe eCare",
"科技睡眠 洞悉万千":"Technological Sleep, Insightful Monitoring",
"本机号码一键登录/注册":"One-click login/registration with this device's number",
"其他手机号码":"Other phone number",
"协议1":"Login means automatic registration and represents your agreement to",
"协议2":"User Agreement",
"协议3":"and",
"协议4":"Privacy Policy",
"协议5":"as well as",
"协议6":"Terms of Use",
"其他登录方式":"Welcome to Taihe eCare",
"未同意协议":"Please confirm agreement"
},
"人员资料":{
"标题":"User Information",
"保存":"Save",
"名字输入提示":"User name",
"生日输入提示":"Birthday",
"体重输入提示":"Weight",
"疾病标题":"Chronic Disease Management",
"提示":"Note: Providing accurate user information can make sleep report monitoring data more precise!"
},
"绑定成功":{
"标题":"Binding Completed",
"绑定成功":"Binding successful! ",
"分享标题":"Share now?",
"分享内容":"After successful device binding, if you want to share your sleep status with friends or family, you can share immediately. After successful sharing, the recipient will have access to view this device's data and receive sleep reports.",
"立即分享":"Share Now",
"返回":"Return to Home"
},
"日期":{
"取消":"Cancel",
"确定":"Confirm",
"年":"Year",
"月":"Month",
"日":"Day"
},
"wifi页":{
"标题":"WiFi Configuration",
"跳过":"Skip",
"WLAN":"WLAN",
"未连接":"Not connected",
"已连接":"Connected",
"可用WLAN":"Available WLAN",
"刷新":"Refresh",
"密码为空":"Password cannot be empty",
"配网成功":"Network configuration successful",
"配网失败":"Network configuration failed",
"配网中":"Configuring network",
"需配网":"Please configure network for the device!"
},
"其他手机登录页":{
"输入内容":"Enter phone number/email",
"输入验证码":"Enter verification code",
"获取验证码":"code",
"登录":"Login",
"请输入手机号":"Please enter phone number/email",
"不正确手机号":"Please enter correct phone number or email",
"请输入验证码":"Please enter verification code",
"登录成功":"Login successful",
"秒":"s",
"发送成功":"Verification code sent successfully",
"发送失败":"Verification code sending failed",
"登录失败":"Login failed"
},
"修改资料页":{
"个人信息":"Personal Information",
"保存":"Save",
"点击更换头像":"Click to change avatar",
"保存成功":"Save successful!",
"保存失败":"Save failed!"
},
"设置页":{
"标题":"Settings",
"主题模式":"Theme Mode",
"选择语言":"Language",
"关于我们":"About Us",
"用户协议":"User Agreement",
"隐私协议":"Privacy Policy",
"退出登录":"Logout",
"注销账号":"Delete Account",
"退出成功":"Logout successful",
"退出失败":"Logout failed"
},
"关于我们":{
"标题":"About Us"
},
"服务器":{
"失败":"Server internal error, please contact administrator"
},
"体征检测设备":{
"标题":"Vital Sign Monitoring Device",
"输入关键词":"Enter keywords",
"搜索":"Search",
"我的e护":"My eCare",
"云关爱":"Cloud Care",
"设备ID":"Device ID",
"更新时间":"Update time",
"设备来源":"Device source",
"设备状态":"Device status status",
"人员资料":"person",
"实时体征":"bodyData",
"消息回看":"message",
"健康报告":"report",
"首页展示":"Show on Home",
"设备详情":"Device Details",
"重命名":"Rename",
"删除":"Delete"
},
"设备详情":{
"标题":"Device Details",
"MAC":"MAC",
"设备来源":"Device source",
"型号":"Model",
"设备名称":"Device name",
"网络状态":"Network status",
"版本":"Version",
"更新状态":"Update status",
"更新时间":"Update time",
"故障状态":"Fault status"
},
"实时体征":{
"标题":"Real-time Data",
"姓名":"Name",
"年龄":"Age",
"设备ID":"Device ID",
"体重":"Weight",
"提示":"Note: During real-time vital sign analysis, the user should lie on the designated bed with the device functioning normally. Keep the body still for about 10-30 seconds to display real-time vital sign data."
},
"待开发":{
"提示":"Feature in development..."
},
"扫一扫":{
"标题":"Scan",
"提示":"Please scan device QR code",
"相册":"Album",
"手电筒":"Flashlight"
},
"设备":{
"设备列表请求失败":"Device list request failed",
"设备列表请求成功":"Device list request successful"
},
"未命名":"Unnamed",
"未知时间":"-",
"设备ID":"ID",
"更新时间":"Update time",
"已分享":"Shared",
"设备来源":"Device source",
"云关爱":"Cloud Care",
"是否确认解绑":"Confirm to unbind?",
"请求失败":"Request failed!",
"操作成功":"Operation successful!",
"操作失败":"Operation failed!",
"暂无数据":"No data available",
"请输入姓名":"Please enter name",
"请选择生日":"Please select birthday",
"请输入体重":"Please enter weight",
"必须登录提示":"Please login first!",
"待开发功能":"Feature in development...",
"未知数据":"-",
"在离床":"On/Off Bed",
"体动":"Movement",
"心率":"Heart Rate",
"打鼾":"Snoring",
"呼吸":"Breathing",
"呼吸暂停":"Apnea",
"请保持静止":"Please keep still",
"健康报告":"Health Report",
"修改人员名称":"Modify user name",
"在线":"Online",
"离线":"Offline",
"有更新":"Update available",
"无更新":"No update",
"有故障":"Fault detected",
"无故障":"No fault",
"人":"Person",
"WIFI配置":"WiFi Configuration",
"分享设备":"Share Device",
"消息设置":"Message Settings",
"设备分享":"Device Sharing",
"请输入对方手机号或邮箱":"Please enter recipient's phone number or email",
"微信好友一键分享":"Share with WeChat friends",
"发送邀请":"Send Invitation",
"要分享的设备":"Device to share",
"主设备":"Main device:",
"从设备":"Secondary device:",
"邀请成功":"Invitation sent successfully!",
"邀请失败":"Invitation failed!",
"请输入手机号或者邮箱":"Please enter phone number or email",
"请输入正确的手机号或者邮箱":"Please enter correct phone number or email",
"体征消息":"Vital Sign Messages",
"系统消息":"System Messages",
"无需处理":"No action needed",
"待处理":"Pending",
"已处理":"Done",
"已超时":"Timeout",
"是否确认接受该设备":"Confirm to accept this device?",
"信号强度":"Signal strength:",
"设备升级":"Device Upgrade",
"设备故障":"Device malfunction!",
"是否在床":"On bed:",
"在床":"On",
"离床":"Off",
"较弱":"Very weak",
"弱":"Weak",
"一般":"Normal",
"强":"Strong",
"网络状态":"Network status:",
"确认":"Confirm",
"输入关键字":"Enter keywords",
"搜索":"Search",
"名称":"Name:",
"账号":"Account:",
"分享时间":"Share time:",
"设备校准":"Device Calibration",
"离床校准":"Off-bed Calibration",
"未完成":"Incomplete",
"已完成":"Completed",
"位置校准":"Position Calibration",
"床头":"Bed head",
"离床校准提示":"Please have the calibrator temporarily leave the bed and keep the surroundings quiet during calibration",
"位置校准提示":"Please have the calibrator lie flat on the designated side (indicated by arrow) for 10 seconds before clicking start",
"开始校准":"Start Calibration",
"校准完成":"Calibration completed!",
"微信安装提示":"Please install WeChat APP first to use WeChat login",
"安卓启用网络提示":"",
"ios启用网络提示":"",
"设备报修":"Device Repair",
"联系人":"Contact",
"手机号":"Phone number",
"名称输入提示":"Please enter contact name",
"手机号输入提示":"Please enter contact phone number",
"提交":"Submit",
"设备参数":"Parameters",
"问题描述":"Problem description (within 60 characters)",
"上传提示":"Click to upload device fault photos or videos",
"设备参数提示":"Please enter device parameters",
"选择设备":"Please select device",
"上传限制":"File cannot exceed 5MB",
"选择语言":"Select Language",
"设备列表":"device list",
"图片未上传": "Image not uploaded",
"设备未选择": "Device not selected",
"参数未填写": "Parameters not filled in",
"问题描述未填写": "Please upload an image or video",
"编辑":"edit",
"滑动退出提醒": "Press again to exit the app",
"操作说明":"help guide",
"关注我们":"Follow Us",
"申请记录": "Application Record",
"设备类型": "Device Type",
"发生时间": "Time of Occurrence",
"最小信号强度": "Minimum RSSI",
"匹配出的外围设备": "Matched Peripheral Device",
"连接中...": "connecting",
"刷新":"refresh",
"版本":"version"
}

View File

@@ -1,4 +1,5 @@
{
"语言名称":"中文简体",
"菜单": {
"首页": "首页",
"报告": "报告",
@@ -30,7 +31,7 @@
"当前版本": "当前版本",
"未命名": "未命名",
"去登录": "去登录",
"头像限制": "头像图片不能超过1MB",
"头像限制": "头像图片不能超过5MB",
"头像上传失败":"头像上传失败",
"未选择图片":"未选择图片",
"上传成功":"上传成功",
@@ -39,13 +40,11 @@
"昵称为空":"昵称不能为空",
"查询失败":"查询用户资料失败"
},
"设备类型": {
"我的设备": "我的设备",
"请选择设备类型": "请选择设备类型",
"体征监测设备": "体征监测设备",
"智能床/床垫": "智能床/床垫",
"摄像头": "摄像头"
},
"摄像头": "摄像头",
"绑定引导":{
"说明标题": "设备放置说明:",
"说明正文": "该体征监测设备在床垫下方使用(注意贴有标签的一面朝上),放置在靠近使用人胸腔正下方为宜。放置完成后,连接电源,控制盒绿灯快闪时,可以进行绑定流程。",
@@ -203,7 +202,6 @@
"设备详情":"设备详情",
"重命名":"重命名",
"删除":"删除"
},
"设备详情":{
"标题":"设备详情",
@@ -287,5 +285,70 @@
"请输入手机号或者邮箱":"请输入手机号或者邮箱",
"请输入正确的手机号或者邮箱":"请输入正确的手机号或者邮箱",
"体征消息":"体征消息",
"系统消息":"系统消息"
"系统消息":"系统消息",
"无需处理":"无需处理",
"待处理":"待处理",
"已处理":"已处理",
"已超时":"已超时",
"是否确认接受该设备":"是否确认接受该设备?",
"信号强度":"信号强度:",
"设备升级":"设备升级",
"设备故障":"设备发生故障!",
"是否在床":"是否在床:",
"在床":"在床",
"离床":"离床",
"较弱":"较弱",
"弱":"弱",
"一般":"一般",
"强":"强",
"网络状态":"网络状态:",
"确认":"确认",
"输入关键字":"输入关键字",
"搜索":"搜索",
"名称":"名称:",
"账号":"账号:",
"分享时间":"分享时间:",
"设备校准":"设备校准",
"离床校准":"离床校准",
"未完成":"未完成",
"已完成":"已完成",
"位置校准":"位置校准",
"床头":"床头",
"离床校准提示":"请校准人员暂时离开床铺且在校准期间保持周围安静",
"位置校准提示":"请校准人员到箭头指定一侧 平躺后点击开始保持10秒",
"开始校准":"开始校准",
"校准完成":"校准完成!",
"微信安装提示":"请先安装微信APP再使用微信登录",
"安卓启用网络提示":"",
"ios启用网络提示":"",
"设备报修":"设备报修",
"联系人":"联系人",
"手机号":"手机号",
"名称输入提示":"请输入联系人名称",
"手机号输入提示":"请输入联系人手机号",
"提交":"提交",
"设备参数":"设备参数",
"问题描述":"问题描述60个字以内",
"上传提示":"点击上传设备故障照片或视频",
"设备参数提示":"请输入设备参数",
"选择设备":"请选择设备",
"上传限制":"文件不能超过5MB",
"选择语言":"选择语言",
"设备列表":"设备列表",
"图片未上传":"图片未上传",
"设备未选择":"设备未选择",
"参数未填写":"参数未填写",
"问题描述未填写":"请上传图片或视频",
"编辑":"编辑",
"滑动退出提醒":"再按一次退出程序",
"操作说明":"操作说明",
"关注我们":"关注我们",
"申请记录":"申请记录",
"设备类型":"设备类型",
"发生时间":"发生时间",
"最小信号强度":"最小信号强度",
"匹配出的外围设备":"发生时间",
"连接中...": "连接中",
"刷新":"刷新",
"版本":"版本:"
}

352
assets/langs/zh_Hant.json Normal file
View File

@@ -0,0 +1,352 @@
{
"语言名称":"中文繁體",
"菜单": {
"首页": "首頁",
"报告": "報告",
"小e": "小e",
"消息": "消息",
"我的": "我的"
},
"首页": {
"登录": "登錄",
"提示标题": "尊敬的用戶您好!綁定前請注意以下幾點:",
"提示内容1": "1.傳感器是否通電成功,控制盒呈綠燈快閃狀態;",
"提示内容2": "2.對APP進行藍牙和定位服務授權",
"提示内容3": "3.若使用掃描功能,請對攝像頭進行授權。",
"扫一扫绑定": "掃描添加新設備",
"蓝牙绑定": "藍牙搜索附近設備",
"已关联体征监测设备": "已關聯體徵監測設備",
"我的e护": "我的e護",
"云关爱": "雲關愛",
"报告详情": "報告詳情"
},
"我的": {
"个人信息": "個人資料",
"未登录": "未登入",
"账号": "帳號:",
"我的设备": "我的設備",
"设备报修": "設備報修",
"操作说明": "操作說明",
"关注我们": "關注我們",
"当前版本": "當前版本",
"未命名": "未命名",
"去登录": "去登錄",
"头像限制": "頭像圖片不能超過5MB",
"头像上传失败":"頭像上傳失敗",
"未选择图片":"未選擇圖片",
"上传成功":"上傳成功",
"保存失败":"保存失敗",
"保存成功":"保存成功",
"昵称为空":"暱稱不能為空",
"查询失败":"查詢用戶資料失敗"
},
"我的设备": "我的設備",
"请选择设备类型": "請選擇設備類型",
"体征监测设备": "體徵監測設備",
"智能床/床垫": "智能床/床墊",
"摄像头": "攝像頭",
"绑定引导":{
"说明标题": "設備放置說明:",
"说明正文": "該體徵監測設備在床墊下方使用(注意貼有標籤的一面朝上),放置在靠近使用人胸腔正下方為宜。放置完成後,連接電源,控制盒綠燈快閃時,可以進行綁定流程。",
"不再提示": "不再提示",
"跳过": "跳過"
},
"蓝牙绑定":{
"标题":"藍牙綁定",
"扫描":"掃描藍牙設備中…",
"信号":"最小信號強度",
"搜索提示":"檢索設備",
"搜索":"搜索",
"匹配":"匹配出的外圍設備",
"信号强度":"信號強度",
"SN":"SN",
"蓝牙地址":"藍牙地址",
"mac":"mac",
"网络":"網絡",
"在线":"在線",
"离线":"離線",
"版本":"版本",
"默认设备名称":"未知設備",
"传感器":"傳感器",
"可绑定":"可綁定",
"已被绑定":"已被綁定",
"双人版绑定标题":"該設備為雙人版,請選擇",
"绑定全部":"綁定全部",
"主设备":"主設備:",
"从设备":"從設備:",
"确定":"確定",
"取消":"取消",
"无法绑定":"無法綁定!",
"无法绑定1":"檢測到該設備",
"无法绑定2":"已被綁定",
"无法绑定3":",綁定前請先進行解綁,有疑問請聯繫客服",
"知道了":"知道了",
"是":"是",
"否":"否",
"确定绑定提示":"確定綁定該設備嗎?",
"连接成功":"連接成功",
"连接异常":"連接異常",
"连接":"連接",
"输入wifi密码":"請輸入wifi密碼",
"显示密码":"顯示",
"不显示密码":"不顯示"
},
"登录页":{
"欢迎使用太和e护":"歡迎使用太和e護",
"科技睡眠 洞悉万千":"科技睡眠 洞悉萬千",
"本机号码一键登录/注册":"本機號碼一鍵登錄/註冊",
"其他手机号码":"其他手機號碼",
"协议1":"登錄時將自動註冊,且代表您同意",
"协议2":"《用戶協議》",
"协议3":"和",
"协议4":"《隱私政策》",
"协议5":"以及",
"协议6":"《用戶使用條款》",
"其他登录方式":"歡迎使用太和e護",
"未同意协议":"請確認同意協議"
},
"人员资料":{
"标题":"人員資料",
"保存":"保存",
"名字输入提示":"使用人員姓名",
"生日输入提示":"生日",
"体重输入提示":"體重",
"疾病标题":"慢病管理",
"提示":"提示:填寫準確的使用人員相關資料,可以使睡眠報告監測數據更加精準!"
},
"绑定成功":{
"标题":"綁定完成",
"绑定成功":"綁定成功! ",
"分享标题":"是否進行分享?",
"分享内容":"設備綁定成功后,如需對朋友或家人共享我的睡眠情況,可以進行立即分享,分享成功后,對方即可享受查看該設備權限,可以收到該設備的睡眠報告。",
"立即分享":"立即分享",
"返回":"返回首頁 開啟體驗"
},
"日期":{
"取消":"取消",
"确定":"確定",
"年":"年",
"月":"月",
"日":"日"
},
"wifi页":{
"标题":"WIFI配置",
"跳过":"跳過",
"WLAN":"WLAN",
"未连接":"未連接",
"已连接":"已連接",
"可用WLAN":"可用WLAN",
"刷新":"刷新",
"密码为空":"密碼不能為空",
"配网成功":"配網成功",
"配网失败":"配網失敗",
"配网中":"配網中",
"需配网":"請給設備配置網絡!"
},
"其他手机登录页":{
"输入内容":"輸入手機號碼/郵箱",
"输入验证码":"輸入驗證碼",
"获取验证码":"獲取驗證碼",
"登录":"登錄",
"请输入手机号":"請輸入手機號/郵箱",
"不正确手机号":"請輸入正確的手機號或者郵箱",
"请输入验证码":"請輸入驗證碼",
"登录成功":"登錄成功",
"秒":"秒",
"发送成功":"驗證碼發送成功",
"发送失败":"驗證碼發送失敗",
"登录失败":"登錄失敗"
},
"修改资料页":{
"个人信息":"個人信息",
"保存":"保存",
"点击更换头像":"點擊更換頭像",
"保存成功":"保存成功!",
"保存失败":"保存失敗!"
},
"设置页":{
"标题":"設置",
"主题模式":"主題模式",
"选择语言":"選擇語言",
"关于我们":"關於我們",
"用户协议":"用戶協議",
"隐私协议":"隱私協議",
"退出登录":"退出登錄",
"注销账号":"註銷賬號",
"退出成功":"退出成功",
"退出失败":"退出失敗"
},
"关于我们":{
"标题":"關於我們"
},
"服务器":{
"失败":"服務器內部錯誤,請聯繫管理員"
},
"体征检测设备":{
"标题":"體徵監測設備",
"输入关键词":"輸入關鍵詞",
"搜索":"搜索",
"我的e护":"我的e護",
"云关爱":"雲關愛",
"设备ID":"設備ID",
"更新时间":"更新時間",
"设备来源":"設備來源",
"设备状态":"設備狀態",
"人员资料":"人員資料",
"实时体征":"實時體徵",
"消息回看":"消息回看",
"健康报告":"健康報告",
"首页展示":"首頁展示",
"设备详情":"設備詳情",
"重命名":"重命名",
"删除":"刪除"
},
"设备详情":{
"标题":"設備詳情",
"MAC":"MAC",
"设备来源":"設備來源",
"型号":"型號",
"设备名称":"設備名稱",
"网络状态":"網絡狀態",
"版本":"版本",
"更新状态":"更新狀態",
"更新时间":"更新時間",
"故障状态":"故障狀態"
},
"实时体征":{
"标题":"實時體徵",
"姓名":"姓名",
"年龄":"年齡",
"设备ID":"設備ID",
"体重":"體重",
"提示":"提示實時體徵分析時人員應躺在指定床位上且設備正常運行保持身體靜止不動狀態下10~30秒左右即可顯示實時體徵數據。"
},
"待开发":{
"提示":"功能開發中..."
},
"扫一扫":{
"标题":"掃一掃",
"提示":"請掃描設備二維碼",
"相册":"相冊",
"手电筒":"手電筒"
},
"设备":{
"设备列表请求失败":"設備列表請求失敗",
"设备列表请求成功":"設備列表請求成功"
},
"未命名":"未命名",
"未知时间":"-",
"设备ID":"設備ID",
"更新时间":"更新時間",
"已分享":"已分享",
"设备来源":"設備來源",
"云关爱":"雲關愛",
"是否确认解绑":"是否確認解綁?",
"请求失败":"請求失敗!",
"操作成功":"操作成功!",
"操作失败":"操作失敗!",
"暂无数据":"暫無數據",
"请输入姓名":"請輸入姓名",
"请选择生日":"請選擇生日",
"请输入体重":"請輸入體重",
"必须登录提示":"請先登錄!",
"待开发功能":"功能開發中...",
"未知数据":"-",
"在离床":"在離床",
"体动":"體動",
"心率":"心率",
"打鼾":"打鼾",
"呼吸":"呼吸",
"呼吸暂停":"呼吸暫停",
"请保持静止":"請保持身體靜止",
"健康报告":"健康報告",
"修改人员名称":"修改人員名稱",
"在线":"在線",
"离线":"離線",
"有更新":"有更新",
"无更新":"無更新",
"有故障":"有故障",
"无故障":"無故障",
"人":"人",
"WIFI配置":"WIFI配置",
"分享设备":"分享設備",
"消息设置":"消息設置",
"设备分享":"設備分享",
"请输入对方手机号或邮箱":"請輸入對方手機號或郵箱",
"微信好友一键分享":"微信好友一鍵分享",
"发送邀请":"發送邀請",
"要分享的设备":"要分享的設備",
"主设备":"主設備:",
"从设备":"從設備:",
"邀请成功":"邀請成功!",
"邀请失败":"邀請失敗!",
"请输入手机号或者邮箱":"請輸入手機號或者郵箱",
"请输入正确的手机号或者邮箱":"請輸入正確的手機號或者郵箱",
"体征消息":"體徵消息",
"系统消息":"系統消息",
"无需处理":"無需處理",
"待处理":"待處理",
"已处理":"已處理",
"已超时":"已超時",
"是否确认接受该设备":"是否確認接受該設備?",
"信号强度":"信號強度:",
"设备升级":"設備升級",
"设备故障":"設備發生故障!",
"是否在床":"是否在床:",
"在床":"在床",
"离床":"離床",
"较弱":"較弱",
"弱":"弱",
"一般":"一般",
"强":"強",
"网络状态":"網絡狀態:",
"确认":"確認",
"输入关键字":"輸入關鍵字",
"搜索":"搜索",
"名称":"名稱:",
"账号":"賬號:",
"分享时间":"分享時間:",
"设备校准":"設備校準",
"离床校准":"離床校準",
"未完成":"未完成",
"已完成":"已完成",
"位置校准":"位置校準",
"床头":"床頭",
"离床校准提示":"請校準人員暫時離開床鋪且在校準期間保持周圍安靜",
"位置校准提示":"請校準人員到箭頭指定一側 平躺後點擊開始保持10秒",
"开始校准":"開始校準",
"校准完成":"校準完成!",
"微信安装提示":"請先安裝微信APP再使用微信登錄",
"安卓启用网络提示":"",
"ios启用网络提示":"",
"设备报修":"設備報修",
"联系人":"聯繫人",
"手机号":"手機號",
"名称输入提示":"請輸入聯繫人名稱",
"手机号输入提示":"請輸入聯繫人手機號",
"提交":"提交",
"设备参数":"設備參數",
"问题描述":"問題描述60個字以內",
"上传提示":"點擊上傳設備故障照片或視頻",
"设备参数提示":"請輸入設備參數",
"选择设备":"請選擇設備",
"上传限制":"文件不能超過5MB",
"选择语言":"選擇語言",
"设备列表":"設備列表",
"图片未上传":"圖片未上傳",
"设备未选择":"設備未選擇",
"参数未填写":"參數未填寫",
"问题描述未填写":"請上傳圖片或視頻",
"编辑":"編輯",
"滑动退出提醒":"再按一次退出程序",
"操作说明":"操作說明",
"关注我们":"關注我們",
"申请记录":"申請記錄",
"设备类型":"設備類型",
"发生时间":"發生時間",
"最小信号强度":"最小信號強度",
"匹配出的外围设备":"發生時間",
"连接中...": "連接中",
"刷新":"刷新",
"版本":"版本:"
}

View File

@@ -16,8 +16,14 @@ class ServiceConstant {
static String sleep_report = "/api/device/sleep/data";//睡眠报告
static String device_share = "/api/device/share";//分享设备
static String message_list = "/api/mesasge/list";//消息列表
static String message_read = "/api/mesasge/read";//消息未读数量
static String device_show = "/api/device/bind";//更新设备绑定
static String disease_list = "/api/personnel/disease/list";//获取疾病类型
static String share_deleted = "";//删除分享
static String start_calibration = "";//开始校准
static String calibration_process = "";//校准进度
static String submit_repair = "";//提交报修

View File

@@ -23,4 +23,8 @@ class AppConstants {
double small_text_fontSize = 20.rpx; //普通文字字号
double normal_text_fontSize = 26.rpx; //普通文字字号
double title_text_fontSize = 30.rpx; //标题文字字号
double dropdown_height = 90.rpx; //标题文字字号
}

View File

@@ -1,32 +0,0 @@
// lib/api_endpoints.dart
import 'package:vbvs_app/common/util/CommonVariables.dart';
class ApiEndpoints {
// Base URL of the API
static String baseUrl = '${CommonVariables.apiUrl}/api';
// Endpoints
static String signIn = '$baseUrl/auth/account/info/signIn'; //登录
static String getUserInfo = '$baseUrl/user/info'; //获取用户信息
static String updateProfile = '$baseUrl/user/updateProfile'; //更新用户信息
static String login_code = '$baseUrl/auth/msg/signIn'; //登录验证码
static String wx_auth_code = '$baseUrl/auth/msg/wxAuth'; //微信授权验证码
static String register = '$baseUrl/auth/account/info/signUp'; //注册
static String reset_code = '$baseUrl/auth/msg/restore'; //重置验证码
static String reset_pd = '$baseUrl/auth/account/info/restore/pw'; //重置密码
static String updateUserInfo =
'$baseUrl/auth/account/info/nameAndHead'; //更新用户资料
static String autoLogin = '$baseUrl/auth/account/info/autoLogin'; //自动登录
//订单管理
static String orderList = "/api/order/info/list"; //订单列表
static String orderQianshou = "/api/order/info/get"; //签收
//分享设备
static String deviceInvite = '$baseUrl/api/device/info/share'; //分享设备
// Add more endpoints as needed
//添加/床垫控制日志
static String addLog = '$baseUrl/oplog/add';
}

View File

@@ -0,0 +1,99 @@
import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:ef/ef.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:vbvs_app/common/util/CommonVariables.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
class Checknetwork {
static final Connectivity _connectivity = Connectivity();
static StreamSubscription<List<ConnectivityResult>>? subscription;
static bool isShowDialog = false;
static connectivityResultChange(connectivityResult) {
if (connectivityResult.contains(ConnectivityResult.mobile)) {
print("ConnectivityResult:Mobile network available.");
// Mobile network available.
toInitData();
} else if (connectivityResult.contains(ConnectivityResult.wifi)) {
print("ConnectivityResult:Wi-fi is available.");
toInitData();
// Wi-fi is available.
// Note for Android:
// When both mobile and Wi-Fi are turned on system will return Wi-Fi only as active network type
} else if (connectivityResult.contains(ConnectivityResult.ethernet)) {
print("ConnectivityResult:Ethernet connection available.");
toInitData();
// Ethernet connection available.
} else if (connectivityResult.contains(ConnectivityResult.vpn)) {
print("ConnectivityResult:Vpn connection active.");
toInitData();
// Vpn connection active.
// Note for iOS and macOS:
// There is no separate network interface type for [vpn].
// It returns [other] on any device (also simulator)
} else if (connectivityResult.contains(ConnectivityResult.bluetooth)) {
// Bluetooth connection available.
} else if (connectivityResult.contains(ConnectivityResult.other)) {
// Connected to a network which is not in the above mentioned networks.
print(
"ConnectivityResult:Connected to a network which is not in the above mentioned networks");
toInitData();
} else if (connectivityResult.contains(ConnectivityResult.none)) {
// No available network types
print("ConnectivityResult:No available network types");
}
}
static toInitData() async {
CommonVariables.isNetWorkOn = true;
if (isShowDialog) {
Get.back();
isShowDialog = false;
}
}
static checkNetwork() async {
// Platform messages may fail, so we use a try/catch PlatformException.
try {
connectivityResultChange(await (_connectivity.checkConnectivity()));
} on PlatformException catch (e) {
print("Couldn't check connectivity status:Error:$e");
}
// 网络状态变化监听,登录成功后移出监听
subscription = _connectivity.onConnectivityChanged
.listen((List<ConnectivityResult> connectivityResult) {
connectivityResultChange(connectivityResult);
});
if (CommonVariables.isNetWorkOn == false) {
int index = 0;
Timer.periodic(const Duration(seconds: 1), (t) {
index++;
if (index > 40 || CommonVariables.isNetWorkOn == true) {
t.cancel();
} else if (index > 20 && isShowDialog == false) {
t.cancel();
var context = Get.context;
String name = "";
if (Platform.isAndroid) {
name = "安卓启用网络提示".tr;
} else {
name = "ios启用网络提示".tr;
}
// isShowDialog = true;
// showCustomConfirmDialog(context!, name).then((d) {
// isShowDialog = false;
// });
showTipDialog(context!,Text(name,style: TextStyle(
color: themeController.currentColor.sc3,
),));
}
});
}
}
}

View File

@@ -1,21 +1,6 @@
class CommonVariables {
static bool isNetWorkOn = false;
static bool test = false;
static String supabaseUrl = "https://mht1.he-info.cn";
// static String wsUrl = "ws://mht-server.he-info.cn/ws";
// static String apiUrl = "https://mht-server.he-info.cn";
// static String shoph5Url = "https://mht-web.he-info.cn";
// static String supabaseUrl = "https://zhmht.swes.com.cn:3443";
// static String wsUrl = "wss://zhmht.swes.com.cn:8089/ws";
// static String apiUrl = "https://zhmht.swes.com.cn:8089";
static String wsUrl = "ws://192.168.1.129:8088/ws";
static String apiUrl = "http://192.168.1.129:8088";
static String shoph5Url = "https://zhmht.swes.com.cn:1443";
static String sleepUrl = "https://alltoone.he-info.cn";
static String efKey = "ef_key";
// // android app 内部更新地址
// static String androidInternalUpgradeUrl = "http://192.168.0.112:1234";
// 企业微信客服拉起的url地址
static String wxKfUrl = "https://work.weixin.qq.com/kfid/kfc7d2337b9c07b1269";
@@ -28,10 +13,5 @@ class CommonVariables {
//备案时间
static String ICPTime = "2022-2025";
// 分享复制文字信息
static String shareText = "您的朋友邀请您使用《智慧眠花糖》APP请复制后面链接在浏览器中打开 " +
shoph5Url +
"/#/pages/download/download";
static Map<String, Function(dynamic)> callMap = {};
}

View File

@@ -3,22 +3,15 @@ import 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/CommonVariables.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/controller/setting/language/language_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/model/api_response.dart';
ThemeController themeController = Get.find();
Future<void> initDataEf({String key = ""}) async {
await ef.init(
'${CommonVariables.supabaseUrl}/',
key,
nosqlEnableWebsocket: false,
);
}
LanguageController languageController = Get.find();
class MyUtils {
static ApiResponse formatResponse(
@@ -330,15 +323,6 @@ String time_08_Formatter_pattern(String time, String pattern) {
return DateFormat(pattern).format(DateTime.parse(time).toLocal());
}
String storagePubSrc =
"${CommonVariables.supabaseUrl}/storage/v1/object/public/";
getStorageResourceUrl(String v) {
if (v.contains('http')) {
return v;
}
return storagePubSrc + v;
}
enum LoadingDialogIcon { ble, wifi, none }

View File

@@ -0,0 +1,105 @@
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:ef/ef.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';
Future<ApiResponse> requestWithLog({
required String logTitle,
required MyHttpMethod method,
required String queryUrl,
Map<String, dynamic>? data,
String successMsg = "操作成功",
String errorMsg = "操作失败",
void Function(ApiResponse res)? onSuccess,
void Function(ApiResponse res)? onFailure,
}) async {
EasyDartModule.logger.info(logTitle);
DailyLogUtils.writeLog(logTitle);
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
try {
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
successMsg = successMsg.tr;
errorMsg = errorMsg.tr;
var response;
switch (method) {
case MyHttpMethod.get:
response = await EasyDartModule.dio.get(queryUrl);
break;
case MyHttpMethod.post:
response = data != null
? await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data))
: await EasyDartModule.dio.post(queryUrl);
break;
case MyHttpMethod.put:
response = data != null
? await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data))
: await EasyDartModule.dio.put(queryUrl);
break;
case MyHttpMethod.delete:
response = data != null
? await EasyDartModule.dio.delete(queryUrl, data: jsonEncode(data))
: await EasyDartModule.dio.delete(queryUrl);
break;
}
if (response != null) {
final responseData =
response.data is String ? jsonDecode(response.data) : response.data;
apiResponse = ApiResponse.fromJson(responseData, (object) => object);
if (apiResponse.code == HttpStatusCodes.ok) {
MyUtils.formatResponse(apiResponse, successMsg, errorMsg);
onSuccess?.call(apiResponse);
} else {
apiResponse.msg = responseData['msg'] ?? errorMsg;
onFailure?.call(apiResponse);
}
return apiResponse;
} else {
apiResponse = ApiResponse(code: -1, msg: "服务器.失败".tr);
onFailure?.call(apiResponse);
return apiResponse;
}
} catch (e) {
EasyDartModule.logger.error("$logTitle 失败->$e");
DailyLogUtils.writeError("$logTitle 失败->$e");
onFailure?.call(apiResponse);
return apiResponse;
}
}
enum MyHttpMethod { get, post, put, delete }
extension HttpMethodExtension on MyHttpMethod {
String get name {
switch (this) {
case MyHttpMethod.get:
return 'GET';
case MyHttpMethod.post:
return 'POST';
case MyHttpMethod.put:
return 'PUT';
case MyHttpMethod.delete:
return 'DELETE';
}
}
}

View File

@@ -43,7 +43,7 @@ class _TestWidgetState extends State<NullDataWidget> {
child: SvgPicture.asset(
'assets/img/icon/nulldata.svg',
fit: BoxFit.cover,
color: themeController.currentColor.sc4,
color: themeController.currentColor.sc4.withOpacity(0.5),
),
),
),

View File

@@ -0,0 +1,264 @@
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/common/util/MyUtils.dart'; // 假设你自己扩展的 .rpx
import 'SleepdateWidget.dart'; // 导入你自定义的 SleepdateWidget
class SleepCalendarWidget extends StatefulWidget {
final int? timestamp; // 可选时间戳
const SleepCalendarWidget({super.key, this.timestamp});
@override
State<SleepCalendarWidget> createState() => _SleepCalendarWidgetState();
}
class _SleepCalendarWidgetState extends State<SleepCalendarWidget> {
late DateTime _currentDate;
@override
void initState() {
super.initState();
_currentDate = widget.timestamp != null
? DateTime.fromMillisecondsSinceEpoch(widget.timestamp!)
: DateTime.now();
}
List<DateTime> getDaysInMonth(DateTime date) {
// 获取当前月份的第一天
DateTime firstDayOfMonth = DateTime(date.year, date.month, 1);
// 获取当前月份的最后一天
DateTime lastDayOfMonth = DateTime(date.year, date.month + 1, 0);
List<DateTime> days = [];
// 获取该月的所有日期
for (int i = 0; i < lastDayOfMonth.day; i++) {
days.add(firstDayOfMonth.add(Duration(days: i)));
}
return days;
}
List<List<DateTime>> getCalendarRows(List<DateTime> daysInMonth) {
// 获取该月所有日期后,处理为 7 列的格式
List<List<DateTime>> calendarRows = [];
int firstWeekday = daysInMonth.first.weekday; // 获取该月第一天是周几
int emptyDays = (firstWeekday == 7 ? 0 : firstWeekday) - 1; // 调整为空白天数
List<DateTime> row = [];
for (int i = 0; i < emptyDays; i++) {
row.add(DateTime(0)); // 填充空白日期
}
for (var day in daysInMonth) {
row.add(day);
if (row.length == 7) {
// 如果当前行满了 7 个日期,则添加到 calendarRows 中,并重置 row
calendarRows.add(List.from(row));
row.clear();
}
}
if (row.isNotEmpty) {
// 如果最后一行的日期不足 7 个,则补充空白日期
while (row.length < 7) {
row.add(DateTime(0)); // 填充空白日期
}
calendarRows.add(List.from(row)); // 添加最后一行
}
return calendarRows;
}
@override
Widget build(BuildContext context) {
List<Map<String, dynamic>> showLabel = [
{
"level": 1,
"name": "优秀",
"color": Color(0xFF4CAF50), // 绿色
},
{
"level": 2,
"name": "良好",
"color": Color(0xFF8BC34A), // 浅绿
},
{
"level": 3,
"name": "合格",
"color": Color(0xFFFFC107), // 黄色
},
{
"level": 4,
"name": "注意",
"color": Color(0xFFF44336), // 红色
},
{
"level": 5,
"name": "无报告",
"color": Color(0xFF9E9E9E), // 灰色
},
];
List sleepData = [];
// 获取当前月的所有日期
List<DateTime> daysInMonth = getDaysInMonth(_currentDate);
// 获取按行排列的日期
List<List<DateTime>> calendarRows = getCalendarRows(daysInMonth);
return Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
topLeft: Radius.circular(20.rpx),
topRight: Radius.circular(20.rpx),
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: double.infinity,
// height: MediaQuery.sizeOf(context).height * 0.055,
constraints: BoxConstraints(
minHeight: 90.rpx,
),
decoration: BoxDecoration(
color: const Color(0xFF313541),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(0),
bottomRight: Radius.circular(0),
topLeft: Radius.circular(20.rpx),
topRight: Radius.circular(20.rpx),
),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
65.rpx,
0.rpx,
65.rpx,
0.rpx,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
Icons.arrow_back_ios_new,
color: const Color(0xFF6D6F73),
size: 24.rpx,
),
Text(
'${_currentDate.year}${_currentDate.month}',
style: TextStyle(
color: Colors.white,
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
Icon(
Icons.arrow_forward_ios,
color: const Color(0xFF6D6F73),
size: 24.rpx,
),
],
),
),
),
Container(
width: double.infinity,
constraints: BoxConstraints(
minHeight: 720.rpx,
),
decoration: const BoxDecoration(
color: Color(0xFF242835),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
65.rpx,
13.rpx,
65.rpx,
38.rpx,
),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
// Weekdays Header
Container(
constraints: BoxConstraints(minHeight: 90.rpx),
child: Row(
children: [
for (var day in ["", "", "", "", "", "", ""])
Expanded(
child: Center(
child: Text(
day,
style: TextStyle(
color: stringToColor("#FFFFFF"),
fontSize: AppConstants().normal_text_fontSize,
),
),
),
),
],
),
),
// 日历显示部分
Column(
children: calendarRows.map((week) {
return Row(
mainAxisAlignment: MainAxisAlignment.start, // 保证每一行左对齐
children: week.map((date) {
return Expanded(
child: Padding(
padding: EdgeInsets.all(4.rpx),
child: date.year != 0 // 如果是空白日期就不显示
? SleepdateWidget(date: date)
: SizedBox.shrink(),
),
);
}).toList(),
);
}).toList(),
),
SizedBox(
height: 55.rpx,
),
Wrap(
direction: Axis.horizontal, // 默认是水平排列的,可以去掉这行
spacing: 20.rpx, // 水平间距
runSpacing: 20.rpx, // 垂直间距
children: showLabel.map<Widget>((item) {
return Container(
padding: EdgeInsets.all(5.rpx), // 可选,添加一点间距
child: Row(
mainAxisSize: MainAxisSize.min, // 确保 Row 不会占满整个宽度
children: [
Container(
width: 20.rpx,
height: 20.rpx,
decoration: BoxDecoration(
color: item["color"],
borderRadius:
BorderRadius.circular(10.rpx), // 圆形效果
),
),
SizedBox(width: 8.rpx), // 标签和文本之间的间距
Text(
item["name"],
style: TextStyle(
color: Colors.white,
fontSize: 24.rpx,
),
),
],
),
);
}).toList(),
),
],
),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
class SleepdateWidget extends StatelessWidget {
final DateTime date;
const SleepdateWidget({super.key, required this.date});
@override
Widget build(BuildContext context) {
return Container(
width: 90.rpx,
height: 90.rpx,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.rpx),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(10.rpx, 10.rpx, 10.rpx, 10.rpx),
child: Container(
decoration: BoxDecoration(
color: Color(0xFFDC1C1C), // 默认红色
shape: BoxShape.circle,
),
child: Align(
alignment: AlignmentDirectional(0, 0),
child: Text(
'${date.day}',
style: TextStyle(
color: Colors.white,
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,461 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
/// A dropdown widget that allows the user to select an option from a list of options.
class THFlutterFlowDropDown<T> extends StatefulWidget {
const THFlutterFlowDropDown({
super.key,
this.controller,
this.multiSelectController,
this.hintText,
this.searchHintText,
required this.options,
this.optionLabels,
this.onChanged,
this.onMenuStateChange,
this.onMultiSelectChanged,
this.icon,
this.width,
this.height,
this.maxHeight,
this.fillColor,
this.searchHintTextStyle,
this.hintTextStyle,
this.searchTextStyle,
this.searchCursorColor,
required this.textStyle,
required this.elevation,
required this.borderWidth,
required this.borderRadius,
required this.borderColor,
required this.margin,
this.hidesUnderline = false,
this.disabled = false,
this.isOverButton = false,
this.menuOffset,
this.isSearchable = false,
this.isMultiSelect = false,
this.labelText,
this.labelTextStyle,
this.allowEmpty = true, // 新增参数,默认值为 true
}) : assert(
isMultiSelect
? (controller == null &&
onChanged == null &&
multiSelectController != null &&
onMultiSelectChanged != null)
: (controller != null &&
onChanged != null &&
multiSelectController == null &&
onMultiSelectChanged == null),
);
/// The controller for the dropdown field.
final FormFieldController<T?>? controller;
/// The controller for the multi-select dropdown field.
final FormFieldController<List<T>?>? multiSelectController;
/// The text to display as a hint when no option is selected.
final String? hintText;
/// The text to display as a hint in the search field.
final String? searchHintText;
/// The list of options to display in the dropdown.
final List<T> options;
/// The list of labels corresponding to the options.
final List<String>? optionLabels;
/// A callback function that is called when the selected option changes.
final Function(T?)? onChanged;
final Function(bool)? onMenuStateChange;
/// A callback function that is called when the selected options change in multi-select mode.
final Function(List<T>?)? onMultiSelectChanged;
/// The icon to display in the dropdown field.
final Widget? icon;
/// The width of the dropdown field.
final double? width;
/// The height of the dropdown field.
final double? height;
/// The maximum height of the dropdown menu.
final double? maxHeight;
/// The background color of the dropdown field.
final Color? fillColor;
/// The text style for the search hint text.
final TextStyle? searchHintTextStyle;
final TextStyle? hintTextStyle;
/// The text style for the search text.
final TextStyle? searchTextStyle;
/// The color of the search cursor.
final Color? searchCursorColor;
/// The text style for the dropdown field.
final TextStyle textStyle;
/// The elevation of the dropdown menu.
final double elevation;
/// The width of the dropdown field's border.
final double borderWidth;
/// The border radius of the dropdown field.
final double borderRadius;
/// The color of the dropdown field's border.
final Color borderColor;
/// The margin around the dropdown field.
final EdgeInsetsGeometry margin;
/// Whether to hide the underline of the dropdown field.
final bool hidesUnderline;
/// Whether the dropdown is disabled.
final bool disabled;
/// Whether the dropdown menu is displayed over the button.
final bool isOverButton;
/// The offset of the dropdown menu.
final Offset? menuOffset;
/// Whether the dropdown is searchable.
final bool isSearchable;
/// Whether the dropdown is in multi-select mode.
final bool isMultiSelect;
/// The label text for the dropdown field.
final String? labelText;
/// The text style for the label text.
final TextStyle? labelTextStyle;
/// Whether the dropdown allows empty selection.
final bool allowEmpty;
@override
State<THFlutterFlowDropDown<T>> createState() =>
_FlutterFlowDropDownState<T>();
}
class _FlutterFlowDropDownState<T> extends State<THFlutterFlowDropDown<T>> {
bool get isMultiSelect => widget.isMultiSelect;
FormFieldController<T?> get controller => widget.controller!;
FormFieldController<List<T>?> get multiSelectController =>
widget.multiSelectController!;
T? get currentValue {
final value = isMultiSelect
? multiSelectController.value?.firstOrNull
: controller.value;
return widget.options.contains(value) ? value : null;
}
Set<T> get currentValues {
if (!isMultiSelect || multiSelectController.value == null) {
return {};
}
return widget.options
.toSet()
.intersection(multiSelectController.value!.toSet());
}
Map<T, String> get optionLabels => Map.fromEntries(
widget.options.asMap().entries.map(
(option) => MapEntry(
option.value,
widget.optionLabels == null ||
widget.optionLabels!.length < option.key + 1
? option.value.toString()
: widget.optionLabels![option.key],
),
),
);
EdgeInsetsGeometry get horizontalMargin => widget.margin.clamp(
EdgeInsetsDirectional.zero,
const EdgeInsetsDirectional.symmetric(horizontal: double.infinity),
);
late void Function() _listener;
final TextEditingController _textEditingController = TextEditingController();
List<T>? _previousSelectedValues;
@override
void initState() {
super.initState();
if (isMultiSelect) {
_listener = () {
if (!widget.allowEmpty &&
(multiSelectController.value == null ||
multiSelectController.value!.isEmpty) &&
(widget.options != null && widget.options.isNotEmpty)) {
// showToast("请至少选择一项", color: color_warning);
multiSelectController.value = _previousSelectedValues;
} else {
_previousSelectedValues = List.from(multiSelectController.value!);
}
widget.onMultiSelectChanged!(multiSelectController.value);
};
multiSelectController.addListener(_listener);
} else {
_listener = () {
setState(() {});
widget.onChanged!(controller.value);
};
controller.addListener(_listener);
}
}
@override
void dispose() {
if (isMultiSelect) {
multiSelectController.removeListener(_listener);
} else {
controller.removeListener(_listener);
}
super.dispose();
}
@override
Widget build(BuildContext context) {
final dropdownWidget = _buildDropdownWidget();
return SizedBox(
width: widget.width,
height: widget.height,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.borderRadius),
border: Border.all(
color: widget.borderColor,
width: widget.borderWidth,
),
color: widget.fillColor,
),
child: Padding(
padding: _useDropdown2() ? EdgeInsets.zero : widget.margin,
child: widget.hidesUnderline
? DropdownButtonHideUnderline(child: dropdownWidget)
: dropdownWidget,
),
),
);
}
bool _useDropdown2() =>
widget.isMultiSelect ||
widget.isSearchable ||
!widget.isOverButton ||
widget.maxHeight != null;
Widget _buildDropdownWidget() =>
_useDropdown2() ? _buildDropdown() : _buildLegacyDropdown();
Widget _buildLegacyDropdown() {
return DropdownButtonFormField<T>(
value: currentValue,
hint: _createHintText(),
items: _createMenuItems(),
elevation: widget.elevation.toInt(),
onChanged: widget.disabled ? null : (value) => controller.value = value,
icon: widget.icon,
isExpanded: true,
dropdownColor: widget.fillColor,
focusColor: Colors.transparent,
decoration: InputDecoration(
labelText: widget.labelText == null || widget.labelText!.isEmpty
? null
: widget.labelText,
labelStyle: widget.labelTextStyle,
border: widget.hidesUnderline
? InputBorder.none
: const UnderlineInputBorder(),
),
);
}
Text? _createHintText() => widget.hintText != null
? Text(
widget.hintText!,
style: widget.hintTextStyle,
maxLines: 1,
overflow: TextOverflow.ellipsis, // 超出部分显示省略号
)
: null;
List<DropdownMenuItem<T>> _createMenuItems() => widget.options
.map(
(option) => DropdownMenuItem<T>(
value: option,
child: Padding(
padding: _useDropdown2() ? horizontalMargin : EdgeInsets.zero,
child: Text(optionLabels[option] ?? '', style: widget.textStyle),
),
),
)
.toList();
List<DropdownMenuItem<T>> _createMultiselectMenuItems() => widget.options
.map(
(item) => DropdownMenuItem<T>(
value: item,
// Disable default onTap to avoid closing menu when selecting an item
enabled: false,
child: StatefulBuilder(
builder: (context, menuSetState) {
final isSelected =
multiSelectController.value?.contains(item) ?? false;
return InkWell(
onTap: () {
multiSelectController.value ??= [];
isSelected
? multiSelectController.value!.remove(item)
: multiSelectController.value!.add(item);
multiSelectController.update();
// This rebuilds the StatefulWidget to update the button's text.
setState(() {});
// This rebuilds the dropdownMenu Widget to update the check mark.
menuSetState(() {});
},
child: Container(
height: double.infinity,
padding: horizontalMargin,
child: Row(
children: [
if (isSelected)
const Icon(Icons.check_box_outlined)
else
const Icon(Icons.check_box_outline_blank),
const SizedBox(width: 16),
Expanded(
child: Text(
optionLabels[item]!,
style: widget.textStyle,
),
),
],
),
),
);
},
),
),
)
.toList();
Widget _buildDropdown() {
final overlayColor = WidgetStateProperty.resolveWith<Color?>((states) =>
states.contains(WidgetState.focused) ? Colors.transparent : null);
final iconStyleData = widget.icon != null
? IconStyleData(icon: widget.icon!)
: const IconStyleData();
return DropdownButton2<T>(
value: currentValue,
hint: _createHintText(),
items: isMultiSelect ? _createMultiselectMenuItems() : _createMenuItems(),
iconStyleData: iconStyleData,
buttonStyleData: ButtonStyleData(
elevation: widget.elevation.toInt(),
overlayColor: WidgetStateProperty.all(Colors.transparent),
padding: widget.margin,
),
menuItemStyleData: MenuItemStyleData(
overlayColor: WidgetStateProperty.all(Colors.transparent),
padding: EdgeInsets.zero,
),
dropdownStyleData: DropdownStyleData(
elevation: widget.elevation.toInt(),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4.0),
color: widget.fillColor,
),
isOverButton: widget.isOverButton,
offset: widget.menuOffset ?? Offset.zero,
maxHeight: widget.maxHeight,
padding: EdgeInsets.zero,
),
onChanged: widget.disabled
? null
: (isMultiSelect ? (_) {} : (val) => widget.controller!.value = val),
isExpanded: true,
selectedItemBuilder: (context) => widget.options
.map(
(item) => Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
isMultiSelect
? currentValues
.where((v) => optionLabels.containsKey(v))
.map((v) => optionLabels[v])
.join(', ')
: optionLabels[item]!,
style: widget.textStyle,
maxLines: 1,
),
),
)
.toList(),
dropdownSearchData: widget.isSearchable
? DropdownSearchData<T>(
searchController: _textEditingController,
searchInnerWidgetHeight: 50,
searchInnerWidget: Container(
height: 50,
padding: const EdgeInsets.only(
top: 8,
bottom: 4,
right: 8,
left: 8,
),
child: TextFormField(
expands: true,
maxLines: null,
controller: _textEditingController,
cursorColor: widget.searchCursorColor,
style: widget.searchTextStyle,
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText: widget.searchHintText,
hintStyle: widget.searchHintTextStyle,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
searchMatchFn: (item, searchValue) {
return (optionLabels[item.value] ?? '')
.toLowerCase()
.contains(searchValue.toLowerCase());
},
)
: null,
// This is to clear the search value when you close the menu
onMenuStateChange: (isOpen) {
if (widget.isSearchable && !isOpen) {
_textEditingController.clear();
}
if (widget.onMenuStateChange != null) {
widget.onMenuStateChange!(isOpen);
}
},
);
}
}

View File

@@ -149,7 +149,7 @@ class DynamicReportDetailWidget extends StatelessWidget {
style: FlutterFlowTheme.of(Get.context!).bodyMedium.override(
fontFamily: 'Inter',
fontSize: 28.rpx,
color: themeController.currentColor.sc3,
color: themeController.currentColor.sc4,
),
),
);

View File

@@ -44,7 +44,8 @@ class _SleepDataModuleWidgetState extends State<SleepDataModuleWidget> {
print('点击了离床次数卡片');
},
child: Container(
width: MediaQuery.sizeOf(context).width * 0.27,
// width: MediaQuery.sizeOf(context).width * 0.267,
width: MediaQuery.sizeOf(context).width * 0.267,
constraints: BoxConstraints(
minWidth: 200.rpx,
minHeight: 161.rpx,

View File

@@ -42,8 +42,9 @@ class _SleepDateWidgetState extends State<SleepDateWidget> {
String day = MyUtils.formatDateTimeDay(widget.date);
// 选中时背景色为黑色,否则为透明
Color backgroundColor =
widget.isSelected == true ? Colors.black : Colors.transparent;
Color backgroundColor = widget.isSelected == true
? Colors.black.withOpacity(0.3)
: Colors.transparent;
return ClickableContainer(
backgroundColor: backgroundColor,

View File

@@ -20,9 +20,7 @@ class ClickableContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(splashFactory: InkRipple.splashFactory),
child: Material(
return Material(
color: Colors.transparent,
child: Ink(
decoration: BoxDecoration(
@@ -32,14 +30,13 @@ class ClickableContainer extends StatelessWidget {
child: InkWell(
borderRadius: BorderRadius.circular(borderRadius),
onTap: onTap,
splashColor: highlightColor.withOpacity(0.2),
splashColor: highlightColor.withOpacity(0.5),
child: Padding(
padding: padding,
child: child,
),
),
),
),
);
}
}

View File

@@ -21,10 +21,151 @@ class TopSlideNotification extends StatefulWidget {
this.duration = const Duration(seconds: 2),
});
static OverlayEntry? _currentEntry; // 单例 OverlayEntry
static bool _isShowing = false;
static void show(
BuildContext context, {
String text = '操作成功!',
double fontSize = 16,
Color? textColor,
double slideOffset = 300.0,
Duration duration = const Duration(seconds: 2),
}) {
// 如果已有弹窗,先移除
_removeCurrentEntry();
final overlay = Overlay.of(context);
final entry = OverlayEntry(
builder: (_) => TopSlideNotification(
text: text,
fontSize: fontSize,
textColor: textColor,
slideOffset: slideOffset,
duration: duration,
),
);
_currentEntry = entry;
_isShowing = true;
overlay.insert(entry);
// 自动移除
Future.delayed(duration + const Duration(milliseconds: 500), () {
_removeCurrentEntry();
});
}
static void _removeCurrentEntry() {
if (_currentEntry != null && _isShowing) {
_currentEntry!.remove();
_currentEntry = null;
_isShowing = false;
}
}
@override
State<TopSlideNotification> createState() => _TopSlideNotificationState();
}
/// 工具方法:调用时直接加进 Overlay 上
class _TopSlideNotificationState extends State<TopSlideNotification>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;
bool _isAnimating = false; // 标志位,控制是否正在动画中
@override
void initState() {
super.initState();
// 初始化 AnimationController
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
// 动画初始化完成后调用
SchedulerBinding.instance.addPostFrameCallback((_) {
_startAnimation(); // 调用动画启动方法
});
}
@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(
child: Text(
widget.text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: widget.fontSize,
color: _textColor,
),
),
),
),
),
),
);
}
// 执行动画
Future<void> _startAnimation() async {
if (_isAnimating) return; // 如果正在动画中,则不执行新的动画
_isAnimating = true; // 标记动画开始
try {
await _controller.forward();
await Future.delayed(widget.duration);
// 只有在组件仍然挂载时才执行 reverse 动作
if (mounted) {
await _controller.reverse();
}
} finally {
_isAnimating = false; // 动画完成后,标记动画结束
}
}
// 工具方法:调用时直接加进 Overlay 上
static void show(
BuildContext context, {
String text = '操作成功!',
@@ -50,81 +191,3 @@ class TopSlideNotification extends StatefulWidget {
});
}
}
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

@@ -1,51 +1,79 @@
// import 'package:flutter/material.dart';
// import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';
// class WebViewWidget extends StatefulWidget {
// final String url;
// const WebViewWidget({Key? key, required this.url}) : super(key: key);
class MyWebView extends StatefulWidget {
final String url;
final Function()? onLoad;
final Function(MyWebView view, String msg)? onMessage;
// @override
// _WebViewWidgetState createState() => _WebViewWidgetState();
// }
const MyWebView({
Key? key,
required this.url,
this.onLoad,
this.onMessage,
}) : super(key: key);
// class _WebViewWidgetState extends State<WebViewWidget> {
// late WebViewController _webViewController;
@override
State<MyWebView> createState() => _MyWebViewState();
}
// @override
// void initState() {
// super.initState();
// // 初始化 WebView 控件
// WebView.platform = SurfaceAndroidWebView();
// }
class _MyWebViewState extends State<MyWebView> {
late final WebViewController _controller;
// @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, // 启用手势返回
// ),
// );
// }
// }
@override
void initState() {
super.initState();
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
widget.onLoad?.call();
},
onWebResourceError: (error) {
print("WebView 加载错误: ${error.description}");
},
onNavigationRequest: (NavigationRequest request) {
final url = request.url;
if (url.startsWith('http') || url.startsWith('https')) {
return NavigationDecision.navigate;
}
if (url.startsWith('weixin://')) {
_launchWeChatUrl(url);
return NavigationDecision.prevent;
}
print('拦截未知协议: $url');
return NavigationDecision.prevent;
},
),
)
..addJavaScriptChannel(
'FlutterChannel',
onMessageReceived: (msg) {
widget.onMessage?.call(widget, msg.message);
},
)
..loadRequest(Uri.parse(widget.url));
}
void _launchWeChatUrl(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
print('⚠️ 无法跳转微信: $url');
}
}
// 提供方法给外部调用 JS
void sendData(String data) {
_controller.runJavaScript("window.postMessage('$data')");
}
@override
Widget build(BuildContext context) {
return WebViewWidget(controller: _controller);
}
}

View File

@@ -66,6 +66,8 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
RxMap connect_wifi = {}.obs;
RxString scanMac = "".obs;
String? currentDeviceMac;
// 安全展示 TopSlideNotification
void safeShowNotification(String msg) {
try {
@@ -97,7 +99,7 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
});
if (_statusTimer == null) {
_statusTimer = Timer.periodic(Duration(seconds: 10), (timer) {
_statusTimer = Timer.periodic(Duration(seconds: 2), (timer) {
updateDeviceStatus().then((res) {
if (res.code == HttpStatusCodes.ok) {
updateAll();
@@ -138,6 +140,17 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
} else {
queryUrl += '?$macParams';
}
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
var responseData =
@@ -177,7 +190,7 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
model.betDevicelist = [];
}
print("获取设备状态成功");
updateAll();
return res;
}
@@ -198,6 +211,17 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_bind;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {
"deviceType": 1,
"mac": d.mac,
@@ -209,7 +233,7 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "蓝牙绑定.绑定成功".tr, "蓝牙绑定.绑定成功".tr);
MyUtils.formatResponse(res, "绑定成功.绑定成功".tr, "绑定成功.绑定成功".tr);
if (res.code == HttpStatusCodes.ok) {
PersonController personController = Get.find();
personController.currentPersonId.value = res.data['id'];
@@ -242,6 +266,17 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_bind;
String queryUrl = "$serviceAddress$serviceName$serviceApi";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {
"deviceType": 1,
"mac": mac,
@@ -253,7 +288,7 @@ class BlueteethBindController extends GetControllerEx<BlueteethBindModel> {
response.data is String ? jsonDecode(response.data) : response.data;
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(res, "蓝牙绑定.绑定成功".tr, "蓝牙绑定.绑定成功".tr);
MyUtils.formatResponse(res, "绑定成功.绑定成功".tr, "绑定成功.绑定成功".tr);
if (res.code == HttpStatusCodes.ok) {
return res;
}

View File

@@ -52,6 +52,17 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
String serviceApi = ServiceConstant.device_list;
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?bindNum=1";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =
@@ -83,6 +94,17 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
String serviceApi = ServiceConstant.device_list;
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?bindType=${model.type}${key != null ? '&key=$key' : ''}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =
@@ -114,6 +136,17 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_bind;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
final data = {
"mac": device['mac'],
};
@@ -171,6 +204,17 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
"$serviceAddress$serviceName$serviceApi?mac=$mac&time=${DateTime.now().millisecondsSinceEpoch}";
try {
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData = response.data is String
@@ -228,6 +272,17 @@ class BodyDeviceController extends GetControllerEx<BodyDeviceModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_show;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {
"id": device['_id'],
"show": !device['show'],

View File

@@ -0,0 +1,32 @@
import 'package:ef/ef.dart';
import 'package:json_annotation/json_annotation.dart';
part 'device_calibration_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class DeviceCalibrationModel {
DeviceCalibrationModel();
factory DeviceCalibrationModel.fromJson(Map<String, dynamic> json) {
try {
return _$DeviceCalibrationModelFromJson(json);
} catch (e) {
return DeviceCalibrationModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
}
}
// 序列化为JSON时的异常处理
Map<String, dynamic> toJson() => _$DeviceCalibrationModelToJson(this);
}
class DeviceCalibrationController
extends GetControllerEx<DeviceCalibrationModel> {
DeviceCalibrationController() {
attr = GetModel(DeviceCalibrationModel()).obs;
}
RxInt process = 0.obs; //校准流程 0.离床校准 1.位置校准
RxInt bed_calibration = 0.obs; //0.未完成 1.完成
RxInt position_calibration = 0.obs; //0.未完成 1.完成
RxInt bed_type = 0.obs; //0.单人 1.双人
}

View File

@@ -0,0 +1,15 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'device_calibration_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DeviceCalibrationModel _$DeviceCalibrationModelFromJson(
Map<String, dynamic> json) =>
DeviceCalibrationModel();
Map<String, dynamic> _$DeviceCalibrationModelToJson(
DeviceCalibrationModel instance) =>
<String, dynamic>{};

View File

@@ -46,11 +46,22 @@ class DeviceShareController extends GetControllerEx<DeviceShareModel> {
RxInt code = 0.obs;
Future<ApiResponse> getDeviceType() async {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备类型.请求失败".tr);
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}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =
@@ -87,6 +98,17 @@ class DeviceShareController extends GetControllerEx<DeviceShareModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_share;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {"type": 1, "userName": account.value, "mac": mac};
var response =
await EasyDartModule.dio.post(queryUrl, data: jsonEncode(data));
@@ -104,7 +126,9 @@ class DeviceShareController extends GetControllerEx<DeviceShareModel> {
res.msg = "操作成功".tr;
}
}
if (res.code != HttpStatusCodes.ok) {
msg.value = res.msg!;
}
code.value = res.code!;
updateAll();
return res;
@@ -117,4 +141,56 @@ class DeviceShareController extends GetControllerEx<DeviceShareModel> {
return ApiResponse(code: -1, msg: "服务器.失败".tr);
}
}
//确认接受设备分享
Future<ApiResponse> confirmShare(String shareCode) async {
EasyDartModule.logger.info("确认消息分享");
DailyLogUtils.writeLog("确认消息分享");
try {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
if (shareCode == null || shareCode.isEmpty) {
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}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
final data = {
"code": shareCode,
};
var response =
await EasyDartModule.dio.put(queryUrl, data: jsonEncode(data));
if (response != null) {
if (response.data['code'] != HttpStatusCodes.ok) {
apiResponse.msg = response.data['msg'];
apiResponse.code = response.data['code'];
return apiResponse;
}
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);
}
}
}

View File

@@ -0,0 +1,135 @@
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_list_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class DeviceShareListModel {
String? key; //关键字搜索
int? all = 0; //是否全选 0不全选 1全选
DeviceShareListModel();
// 从JSON反序列化时的异常处理
factory DeviceShareListModel.fromJson(Map<String, dynamic> json) {
try {
return _$DeviceShareListModelFromJson(json);
} catch (e) {
// 在实际应用中,应该有更细致的异常处理策略和错误日志
return DeviceShareListModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
}
}
// 序列化为JSON时的异常处理
Map<String, dynamic> toJson() => _$DeviceShareListModelToJson(this);
}
class DeviceShareListController extends GetControllerEx<DeviceShareListModel> {
DeviceShareListController() {
attr = GetModel(DeviceShareListModel()).obs;
}
RxList shareInfoList = [].obs;
RxList selectedShareInfo = [].obs;
//查询分享信息
Future<ApiResponse> getDeviceShareList(String mac, {String? key}) async {
try {
EasyDartModule.logger.info("设备设备分享列表");
DailyLogUtils.writeLog("设备设备分享列表");
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.device_share;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}"
"${key != null ? '?key=$key&' : '?'}mac=$mac";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
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) {
shareInfoList.value = res.data!;
updateAll();
return res;
}
} else {
return ApiResponse(code: -1, msg: "服务器.失败".tr);
}
return apiResponse;
} catch (e) {
EasyDartModule.logger.error("设备请求列表: $e");
DailyLogUtils.writeError("设备请求列表: $e");
}
return ApiResponse(code: -1, msg: "未知错误".tr);
}
//删除分享
Future<ApiResponse> deleteShareDevice() async {
try {
EasyDartModule.logger.info("删除分享设备");
DailyLogUtils.writeLog("删除分享设备");
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.share_deleted;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
final data = {
"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.error("删除分享设备: $e");
DailyLogUtils.writeError("删除分享设备: $e");
} finally {
EasyDartModule.logger.info("用户操作:删除分享设备");
DailyLogUtils.writeLog("用户操作:删除分享设备");
}
return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement
}
}

View File

@@ -0,0 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'device_share_list_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DeviceShareListModel _$DeviceShareListModelFromJson(
Map<String, dynamic> json) =>
DeviceShareListModel()
..key = json['key'] as String?
..all = (json['all'] as num?)?.toInt();
Map<String, dynamic> _$DeviceShareListModelToJson(
DeviceShareListModel instance) =>
<String, dynamic>{
'key': instance.key,
'all': instance.all,
};

View File

@@ -6,6 +6,7 @@ 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/controller/setting/language/language_controller.dart';
import 'package:vbvs_app/model/api_response.dart';
part 'device_type_controller.g.dart'; // 由json_serializable自动生成的部分
@@ -38,18 +39,33 @@ class DeviceTypeController extends GetControllerEx<DeviceTypeModel> {
attr = GetModel(DeviceTypeModel()).obs;
}
RxList deviceTypeList = [].obs;
LanguageController languageController = Get.find();
Future<ApiResponse> getDeviceType() async {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "设备类型.请求失败".tr);
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}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
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);
ApiResponse res =
ApiResponse.fromJson(responseData, (object) => object);
if (res.code != HttpStatusCodes.ok) {
if (res.msg == null || res.msg!.isEmpty) {
res.msg = apiResponse.msg;
@@ -61,5 +77,8 @@ class DeviceTypeController extends GetControllerEx<DeviceTypeModel> {
} else {
return ApiResponse(code: -1, msg: "服务器.失败".tr);
}
} catch (e) {
return apiResponse;
}
}
}

View File

@@ -4,12 +4,15 @@ import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:fluwx/fluwx.dart';
import 'package:get_storage/get_storage.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/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/time/countdown_controller.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';
@@ -46,9 +49,9 @@ class LoginModel {
class LoginController extends GetControllerEx<LoginModel> {
// 初始化实例
// final Fluwx fluwx = Fluwx();
final Fluwx fluwx = Fluwx();
// 微信监听返回值
// FluwxCancelable? fluwxCancelable;
FluwxCancelable? fluwxCancelable;
// final UserRepository repository = UserRepository();
LoginController() {
@@ -70,6 +73,17 @@ class LoginController extends GetControllerEx<LoginModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.login;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {
"type": 1,
"userName": model.phone,
@@ -83,6 +97,8 @@ class LoginController extends GetControllerEx<LoginModel> {
ApiResponse res = ApiResponse.fromJson(responseData, (object) => object);
MyUtils.formatResponse(apiResponse, "其他手机登录页.登陆成功".tr, "其他手机登录页.登陆失败".tr);
if (res.code == HttpStatusCodes.ok) {
CountdownController countdownController = Get.find();
countdownController.countdown.value = 0;
UserInfoController userInfoController = Get.find();
userInfoController.model.login = 1;
userInfoController.model.user = UserModel.fromJson(res.data);
@@ -118,6 +134,17 @@ class LoginController extends GetControllerEx<LoginModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.send_code;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var data = {
"userName": model.phone,
};
@@ -147,32 +174,34 @@ class LoginController extends GetControllerEx<LoginModel> {
}
//微信登录
// Future<void> wxLoginSendAuth() async {
// /*
// 1、目前移动应用上微信登录只提供原生的登录方式需要用户安装微信客户端才能配合使用。
// 2、对于Android应用建议总是显示微信登录按钮当用户手机没有安装微信客户端时请引导用户下载安装微信客户端。
// 3、对于iOS应用考虑到iOS应用商店审核指南中的相关规定建议开发者接入微信登录时先检测用户手机是否已安装微信客户端
// 使用sdk中isWXAppInstalled函数 ),对未安装的用户隐藏微信登录按钮,只提供其他登录方式(比如手机号注册登录、游客登录等)
// */
// if (isAndroid) {
// bool isWeChatInstalled = await fluwx.isWeChatInstalled;
// debugPrint('is wechat installed: $isWeChatInstalled');
// if (!isWeChatInstalled) {
// showToast("请先安装微信APP再使用微信登录");
// return;
// }
// }
// fluwx
// .authBy(
// which: NormalAuth(
// scope: 'snsapi_userinfo',
// state: 'wechat_sdk_zhmht_wxlogin',
// ))
// .then((data) {
// //返回true表示成功或者false表示失败这边没有意义从login_controller页面构造函数监听中去处理
// debugPrint('msg:$data');
// });
// }
Future<void> wxLoginSendAuth(BuildContext context) async {
/*
1、目前移动应用上微信登录只提供原生的登录方式需要用户安装微信客户端才能配合使用。
2、对于Android应用建议总是显示微信登录按钮当用户手机没有安装微信客户端时请引导用户下载安装微信客户端。
3、对于iOS应用考虑到iOS应用商店审核指南中的相关规定建议开发者接入微信登录时先检测用户手机是否已安装微信客户端
使用sdk中isWXAppInstalled函数 ),对未安装的用户隐藏微信登录按钮,只提供其他登录方式(比如手机号注册登录、游客登录等)
*/
if (isAndroid) {
bool isWeChatInstalled = await fluwx.isWeChatInstalled;
if (!isWeChatInstalled) {
TopSlideNotification.show(context,
text: "微信安装提示".tr, textColor: themeController.currentColor.sc9);
return;
}
}
fluwx
.authBy(
which: NormalAuth(
scope: 'snsapi_userinfo',
state: 'wechat_sdk_theh_wxlogin',
))
.then((data) {
//返回true表示成功或者false表示失败这边没有意义从login_controller页面构造函数监听中去处理
debugPrint('msg:$data');
});
}
loginByWechatCode(String code) {}
//退出登录
// Future<void> logout() async {

View File

@@ -15,6 +15,8 @@ part 'message_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class MessageModel {
int? type = 1; //设备类型 1:体征消息 2.系统消息
int? body_message_read = 0; //体征消息 0已读 1未读
int? system_message_read = 0; //系统消息 0已读 1未读
MessageModel();
@@ -38,7 +40,7 @@ class MessageController extends GetControllerEx<MessageModel> {
attr = GetModel(MessageModel()).obs;
}
RxList msssageList = [].obs;
RxList messageList = [].obs;
Future<ApiResponse> getMessageList({String? key}) async {
try {
@@ -53,7 +55,18 @@ class MessageController extends GetControllerEx<MessageModel> {
messageType = "app_system";
}
String queryUrl =
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}'}";
"${serviceAddress}${serviceName}${serviceApi}?type=${messageType}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =
@@ -63,6 +76,7 @@ class MessageController extends GetControllerEx<MessageModel> {
MyUtils.formatResponse(res, "请求成功".tr, "请求失败".tr);
if (res.code == HttpStatusCodes.ok) {
updateAll();
messageList.value = res.data;
return res;
}
} else {
@@ -75,4 +89,110 @@ class MessageController extends GetControllerEx<MessageModel> {
}
return ApiResponse(code: -1, msg: "未知错误".tr); // Default return statement
}
//获取消息已读未读
Future<ApiResponse> getMessageStatus() async {
try {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "请求失败".tr);
// return apiResponse;
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.message_read;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
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();
List dataList = res.data;
// 查找 type 为 app_vsm 的项
var vsmItem = dataList.firstWhere(
(e) => e['type'] == 'app_vsm',
orElse: () => null,
);
model.body_message_read = vsmItem?['count'] ?? 0;
// 查找 type 为 app_system 的项
var systemItem = dataList.firstWhere(
(e) => e['type'] == 'app_system',
orElse: () => null,
);
model.system_message_read = systemItem?['count'] ?? 0;
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> updateMessageStatus({String? type}) 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.message_read;
// 拼接 URL添加 type 参数
String queryUrl = "$serviceAddress$serviceName$serviceApi";
if (type != null && type.isNotEmpty) {
queryUrl += "?type=$type";
}
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.post(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);
return res;
} else {
return ApiResponse(code: -1, msg: "服务器.失败".tr);
}
} catch (e) {
EasyDartModule.logger.info("更新消息已读状态->$e");
DailyLogUtils.writeLog("更新消息已读状态->$e");
return ApiResponse(code: -1, msg: "服务器.失败".tr);
}
}
}

View File

@@ -6,10 +6,14 @@ part of 'message_controller.dart';
// JsonSerializableGenerator
// **************************************************************************
MessageModel _$MessageModelFromJson(Map<String, dynamic> json) =>
MessageModel()..type = (json['type'] as num?)?.toInt();
MessageModel _$MessageModelFromJson(Map<String, dynamic> json) => MessageModel()
..type = (json['type'] as num?)?.toInt()
..body_message_read = (json['body_message_read'] as num?)?.toInt()
..system_message_read = (json['system_message_read'] as num?)?.toInt();
Map<String, dynamic> _$MessageModelToJson(MessageModel instance) =>
<String, dynamic>{
'type': instance.type,
'body_message_read': instance.body_message_read,
'system_message_read': instance.system_message_read,
};

View File

@@ -76,6 +76,17 @@ class PersonController extends GetControllerEx<PersonModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.person_info;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
if (name.value.isEmpty) {
apiResponse.msg = "请输入姓名".tr;
return apiResponse;
@@ -126,10 +137,25 @@ class PersonController extends GetControllerEx<PersonModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.person_info;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
if (name.value.isEmpty) {
apiResponse.msg = "请输入姓名".tr;
return apiResponse;
}
if (person == null) {
person = {};
}
person['name'] = name.value;
person['id'] = deviceId;
var response =
@@ -161,6 +187,17 @@ class PersonController extends GetControllerEx<PersonModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.disease_list;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =

View File

@@ -0,0 +1,108 @@
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
import 'package:ef/ef.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/util/DailyLogUtils.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:dio/dio.dart' as dio;
part 'repair_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class RepairModel {
String? id; //设备id
String? param; //设备参数
String? issue; //问题描述
String? fileUrl; //文件地址
RepairModel();
factory RepairModel.fromJson(Map<String, dynamic> json) {
try {
return _$RepairModelFromJson(json);
} catch (e) {
// 在实际应用中,应该有更细致的异常处理策略和错误日志
return RepairModel(); // 或者返回一个带有错误信息的特定DeviceInfoModel实例
}
}
// 序列化为JSON时的异常处理
Map<String, dynamic> toJson() => _$RepairModelToJson(this);
}
class RepairController extends GetControllerEx<RepairModel> {
RepairController() {
attr = GetModel(RepairModel()).obs;
}
RxString device_type = "".obs;
RxList repairList = [].obs;
RxString name = "".obs;
RxString phone = "".obs;
List<String>? deviceListId = [];
List<String>? deviceListName = [];
RxList repairHistory = [].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 * 5) {
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}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
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(res, "我的.上传成功".tr, "我的.头像上传失败".tr);
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);
}
}
}

View File

@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'repair_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
RepairModel _$RepairModelFromJson(Map<String, dynamic> json) => RepairModel()
..id = json['id'] as String?
..param = json['param'] as String?
..issue = json['issue'] as String?
..fileUrl = json['fileUrl'] as String?;
Map<String, dynamic> _$RepairModelToJson(RepairModel instance) =>
<String, dynamic>{
'id': instance.id,
'param': instance.param,
'issue': instance.issue,
'fileUrl': instance.fileUrl,
};

View File

@@ -0,0 +1,94 @@
import 'dart:convert';
import 'package:ef/ef.dart';
import 'package:flutter/services.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:path/path.dart' as path;
import 'package:vbvs_app/language/AppLanguage.dart';
part 'language_controller.g.dart'; // 由json_serializable自动生成的部分
@JsonSerializable()
class LanguageModel {
String? filename; // 语言文件名称
String? language_name; // 语言名称
bool? selected; // 是否选中
String? language_code;//语言简称
LanguageModel();
factory LanguageModel.fromJson(Map<String, dynamic> json) {
try {
return _$LanguageModelFromJson(json);
} catch (e) {
return LanguageModel(); // 或者返回一个带有错误信息的特定实例
}
}
Map<String, dynamic> toJson() => _$LanguageModelToJson(this);
}
class LanguageController extends GetControllerEx<LanguageModel> {
LanguageController() {
attr = GetModel(LanguageModel()).obs;
}
RxList languageList = [].obs;
var selectLanguage = Rx<LanguageModel?>(null);
Future<void> initLanuageList() async {
try {
String currentLanguageCode = AppLanguage().getCurrentLanguageCode();
languageList.clear(); // 清空现有列表
// 1. 获取 assets/langs/ 目录下所有 .json 文件
final manifestContent = await rootBundle.loadString('AssetManifest.json');
final Map<String, dynamic> manifestMap = json.decode(manifestContent);
// 筛选出 langs 目录下的 .json 文件
final langFiles = manifestMap.keys
.where((String key) =>
key.startsWith('assets/langs/') && key.endsWith('.json'))
.toList();
// 2. 遍历所有语言文件
for (final filePath in langFiles) {
try {
// 加载 JSON 文件内容
final jsonString = await rootBundle.loadString(filePath);
final jsonMap = json.decode(jsonString);
// 提取语言名称(假设 JSON 文件中有 "语言名称" 字段)
final languageName = jsonMap['语言名称'] as String? ??
path.basenameWithoutExtension(filePath);
// 3. 构造 LanguageModel 并添加到列表
final languageModel = LanguageModel()
..filename = path.basename(filePath) // 如 "zh-CN.json"
..language_name = languageName // 如 "简体中文"
..selected =
(path.basename(filePath) == '$currentLanguageCode.json')
..language_code =
path.basenameWithoutExtension(filePath); // 如 "zh-CN"
if (languageModel.selected == true) {
selectLanguage.value = languageModel;
}
languageList.add(languageModel);
} catch (e) {
print('加载语言文件失败: $filePath, 错误: $e');
}
}
print("object");
} catch (e) {
print('初始化语言列表失败: $e');
// 可选:添加默认语言(如英文)作为后备
languageList.add(
LanguageModel()
..filename = 'en-US.json'
..language_name = 'English'
..selected = true, // 默认语言为选中状态
);
}
}
}

View File

@@ -0,0 +1,18 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'language_controller.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LanguageModel _$LanguageModelFromJson(Map<String, dynamic> json) =>
LanguageModel()
..filename = json['filename'] as String?
..language_name = json['language_name'] as String?;
Map<String, dynamic> _$LanguageModelToJson(LanguageModel instance) =>
<String, dynamic>{
'filename': instance.filename,
'language_name': instance.language_name,
};

View File

@@ -0,0 +1,24 @@
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
class PdfController extends GetxController {
var localPdfPath = Rx<String?>(null);
// 加载 PDF 文件
Future<void> loadPdf() async {
final byteData = await rootBundle.load('assets/img/test.pdf');
final tempDir = await getTemporaryDirectory();
// 使用 path 包拼接路径
final filePath = p.join(tempDir.path, 'test.pdf');
final file = File(filePath);
// 保存文件到临时目录
await file.writeAsBytes(byteData.buffer.asUint8List());
localPdfPath.value = filePath; // 更新 PDF 路径
}
}

View File

@@ -11,6 +11,9 @@ 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/controller/login/login_controller.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
import 'package:vbvs_app/controller/time/countdown_controller.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/model/user_data.dart';
@@ -64,7 +67,7 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
ApiResponse apiResponse = ApiResponse(code: -1, msg: "我的.头像上传失败".tr);
if (image != null) {
int fileSize = await image.length(); // 获取图片大小,单位为字节
if (fileSize > 1048576) {
if (fileSize > 1048576 * 5) {
apiResponse.msg = "我的.头像限制".tr;
return apiResponse;
}
@@ -72,6 +75,17 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.upload_file;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var formData = dio.FormData.fromMap({
"type": 1,
"file": await dio.MultipartFile.fromFile(
@@ -119,6 +133,17 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_info;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
final data = {
"nickName": user.tmpNickName,
if (user.tmpHead != null && user.tmpHead!.isNotEmpty)
@@ -154,6 +179,17 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
String serviceName = ServiceConstant.server_service;
String serviceApi = ServiceConstant.user_info;
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? language = "";
if (languageController.selectLanguage != null) {
language = languageController.selectLanguage.value!.language_code;
}
if (language != null && language.isNotEmpty) {
if (queryUrl.contains("?")) {
queryUrl += "&lang=$language";
} else {
queryUrl += "?lang=$language";
}
}
var response = await EasyDartModule.dio.get(queryUrl);
if (response != null) {
var responseData =
@@ -192,8 +228,14 @@ class UserInfoController extends GetControllerEx<UserInfoModel> {
bodyDeviceController.bindDeviceNum.value = 0;
HomeController homeController = Get.find();
homeController.sleepNum.value = 0;
LoginController loginController = Get.find();
loginController.model.register_agree = null;
bodyDeviceController.sleepReportData.value = {};
MessageController messageController = Get.find();
messageController.model.body_message_read = 0;
messageController.model.system_message_read = 0;
CountdownController countdownController = Get.find();
countdownController.countdown.value = 0;
return apiResponse;
}
}

View File

@@ -0,0 +1,11 @@
enum MessageStatus {
noNeed(0, '无需处理'),
pending(1, '待处理'),
completed(2, '已处理'),
timeout(3, '已超时');
final int code;
final String description;
const MessageStatus(this.code, this.description);
}

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:ui';
import 'package:ef/ef.dart';
import 'package:flutter/services.dart';
@@ -7,6 +8,9 @@ class AppLanguage extends Translations {
// 私有构造函数
AppLanguage._internal();
// 当前语言
String _currentLanguageCode = 'zh_CN'; //
// 单例实例
static final AppLanguage _instance = AppLanguage._internal();
@@ -19,23 +23,47 @@ class AppLanguage extends Translations {
Map<String, Map<String, String>> get keys => _localizedValues;
// 动态加载语言文件
Future<void> loadLanguage(String languageCode) async {
if (_localizedValues.containsKey(languageCode)) {
return; // 如果语言已经加载,无需重复加载
}
// Future<void> loadLanguage(String languageCode) async {
// if (_localizedValues.containsKey(languageCode)) {
// return; // 如果语言已经加载,无需重复加载
// }
// try {
// // 加载 JSON 文件
// final jsonString =
// await rootBundle.loadString('assets/langs/$languageCode.json');
// final Map<String, dynamic> jsonMap = json.decode(jsonString);
// // 扁平化 JSON 数据并存储到缓存
// _localizedValues[languageCode] = _flattenJson(jsonMap);
// // 通知 GetX 更新 keys
// // Get.forceAppUpdate();
// Get.appendTranslations(keys);
// _currentLanguageCode = languageCode;
// Get.updateLocale(Locale(languageCode));
// } catch (e) {
// print('Error loading $languageCode language file: $e');
// }
// }
Future<void> loadLanguage(String languageCode) async {
try {
// 加载 JSON 文件
final jsonString =
await rootBundle.loadString('assets/langs/$languageCode.json');
final Map<String, dynamic> jsonMap = json.decode(jsonString);
// 扁平化 JSON 数据并存储到缓存
_localizedValues[languageCode] = _flattenJson(jsonMap);
// 扁平化 JSON 数据
final Map<String, String> translations = _flattenJson(jsonMap);
// 通知 GetX 更新 keys
// Get.forceAppUpdate();
Get.appendTranslations(keys);
// 加载并设置翻译
Get.clearTranslations(); // 清除之前加载的翻译,确保切换时不混乱
Get.addTranslations({languageCode: translations});
Get.updateLocale(Locale(languageCode)); // 切换语言
_currentLanguageCode = languageCode; // 可选:记录当前语言码
} catch (e) {
print('Error loading $languageCode language file: $e');
}
@@ -60,4 +88,14 @@ class AppLanguage extends Translations {
void clearLanguage(String languageCode) {
_localizedValues.remove(languageCode);
}
// 获取当前使用的语言
String getCurrentLanguageCode() {
return _currentLanguageCode;
}
Locale get currentLocale {
final parts = _currentLanguageCode.split('-');
return Locale(parts[0], parts.length > 1 ? parts[1] : null);
}
}

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart';
@@ -7,15 +8,19 @@ import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:fluwx/fluwx.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/CheckNetwork.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_calibration_controller.dart';
import 'package:vbvs_app/controller/device/device_share_controller.dart';
import 'package:vbvs_app/controller/device/device_share_list_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';
@@ -23,6 +28,9 @@ 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/repair/repair_controller.dart';
import 'package:vbvs_app/controller/setting/language/language_controller.dart';
import 'package:vbvs_app/controller/setting/pdf/PdfController.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';
@@ -34,7 +42,12 @@ import 'routers/routers.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final Locale? deviceLocale = Get.deviceLocale;
print('系统语言: ${deviceLocale?.languageCode}');
// await AppLanguage().loadLanguage("en_US");
await AppLanguage().loadLanguage("zh_CN");
await initLanguageSetting();
WidgetsFlutterBinding.ensureInitialized();
// ApiService.init();
@@ -44,10 +57,12 @@ Future<void> main() async {
initEasyDartModule();
await initLogin();
await initLog();
await messageStatus();
startMessagePolling();
// 检查网络
// Checknetwork.checkNetwork();
Checknetwork.checkNetwork();
// 微信开放平台注册
// initWX();
initWX();
// // 初始化 flutter_xupdate android app 更新
// initXUpdate();
@@ -58,6 +73,45 @@ Future<void> main() async {
});
}
initLanguageSetting() {
Get.put(LanguageController());
LanguageController languageController = Get.find();
languageController.initLanuageList();
}
// 初始化微信开放平台注册
Future<void> initWX() async {
Fluwx fluwx = Fluwx();
fluwx.registerApi(
//请填写自己的微信appid
appId: "wxeb2688220799e2c5",
doOnAndroid: true,
doOnIOS: true,
);
}
Timer? _messageTimer;
void startMessagePolling() {
// 避免重复启动
_messageTimer?.cancel();
_messageTimer = Timer.periodic(Duration(seconds: 5), (timer) async {
try {
MessageController messageController = Get.find();
messageController.getMessageStatus();
// print("轮询消息状态成功");
} catch (e) {
print("轮询消息状态失败: $e");
}
});
}
messageStatus() async {
Get.put(MessageController());
MessageController messageController = Get.find();
await messageController.getMessageStatus();
}
void initEasyDartModule() {
try {
EasyDartModule.init(
@@ -129,49 +183,6 @@ Future<void> initLogin() async {
}
}
// 初始化微信开放平台注册
// Future<void> initWX() async {
// Fluwx fluwx = Fluwx();
// fluwx.registerApi(
// //请填写自己的微信appid
// appId: "wx929c548fea6af9c7",
// doOnAndroid: true,
// doOnIOS: true,
// universalLink: "https://zhmht.swes.com.cn/app/");
// }
// // 初始化 flutter_xupdate android app 更新
// Future<void> initXUpdate() async {
// if (Platform.isAndroid) {
// FlutterXUpdate.init(
// // 是否输出日志
// debug: true,
// // 是否使用post请求
// isPost: true,
// // post请求是否是上传json
// isPostJson: false,
// // 请求响应超时时间
// timeout: 25000,
// // 是否开启自动模式
// isWifiOnly: false,
// // 是否开启自动模式
// isAutoMode: false,
// // 需要设置的公共参数
// supportSilentInstall: false,
// // 在下载过程中,如果点击了取消的话,是否弹出切换下载方式的重试提示弹窗
// enableRetry: false)
// .then((value) {
// print('初始化成功: $value');
// }).catchError((error) {
// print(error);
// });
// FlutterXUpdate.setUpdateHandler(onUpdateError: (message) async {
// print('onUpdateError: $message');
// });
// }
// }
class MyApp extends StatelessWidget {
MyApp({super.key});
final ThemeController themeController = Get.put(ThemeController());
@@ -190,7 +201,8 @@ class MyApp extends StatelessWidget {
FitTool.init(width < height ? width : height);
return GetMaterialApp(
translations: AppLanguage(),
locale: const Locale("zh", "CN"),
// locale: const Locale("zh", "CN"),
locale: AppLanguage().currentLocale, // ✅ 动态读取当前语言
fallbackLocale: const Locale("zh", "CN"),
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
@@ -200,6 +212,7 @@ class MyApp extends StatelessWidget {
],
supportedLocales: [
const Locale('zh', 'CN'), // 中文
const Locale('en', 'US'), // ⚠️ 添加你支持的语言
],
debugShowCheckedModeBanner: false,
title: '',
@@ -219,7 +232,10 @@ class MyApp extends StatelessWidget {
Get.lazyPut(() => BodyDeviceController()),
Get.lazyPut(() => HomeController()),
Get.lazyPut(() => DeviceShareController()),
Get.lazyPut(() => MessageController()),
Get.lazyPut(() => DeviceShareListController()),
Get.lazyPut(() => DeviceCalibrationController()),
Get.lazyPut(() => RepairController()),
Get.lazyPut(() => PdfController()),
]));
});
}

View File

@@ -1,8 +1,10 @@
import 'package:ef/ef.dart';
import 'package:flutter/cupertino.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/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
getOnePicker(context, List arr, int checkIndex, Function onSelectedItemChanged,
@@ -780,3 +782,133 @@ void showPermissionInfoDialog(BuildContext context, List data) {
},
);
}
void showProgressDialog(
BuildContext context,
ValueNotifier<double> progressNotifier,
ValueNotifier<bool> failureNotifier,
) {
ThemeController themeController = Get.find();
showDialog(
context: context,
barrierDismissible: false, // 点击对话框外部不可关闭
builder: (BuildContext dialogContext) {
return Stack(
children: [
Positioned(
top: MediaQuery.of(context).size.height * 0.4,
left: MediaQuery.of(context).size.width * 0.05,
right: MediaQuery.of(context).size.width * 0.05,
child: Material(
color: Colors.transparent,
child: Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: ValueListenableBuilder<double>(
valueListenable: progressNotifier,
builder: (context, progress, _) {
return ValueListenableBuilder<bool>(
valueListenable: failureNotifier,
builder: (context, isFailure, __) {
// 关闭弹窗的逻辑
if (isFailure) {
// 延迟关闭弹窗和提示错误
Future.delayed(Duration(milliseconds: 300), () {
if (Navigator.canPop(dialogContext)) {
Navigator.of(dialogContext).pop();
}
TopSlideNotification.show(
context,
text: "设备校准失败".tr,
textColor: Colors.red,
);
});
} else if (progress >= 1.0) {
// 延迟关闭弹窗和提示成功(可选)
Future.delayed(Duration(milliseconds: 300), () {
if (Navigator.canPop(dialogContext)) {
Navigator.of(dialogContext).pop();
}
TopSlideNotification.show(
context,
text: "设备校准完成".tr,
textColor: themeController.currentColor.sc3,
);
});
}
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
isFailure
? '失败'.tr
: '${(progress * 100).toStringAsFixed(0)}%',
style: TextStyle(
fontSize: 26.rpx,
color: isFailure
? Colors.red
: themeController.currentColor.sc3,
),
),
SizedBox(height: 40.rpx),
Stack(
children: [
Container(
width: double.infinity,
height: 21.rpx,
decoration: BoxDecoration(
color: stringToColor("#D9D9D9"),
borderRadius: BorderRadius.circular(
AppConstants().button_container_radius,
),
),
),
Container(
width: progress *
MediaQuery.of(context).size.width *
0.8,
height: 10.rpx,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isFailure
? [themeController.currentColor.sc9]
: [
themeController
.currentColor.sc1,
themeController
.currentColor.sc2,
],
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius:
BorderRadius.circular(5.rpx),
),
),
],
),
],
);
},
);
},
),
),
),
),
),
],
);
},
);
}

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
@@ -26,6 +28,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
final BodyDeviceController bodyDeviceController = Get.find();
final GlobalKey addIconKey = GlobalKey();
OverlayEntry? _popupEntry;
Timer? _timer;
void _showPopup() {
final renderBox =
@@ -95,7 +98,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
width: double.infinity,
child: Center(
child: Text(
'扫一扫'.tr,
'扫一扫.标题'.tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
@@ -119,7 +122,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
width: double.infinity,
child: Center(
child: Text(
'蓝牙绑定'.tr,
'蓝牙绑定.标题'.tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
@@ -151,6 +154,16 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
void initState() {
bodyDeviceController.keyWord.value = "";
super.initState();
// 初次请求一次
_fetchDeviceList();
// 每 5 秒定时请求一次
_timer = Timer.periodic(Duration(seconds: 5), (timer) {
_fetchDeviceList();
});
}
void _fetchDeviceList() {
bodyDeviceController.getDeviceList().then((apiResponse) {
if (apiResponse.code != HttpStatusCodes.ok) {
TopSlideNotification.show(
@@ -162,6 +175,12 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
});
}
@override
void dispose() {
_timer?.cancel(); // 页面销毁时取消定时器
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
@@ -265,127 +284,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
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: [
@@ -520,7 +418,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
}),
],
),
Container(
width:
MediaQuery.sizeOf(context).width * 0.38,
@@ -661,20 +558,6 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
.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,
@@ -722,11 +605,9 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
Obx(() {
final isEmpty =
bodyDeviceController.deviceList.value.isEmpty;
return isEmpty
? Expanded(
child: NullDataWidget(),
)
return Expanded(
child: isEmpty
? NullDataWidget()
: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 26.rpx, 30.rpx, 0),
@@ -742,6 +623,7 @@ class _BodyDevicePageState extends State<BodyDeviceWidget> {
.divide(SizedBox(height: 25.rpx)),
),
),
),
);
}),
],

File diff suppressed because it is too large Load Diff

View File

@@ -281,15 +281,6 @@ class _DeviceDetailPageState extends State<DeviceDetailPage> {
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,
@@ -320,7 +311,7 @@ class _DeviceDetailPageState extends State<DeviceDetailPage> {
child: Align(
alignment: AlignmentDirectional(-1.rpx, 0.rpx),
child: Text(
'${widget.device['person']['name'] ?? '未命名'.tr}',
'${widget.device['person']?['name'] ?? '未命名'.tr}',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -446,7 +437,7 @@ class _DeviceDetailPageState extends State<DeviceDetailPage> {
child: Align(
alignment: AlignmentDirectional(-1.rpx, 0.rpx),
child: Text(
'${widget.device['update_time'] ?? '-'.tr}',
'${widget.device['status']?['updateTime'] == null ? '-'.tr : MyUtils.timestampToDateString(widget.device['status']?['updateTime'])}',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(

View File

@@ -226,7 +226,7 @@ class _InstantBodyPageState extends State<InstantBodyPage> {
CrossAxisAlignment.start,
children: [
Text(
'${device['person']['name'] ?? '未命名'.tr}',
'${device['person']?['name'] ?? '未命名'.tr}',
style:
FlutterFlowTheme.of(context)
.bodyMedium
@@ -239,7 +239,7 @@ class _InstantBodyPageState extends State<InstantBodyPage> {
),
),
Text(
'${MyUtils.getAgeByDate(MyUtils.formatBirthdayTime(device['person']['birthday'])) ?? '未知数据'.tr}',
'${MyUtils.getAgeByDate(MyUtils.formatBirthdayTime(device['person']?['birthday'])) ?? '未知数据'.tr}',
style:
FlutterFlowTheme.of(context)
.bodyMedium
@@ -306,8 +306,8 @@ class _InstantBodyPageState extends State<InstantBodyPage> {
CrossAxisAlignment.start,
children: [
Text(
// '${device['_id']??'未知数据'.tr}',
"D11250300003",
'${device['code'] ?? '未知数据'.tr}',
// "D11250300003",
style:
FlutterFlowTheme.of(context)
.bodyMedium
@@ -320,7 +320,7 @@ class _InstantBodyPageState extends State<InstantBodyPage> {
),
),
Text(
'${device['person']['weight'] ?? '未知数据'.tr}kg',
'${device['person']?['weight'] ?? '未知数据'.tr}kg',
style:
FlutterFlowTheme.of(context)
.bodyMedium

View File

@@ -69,7 +69,6 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
}
Future<void> _requestBluetoothPermission() async {
// Android 13+ 使用新的蓝牙权限
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
@@ -113,7 +112,9 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
// 开始扫描蓝牙设备
void _startScanning() async {
if (!mounted) return;
if (!mounted || isScanning) return;
_scanSubscription?.cancel();
var bluetoothState = await FlutterBluePlus.isOn;
if (!bluetoothState && !_isDialogShowing) {
_isDialogShowing = true;
@@ -131,6 +132,7 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
// await FlutterBluePlus.startScan(timeout: Duration(minutes: 30));
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
if (!mounted) return; // 确保页面未销毁
final signalThreshold = blueteethBindController.model.singal!;
final filteredResults = results
.where((r) =>
@@ -334,7 +336,7 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
mainAxisSize: MainAxisSize.max,
children: [
Text(
'最小信号强度',
'最小信号强度'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -415,7 +417,7 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
Expanded(
child: Container(
width: 100.rpx,
height: 100.rpx,
height: 80.rpx,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context)
.secondaryBackground,
@@ -519,7 +521,7 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
),
),
Text(
'搜索',
'搜索'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -547,7 +549,8 @@ class _BlueteethDevicePageState extends State<BlueteethDevicePage> {
EdgeInsetsDirectional.fromSTEB(19.rpx, 0, 0, 0),
child: Obx(() {
return Text(
'匹配出的外围设备${blueteethBindController.model.betDevicelist!.length}',
'匹配出的外围设备'.tr +
"${blueteethBindController.model.betDevicelist!.length}",
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(

View File

@@ -0,0 +1,123 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:flutterflow_ui/flutterflow_ui.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/device_share_list_controller.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
class DeviceShareInfoWidget extends StatefulWidget {
final data;
const DeviceShareInfoWidget({super.key, required this.data});
@override
State<DeviceShareInfoWidget> createState() => _DeviceShareInfoWidgetState();
}
class _DeviceShareInfoWidgetState extends State<DeviceShareInfoWidget> {
ThemeController themeController = Get.find();
MessageController messageController = Get.find();
DeviceShareListController deviceShareListController = Get.find();
@override
Widget build(BuildContext context) {
bool selected = false;
return ClickableContainer(
backgroundColor: themeController.currentColor.sc5,
highlightColor: themeController.currentColor.sc3,
borderRadius: 20.rpx,
padding: EdgeInsetsDirectional.fromSTEB(0.rpx, 33.rpx, 0.rpx, 33.rpx),
onTap: () {},
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Obx(() {
return Theme(
data: ThemeData(
checkboxTheme: CheckboxThemeData(
visualDensity: VisualDensity.compact,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(64),
),
),
unselectedWidgetColor: Color(0xFFD3D3D3),
),
child: Checkbox(
value: deviceShareListController.selectedShareInfo.value
.contains(widget.data[0]['k']),
onChanged: (newValue) async {
if (newValue != null) {
if (newValue == true) {
deviceShareListController.selectedShareInfo.value
.add(widget.data[0]['k']);
} else {
deviceShareListController.selectedShareInfo.value
.remove(widget.data[0]['k']);
}
}
final selectedCount =
deviceShareListController.selectedShareInfo.length;
final totalCount =
deviceShareListController.shareInfoList.length;
deviceShareListController.model.all =
(selectedCount == totalCount) ? 1 : 0;
deviceShareListController.selectedShareInfo
.refresh(); // ✅ 关键代码
deviceShareListController.updateAll();
},
side: BorderSide(
width: 1.5,
color: FlutterFlowTheme.of(context).secondaryText,
),
activeColor: stringToColor("#16C89F"),
checkColor: FlutterFlowTheme.of(context).info,
),
);
}),
Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
constraints: BoxConstraints(
minWidth: 30.rpx,
),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(widget.data.length, (index) {
final item = widget.data[index];
return Container(
constraints: BoxConstraints(
minHeight: 62.rpx,
),
child: Align(
alignment: AlignmentDirectional(-1, 0),
child: Text(
"${item['k']}" + ":" + "${item['v']}",
style:
FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
),
),
),
);
}),
),
),
].divide(SizedBox(width: 50.rpx)),
),
].divide(SizedBox(
width: 10.rpx,
)),
),
);
}
}

View File

@@ -41,6 +41,7 @@ class _SingleBlueteethDeviceCompoentWidgetState
deviceData.rssi = widget.bleDevice.rssi;
deviceData.mac = deviceData.deviceId.replaceAll(':', '');
BleDeviceData device = deviceData;
blueteethBindController.currentDeviceMac = device.mac;
device = blueteethBindController.model.betDevicelist!.firstWhere(
(d) => d.mac == device.mac,
orElse: () => device,
@@ -208,7 +209,7 @@ class _SingleBlueteethDeviceCompoentWidgetState
],
),
Text(
'版本:${device.version ?? '-'}',
"版本".tr + '${device.version ?? '-'}',
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFF6FAFD),

View File

@@ -708,3 +708,133 @@ void showWifiDialog(
},
);
}
void showTipDialog(
BuildContext context,
Widget widget,
// String title,
) {
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, 93.rpx, 0, 74.rpx),
// child: Text(
// title,
// style: FlutterFlowTheme.of(context).bodyMedium.override(
// fontFamily: 'Inter',
// fontSize: 30.rpx,
// letterSpacing: 0.0,
// color: themeController.currentColor.sc3,
// ),
// ),
// ),
// ),
Align(
alignment: AlignmentDirectional(0, 0),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 93.rpx, 0, 74.rpx),
child: widget,
),
),
// widget,
Padding(
padding: EdgeInsetsDirectional.fromSTEB(0, 19.rpx, 0, 60.rpx),
child: CustomCard(
borderRadius: AppConstants().button_container_radius,
onTap: () {
Get.back();
},
colors: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
child: Container(
width: MediaQuery.sizeOf(context).width,
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

@@ -0,0 +1,617 @@
import 'dart:async';
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/common/util/requestWithLog.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
import 'package:vbvs_app/controller/device/device_calibration_controller.dart';
import 'package:vbvs_app/pages/common/selectDialog.dart';
class CalibrationPage extends StatefulWidget {
int? type; //1.绑定时 2.绑定后
CalibrationPage({super.key, required this.type});
@override
State<CalibrationPage> createState() => _CalibrationPageState();
}
class _CalibrationPageState extends State<CalibrationPage> {
DeviceCalibrationController deviceCalibrationController = Get.find();
@override
void initState() {
super.initState();
deviceCalibrationController.process.value = 0;
deviceCalibrationController.bed_calibration.value = 0;
deviceCalibrationController.position_calibration.value = 0;
}
@override
void dispose() {
super.dispose();
}
@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,
),
),
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,
),
if (widget.type == 1)
Positioned(
right: 20.rpx,
child: CustomCard(
borderRadius: 20.rpx,
onTap: () async {
Get.toNamed("/personPage");
},
colors: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
child: Container(
width: 100.rpx,
height: 60.rpx,
alignment: Alignment.center,
padding: EdgeInsetsDirectional.fromSTEB(
16.rpx, 0, 16.rpx, 0),
child: Text(
'跳过'.tr,
style: TextStyle(
fontFamily: 'Inter Tight',
color: themeController.currentColor.sc3,
letterSpacing: 0.0,
),
),
),
),
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
// child: Container(),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 0.rpx, 30.rpx, 0.rpx),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0.rpx, 30.rpx, 0, 0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc5,
borderRadius: BorderRadius.circular(16.rpx),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
10.rpx, 20.rpx, 34.rpx, 20.rpx),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc21,
borderRadius: 0, // 可按需设置圆角
padding: EdgeInsets.symmetric(
vertical: 10.rpx), // 可自定义内边距
onTap: () {
deviceCalibrationController.process.value = 0;
deviceCalibrationController.updateAll();
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Obx(() {
return Visibility(
maintainState: true,
maintainAnimation: true,
maintainSize: true,
visible: deviceCalibrationController
.process.value ==
0,
child: SvgPicture.asset(
'assets/img/icon/select_arrow.svg',
width: 17.rpx,
height: 17.rpx,
color: themeController
.currentColor.sc9,
),
);
}),
SizedBox(width: 8.rpx),
Text(
'离床校准'.tr,
style: TextStyle(
fontSize: 30.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
],
),
Obx(() {
return Text(
deviceCalibrationController
.bed_calibration.value ==
0
? '未完成'.tr
: "已完成".tr,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: deviceCalibrationController
.bed_calibration.value ==
0
? themeController.currentColor.sc3
: themeController
.currentColor.sc1,
),
);
}),
],
),
),
SizedBox(height: 41.rpx),
ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc21,
borderRadius: 0, // 可根据需要设为圆角
padding: EdgeInsets.symmetric(
vertical: 10.rpx), // 可根据需要调整上下内边距
onTap: () {
deviceCalibrationController.process.value = 1;
deviceCalibrationController.updateAll();
},
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Obx(() {
return Visibility(
maintainState: true,
maintainAnimation: true,
maintainSize: true,
visible: deviceCalibrationController
.process.value ==
1,
child: SvgPicture.asset(
'assets/img/icon/select_arrow.svg',
width: 17.rpx,
height: 17.rpx,
color: themeController
.currentColor.sc9,
),
);
}),
SizedBox(width: 8.rpx),
Text(
'位置校准'.tr,
style: TextStyle(
fontSize: 30.rpx,
letterSpacing: 0.0,
color: themeController
.currentColor.sc3,
),
),
],
),
Obx(() {
return Text(
deviceCalibrationController
.position_calibration
.value ==
0
? '未完成'.tr
: "已完成".tr,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: deviceCalibrationController
.position_calibration
.value ==
0
? themeController.currentColor.sc3
: themeController
.currentColor.sc1,
),
);
}),
],
),
),
],
),
),
),
),
Obx(() {
return Expanded(
child: SizedBox(
width: double.infinity,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
100.rpx, 55.rpx, 100.rpx, 0),
child: Stack(
clipBehavior: Clip.none,
children: [
Container(
width:
MediaQuery.sizeOf(context).width * 0.65,
height:
MediaQuery.sizeOf(context).height * 0.3,
child: Stack(
children: [
// Image.asset(
// deviceCalibrationController
// .bed_type.value ==
// 0
// ? "assets/img/single_pillow.png"
// : "assets/img/double_pillow.png",
// width: double.infinity,
// height: double.infinity,
// fit: BoxFit.contain,
// ),
Image.asset(
deviceCalibrationController
.process.value ==
1 &&
deviceCalibrationController
.position_calibration
.value ==
1
? (deviceCalibrationController
.bed_type.value ==
0
? "assets/img/single_person.png"
: "assets/img/double_person.png")
: (deviceCalibrationController
.bed_type.value ==
0
? "assets/img/single_pillow.png"
: "assets/img/double_pillow.png"),
width: double.infinity,
height: double.infinity,
fit: BoxFit.contain,
),
// 床头文字
Positioned(
top: 23.rpx,
left:
(MediaQuery.sizeOf(context).width *
0.65) /
2 -
26.rpx,
child: Text(
'床头'.tr,
style: TextStyle(
fontSize: 26.rpx,
color: themeController
.currentColor.sc4,
),
),
),
],
),
),
Obx(() {
final double centerLeft =
(bodySize.maxWidth * 0.56) / 2;
if (deviceCalibrationController
.bed_type.value ==
0 &&
deviceCalibrationController
.process.value ==
1) {
//单人床位置校准
return Positioned(
top: -40.rpx,
left: centerLeft - 0.rpx,
child: Container(
width: bodySize.maxWidth * 0.087,
height: bodySize.maxHeight * 0.06,
constraints: BoxConstraints(
minWidth: 65.rpx,
minHeight: 76.rpx,
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/img/tip_arrow.gif'),
fit: BoxFit.cover,
),
),
),
);
}
if (deviceCalibrationController
.bed_type.value ==
1 &&
deviceCalibrationController
.process.value ==
1) {
//双人人床位置校准
return Positioned(
top: -40.rpx,
left: (MediaQuery.sizeOf(context).width *
0.65) *
0.22 -
0.rpx,
child: Container(
width: bodySize.maxWidth * 0.087,
height: bodySize.maxHeight * 0.06,
constraints: BoxConstraints(
minWidth: 65.rpx,
minHeight: 76.rpx,
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/img/tip_arrow.gif'),
fit: BoxFit.cover,
),
),
),
);
}
return Container();
}),
Positioned(
top: MediaQuery.sizeOf(context).height * 0.3 +
82.rpx,
left: 0,
right: 0,
child: Obx(() {
return Column(
children: [
Text(
(deviceCalibrationController
.process.value ==
0 &&
deviceCalibrationController
.bed_calibration
.value ==
1) ||
(deviceCalibrationController
.process.value ==
1 &&
deviceCalibrationController
.position_calibration
.value ==
1)
? '校准完成'.tr
: deviceCalibrationController
.process.value ==
0
? '离床校准提示'.tr
: '位置校准提示'.tr,
style: TextStyle(
fontSize: 38.rpx,
letterSpacing: 0.0,
color: ((deviceCalibrationController
.process
.value ==
0 &&
deviceCalibrationController
.bed_calibration
.value ==
1) ||
(deviceCalibrationController
.process
.value ==
1 &&
deviceCalibrationController
.position_calibration
.value ==
1))
? themeController
.currentColor.sc1
: themeController
.currentColor.sc9,
),
),
],
);
}),
),
],
),
),
),
);
}),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
100.rpx, 0.rpx, 100.rpx, 60.rpx),
child: CustomCard(
borderRadius:
AppConstants().button_container_radius, // 圆角半径
onTap: () async {
BlueteethBindController blueteethBindController =
Get.find();
requestWithLog(
logTitle: "设备校准",
method: MyHttpMethod.post,
queryUrl: ServiceConstant.start_calibration,
data: {
"deviceId":
blueteethBindController.currentDeviceMac
},
onSuccess: (res) {
final ValueNotifier<double> progressNotifier =
ValueNotifier<double>(0.0);
final ValueNotifier<bool> failureNotifier =
ValueNotifier<bool>(false);
Timer? pollingTimer;
// 定义请求函数
void requestCalibrationProgress() {
requestWithLog(
logTitle: "设备校准进度",
method: MyHttpMethod.post,
queryUrl: ServiceConstant.calibration_process,
data: {
"deviceId":
blueteethBindController.currentDeviceMac
},
onSuccess: (res) {
double process =
(res.data['process'] ?? 0).toDouble();
progressNotifier.value = process;
if (process >= 100) {
if (deviceCalibrationController
.process.value ==
0) {
deviceCalibrationController
.bed_calibration.value = 1;
deviceCalibrationController.updateAll();
}
if (deviceCalibrationController
.process.value ==
1) {
deviceCalibrationController
.position_calibration.value = 1;
deviceCalibrationController.updateAll();
}
pollingTimer?.cancel();
}
},
onFailure: (res) {
pollingTimer?.cancel();
failureNotifier.value = true;
TopSlideNotification.show(
context,
text: res.msg ?? "服务器.失败".tr,
textColor:
themeController.currentColor.sc9,
);
},
);
}
// 启动轮询
pollingTimer =
Timer.periodic(Duration(seconds: 2), (timer) {
requestCalibrationProgress();
});
// 初始调用一次
requestCalibrationProgress();
// 显示进度弹窗
showProgressDialog(
context, progressNotifier, failureNotifier);
},
onFailure: (res) {
TopSlideNotification.show(
context,
text: res.msg ?? "服务器.失败".tr,
textColor: themeController.currentColor.sc9,
);
},
);
},
colors: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
child: Container(
width: bodySize.maxWidth,
height: MediaQuery.sizeOf(context).height * 0.055,
constraints: BoxConstraints(
minWidth: 500.rpx,
minHeight: 90.rpx,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'开始校准'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
//todo 颜色
color: themeController.currentColor.sc3,
fontFamily: 'Inter',
fontSize:
AppConstants().normal_text_fontSize,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
width: 17.rpx,
)),
),
),
),
),
],
),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,461 @@
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/NullDataComponentWidget.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/controller/device/device_share_list_controller.dart';
import 'package:vbvs_app/model/BleDeviceData.dart';
import 'package:vbvs_app/pages/device_bind/componnet/DeviceShareInfoWidget.dart';
class DeviceShareListPage extends StatefulWidget {
String device = "";
DeviceShareListPage({super.key, required this.device});
@override
State<DeviceShareListPage> createState() => _DeviceShareListPageState();
}
class _DeviceShareListPageState extends State<DeviceShareListPage> {
DeviceShareListController deviceShareListController = Get.find();
@override
void initState() {
deviceShareListController.model.key = null;
deviceShareListController.shareInfoList.value = [];
deviceShareListController.selectedShareInfo.value = [];
deviceShareListController.getDeviceShareList(widget.device);
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, boxConstraints) => 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(
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
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,
),
),
//todo 返回刷新列表
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
height: 40.rpx,
),
Container(
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc3,
borderRadius: BorderRadius.circular(20.rpx),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
35.rpx, 0, 35.rpx, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, 0.rpx, 0, 0),
child: Container(
width: 25.rpx,
height: 25.rpx,
// width: double.infinity,
decoration: BoxDecoration(),
child: SvgPicture.asset(
'assets/img/icon/query.svg',
fit: BoxFit.cover,
color: stringToColor("#333333"), //固定
),
),
),
Expanded(
child: Container(
width: 100.rpx,
height: 80.rpx,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context)
.secondaryBackground,
),
child: Align(
alignment: AlignmentDirectional(-1, 0),
child: TextFormField(
onChanged: (value) {
deviceShareListController
.model.key = value;
},
autofocus: false,
obscureText: false,
decoration: InputDecoration(
isDense: true,
labelStyle:
FlutterFlowTheme.of(context)
.labelMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
),
hintText: '输入关键字'.tr,
hintStyle:
FlutterFlowTheme.of(context)
.labelMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
),
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: themeController
.currentColor.sc22,
),
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
letterSpacing: 0.0,
),
cursorColor:
FlutterFlowTheme.of(context)
.primaryText,
),
),
),
),
].divide(SizedBox(width: 6.rpx)),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
26.rpx, 0, 0, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
SizedBox(
height: 50.rpx,
child: VerticalDivider(
thickness: 2.rpx,
color: stringToColor("#333333"), //固定
),
),
ClickableContainer(
onTap: () async {
// ApiResponse apiResponse =
// await deviceShareListController
// .getDeviceShareList(
// key: deviceShareListController
// .model.key);
// if (apiResponse.code ==
// HttpStatusCodes.ok) {
// TopSlideNotification.show(
// context,
// text: apiResponse.msg!,
// textColor:
// themeController.currentColor.sc2,
// );
// deviceShareListController.updateAll();
// } else {
// TopSlideNotification.show(
// context,
// text: apiResponse.msg!,
// textColor:
// themeController.currentColor.sc9,
// );
// }
},
padding: EdgeInsets.symmetric(
horizontal: 10.rpx, vertical: 6.rpx),
borderRadius: 12.rpx,
backgroundColor: Colors.transparent,
highlightColor: stringToColor("#333333")
.withOpacity(0.08), // 可选点击反馈色
child: Text(
'搜索'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
fontSize: 30.rpx,
letterSpacing: 0.0,
color:
stringToColor("#333333"), //固定
),
),
),
].divide(SizedBox(width: 26.rpx)),
),
),
],
),
),
),
Container(
constraints: BoxConstraints(
minHeight: 100.rpx,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Theme(
data: ThemeData(
checkboxTheme: CheckboxThemeData(
visualDensity: VisualDensity.compact,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(64),
),
),
unselectedWidgetColor: Color(0xFFD3D3D3),
),
child: Obx(
() => Checkbox(
value:
deviceShareListController.model.all ==
0
? false
: true,
onChanged: (newValue) async {
if (newValue == true) {
// 设置为全选
deviceShareListController.model.all =
1;
deviceShareListController
.selectedShareInfo.value =
deviceShareListController
.shareInfoList
.map((e) => e['id'] as String)
.toList();
} else {
// 取消全选
deviceShareListController.model.all =
0;
deviceShareListController
.selectedShareInfo
.clear();
}
deviceShareListController
.selectedShareInfo
.refresh();
deviceShareListController.updateAll();
},
side: BorderSide(
width: 1.5,
color: FlutterFlowTheme.of(context)
.secondaryText,
),
activeColor: stringToColor("#16C89F"),
checkColor:
FlutterFlowTheme.of(context).info,
),
)),
Text(
"全选".tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
),
),
].divide(SizedBox(
width: 10.rpx,
)),
),
ClickableContainer(
onTap: () async {
//todo 删除分享
// ApiResponse apiResponse =
// await deviceShareListController
// .deleteShareDevice();
// if (apiResponse.code == HttpStatusCodes.ok) {
// TopSlideNotification.show(context,
// text: apiResponse.msg!);
// } else {
// TopSlideNotification.show(context,
// text: apiResponse.msg!,
// textColor:
// themeController.currentColor.sc9);
// }
},
padding: EdgeInsets.symmetric(
horizontal: 20.rpx, vertical: 10.rpx),
borderRadius: 20.rpx,
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc3
.withOpacity(0.1),
child: Text(
"删除".tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
),
),
),
],
),
),
Expanded(
child: deviceShareListController
.shareInfoList.value.isNotEmpty
? SingleChildScrollView(
child: Obx(() {
var selected = deviceShareListController
.selectedShareInfo.value;
return Column(
children: deviceShareListController
.shareInfoList.value
.map((item) =>
DeviceShareInfoWidget(data: item))
.toList()
.divide(SizedBox(height: 30.rpx))
.addToEnd(SizedBox(
height: 30.rpx,
)),
);
}),
)
: NullDataWidget(),
),
].divide(SizedBox(height: 30.rpx)),
),
),
),
),
),
),
);
}
}
BleDeviceData parseBleData(List<int> data) {
if (data.length < 18) {
throw Exception('BLE广播数据长度不足18字节');
}
int type = data[0];
int sn = data[1];
// 设备唯一ID (6字节),格式化为 MAC 地址样式
String deviceId =
List.generate(6, (i) => data[2 + i].toRadixString(16).padLeft(2, '0'))
.join(":")
.toUpperCase();
int bre = data[8];
int ht = data[9];
int active = data[10];
int flag = data[11];
// version 是4字节 uint大端字节序
int version =
(data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15];
// qsn 是2字节 ushort大端字节序
int qsn = (data[16] << 8) | data[17];
return BleDeviceData(
type: type,
sn: sn,
deviceId: deviceId,
bre: bre,
ht: ht,
active: active,
flag: flag,
version: version,
qsn: qsn,
);
}

View File

@@ -276,7 +276,7 @@ class _DeviceSharePageState extends State<DeviceSharePage> {
children: [
TextSpan(
text:
'${deviceShareController.code == 1 ? "邀请成功".tr : "邀请失败".tr}',
'${deviceShareController.code.value == 1 ? "邀请成功".tr : "邀请失败".tr}',
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -284,7 +284,7 @@ class _DeviceSharePageState extends State<DeviceSharePage> {
fontSize: 26.rpx,
letterSpacing: 0.0,
color: deviceShareController
.code ==
.code.value ==
1
? themeController
.currentColor.sc2

View File

@@ -35,8 +35,9 @@ class _EPageState extends State<DeviceTypePage> {
super.initState();
deviceTypeController.getDeviceType().then((response) {
if (response.code != HttpStatusCodes.ok) {
// 延迟到 widget 构建后再执行 show
TopSlideNotification.show(
Get.context!,
context, // 用当前页面 context 替代 Get.context!
text: response.msg ?? "服务器.失败".tr,
textColor: themeController.currentColor.sc9,
);
@@ -273,7 +274,7 @@ class _EPageState extends State<DeviceTypePage> {
),
alignment: Alignment.center, // 居中对齐
child: Text(
'绑定引导.跳过'.tr,
'确认'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -332,7 +333,7 @@ class _EPageState extends State<DeviceTypePage> {
children: [
/// 居中标题
Text(
'设备列表',
'设备列表'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
@@ -429,7 +430,7 @@ class _EPageState extends State<DeviceTypePage> {
title,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFC2CED7),
color: themeController.currentColor.sc3,
fontSize: 30.rpx,
letterSpacing: 0.0,
),

View File

@@ -30,11 +30,13 @@ class _DeviceTypeListPageState extends State<DeviceTypeListPage> {
super.initState();
deviceTypeController.getDeviceType().then((response) {
if (response.code != HttpStatusCodes.ok) {
WidgetsBinding.instance.addPostFrameCallback((_) {
TopSlideNotification.show(
Get.context!,
context,
text: response.msg ?? "服务器.失败".tr,
textColor: themeController.currentColor.sc9,
);
});
}
});
}
@@ -68,7 +70,7 @@ class _DeviceTypeListPageState extends State<DeviceTypeListPage> {
children: [
/// 居中标题
Text(
'设备列表',
'设备列表'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
@@ -165,7 +167,7 @@ class _DeviceTypeListPageState extends State<DeviceTypeListPage> {
title,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFC2CED7),
color: themeController.currentColor.sc3,
fontSize: 30.rpx,
letterSpacing: 0.0,
),

View File

@@ -19,7 +19,6 @@ import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
class WifiPage extends StatefulWidget {
WifiPage({super.key});
// WifiPage({super.key});
@override
State<WifiPage> createState() => _WifiPageState();
@@ -32,8 +31,6 @@ class _WifiPageState extends State<WifiPage> {
PersonController personController = Get.find();
ThemeController themeController = Get.find();
var lisObj;
// List<String> _logBuffer = [];
// bool _isCollecting = false;
@override
void initState() {
@@ -106,7 +103,8 @@ class _WifiPageState extends State<WifiPage> {
textColor: themeController.currentColor.sc9,
);
}
Get.toNamed("/personPage");
Get.toNamed("/calibrationPage",arguments: 1);
// Get.toNamed("/personPage");
// Get.toNamed("/bindDeviceSuccess");
},
colors: [
@@ -686,7 +684,7 @@ class _WifiPageState extends State<WifiPage> {
),
),
Text(
'刷新',
'刷新'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -779,13 +777,13 @@ class _WifiPageState extends State<WifiPage> {
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;
}
}
// 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) {

View File

@@ -2,7 +2,10 @@ 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:fluwx/fluwx.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/util/CheckNetwork.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';
@@ -26,6 +29,58 @@ class _EPageState extends State<LoginPage> {
LoginController loginController = Get.find();
ThemeController themeController = Get.find();
@override
void initState() {
super.initState();
LoginController loginController = Get.find();
Fluwx fluwx = loginController.fluwx;
if (isiOS) {
loginController.model.isIos = true;
fluwx.isWeChatInstalled.then((isWeChatInstalled) {
debugPrint('is wechat installed: $isWeChatInstalled');
if (!isWeChatInstalled) {
// TODO ios未安装微信 隐藏微信一键登录按钮
loginController.model.isWeChatNotInstalled = false;
} else {
loginController.model.isWeChatNotInstalled = true;
}
loginController.updateAll();
});
}
// 微信监听回调
loginController.fluwxCancelable = fluwx.addSubscriber((response) async {
if (response is WeChatAuthResponse) {
debugPrint('state :${response.state} \n code:${response.code}');
int errCode = response.errCode ?? -9999;
if (errCode == 0) {
// TODO 微信登录成功 传递code给后台 再操作逻辑
String code = response.code ?? "";
//把微信登录返回的code传给后台剩下的事就交给后台处理
//首次未注册的用户引导去手机号填写页面
//已注册的用户直接跳转首页
if (CommonVariables.isNetWorkOn == false) {
showToast("网络未连接,请开启设备网络后重试");
return;
}
String msg = await loginController.loginByWechatCode(code);
if (msg.isEmpty) {
// TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听
loginController.fluwxCancelable?.cancel();
// 登录成功移出网络检查监听
Checknetwork.subscription?.cancel();
Get.offAndToNamed("/mianPageBottomChange");
}
// TODO 操作全部跳转页面前成功以后移除监听,防止重复监听,其他方式登录成功也需要移出监听
// fluwxCancelable?.cancel();
} else if (errCode == -4) {
showToast("用户拒绝授权");
} else if (errCode == -2) {
showToast("用户取消授权");
}
}
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
@@ -186,9 +241,7 @@ class _EPageState extends State<LoginPage> {
], // 渐变色是同一个色,也可以根据需要调整
child: Container(
width:
// MediaQuery.sizeOf(context).width * 0.66,
bodysize.maxWidth,
width: bodysize.maxWidth,
height: MediaQuery.sizeOf(context).height *
0.055,
constraints: BoxConstraints(
@@ -200,7 +253,9 @@ class _EPageState extends State<LoginPage> {
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
Expanded(
child: Text(
textAlign: TextAlign.center,
'登录页.本机号码一键登录/注册'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
@@ -213,6 +268,9 @@ class _EPageState extends State<LoginPage> {
.normal_text_fontSize,
letterSpacing: 0.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
].divide(SizedBox(
width: 17.rpx,
@@ -241,6 +299,8 @@ class _EPageState extends State<LoginPage> {
fontSize: 26.rpx,
color: themeController.currentColor.sc3,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Padding(
@@ -433,7 +493,40 @@ class _EPageState extends State<LoginPage> {
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
ClickableContainer(
backgroundColor: Colors.white, // 背景色
highlightColor: Colors.grey, // 点击水波纹颜色
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
// onTap: () {
// TopSlideNotification.show(context,
// text: "待开发功能".tr);
// },
onTap: () async {
if (loginController.model.isIos == true &&
loginController
.model.isWeChatNotInstalled !=
true) {
} else {
if (loginController
.model.register_agree ==
null ||
loginController
.model.register_agree !=
true) {
TopSlideNotification.show(
context,
text: "登录页.未同意协议".tr,
textColor:
themeController.currentColor.sc9,
);
return;
}
await loginController
.wxLoginSendAuth(context);
}
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -446,7 +539,17 @@ class _EPageState extends State<LoginPage> {
height: 30.rpx,
),
),
Container(
),
ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.grey,
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
onTap: () {
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -459,7 +562,17 @@ class _EPageState extends State<LoginPage> {
height: 30.rpx,
),
),
Container(
),
ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.grey,
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
onTap: () {
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -472,6 +585,7 @@ class _EPageState extends State<LoginPage> {
height: 30.rpx,
),
),
),
].divide(SizedBox(width: 35.rpx)),
),
],

View File

@@ -1,11 +1,8 @@
import 'dart:io';
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: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';
@@ -575,6 +572,10 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
.currentColor.sc9,
);
} else {
CountdownController
countdownController = Get.find();
countdownController.countdown.value =
0;
// 登录成功,跳转到主页面
TopSlideNotification.show(
context,
@@ -821,7 +822,16 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
ClickableContainer(
backgroundColor: Colors.white, // 背景色
highlightColor: Colors.grey, // 点击水波纹颜色
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
onTap: () {
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -834,7 +844,17 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
height: 30.rpx,
),
),
Container(
),
ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.grey,
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
onTap: () {
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -847,7 +867,17 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
height: 30.rpx,
),
),
Container(
),
ClickableContainer(
backgroundColor: Colors.white,
highlightColor: Colors.grey,
borderRadius: 999.rpx,
padding: EdgeInsets.zero,
onTap: () {
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
width: 91.rpx,
height: 91.rpx,
clipBehavior: Clip.antiAlias,
@@ -860,6 +890,7 @@ class _OtherLoginPageState extends State<OtherLoginPage> {
height: 30.rpx,
),
),
),
].divide(SizedBox(width: 35.rpx)),
),
],

View File

@@ -2,13 +2,22 @@ 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/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/device_share_controller.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/enum/MessageStatus.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/device_bind/componnet/bind_dialog.dart';
class MessageWidgetWidget extends StatefulWidget {
const MessageWidgetWidget({super.key});
final data;
const MessageWidgetWidget({super.key, required this.data});
@override
State<MessageWidgetWidget> createState() => _MessageWidgetWidgetState();
@@ -16,9 +25,12 @@ class MessageWidgetWidget extends StatefulWidget {
class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
ThemeController themeController = Get.find();
MessageController messageController = Get.find();
DeviceShareController deviceShareController = Get.find();
@override
Widget build(BuildContext context) {
var messageInfo = widget.data;
return Stack(
children: [
ClickableContainer(
@@ -39,7 +51,8 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
child: Align(
alignment: AlignmentDirectional(-1, 0),
child: Text(
'实时监测结果通知'.tr,
// '实时监测结果通知'.tr,
"${messageInfo['data']['title']}",
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
fontSize: 30.rpx,
@@ -55,18 +68,15 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
Container(
constraints: BoxConstraints(
minWidth: 30.rpx,
maxWidth: 120.rpx,
maxWidth: 140.rpx,
),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoItem(context, '设备ID'),
_buildInfoItem(context, '使用人员'),
_buildInfoItem(context, '消息类型'),
_buildInfoItem(context, '检测数值'),
_buildInfoItem(context, '发生时间'),
],
children: messageInfo['data']['val']
.map<Widget>((mapItem) =>
_buildInfoItem(context, mapItem['k'] ?? ''))
.toList(),
),
),
Container(
@@ -76,13 +86,10 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildValueItem(context, '设备ID'),
_buildValueItem(context, '使用人员'),
_buildValueItem(context, '消息类型'),
_buildValueItem(context, '检测数值'),
_buildValueItem(context, '发生时间'),
],
children: messageInfo['data']['val']
.map<Widget>((mapItem) =>
_buildValueItem(context, mapItem['v'] ?? ''))
.toList(),
),
),
].divide(SizedBox(width: 35.rpx)),
@@ -98,19 +105,43 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
height: 47.rpx,
child: CustomCard(
borderRadius: AppConstants().button_container_radius, // 直角
colors: [
colors: messageInfo['status'] == 1
? [
themeController.currentColor.sc1,
themeController.currentColor.sc2
], // 单色背景
]
: [themeController.currentColor.sc4], // 单色背景
enableAnimation: true, // 有点击缩放动画
enableGradient: false, // 不用渐变
onTap: () {
// 点击处理逻辑
print('处理按钮点击了');
if (messageInfo['status'] == 1) {
showConfirmDialog(context, Container(), "是否确认接受该设备".tr,
onConfirm: () async {
ApiResponse apiResponse = await deviceShareController
.confirmShare(messageInfo['data']['shareCode']);
if (apiResponse.code == HttpStatusCodes.ok) {
TopSlideNotification.show(
context,
text: apiResponse.msg!,
textColor: themeController.currentColor.sc2,
);
messageController.getMessageList();
messageController.updateAll();
} else {
TopSlideNotification.show(
context,
text: apiResponse.msg!,
textColor: themeController.currentColor.sc9,
);
messageController.getMessageList();
messageController.updateAll();
}
}, onCancel: () {});
}
},
child: Center(
child: Text(
'处理'.tr,
getMessageStatus(messageInfo['status']),
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
fontSize: 26.rpx,
@@ -134,6 +165,8 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
child: Align(
alignment: AlignmentDirectional(-1, 0),
child: Text(
overflow: TextOverflow.ellipsis,
maxLines: 1,
label.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
@@ -154,6 +187,8 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
child: Align(
alignment: AlignmentDirectional(-1, 0),
child: Text(
overflow: TextOverflow.ellipsis,
maxLines: 1,
value,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
@@ -165,4 +200,23 @@ class _MessageWidgetWidgetState extends State<MessageWidgetWidget> {
),
);
}
String getMessageStatus(int status) {
if (status == null) {
return '未知数据'.tr;
}
if (status == MessageStatus.noNeed.code) {
return '无需处理'.tr;
}
if (status == MessageStatus.pending.code) {
return "待处理".tr;
}
if (status == MessageStatus.completed.code) {
return "已处理".tr;
}
if (status == MessageStatus.timeout.code) {
return "已超时".tr;
}
return '未知数据'.tr;
}
}

View File

@@ -1,5 +1,6 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
@@ -19,10 +20,13 @@ class _EPageState extends State<EPage> {
builder: (context, boxConstraints) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
backgroundColor: Colors.transparent,
body: SafeArea(
top: true,
child: Text("小e"),
child: Text("小e",
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: Colors.white)),
),
),
),

View File

@@ -0,0 +1,101 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/WebViewWidget.dart';
class FollowPage extends StatefulWidget {
late MyWebView webView;
FollowPage({super.key});
@override
State<FollowPage> createState() => _FollowPageState();
}
class _FollowPageState extends State<FollowPage> {
@override
void initState() {
super.initState();
// widget.webView = MyWebView(
// // url: "assets/map/index.html",
// url: "https://mp.weixin.qq.com/s/LvDtjFSI1au4o0eNrxLHwg",
// onLoad: () {
// print('网页载入完毕');
// },
// );
}
@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/followus.png'), // 本地图片
// fit: BoxFit.contain, // 填满整个 Container
// ),
// ),
child: Scaffold(
backgroundColor: Colors.transparent, // 加上这一行
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
// backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
titleSpacing: 0,
// leading: returnIconButtom,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
/// 居中标题
Text(
'关注我们'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
/// 左边返回按钮
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 0.rpx),
child: Column(children: [
Expanded(
child: Container(
// child: widget.webView,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/followus.png'), // 本地图片
fit: BoxFit.cover, // 填满整个 Container
),
),
),
),
]),
),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,129 @@
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/WebViewWidget.dart';
import 'package:vbvs_app/controller/setting/pdf/PdfController.dart';
class HelpPage extends StatefulWidget {
late MyWebView webView;
HelpPage({super.key});
@override
State<HelpPage> createState() => _HelpPageState();
}
class _HelpPageState extends State<HelpPage> {
PdfController pdfController = Get.find();
@override
void initState() {
super.initState();
// pdfController.loadPdf();
widget.webView = MyWebView(
url: "https://mp.weixin.qq.com/s/hBo0FMYrpe04I2WV6wbcxQ",
onLoad: () {
print('网页载入完毕');
},
);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, bodySize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent, // 加上这一行
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
// backgroundColor: Colors.transparent,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
titleSpacing: 0,
// leading: returnIconButtom,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
/// 居中标题
Text(
'操作说明'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
/// 左边返回按钮
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
actions: [],
centerTitle: false,
),
// body: SafeArea(
// top: true,
// child: Padding(
// padding: EdgeInsets.symmetric(horizontal: 30.rpx),
// child: Column(
// children: [
// Expanded(
// child: Obx(() {
// if (pdfController.localPdfPath.value == null) {
// return Center(child: CircularProgressIndicator());
// } else {
// return PDFView(
// filePath: pdfController.localPdfPath.value!,
// autoSpacing: false,
// enableSwipe: true,
// swipeHorizontal: false,
// pageSnap: true,
// fitEachPage: true,
// defaultPage: 0,
// onRender: (pages) => print('PDF 渲染完成,共 $pages 页'),
// onError: (error) => print('PDF 加载错误: $error'),
// );
// }
// }),
// ),
// ],
// ),
// ),
// ),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
child: Column(children: [
Expanded(
child: Container(
child: widget.webView,
),
),
]),
),
),
),
),
),
);
}
}

View File

@@ -44,7 +44,7 @@ class _HomePageState extends State<HomePage> {
final position = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
double popupWidth = 190.rpx; // 弹窗宽度(可以改)
double popupWidth = 190.rpx;
_popupEntry?.remove(); // 清除旧弹窗
_popupEntry = OverlayEntry(
@@ -106,7 +106,7 @@ class _HomePageState extends State<HomePage> {
width: double.infinity,
child: Center(
child: Text(
'扫一扫'.tr,
'扫一扫.标题'.tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
@@ -131,7 +131,7 @@ class _HomePageState extends State<HomePage> {
width: double.infinity,
child: Center(
child: Text(
'蓝牙绑定'.tr,
'蓝牙绑定.标题'.tr,
style: TextStyle(
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc3,
@@ -254,7 +254,7 @@ class _HomePageState extends State<HomePage> {
themeController.currentColor.sc2,
],
child: Container(
width: 100.rpx,
// width: 100.rpx,
height: 60.rpx,
alignment: Alignment.center,
padding: EdgeInsetsDirectional
@@ -314,13 +314,6 @@ class _HomePageState extends State<HomePage> {
),
);
}),
// SvgPicture.asset(
// 'assets/img/icon/add.svg',
// width: 39.rpx,
// height: 39.rpx, // 如果 SVG 中没有固定颜色,可以这样设置
// //todo 颜色
// color: themeController.currentColor.sc16,
// ),
ClickableContainer(
key: addIconKey,
backgroundColor: Colors.transparent,
@@ -553,10 +546,41 @@ class _HomePageState extends State<HomePage> {
CustomCard(
borderRadius: AppConstants()
.button_container_radius, // 圆角半径
onTap: () {
onTap: () async {
if (userInfoController.model.login ==
LoginStatus.LOGIN.code) {
Get.toNamed("/deviceType");
await Get.toNamed("/deviceType");
homeController.getSleepReport();
deviceController
.getDeviceNum()
.then((apiResponse) {
if (apiResponse.code !=
HttpStatusCodes.ok) {
TopSlideNotification.show(
Get.context!,
text: apiResponse.msg!,
textColor: themeController
.currentColor.sc9,
);
}
});
deviceController
.getDeviceList()
.then((apiResponse) {
if (apiResponse.code !=
HttpStatusCodes.ok) {
TopSlideNotification.show(
Get.context!,
text: apiResponse.msg!,
textColor: themeController
.currentColor.sc9,
);
} else {
//请求睡眠报告
deviceController
.getSleepReport();
}
});
} else {
TopSlideNotification.show(
context,
@@ -681,7 +705,7 @@ class _HomePageState extends State<HomePage> {
.normal_text_fontSize,
letterSpacing: 0.0,
fontWeight:
FontWeight.w900,
FontWeight.w600,
color: stringToColor(
"#916D46"), //固定
),
@@ -763,19 +787,22 @@ class _HomePageState extends State<HomePage> {
backgroundColor:
themeController.currentColor.sc5,
highlightColor:
themeController.currentColor.sc3,
themeController.currentColor.sc21,
borderRadius: AppConstants()
.normal_container_radius,
padding: EdgeInsets
.zero, // 原始Container没有padding
onTap: () {
onTap: () async {
// BodyDeviceController
// bodyDeviceController = Get.find();
// bodyDeviceController.model.type = 1;
// Get.toNamed("/bodyDevice");
homeController.model.type = 1;
deviceController.model.type = 1;
deviceController.getDeviceList();
await deviceController
.getDeviceList();
await deviceController
.getSleepReport();
homeController.updateAll();
},
child: Container(
@@ -823,19 +850,17 @@ class _HomePageState extends State<HomePage> {
backgroundColor:
themeController.currentColor.sc5,
highlightColor:
themeController.currentColor.sc3,
themeController.currentColor.sc21,
borderRadius: AppConstants()
.normal_container_radius,
padding: EdgeInsets
.zero, // 原本的Container没有 padding这里设置为 zero
onTap: () {
// BodyDeviceController
// bodyDeviceController = Get.find();
// bodyDeviceController.model.type = 2;
// Get.toNamed("/bodyDevice");
padding: EdgeInsets.zero,
onTap: () async {
homeController.model.type = 2;
deviceController.model.type = 2;
deviceController.getDeviceList();
await deviceController
.getDeviceList();
await deviceController
.getSleepReport();
homeController.updateAll();
},
child: Container(
@@ -903,7 +928,6 @@ class _HomePageState extends State<HomePage> {
if (reportData.isEmpty) {
return Expanded(child: NullDataWidget());
}
List<String> macList = reportData.keys.toList();
macList = macList
.where(
@@ -911,8 +935,6 @@ class _HomePageState extends State<HomePage> {
.any((device) => device['mac'] == mac),
)
.toList();
// ⛔️防止 macList 长度和 deviceList.length 不对应导致崩溃
if (macList.length != deviceList.length) {
return Expanded(
child: Center(child: CircularProgressIndicator()),

View File

@@ -6,10 +6,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/main_bottom/main_page_controller.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/enum/LoginStatus.dart';
@@ -21,33 +21,48 @@ import 'package:vbvs_app/pages/main_bottom/mine_page.dart';
class MainPageBottomChange extends GetView<MainPageController> {
GlobalController globalController = Get.find();
ThemeController themeController = Get.find();
MessageController messageController = Get.find();
getBottomNavigationBarItem(String svgPath, String actSvgPath, String label,
{double size = 0, bool isEmpty = false}) {
{double size = 0, bool isEmpty = false, bool showBadge = false}) {
if (size == 0) {
size = 36.rpx;
}
Widget buildIcon(String path) {
return Padding(
padding: EdgeInsets.only(bottom: 6.rpx),
child: isEmpty
? Container()
: Stack(
clipBehavior: Clip.none,
children: [
SvgPicture.asset(
path,
width: size,
height: size,
),
if (showBadge)
Positioned(
right: -20.rpx,
top: -2.rpx,
child: Container(
width: 14.rpx,
height: 14.rpx,
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
)
],
),
);
}
return BottomNavigationBarItem(
icon: Padding(
padding: EdgeInsets.only(bottom: 6.rpx),
child: isEmpty
? Container()
: SvgPicture.asset(
actSvgPath,
width: size,
height: size,
),
),
activeIcon: Padding(
padding: EdgeInsets.only(bottom: 6.rpx),
child: isEmpty
? Container()
: SvgPicture.asset(
svgPath,
width: size,
height: size,
),
),
label: label);
icon: buildIcon(actSvgPath),
activeIcon: buildIcon(svgPath),
label: label,
);
}
List arr = [
@@ -96,9 +111,21 @@ class MainPageBottomChange extends GetView<MainPageController> {
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: Theme(
data: ThemeData(
data: Theme.of(context).copyWith(
splashFactory: NoSplash.splashFactory,
highlightColor: Colors.transparent),
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: Container(
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: themeController.currentColor.sc4
.withOpacity(0.5),
width: 0.5,
),
),
),
child: BottomNavigationBar(
unselectedItemColor: themeController.currentColor.sc4,
selectedItemColor: themeController.currentColor.sc1,
@@ -113,7 +140,6 @@ class MainPageBottomChange extends GetView<MainPageController> {
bool isLoggedIn = userInfoController.model.login ==
LoginStatus.LOGIN.code;
// 需要登录的页面
if ((index == 1 || index == 2) && !isLoggedIn) {
TopSlideNotification.show(
context,
@@ -132,7 +158,6 @@ class MainPageBottomChange extends GetView<MainPageController> {
return;
}
// 已登录 或 index 是不需要登录的页面,正常跳转
if (controller.model.currentIndex != index) {
globalController.model.hideBottomNavigationBar =
false;
@@ -146,19 +171,24 @@ class MainPageBottomChange extends GetView<MainPageController> {
items: [
getBottomNavigationBarItem("assets/img/menu/home.svg",
"assets/img/menu/n_home.svg", "菜单.首页".tr),
// getBottomNavigationBarItem("assets/img/menu/report.svg",
// "assets/img/menu/n_report.svg", "菜单.报告".tr),
getBottomNavigationBarItem("assets/img/menu/e.svg",
"assets/img/menu/n_e.svg", "菜单.小e".tr),
getBottomNavigationBarItem("assets/img/menu/message.svg",
"assets/img/menu/n_message.svg", "菜单.消息".tr),
getBottomNavigationBarItem(
"assets/img/menu/message.svg",
"assets/img/menu/n_message.svg",
"菜单.消息".tr,
showBadge: (messageController
.model.body_message_read ==
1 ||
messageController.model.system_message_read ==
1)),
getBottomNavigationBarItem("assets/img/menu/mine.svg",
"assets/img/menu/n_mine.svg", "菜单.我的".tr),
],
),
),
),
);
));
}
},
),
@@ -172,7 +202,8 @@ class MainPageBottomChange extends GetView<MainPageController> {
if (_lastBackPressedTime == null ||
currentTime.difference(_lastBackPressedTime!) > Duration(seconds: 2)) {
_lastBackPressedTime = currentTime;
showToast("再按一次退出程序", color: color_warning, closeTime: 2);
// showToast("再按一次退出程序", color: color_warning, closeTime: 2);
TopSlideNotification.show(context,text: "滑动退出提醒".tr);
return false; // 阻止退出程序
} else {
return true; // 允许退出程序

View File

@@ -5,6 +5,7 @@ import 'package:flutterflow_ui/flutterflow_ui.dart';
import 'package:vbvs_app/common/color/appConstants.dart';
import 'package:vbvs_app/common/color/app_uri_status.dart';
import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/component/NullDataComponentWidget.dart';
import 'package:vbvs_app/component/tool/ClickableContainer.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/message/message_controller.dart';
@@ -21,9 +22,20 @@ class MessagePage extends StatefulWidget {
class _MessagePageState extends State<MessagePage> {
ThemeController themeController = Get.find();
MessageController messageController = Get.find();
late PageController _pageController;
@override
void initState() {
super.initState();
_pageController =
PageController(initialPage: messageController.model.type == 1 ? 0 : 1);
messageController.getMessageStatus();
_fetchMessageData();
}
void _fetchMessageData() {
String type = messageController.model.type == 1 ? "app_vsm" : "app_system";
messageController.updateMessageStatus(type: type);
messageController.getMessageList().then((response) {
if (response.code != HttpStatusCodes.ok) {
TopSlideNotification.show(
@@ -35,44 +47,61 @@ class _MessagePageState extends State<MessagePage> {
});
}
void _onTabChanged(int index) {
messageController.model.type = index == 0 ? 1 : 2;
messageController.updateAll();
_fetchMessageData();
_pageController.animateToPage(index,
duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
}
void _onPageChanged(int index) {
int newType = index == 0 ? 1 : 2;
if (messageController.model.type != newType) {
messageController.model.type = newType;
messageController.updateAll();
_fetchMessageData();
}
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // 这里设置你希望的颜色
statusBarIconBrightness: Brightness.light, // 状态栏图标的亮度
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
));
return LayoutBuilder(
builder: (context, boxConstraints) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
image: AssetImage('assets/img/bgNoImg.png'),
fit: BoxFit.fill,
),
),
child: Scaffold(
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
iconTheme: IconThemeData(color: themeController.currentColor.sc3),
toolbarHeight: 140.rpx,
titleSpacing: 0,
title: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx,
0.rpx,
0.rpx,
0.rpx,
),
padding: EdgeInsetsDirectional.fromSTEB(40.rpx, 0, 0, 0),
child: Container(
width: double.infinity,
height: 140.rpx, // 👈 明确告诉 Flutter 高度
height: 140.rpx,
child: Column(
children: [
SizedBox(height: 40.rpx), // 上边距
SizedBox(height: 40.rpx),
Expanded(
child: Stack(
alignment: Alignment.bottomLeft,
@@ -82,20 +111,20 @@ class _MessagePageState extends State<MessagePage> {
children: [
Obx(() {
return ClickableContainer(
padding: EdgeInsets.all(0),
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc3,
themeController.currentColor.sc21,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () async {
messageController.model.type = 1;
await messageController.getMessageList();
messageController.updateAll();
},
onTap: () => _onTabChanged(0),
child: Container(
width: 160.rpx,
alignment: Alignment.center,
child: Text(
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
Text(
'体征消息'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
@@ -103,7 +132,6 @@ class _MessagePageState extends State<MessagePage> {
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
messageController
.model.type ==
@@ -114,25 +142,46 @@ class _MessagePageState extends State<MessagePage> {
.currentColor.sc2,
),
),
Obx(() {
return messageController.model
.body_message_read ==
1
? Positioned(
top: -4,
right: -14,
child: Container(
width: 8,
height: 8,
decoration:
const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
),
)
: const SizedBox.shrink();
}),
],
),
),
);
}),
Obx(() {
return ClickableContainer(
padding: EdgeInsets.all(0),
backgroundColor: Colors.transparent,
highlightColor:
themeController.currentColor.sc3,
themeController.currentColor.sc21,
borderRadius: 8.rpx,
padding: EdgeInsets.all(0),
onTap: () async {
messageController.model.type = 2;
await messageController.getMessageList();
messageController.updateAll();
},
onTap: () => _onTabChanged(1),
child: Container(
width: 160.rpx,
alignment: Alignment.center,
child: Text(
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
Text(
'系统消息'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
@@ -140,7 +189,6 @@ class _MessagePageState extends State<MessagePage> {
fontFamily: 'Inter',
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
color:
messageController
.model.type ==
@@ -151,20 +199,41 @@ class _MessagePageState extends State<MessagePage> {
.currentColor.sc2,
),
),
Obx(() {
return messageController.model
.system_message_read ==
1
? Positioned(
top: -4,
right: -14,
child: Container(
width: 8,
height: 8,
decoration:
const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
);
),
)
: const SizedBox.shrink();
}),
],
),
),
);
}),
].divide(SizedBox(width: 10.rpx)),
),
Obx(() {
double lineWidth = 160.rpx;
double lineWidth = 170.rpx;
return AnimatedPositioned(
duration: Duration(milliseconds: 300),
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
bottom: 0, // 👈 现在 Stack 够大,线能放得下
bottom: 0,
left: messageController.model.type == 1
? 0
: 160.rpx,
: 170.rpx,
child: Container(
width: lineWidth,
height: 4.rpx,
@@ -178,47 +247,33 @@ class _MessagePageState extends State<MessagePage> {
],
),
),
SizedBox(height: 17.rpx), // 上边距
SizedBox(height: 17.rpx),
],
),
),
),
actions: [],
actions: const [],
centerTitle: false,
),
backgroundColor: Colors.transparent,
body: SafeArea(
top: true,
child: Column(
mainAxisSize: MainAxisSize.max,
child: PageView(
controller: _pageController,
onPageChanged: _onPageChanged,
children: [
Expanded(
child: Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx,
0.rpx,
30.rpx,
0.rpx,
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
MessageWidgetWidget(),
MessageWidgetWidget(),
MessageWidgetWidget(),
]
.divide(SizedBox(height: 30.rpx))
.addToStart(SizedBox(height: 30.rpx))
.addToEnd(SizedBox(height: 30.rpx)),
),
),
),
),
),
Obx(() {
final list = messageController.messageList.value;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
}),
Obx(() {
final list = messageController.messageList.value;
return list.isEmpty
? const NullDataWidget()
: _buildMessageListView(list);
}),
],
),
),
@@ -227,4 +282,23 @@ class _MessagePageState extends State<MessagePage> {
),
);
}
Widget _buildMessageListView(List dataList) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 30.rpx),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: 30.rpx),
...dataList
.map((item) => MessageWidgetWidget(data: item))
.toList()
.divide(SizedBox(height: 30.rpx)),
SizedBox(height: 30.rpx),
],
),
),
);
}
}

View File

@@ -6,6 +6,7 @@ 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/base/SleepCalendarWidget.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';
@@ -157,7 +158,10 @@ class _MinePageState extends State<MinePage> {
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Row(
Obx(() {
var userInfo =
userInfoController.model.user;
return Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
@@ -198,7 +202,8 @@ class _MinePageState extends State<MinePage> {
.user!.nick_name ??
'未命名'.tr)
: "我的.未登录".tr,
style: FlutterFlowTheme.of(context)
style:
FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
@@ -217,15 +222,18 @@ class _MinePageState extends State<MinePage> {
? userInfoController
.model.user!.email!
: MyUtils.hidePhoneNumber(
userInfoController.model
.user!.phone!))
userInfoController
.model
.user!
.phone!))
: "我的.未登录".tr,
style: FlutterFlowTheme.of(context)
style:
FlutterFlowTheme.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color: themeController
.currentColor.sc3,
.currentColor.sc4,
fontSize: AppConstants()
.normal_text_fontSize,
letterSpacing: 0.0,
@@ -234,12 +242,15 @@ class _MinePageState extends State<MinePage> {
].divide(SizedBox(height: 20.rpx)),
),
].divide(SizedBox(width: 35.rpx)),
),
);
}),
CustomCard(
borderRadius: 0,
onTap: () {
onTap: () async {
if (login == 1) {
Get.toNamed("/updateUserPage");
await Get.toNamed("/updateUserPage");
await userInfoController.getUserInfo();
userInfoController.updateAll();
} else {
Get.toNamed("/loginPage");
}
@@ -250,9 +261,7 @@ class _MinePageState extends State<MinePage> {
mainAxisSize: MainAxisSize.max,
children: [
Text(
login == 1
? '我的.个人信息'.tr
: '我的.去登录'.tr,
login == 1 ? '编辑'.tr : '我的.去登录'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
@@ -328,7 +337,7 @@ class _MinePageState extends State<MinePage> {
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -392,18 +401,19 @@ class _MinePageState extends State<MinePage> {
);
Get.toNamed("/loginPage");
} else {
TopSlideNotification.show(
context,
text: "待开发.提示".tr,
textColor:
themeController.currentColor.sc2,
);
// TopSlideNotification.show(
// context,
// text: "待开发.提示".tr,
// textColor:
// themeController.currentColor.sc2,
// );
Get.toNamed("/applyRepairPage");
}
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 20.rpx, 40.rpx, 20.rpx),
40.rpx, 30.rpx, 40.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -467,18 +477,19 @@ class _MinePageState extends State<MinePage> {
);
Get.toNamed("/loginPage");
} else {
TopSlideNotification.show(
context,
text: "待开发.提示".tr,
textColor:
themeController.currentColor.sc2,
);
// TopSlideNotification.show(
// context,
// text: "待开发.提示".tr,
// textColor:
// themeController.currentColor.sc2,
// );
Get.toNamed("/helpPage");
}
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 20.rpx, 40.rpx, 20.rpx),
40.rpx, 30.rpx, 40.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -542,18 +553,19 @@ class _MinePageState extends State<MinePage> {
);
Get.toNamed("/loginPage");
} else {
TopSlideNotification.show(
context,
text: "待开发.提示".tr,
textColor:
themeController.currentColor.sc2,
);
// TopSlideNotification.show(
// context,
// text: "待开发.提示".tr,
// textColor:
// themeController.currentColor.sc2,
// );
Get.toNamed("/followPage");
}
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -570,7 +582,7 @@ class _MinePageState extends State<MinePage> {
color: stringToColor("#00C1AA"),
),
Text(
'关注我们'.tr,
'我的.关注我们'.tr,
style:
FlutterFlowTheme.of(context)
.bodyMedium
@@ -617,18 +629,38 @@ class _MinePageState extends State<MinePage> {
);
Get.toNamed("/loginPage");
} else {
TopSlideNotification.show(
context,
text: "待开发.提示".tr,
textColor:
themeController.currentColor.sc2,
// TopSlideNotification.show(
// context,
// text: "待开发.提示".tr,
// textColor:
// themeController.currentColor.sc2,
// );
showModalBottomSheet(
context: context,
isScrollControlled: false, // 不需要滚动全屏
backgroundColor:
Colors.transparent, // 为了圆角和美观
builder: (context) {
return Container(
decoration: BoxDecoration(
color: const Color(
0xFF242835), // 你组件的底色
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20.rpx),
topRight: Radius.circular(20.rpx),
),
),
child:
SleepCalendarWidget(), // 显示日历组件
);
},
);
}
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:

View File

@@ -1,31 +1,30 @@
// import 'package:ef/ef.dart';
// import 'package:flutter/material.dart';
// import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
// import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:ef/ef.dart';
import 'package:flutter/material.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
// class SleepReportPage extends StatefulWidget {
// const SleepReportPage({super.key});
class SleepReportPage extends StatefulWidget {
const SleepReportPage({super.key});
// @override
// State<SleepReportPage> createState() => _SleepReportPageState();
// }
@override
State<SleepReportPage> createState() => _SleepReportPageState();
}
// class _SleepReportPageState extends State<SleepReportPage> {
// GlobalController globalController = Get.find();
// UserInfoController userInfoController = Get.find();
// @override
// Widget build(BuildContext context) {
// return LayoutBuilder(
// builder: (context, boxConstraints) => GestureDetector(
// onTap: () => FocusScope.of(context).unfocus(),
// child: Scaffold(
// body: SafeArea(
// top: true,
// child: Text("睡眠报告"),
// ),
// ),
// ),
// );
// }
// }
class _SleepReportPageState extends State<SleepReportPage> {
GlobalController globalController = Get.find();
UserInfoController userInfoController = Get.find();
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, boxConstraints) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
body: SafeArea(
top: true,
child: Text("睡眠报告"),
),
),
),
);
}
}

View File

@@ -34,6 +34,17 @@ class _EPageState extends State<PersonPage> {
@override
void initState() {
super.initState();
personController.getDiseaseData().then((apiResponse) {
WidgetsBinding.instance.addPostFrameCallback((_) {
TopSlideNotification.show(
context,
text: apiResponse.msg!,
textColor: apiResponse.code != HttpStatusCodes.ok
? themeController.currentColor.sc9
: themeController.currentColor.sc2,
);
});
});
personController.selectedDiseaseIds.value = [];
personController.name.value = '';
personController.gender.value = 1;

View File

@@ -3,7 +3,6 @@ import 'package:flutter/cupertino.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/common/util/MyUtils.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
Future showDateSelectionDialog(BuildContext context,

View File

@@ -15,11 +15,13 @@ 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/enum/BindType.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/person/select_time.dart';
class UpdatePersonPage extends StatefulWidget {
const UpdatePersonPage({super.key});
var status;
UpdatePersonPage({super.key, required this.status});
@override
State<UpdatePersonPage> createState() => _UpdatePageState();
@@ -38,7 +40,7 @@ class _UpdatePageState extends State<UpdatePersonPage> {
super.initState();
personController.getDiseaseData().then((apiResponse) {
TopSlideNotification.show(
Get.context!,
context,
text: apiResponse.msg!,
textColor: apiResponse.code != HttpStatusCodes.ok
? themeController.currentColor.sc9
@@ -49,6 +51,8 @@ class _UpdatePageState extends State<UpdatePersonPage> {
@override
Widget build(BuildContext context) {
print(widget.status);
print(widget.status);
return LayoutBuilder(
builder: (context, bodySize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
@@ -85,7 +89,6 @@ class _UpdatePageState extends State<UpdatePersonPage> {
fontSize: 30.rpx,
),
),
/// 左边返回按钮
Positioned(
left: 0,
@@ -94,7 +97,9 @@ class _UpdatePageState extends State<UpdatePersonPage> {
bodyDeviceController.updateAll();
}),
),
Positioned(
Visibility(
visible: widget.status == BindType.active.code,
child: Positioned(
right: 20.rpx,
child: CustomCard(
borderRadius: 20.rpx,
@@ -133,6 +138,7 @@ class _UpdatePageState extends State<UpdatePersonPage> {
),
),
),
),
],
),
),
@@ -670,6 +676,7 @@ class _UpdatePageState extends State<UpdatePersonPage> {
),
);
}),
Padding(
padding:
EdgeInsetsDirectional.fromSTEB(0, 152.rpx, 0, 0),

View File

@@ -0,0 +1,538 @@
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/common/util/requestWithLog.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/device/device_type_controller.dart';
import 'package:vbvs_app/controller/repair/repair_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/pages/repair/component/RepairModelWidget.dart';
class ApplyRepairPage extends StatefulWidget {
const ApplyRepairPage({super.key});
@override
State<ApplyRepairPage> createState() => _ApplyRepairPageState();
}
class _ApplyRepairPageState extends State<ApplyRepairPage> {
final ThemeController themeController = Get.find();
DeviceTypeController deviceTypeController = Get.find();
RepairController repairController = Get.find();
BodyDeviceController bodyDeviceController = Get.find();
final List<GlobalKey> repairItemKeys = [];
final GlobalKey contactKey = GlobalKey();
final GlobalKey phoneKey = GlobalKey();
@override
void initState() {
super.initState();
_loadData();
repairItemKeys.addAll(List.generate(
repairController.repairList.length, (index) => GlobalKey()));
}
void _updateRepairItemKeys() {
try {
final int currentLength = repairController.repairList.length;
if (repairItemKeys.length != currentLength) {
repairItemKeys.clear();
repairItemKeys.addAll(
List.generate(currentLength, (index) => GlobalKey()),
);
}
} catch (e) {
print(e);
}
}
Future<void> _loadData() async {
repairController.repairList = [{}].obs;
repairController.name.value = "";
repairController.phone.value = "";
await deviceTypeController.getDeviceType(); // 等待数据加载
if (deviceTypeController.deviceTypeList.isNotEmpty) {
repairController.device_type.value =
deviceTypeController.deviceTypeList.first['_id'];
}
await bodyDeviceController.getDeviceList();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateRepairItemKeys();
});
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
if (deviceTypeController.deviceTypeList.isNotEmpty) {
repairController.device_type.value =
deviceTypeController.deviceTypeList.first['_id'];
}
if (bodyDeviceController.deviceList.isNotEmpty) {
repairController.deviceListId = bodyDeviceController.deviceList
.map((e) => e['_id'] as String)
.toList();
}
return LayoutBuilder(
builder: (context, bodysize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
titleSpacing: 0,
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(() {}),
),
Positioned(
right: 20.rpx,
child: ClickableContainer(
backgroundColor: Colors.transparent,
highlightColor: themeController.currentColor.sc16,
padding: EdgeInsets.all(8.rpx),
onTap: () {
Get.toNamed("/repairListPage");
},
child: SvgPicture.asset(
'assets/img/icon/history.svg',
width: 39.rpx,
height: 39.rpx,
color: themeController.currentColor.sc16,
),
),
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(30.rpx, 29.rpx, 30.rpx, 0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Obx(() {
return Row(
mainAxisSize: MainAxisSize.max,
children: deviceTypeController.deviceTypeList.value
.map(
(deviceType) => CustomCard(
borderRadius:
AppConstants().button_container_radius,
onTap: () async {
repairController.device_type.value =
deviceType['_id'];
repairController.repairList
.clear(); // 清空旧数据
repairController.repairList
.add({}); // 添加新数据
await bodyDeviceController
.getDeviceList(); // 等待数据加载
_updateRepairItemKeys(); // 清空后更新 keys
repairController
.updateAll(); // 手动触发更新(如果未自动更新)
},
colors: deviceType['_id'] ==
repairController.device_type.value
? [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
]
: [themeController.currentColor.sc5],
child: Container(
width: (MediaQuery.sizeOf(context).width *
0.284)
.rpx,
constraints: BoxConstraints(
minWidth: 213.rpx,
minHeight: 91.rpx,
),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(20.rpx),
),
child: Align(
alignment: AlignmentDirectional(0, 0),
child: Text(
deviceType[
'name'], // 假设 deviceType 有 name 字段
style: TextStyle(
letterSpacing: 0.0,
color:
themeController.currentColor.sc3,
fontSize: AppConstants()
.normal_text_fontSize,
),
),
),
),
),
)
.toList()
.divide(SizedBox(width: 25.rpx)),
);
}),
Obx(() {
return Column(
children: repairController.repairList
.map((item) {
final int index =
repairController.repairList.indexOf(item);
return RepairModelWidget(
widgetKey:
repairItemKeys[index], // 确保 index 有效
model: item,
onTap: () {
repairController.repairList.remove(item);
_updateRepairItemKeys(); // 删除后更新 keys
},
length: repairController.repairList.length,
);
})
.toList()
.divide(SizedBox(
height: 25.rpx,
)),
);
}),
ClickableContainer(
backgroundColor: themeController.currentColor.sc5,
highlightColor: themeController.currentColor.sc21,
borderRadius: 20.rpx,
padding: EdgeInsets.zero,
onTap: () {
repairController.repairList.add({});
_updateRepairItemKeys(); // 初始化 keys
repairController.updateAll();
},
child: SizedBox(
width: double.infinity,
height: 90.rpx,
child: Center(
child: Image.asset(
"assets/img/addItem.png",
width: 39.rpx,
height: 39.rpx,
),
),
),
),
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: [
_buildParamRow(
context,
"联系人".tr,
"名称输入提示".tr,
(value) {
repairController.name.value = value;
},
key: contactKey,
),
_buildParamRow(
context,
"手机号".tr,
"手机号输入提示".tr,
(value) {
repairController.phone.value = value;
},
key: phoneKey,
),
].divide(SizedBox(height: 30.rpx)),
),
),
),
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
100.rpx, 0.rpx, 100.rpx, 60.rpx),
child: CustomCard(
borderRadius:
AppConstants().button_container_radius, // 圆角半径
onTap: () async {
// TopSlideNotification.show(context,
// text: "提交成功",
// textColor: themeController.currentColor.sc1);
// Future.delayed(const Duration(seconds: 1), () {
// Get.offAllNamed("/mianPageBottomChange");
// });
String msg = checkRepairParam();
if (msg.isNotEmpty) {
TopSlideNotification.show(context,
text: msg,
textColor: themeController.currentColor.sc9);
} else {
String serviceAddress =
ServiceConstant.service_address;
String serviceName =
ServiceConstant.server_service;
String serviceApi = ServiceConstant.submit_repair;
String queryUrl =
"$serviceAddress$serviceName$serviceApi";
var data = {
"repairList": repairController.repairList,
"type": repairController.device_type.value,
};
ApiResponse apiResponse = await requestWithLog(
logTitle: "提交报修信息",
method: MyHttpMethod.post,
queryUrl: queryUrl,
data: data,
);
Get.back();
}
},
colors: [
themeController.currentColor.sc1,
themeController.currentColor.sc2,
],
child: Container(
color: Colors.transparent,
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,
)),
),
),
),
),
]
.divide(SizedBox(
height: 25.rpx,
))
.addToEnd(SizedBox(
height: 25.rpx,
)),
),
),
),
),
),
),
),
);
}
Widget _buildParamRow(BuildContext context, String text, String hinttext,
void Function(String) onChanged,
{Key? key} // 新增可选参数
) {
return Row(
key: key, // 使用传入的 key
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 110.rpx,
child: Text(
text,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
),
overflow: TextOverflow.ellipsis,
softWrap: false,
maxLines: 1,
),
),
Expanded(
child: Container(
width: 200.rpx,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.rpx),
color: Colors.transparent,
),
child: TextFormField(
autofocus: false,
obscureText: false,
decoration: InputDecoration(
isDense: true,
labelStyle: TextStyle(
letterSpacing: 0.0,
fontWeight:
FlutterFlowTheme.of(context).labelMedium.fontWeight,
fontStyle: FlutterFlowTheme.of(context).labelMedium.fontStyle,
),
hintText: hinttext,
hintStyle: TextStyle(
letterSpacing: 0.0,
fontSize: AppConstants().normal_text_fontSize,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1.rpx,
),
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
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,
fillColor: themeController.currentColor.sc15,
),
style: TextStyle(
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
cursorColor: themeController.currentColor.sc3,
onChanged: onChanged,
),
),
),
].divide(SizedBox(width: 24.rpx)),
);
}
String checkRepairParam() {
String errormsg = "";
// 检查报修项
for (var i = 0; i < repairController.repairList.length; i++) {
final item = repairController.repairList[i];
if (item["path"] == null || item["path"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项图片未上传";
return "图片未上传".tr;
}
if (item["id"] == null || item["id"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项设备未选择";
return "设备未选择".tr;
}
if (item["param"] == null || item["param"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项参数未填写";
return "参数未填写".tr;
}
if (item["issue"] == null || item["issue"].toString().isEmpty) {
_scrollToKey(repairItemKeys[i]);
// return "第 ${i + 1} 项问题描述未填写";
return "问题描述未填写".tr;
}
}
// 检查联系人
if (repairController.name.trim().isEmpty) {
_scrollToKey(contactKey);
return "联系人不能为空";
}
if (repairController.phone.trim().isEmpty) {
_scrollToKey(phoneKey);
return "联系电话不能为空";
}
return errormsg;
}
// 新增滚动方法
void _scrollToKey(GlobalKey key) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
key.currentContext!,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}
}

View File

@@ -0,0 +1,250 @@
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/component/tool/CustomCard.dart';
import 'package:vbvs_app/routers/routers.dart';
class RepairHistoryInfoWidget extends StatefulWidget {
const RepairHistoryInfoWidget({super.key});
@override
State<RepairHistoryInfoWidget> createState() =>
_RepairHistoryInfoWidgetState();
}
class _RepairHistoryInfoWidgetState extends State<RepairHistoryInfoWidget> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return CustomCard(
borderRadius: AppConstants().normal_container_radius,
onTap: () {
//跳转详情
},
colors: [
themeController.currentColor.sc5,
],
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, 36.rpx, 0.rpx, 36.rpx),
child: Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(
maxWidth: 130.rpx,
),
decoration: BoxDecoration(),
child: Text(
'设备类型'.tr,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle: FlutterFlowTheme.of(context)
.bodyMedium
.fontStyle,
color: themeController.currentColor.sc4,
),
),
),
Text(
'智能床',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle:
FlutterFlowTheme.of(context).bodyMedium.fontStyle,
color: themeController.currentColor.sc3,
),
),
].divide(SizedBox(width: 33.rpx)),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(
maxWidth: 130.rpx,
),
decoration: BoxDecoration(),
child: Text(
'设备ID'.tr,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle: FlutterFlowTheme.of(context)
.bodyMedium
.fontStyle,
color: themeController.currentColor.sc4,
),
),
),
Text(
'15561651',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle:
FlutterFlowTheme.of(context).bodyMedium.fontStyle,
color: themeController.currentColor.sc3,
),
),
].divide(SizedBox(width: 33.rpx)),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(
maxWidth: 130.rpx,
),
decoration: BoxDecoration(),
child: Text(
'联系人'.tr,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle: FlutterFlowTheme.of(context)
.bodyMedium
.fontStyle,
color: themeController.currentColor.sc4,
),
),
),
Text(
'王明',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle:
FlutterFlowTheme.of(context).bodyMedium.fontStyle,
color: themeController.currentColor.sc3,
),
),
].divide(SizedBox(width: 33.rpx)),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: MediaQuery.sizeOf(context).width * 0.14,
constraints: BoxConstraints(
maxWidth: 130.rpx,
),
decoration: BoxDecoration(),
child: Text(
'发生时间'.tr,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle: FlutterFlowTheme.of(context)
.bodyMedium
.fontStyle,
color: themeController.currentColor.sc4,
),
),
),
Text(
'2022-08-01 15:05',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.bodyMedium
.fontWeight,
fontStyle:
FlutterFlowTheme.of(context).bodyMedium.fontStyle,
color: themeController.currentColor.sc3,
),
),
].divide(SizedBox(width: 33.rpx)),
),
),
].divide(SizedBox(height: 34.rpx)),
),
),
),
),
);
}
}

View File

@@ -0,0 +1,435 @@
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/component/base/THFlutterFlowDropDown.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/repair/repair_controller.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart';
import 'package:vbvs_app/routers/routers.dart';
class RepairModelWidget extends StatefulWidget {
var model;
final VoidCallback onTap; // 添加回调函数
int length;
final GlobalKey widgetKey; // 新增
RepairModelWidget({
super.key,
required this.model,
required this.onTap,
required this.length,
required this.widgetKey,
});
@override
State<RepairModelWidget> createState() => _RepairModelWidgetState();
}
class _RepairModelWidgetState extends State<RepairModelWidget> {
FormFieldController<String> _deviceController =
FormFieldController<String>(null);
RepairController repairController = Get.find();
UserInfoController userInfoController = Get.find();
@override
void setState(VoidCallback callback) {
super.setState(callback);
}
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// _deviceController.value = "";
return Container(
key: widget.widgetKey, // 使用传入的 key
width: double.infinity,
decoration: BoxDecoration(
color: themeController.currentColor.sc5,
borderRadius:
BorderRadius.circular(AppConstants().normal_container_radius),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 35.rpx, 30.rpx, 35.rpx),
child: Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 110.rpx,
decoration: BoxDecoration(),
child: Text(
'设备ID'.tr,
style: TextStyle(
fontSize: 26.rpx,
color: themeController.currentColor.sc3,
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: themeController.currentColor.sc15,
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
height: AppConstants().dropdown_height,
child: THFlutterFlowDropDown<String>(
onChanged: (val) {
widget.model['id'] = val;
},
width: 200.rpx,
height: 40.rpx,
textStyle: TextStyle(
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
hintText: '选择设备'.tr,
hintTextStyle: TextStyle(
color: themeController.currentColor.sc4,
fontSize: AppConstants().normal_text_fontSize,
),
icon: Icon(
Icons.keyboard_arrow_down_rounded,
color: FlutterFlowTheme.of(context).secondaryText,
size: 24.rpx,
),
fillColor: themeController.currentColor.sc15,
elevation: 0,
borderColor: Colors.transparent,
borderWidth: 0,
borderRadius: 20.rpx,
margin: EdgeInsetsDirectional.fromSTEB(
20.rpx, 0, 20.rpx, 0),
hidesUnderline: true,
isOverButton: false,
isSearchable: false,
isMultiSelect: false,
controller: _deviceController,
options: repairController.deviceListId!,
optionLabels: repairController.deviceListId!,
),
),
),
].divide(SizedBox(width: 24.rpx)),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Container(
width: 110.rpx,
decoration: BoxDecoration(),
child: Text(
'设备参数'.tr,
style: TextStyle(
fontSize: 26.rpx,
color: themeController.currentColor.sc3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
Expanded(
child: Container(
width: 200.rpx,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.rpx),
color: Colors.transparent,
),
child: TextFormField(
controller: repairController.onReDraw(
TextEditingController(
text: widget.model['param']),
(textEditingController) {
textEditingController.text = widget.model['param'];
}),
// initialValue: widget.model['param'],
autofocus: false,
obscureText: false,
decoration: InputDecoration(
isDense: true,
labelStyle: TextStyle(
letterSpacing: 0.0,
fontWeight: FlutterFlowTheme.of(context)
.labelMedium
.fontWeight,
fontStyle: FlutterFlowTheme.of(context)
.labelMedium
.fontStyle,
),
hintText: "设备参数提示".tr,
hintStyle: TextStyle(
letterSpacing: 0.0,
fontSize: AppConstants().normal_text_fontSize,
color: themeController.currentColor.sc4,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1.rpx,
),
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
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,
fillColor: themeController.currentColor.sc15,
),
style: TextStyle(
letterSpacing: 0.0,
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
cursorColor: themeController.currentColor.sc3,
onChanged: (value) {
widget.model['param'] = value;
},
),
),
),
].divide(SizedBox(width: 24.rpx)),
),
),
Container(
width: double.infinity,
height: 151.rpx,
decoration: BoxDecoration(
color: themeController.currentColor.sc15,
borderRadius: BorderRadius.circular(20.rpx),
),
child: Container(
width: 200.rpx,
child: TextFormField(
onChanged: (value) {
widget.model['issue'] = value;
},
maxLines: null, // 👈 支持无限行
controller: TextEditingController(),
autofocus: false,
obscureText: false,
decoration: InputDecoration(
isDense: true,
labelStyle: TextStyle(
letterSpacing: 0.0,
fontWeight:
FlutterFlowTheme.of(context).labelMedium.fontWeight,
fontStyle:
FlutterFlowTheme.of(context).labelMedium.fontStyle,
),
hintText: '问题描述'.tr,
hintStyle: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc4,
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.circular(8.rpx),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000),
width: 1,
),
borderRadius: BorderRadius.circular(8.rpx),
),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).error,
width: 1,
),
borderRadius: BorderRadius.circular(8.rpx),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: FlutterFlowTheme.of(context).error,
width: 1,
),
borderRadius: BorderRadius.circular(8.rpx),
),
filled: false,
contentPadding: EdgeInsets.symmetric(
vertical: 20.rpx, horizontal: 20.rpx),
),
style: TextStyle(
color: themeController.currentColor.sc3,
fontSize: AppConstants().normal_text_fontSize,
),
cursorColor: FlutterFlowTheme.of(context).primaryText,
),
),
),
ClickableContainer(
backgroundColor: themeController.currentColor.sc15,
highlightColor: themeController.currentColor.sc21,
borderRadius: 20.rpx,
padding: EdgeInsets.zero, // 原来没有额外 padding这里保持一致
onTap: () async {
ApiResponse apiResponse = await repairController.uploadImg();
print(apiResponse);
if (apiResponse.code == HttpStatusCodes.ok) {
print(widget.model);
// setState(() {
// widget.model['path'] = apiResponse.data['path'];
// });
widget.model['path'] = apiResponse.data['path'];
repairController.repairList
.refresh(); // 通知 Obx 更新(如果用了 GetX
print(widget.model);
} else {
TopSlideNotification.show(
context,
text: apiResponse.msg!,
textColor: apiResponse.code == HttpStatusCodes.ok
? themeController.currentColor.sc2
: themeController.currentColor.sc9,
);
}
},
child: SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.203,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: 151.rpx,
),
child: Obx(() {
var list = repairController.repairList;
print(list);
if (widget.model['path'] == null ||
widget.model['path'] == "") {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 60.rpx,
height: 53.rpx,
child: SvgPicture.asset(
'assets/img/icon/camera.svg',
width: 24.rpx,
height: 24.rpx,
color: themeController.currentColor.sc4,
),
),
Text(
'上传提示'.tr,
style: TextStyle(
fontSize: 26.rpx,
letterSpacing: 0.0,
color: themeController.currentColor.sc4,
),
),
].divide(SizedBox(height: 28.rpx)),
);
}
return Container(
// clipBehavior: Clip.antiAlias,
child: Image.network(
widget.model['path'],
fit: BoxFit.cover,
),
);
})),
),
),
if (widget.length > 1)
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
100.rpx, 0.rpx, 100.rpx, 0.rpx),
child: CustomCard(
borderRadius:
AppConstants().button_container_radius, // 圆角半径
onTap: widget.onTap,
colors: [
Colors.transparent,
],
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(
AppConstants().button_container_radius),
),
width: MediaQuery.sizeOf(context).width * 0.216,
height: MediaQuery.sizeOf(context).height * 0.038,
constraints: BoxConstraints(
minHeight: 62.rpx,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'删除'.tr,
style: FlutterFlowTheme.of(context)
.bodyMedium
.override(
color: themeController.currentColor.sc9,
fontFamily: 'Inter',
fontSize: AppConstants().normal_text_fontSize,
letterSpacing: 0.0,
),
),
].divide(SizedBox(
width: 17.rpx,
)),
),
),
),
),
].divide(SizedBox(height: 29.rpx)),
),
),
),
);
}
}

View File

@@ -0,0 +1,136 @@
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/controller/device/body_device_controller.dart';
import 'package:vbvs_app/controller/device/device_type_controller.dart';
import 'package:vbvs_app/controller/repair/repair_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/pages/repair/component/RepairHistoryInfoWidget.dart';
class RepairListPage extends StatefulWidget {
const RepairListPage({super.key});
@override
State<RepairListPage> createState() => _RepairListPageState();
}
class _RepairListPageState extends State<RepairListPage> {
@override
void initState() {
super.initState();
loadData();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, bodysize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
titleSpacing: 0,
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(() {}),
),
// Positioned(
// right: 20.rpx,
// child: ClickableContainer(
// backgroundColor: Colors.transparent,
// highlightColor: themeController.currentColor.sc16,
// padding: EdgeInsets.all(8.rpx),
// onTap: () {},
// child: SvgPicture.asset(
// 'assets/img/icon/history.svg',
// width: 39.rpx,
// height: 39.rpx,
// color: themeController.currentColor.sc16,
// ),
// ),
// ),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding:
EdgeInsetsDirectional.fromSTEB(30.rpx, 29.rpx, 30.rpx, 0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
RepairHistoryInfoWidget(),
]
.divide(SizedBox(
height: 25.rpx,
))
.addToEnd(SizedBox(
height: 25.rpx,
)),
),
),
),
),
),
),
),
);
}
Future<void> loadData() async {
// repairController.repairHistory.value = [{}];
// ApiResponse apiResponse = await requestWithLog(
// logTitle: "请求维修列表", method: MyHttpMethod.get, queryUrl: "");
// if (apiResponse.code == HttpStatusCodes.ok) {
// //请求维修列表
// }
}
}

View File

@@ -0,0 +1,272 @@
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/component/tool/CustomCard.dart';
import 'package:vbvs_app/controller/setting/language/language_controller.dart';
import 'package:vbvs_app/language/AppLanguage.dart';
import 'package:vbvs_app/pages/device_bind/componnet/FancyCircleCheckbox.dart';
class LanguageSetting extends StatefulWidget {
const LanguageSetting({super.key});
@override
State<LanguageSetting> createState() => _LanguageSettingState();
}
class _LanguageSettingState extends State<LanguageSetting> {
LanguageController languageController = Get.find();
@override
void initState() {
// languageController.initLanuageList();
super.initState();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, bodySize) => GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/img/bgNoImg.png'), // 本地图片
fit: BoxFit.fill, // 填满整个 Container
),
),
child: Scaffold(
backgroundColor: Colors.transparent, // 加上这一行
appBar: AppBar(
backgroundColor: themeController.currentColor.sc17,
automaticallyImplyLeading: false,
iconTheme: IconThemeData(
color: themeController.currentColor.sc3,
),
titleSpacing: 0,
// leading: returnIconButtom,
title: Container(
width: double.infinity,
height: 180.rpx,
child: Stack(
alignment: Alignment.center,
children: [
/// 居中标题
Text(
'选择语言'.tr,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Readex Pro',
color: themeController.currentColor.sc3,
letterSpacing: 0,
fontSize: 30.rpx,
),
),
Positioned(
left: 0,
child: returnIconButtom,
),
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
30.rpx, 25.rpx, 30.rpx, 0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Color(0xFF242835),
borderRadius: BorderRadius.circular(
AppConstants().normal_container_radius),
),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0, 0.rpx, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 20.rpx, 40.rpx, 20.rpx),
child: Obx(() {
return Column(
children: languageController.languageList
.map<Widget>((language) {
return ClickableContainer(
backgroundColor:
Colors.transparent, // 背景透明
highlightColor: themeController
.currentColor.sc21, // 点击时的背景色
padding: EdgeInsets.symmetric(
vertical: 10.rpx,
horizontal: 16.rpx),
onTap: () async {
// 点击事件逻辑
print('点击了语言项');
for (var lang
in languageController
.languageList) {
lang.selected = (lang ==
language); // 根据选择更新状态
}
languageController
.selectLanguage
.value = language;
await AppLanguage()
.loadLanguage(language
.language_code); // 加载语言
languageController
.updateAll(); // 更新操作
},
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Expanded(
child: Text(
language.language_name ??
'语言名称', // 显示语言名称
style: FlutterFlowTheme
.of(context)
.bodyMedium
.override(
fontFamily: 'Inter',
color:
themeController
.currentColor
.sc3,
fontSize: AppConstants()
.title_text_fontSize,
letterSpacing: 0.0,
),
maxLines: 1,
overflow: TextOverflow
.ellipsis, // 省略号处理
),
),
FancyCircleCheckbox(
borderColor: themeController
.currentColor.sc3,
fillColor: themeController
.currentColor.sc2,
value: language.selected ??
false, // 根据 selected 状态显示选中或未选中
onChanged: (value) async {
// 保持原有的状态更改逻辑
for (var lang
in languageController
.languageList) {
lang.selected = (lang ==
language); // 更新选中状态
}
languageController
.selectLanguage
.value = language;
await AppLanguage()
.loadLanguage(language
.language_code); // 加载语言
languageController
.updateAll(); // 更新操作
},
),
].divide(SizedBox(
width: 28.rpx)), // 为每个项添加间隔
),
);
})
.toList()
.divide(SizedBox(
height: 30.rpx,
)),
);
}),
),
// ClickableContainer(
// backgroundColor: Colors.transparent, // 容器背景色
// highlightColor: themeController
// .currentColor.sc21, // 点击时的背景色
// padding: EdgeInsetsDirectional.fromSTEB(
// 40.rpx, 0.rpx, 40.rpx, 0.rpx),
// onTap: () {},
// child: Container(),
// ),
]
.divide(SizedBox(height: 0.rpx))
.addToStart(SizedBox(height: 30.rpx))
.addToEnd(SizedBox(height: 30.rpx)),
),
),
),
),
],
),
),
),
),
),
),
),
);
}
Widget _buildDeviceCard(BuildContext context,
{required String title, required String imageUrl, required String type}) {
return CustomCard(
borderRadius: 20.rpx, // 圆角大小
onTap: () {
if (type != null) {
if (type == '1') {
Get.toNamed("/blueteethDevice");
}
}
},
colors: [themeController.currentColor.sc17], // 背景色
child: Container(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.135,
constraints: BoxConstraints(
minHeight: 220.rpx,
),
padding: EdgeInsetsDirectional.fromSTEB(77.rpx, 0, 21.rpx, 0),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: 'Inter',
color: const Color(0xFFC2CED7),
fontSize: 30.rpx,
letterSpacing: 0.0,
),
),
ClipRRect(
borderRadius: BorderRadius.circular(8.rpx),
child: Image.asset(
imageUrl,
width: 212.rpx,
height: 168.rpx,
),
),
],
),
),
);
}
}

View File

@@ -5,7 +5,6 @@ import 'package:vbvs_app/common/util/FitTool.dart';
import 'package:vbvs_app/common/util/MyUtils.dart';
import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/component/tool/WebViewWidget.dart';
import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
import 'package:vbvs_app/controller/device/device_type_controller.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
@@ -27,11 +26,21 @@ class _SleepReportPageState extends State<SleepReportPage> {
ThemeController themeController = Get.find();
DeviceTypeController deviceTypeController = Get.find();
// 使用 ValueNotifier 来管理加载状态
ValueNotifier<bool> isPageLoading = ValueNotifier<bool>(true);
@override
void initState() {
super.initState();
}
@override
void dispose() {
// 清理 ValueNotifier
isPageLoading.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
@@ -45,14 +54,12 @@ class _SleepReportPageState extends State<SleepReportPage> {
),
),
child: Scaffold(
backgroundColor: Colors.transparent, // 加上这一行
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,
@@ -78,50 +85,35 @@ class _SleepReportPageState extends State<SleepReportPage> {
],
),
),
actions: [],
centerTitle: false,
),
body: SafeArea(
top: true,
child: InAppWebView(
child: Stack(
children: [
InAppWebView(
key: UniqueKey(),
initialUrlRequest: URLRequest(url: WebUri(widget.sleepUri)),
onLoadStart: (controller, url) {
// 页面开始加载时显示加载指示器
isPageLoading.value = true;
},
onLoadStop: (controller, url) {
// 页面加载完成后隐藏加载指示器
isPageLoading.value = false;
},
),
ValueListenableBuilder<bool>(
valueListenable: isPageLoading,
builder: (context, isLoading, child) {
return isLoading
? Center(
child: CircularProgressIndicator(), // 加载指示器
)
: SizedBox.shrink();
},
),
],
),
// child: WebViewWidget(url: "${widget.sleepUri}"),
// child: Padding(
// padding: EdgeInsetsDirectional.fromSTEB(30.rpx, 0, 30.rpx, 0),
// child: SingleChildScrollView(
// child: Column(
// mainAxisSize: MainAxisSize.max,
// children: [
// // 使用 Obx 来监听 deviceTypeList 的变化
// Obx(() {
// return Column(
// children: [
// SizedBox(height: 26.rpx), // 开始的间隔
// ...deviceTypeController.deviceTypeList.value
// .map((device) {
// return Padding(
// padding: EdgeInsets.only(
// bottom: 26.rpx), // 添加每个设备之间的间隔
// child: _buildDeviceCard(
// context,
// title: device['name'], // 这里假设 device 是一个 Map
// imageUrl: device['image'],
// type: device['type'],
// ),
// );
// }).toList(),
// SizedBox(height: 26.rpx), // 结束的间隔
// ],
// );
// }),
// ],
// ),
// ),
// ),
),
),
),
@@ -170,14 +162,8 @@ class _SleepReportPageState extends State<SleepReportPage> {
),
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

@@ -11,6 +11,7 @@ import 'package:vbvs_app/component/tool/CustomCard.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/device/blueteeth_bind_controller.dart';
import 'package:vbvs_app/controller/main_bottom/global_controller.dart';
import 'package:vbvs_app/controller/setting/language/language_controller.dart';
import 'package:vbvs_app/controller/theme_controller/ThemeController.dart';
import 'package:vbvs_app/controller/user_info_controller.dart';
import 'package:vbvs_app/model/api_response.dart';
@@ -27,9 +28,11 @@ class _SettingPageState extends State<SettingPage> {
UserInfoController userInfoController = Get.find();
BlueteethBindController blueteethBindController = Get.find();
ThemeController themeController = Get.find();
LanguageController languageController = Get.find();
@override
void initState() {
languageController.initLanuageList();
super.initState();
}
@@ -115,12 +118,13 @@ class _SettingPageState extends State<SettingPage> {
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 0.rpx, 40.rpx, 0.rpx),
onTap: () {
print('点击了容器');
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -183,12 +187,14 @@ class _SettingPageState extends State<SettingPage> {
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 0.rpx, 40.rpx, 0.rpx),
onTap: () {
print('点击了容器');
// TopSlideNotification.show(context,
// text: "待开发功能".tr);
Get.toNamed("/languageSetting");
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -216,10 +222,14 @@ class _SettingPageState extends State<SettingPage> {
Row(
mainAxisSize: MainAxisSize.max,
children: [
Text(
'中文',
style:
FlutterFlowTheme.of(context)
Obx(() {
return Text(
languageController
.selectLanguage
.value!
.language_name!,
style: FlutterFlowTheme.of(
context)
.bodyMedium
.override(
fontFamily: 'Inter',
@@ -228,7 +238,8 @@ class _SettingPageState extends State<SettingPage> {
fontSize: 26.rpx,
letterSpacing: 0.0,
),
),
);
}),
SvgPicture.asset(
'assets/img/icon/arrow_right.svg',
width: 8.rpx,
@@ -257,7 +268,7 @@ class _SettingPageState extends State<SettingPage> {
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 20.rpx, 40.rpx, 20.rpx),
40.rpx, 30.rpx, 40.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -302,12 +313,13 @@ class _SettingPageState extends State<SettingPage> {
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 0.rpx, 40.rpx, 0.rpx),
onTap: () {
print('点击了容器');
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 20.rpx, 0.rpx, 20.rpx),
0.rpx, 30.rpx, 0.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:
@@ -352,12 +364,13 @@ class _SettingPageState extends State<SettingPage> {
padding: EdgeInsetsDirectional.fromSTEB(
0.rpx, 0.rpx, 0.rpx, 0.rpx),
onTap: () {
print('点击了容器');
TopSlideNotification.show(context,
text: "待开发功能".tr);
},
child: Container(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
40.rpx, 20.rpx, 40.rpx, 20.rpx),
40.rpx, 30.rpx, 40.rpx, 30.rpx),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment:

View File

@@ -102,6 +102,7 @@ class _UpdateUserPageState extends State<UpdateUserPage> {
userInfoController.model.user!.tmpHead = null;
userInfoController.model.user!.tmpNickName = null;
await userInfoController.getUserInfo();
userInfoController.updateAll();
Get.back();
}
},

View File

@@ -1,10 +1,17 @@
import 'package:ef/ef.dart';
import 'package:flutter/cupertino.dart';
import 'package:vbvs_app/component/tool/TopSlideNotification.dart';
import 'package:vbvs_app/controller/login/login_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/device/BodyDeviceWidget.dart';
import 'package:vbvs_app/pages/device/device_detail.dart';
import 'package:vbvs_app/pages/device/instant_body_page.dart';
import 'package:vbvs_app/pages/device_bind/MobileScannerTestPage.dart';
import 'package:vbvs_app/pages/device_bind/bind_device_success.dart';
import 'package:vbvs_app/pages/device_bind/blueteeth_device_page.dart';
import 'package:vbvs_app/pages/device_bind/device_calibration.dart';
import 'package:vbvs_app/pages/device_bind/device_share_list_page.dart';
import 'package:vbvs_app/pages/device_bind/device_share_page.dart';
import 'package:vbvs_app/pages/device_bind/device_type.dart';
import 'package:vbvs_app/pages/device_bind/device_type_list.dart';
@@ -12,23 +19,28 @@ import 'package:vbvs_app/pages/device_bind/wifi_page.dart';
import 'package:vbvs_app/pages/login/login.dart';
import 'package:vbvs_app/pages/login/other_login.dart';
import 'package:vbvs_app/pages/main_bottom/e_page.dart';
import 'package:vbvs_app/pages/main_bottom/follow_page.dart';
import 'package:vbvs_app/pages/main_bottom/help_page.dart';
import 'package:vbvs_app/pages/main_bottom/home_page.dart';
import 'package:vbvs_app/pages/main_bottom/main_page_bottom_change.dart';
import 'package:vbvs_app/pages/main_bottom/message_page.dart';
import 'package:vbvs_app/pages/main_bottom/mine_page.dart';
import 'package:vbvs_app/pages/person/person_page.dart';
import 'package:vbvs_app/pages/person/update_person_page.dart';
import 'package:vbvs_app/pages/repair/apply_repair_page.dart';
import 'package:vbvs_app/pages/repair/repair_list_page.dart';
import 'package:vbvs_app/pages/setting/language_setting.dart';
import 'package:vbvs_app/pages/sleep_report/sleep_report_page.dart';
import 'package:vbvs_app/pages/user/about_us_page.dart';
import 'package:vbvs_app/pages/user/setting_page.dart';
import 'package:vbvs_app/pages/user/update_user_page.dart';
ThemeController themeController = Get.find();
var routes = {
"/homePage": (contxt) => HomePage(),
"/sleepReportPage": (contxt,{arguments}) => SleepReportPage(sleepUri:arguments),
"/sleepReportPage": (contxt, {arguments}) =>
SleepReportPage(sleepUri: arguments),
"/ePage": (contxt) => EPage(),
"/messagePage": (contxt) => MessagePage(),
"/minePage": (contxt) => MinePage(),
@@ -46,32 +58,72 @@ var routes = {
"/bodyDevice": (contxt) => BodyDeviceWidget(),
"/deviceTypeList": (contxt) => DeviceTypeListPage(),
"/deviceDetail": (contxt, {arguments}) => DeviceDetailPage(device: arguments),
"/instantBodyPage": (contxt,{arguments}) => InstantBodyPage(personInfo:arguments),
"/instantBodyPage": (contxt, {arguments}) =>
InstantBodyPage(personInfo: arguments),
"/qrView": (contxt) => MobileScannerTestPage(),
"/updatePersonPage": (contxt) => UpdatePersonPage(),
"/deviceSharePage": (contxt,{arguments}) => DeviceSharePage(device:arguments),
"/updatePersonPage": (contxt, {arguments}) =>
UpdatePersonPage(status: arguments),
"/deviceSharePage": (contxt, {arguments}) =>
DeviceSharePage(device: arguments),
"/deviceShareListPage": (contxt, {arguments}) =>
DeviceShareListPage(device: arguments),
"/calibrationPage": (contxt, {arguments}) => CalibrationPage(type: arguments),
"/applyRepairPage": (contxt) => ApplyRepairPage(),
"/languageSetting": (contxt) => LanguageSetting(),
"/helpPage": (contxt) => HelpPage(),
"/followPage": (contxt) => FollowPage(),
"/repairListPage": (contxt) => RepairListPage(),
};
//2、配置onGenerateRoute 固定写法 这个方法也相当于一个中间件,这里可以做权限判断
var onGenerateRoute = (RouteSettings settings) {
final String? name = settings.name; // /news 或 /search
final Function? pageContentBuilder =
routes[name]; // Function = (contxt) { return const NewsPage()}
final String? name = settings.name; // 获取路由名称,如 /news 或 /search
final Function? pageContentBuilder = routes[name]; // 根据路由名称获取对应的页面构建器
if (pageContentBuilder != null) {
UserInfoController userInfoController = Get.find();
int loginStatus = userInfoController.model.login!;
// 检查用户是否已登录,如果未登录且路由不是登录相关页面,跳转到登录页面
if (loginStatus != 1) {
// 如果用户未登录且访问的不是登录页面等,展示提示并重定向
if (settings.name != "/loginPage" &&
settings.name != "/otherLoginPage" &&
settings.name != "/mianPageBottomChange") {
TopSlideNotification.show(
Get.context!,
text: "必须登录提示".tr,
textColor: themeController.currentColor.sc9,
);
// 创建新的 RouteSettings并修改 name 为 /loginPage
final updatedSettings = RouteSettings(
name: "/loginPage",
arguments: settings.arguments,
);
// 创建一个新的路由,跳转到登录页面
return CupertinoPageRoute(
settings: updatedSettings,
builder: (context) => pageContentBuilder!(context),
);
}
}
// 如果有路由参数,传递参数
if (settings.arguments != null) {
final Route route = CupertinoPageRoute(
return CupertinoPageRoute(
settings: settings,
builder: (context) =>
pageContentBuilder(context, arguments: settings.arguments));
return route;
pageContentBuilder!(context, arguments: settings.arguments),
);
} else {
final Route route = CupertinoPageRoute(
// 没有路由参数时,直接构建页面
return CupertinoPageRoute(
settings: settings,
builder: (context) => pageContentBuilder(context));
builder: (context) => pageContentBuilder!(context),
);
}
}
return route;
}
}
return null;
return null; // 如果路由名称没有匹配的页面,返回 null
};

View File

@@ -6,12 +6,14 @@ import FlutterMacOS
import Foundation
import app_links
import connectivity_plus
import device_info_plus
import file_picker
import flutter_blue_plus
import flutter_inappwebview_macos
import flutter_localization
import flutter_web_auth_2
import google_sign_in_ios
import mobile_scanner
import package_info_plus
import path_provider_foundation
@@ -24,12 +26,14 @@ import window_to_front
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalizationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalizationPlugin"))
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))

View File

@@ -335,6 +335,22 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
@@ -407,6 +423,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.16"
dbus:
dependency: transitive
description:
name: dbus
sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.11"
decimal:
dependency: transitive
description:
@@ -596,14 +620,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.5.2"
flutter_barcode_scanner:
dependency: "direct main"
description:
name: flutter_barcode_scanner
sha256: a4ba37daf9933f451a5e812c753ddd045d6354e4a3280342d895b07fecaab3fa
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
flutter_blue_plus:
dependency: "direct main"
description:
@@ -721,6 +737,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_pdfview:
dependency: "direct main"
description:
name: flutter_pdfview
sha256: "51413e36ab3f1a2fe0edf97ebfa770e20182ea4a066bc9f292920330d9245c9d"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.4.0+1"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -788,6 +812,14 @@ packages:
url: "http://admin@git.real.he-info.cn:8080/r/~lu/flutterflow-ui.git"
source: git
version: "0.3.1"
fluwx:
dependency: "direct main"
description:
name: fluwx
sha256: "8dbbe3f2dd93cd5f689de7b1342b86a47db3f03005e2020904fc45a204d0df4f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.5.2"
font_awesome_flutter:
dependency: transitive
description:
@@ -860,6 +892,46 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.3+1"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
sha256: fad6ddc80c427b0bba705f2116204ce1173e09cf299f85e053d57a55e5b2dd56
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.2.2"
google_sign_in_android:
dependency: transitive
description:
name: google_sign_in_android
sha256: "7af72e5502c313865c729223b60e8ae7bce0a1011b250c24edcf30d3d7032748"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.35"
google_sign_in_ios:
dependency: transitive
description:
name: google_sign_in_ios
sha256: "102005f498ce18442e7158f6791033bbc15ad2dcc0afa4cf4752e2722a516c96"
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.9.0"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
sha256: "5f6f79cf139c197261adb6ac024577518ae48fdff8e53205c5373b5f6430a8aa"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.0"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
sha256: "460547beb4962b7623ac0fb8122d6b8268c951cf0b646dd150d60498430e4ded"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.4+4"
googleapis_auth:
dependency: transitive
description:
@@ -1237,6 +1309,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.5.0"
octo_image:
dependency: transitive
description:
@@ -1286,7 +1366,7 @@ packages:
source: hosted
version: "2.2.1"
path:
dependency: transitive
dependency: "direct main"
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"

View File

@@ -42,8 +42,13 @@ dependencies:
img_picker: ^1.0.2
uuid: ^4.5.1
mobile_scanner: ^6.0.10
flutter_barcode_scanner: ^2.0.0
# flutter_barcode_scanner: ^2.0.0
flutter_inappwebview: ^6.1.5
fluwx: ^5.5.2
connectivity_plus: ^6.1.4
google_sign_in: ^6.2.1
path: ^1.8.0
flutter_pdfview: ^1.4.0+1
dev_dependencies:

View File

@@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <app_links/app_links_plugin_c_api.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <flutter_localization/flutter_localization_plugin_c_api.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
@@ -17,6 +18,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
AppLinksPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("AppLinksPluginCApi"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
FlutterLocalizationPluginCApiRegisterWithRegistrar(

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