feat: 完善设备管理页面、添加设备、删除设备

This commit is contained in:
liyi 2025-09-15 16:00:10 +08:00
parent 496ac22528
commit 80d8a7b104
26 changed files with 865 additions and 306 deletions

View File

@ -1,13 +1,15 @@
class BindTeamStarCloudAccountRequest {
String teamId;
int teamId;
String teamNo;
String username;
int cloudUid;
String password;
BindTeamStarCloudAccountRequest({
required this.teamId,
required this.teamNo,
required this.username,
required this.cloudUid,
required this.password,
});
@ -16,21 +18,25 @@ class BindTeamStarCloudAccountRequest {
data['teamId'] = teamId;
data['teamNo'] = teamNo;
data['username'] = username;
data['cloudUid'] = cloudUid;
data['password'] = password;
return data;
}
factory BindTeamStarCloudAccountRequest.fromJson(Map<String, dynamic> json) {
return BindTeamStarCloudAccountRequest(
teamId: json['teamId'] as String,
teamId: json['teamId'] as int,
teamNo: json['teamNo'] as String,
username: json['username'] as String,
cloudUid: json['cloudUid'] as int,
password: json['password'] as String,
);
}
@override
String toString() {
return 'BindTeamStarCloudAccountRequest{teamId: $teamId, teamNo: $teamNo, username: $username, password: $password}';
return 'BindTeamStarCloudAccountRequest{teamId: $teamId,cloudUid:$cloudUid, teamNo: $teamNo, username: $username,'
' password:'
' $password}';
}
}

View File

@ -1,19 +1,23 @@
class CreateTeamResponse {
final String? teamNo;
final int? teamId;
CreateTeamResponse({
this.teamNo,
this.teamId,
});
factory CreateTeamResponse.fromJson(Map<String, dynamic> json) {
return CreateTeamResponse(
teamNo: json['teamNo'] as String?,
teamId: json['teamId'] as int?,
);
}
Map<String, dynamic> toJson() {
return {
'teamNo': teamNo,
'teamId': teamId,
};
}
}

View File

@ -8,7 +8,7 @@ class TeamInfoResponse {
String? sceneCustomName; //
String? personNo; //
bool? isOwner; //
TeamCloudInfo? cloudInfo;
TeamCloudInfo? teamCloudInfo;
TeamInfoResponse({
this.id,
@ -20,7 +20,7 @@ class TeamInfoResponse {
this.sceneCustomName,
this.personNo,
this.isOwner,
this.cloudInfo,
this.teamCloudInfo,
});
// JSON构造函数
@ -35,7 +35,7 @@ class TeamInfoResponse {
sceneCustomName: json['sceneCustomName'] as String?,
personNo: json['personNo'] as String?,
isOwner: json['isOwner'] as bool?,
cloudInfo: json['cloudInfo'] != null ? TeamCloudInfo.fromJson(json['cloudInfo']) : null,
teamCloudInfo: json['teamCloudInfo'] != null ? TeamCloudInfo.fromJson(json['teamCloudInfo']) : null,
);
}
@ -51,7 +51,7 @@ class TeamInfoResponse {
'sceneCustomName': sceneCustomName,
'personNo': personNo,
'isOwner': isOwner,
'cloudInfo': cloudInfo?.toJson(),
'teamCloudInfo': teamCloudInfo?.toJson(),
};
}
@ -62,7 +62,7 @@ class TeamInfoResponse {
'scene: $scene, sceneCustomName: $sceneCustomName,'
' sceneName: $sceneName, '
' personNo: $personNo, '
' cloudInfo: $cloudInfo,'
' teamCloudInfo: $teamCloudInfo,'
' isOwner: $isOwner}';
}
}

View File

@ -59,21 +59,21 @@ class TeamApiService {
//
path: ApiPath.changeTeam,
method: HttpConstant.post,
data: request,
data: request.toJson(),
fromJson: (data) {},
);
}
//
Future<ApiResponse<SceneInfoResponseList>> requestBindTeamStarCloudAccount({
Future<ApiResponse<void>> requestBindTeamStarCloudAccount({
required BindTeamStarCloudAccountRequest request,
}) {
return _api.makeRequest(
//
path: ApiPath.bindTeamStarCloudAccount,
method: HttpConstant.post,
data: request,
fromJson: (data) => SceneInfoResponseList.fromJson(data),
data: request.toJson(),
fromJson: (data){},
);
}
@ -85,7 +85,7 @@ class TeamApiService {
//
path: ApiPath.updateTeamInfo,
method: HttpConstant.post,
data: request,
data: request.toJson(),
fromJson: (data) {},
);
}

View File

@ -29,21 +29,27 @@ class _AppState extends State<App> {
builder: (_, child) {
return GetMaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
brightness: Brightness.light,
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.blue.shade200,
selectionColor: Colors.blue.shade100,
selectionHandleColor: Colors.blue.shade300,
scaffoldBackgroundColor: Colors.white,
brightness: Brightness.light,
textSelectionTheme: TextSelectionThemeData(
cursorColor: Colors.blue.shade200,
selectionColor: Colors.blue.shade100,
selectionHandleColor: Colors.blue.shade300,
),
dialogBackgroundColor: Colors.white,
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
elevation: 0,
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
scrolledUnderElevation: 0,
titleTextStyle: TextStyle(
color: Colors.black87,
fontSize: 18.sp,
fontWeight: FontWeight.w500,
),
dialogBackgroundColor: Colors.white,
appBarTheme: AppBarTheme(
backgroundColor: Colors.white,
elevation: 0,
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
scrolledUnderElevation: 0,
)),
),
),
//
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate, // Material组件本地化字符串

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:starwork_flutter/common/constant/app_toast_messages.dart';
import 'package:starwork_flutter/common/widgets/custom_dialog_widget.dart';
class BaseController extends GetxController {
@ -14,7 +15,7 @@ class BaseController extends GetxController {
}
void showLoading() {
EasyLoading.show(status: 'loading...');
EasyLoading.show(status: AppToastMessages.loading);
}
void hideLoading() {
@ -33,12 +34,8 @@ class BaseController extends GetxController {
EasyLoading.showError(message.tr);
}
void showCustomDialog({
required String title,
required Widget content,
required VoidCallback onConfirm,
String? confirmText
}) {
void showCustomDialog(
{required String title, required Widget content, required VoidCallback onConfirm, String? confirmText}) {
Get.dialog(
CustomDialogWidget(
title: title,

View File

@ -8,10 +8,7 @@ class AppSupportDeviceType {
//
static List<AppSupportDeviceType> get allTypes => [
all,
lock,
gateway,
attendanceMachine,
];
final String value;

View File

@ -0,0 +1,4 @@
class AppViewParameterKeys {
static const String deviceList = "deviceList";
}

View File

@ -0,0 +1,3 @@
class RefreshDeviceListEvent {
RefreshDeviceListEvent();
}

View File

@ -0,0 +1,49 @@
import 'package:event_bus/event_bus.dart';
// EventBus
EventBus _eventBus = EventBus();
/// 线
class EventBusUtil {
//
factory EventBusUtil() => _instance;
//
EventBusUtil._internal();
//
static final EventBusUtil _instance = EventBusUtil._internal();
/// EventBus
EventBus get instance => _eventBus;
///
///
///
/// ```dart
/// EventBusUtil().on<UserLoginEvent>((event) {
/// print('用户登录:${event.userId}');
/// });
/// ```
Stream<T> on<T>() {
return _eventBus.on<T>();
}
///
///
///
/// ```dart
/// EventBusUtil().fire(UserLoginEvent('123'));
/// ```
void fire<T>(T event) {
_eventBus.fire(event);
}
/// EventBus退
void destroy() {
_eventBus.destroy();
}
}
/// 访
EventBusUtil $eventBus = EventBusUtil();

View File

@ -4,6 +4,8 @@ import 'package:starwork_flutter/views/device/confirmPairDevice/confirm_pair_dev
import 'package:starwork_flutter/views/device/confirmPairDevice/confirm_pair_device_view.dart';
import 'package:starwork_flutter/views/device/deviceManage/device_manage_binding.dart';
import 'package:starwork_flutter/views/device/deviceManage/device_manage_view.dart';
import 'package:starwork_flutter/views/device/removeDevice/remove_device_binding.dart';
import 'package:starwork_flutter/views/device/removeDevice/remove_device_view.dart';
import 'package:starwork_flutter/views/device/searchDevice/search_device_binding.dart';
import 'package:starwork_flutter/views/device/searchDevice/search_device_view.dart';
import 'package:starwork_flutter/views/home/home_binding.dart';
@ -130,5 +132,10 @@ class AppPages {
page: () => DeviceManageView(),
binding: DeviceManageBinding(),
),
GetPage(
name: AppRoutes.deviceRemoveDevice,
page: () => RemoveDeviceView(),
binding: RemoveDeviceBinding(),
),
];
}

View File

@ -18,4 +18,5 @@ class AppRoutes{
static const String deviceManage = '/device/deviceManage';
static const String searchDevice = '/device/searchDevice';
static const String confirmPairDevice = '/device/confirmPairDevice';
static const String deviceRemoveDevice = '/device/removeDevice';
}

View File

@ -1,12 +1,116 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starcloud/sdk/sdk_device_operate_extension.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/base/base_controller.dart';
import 'package:starwork_flutter/common/constant/app_support_device_type.dart';
import 'package:starwork_flutter/common/events/refresh_device_list_event.dart';
import 'package:starwork_flutter/common/utils/event_bus_util.dart';
import 'package:starwork_flutter/views/main/main_controller.dart';
class DeviceManageController extends BaseController with GetSingleTickerProviderStateMixin {
final mainController = Get.find<MainController>();
class DeviceManageController extends GetxController with GetSingleTickerProviderStateMixin {
// 0: 1线 2线
final RxInt selectedStatusIndex = 0.obs;
//
final selectedDeviceType = AppSupportDeviceType.all.obs;
final selectedDeviceType = AppSupportDeviceType.lock.obs;
//
final deviceList = <StarCloudLock>[].obs;
// UI
final filteredDeviceList = <StarCloudLock>[].obs;
// 线
final onlineDeviceCount = 0.obs;
// 线
final offlineDeviceCount = 0.obs;
//
late StreamSubscription _refreshDeviceListSubscription;
@override
void onInit() {
super.onInit();
initData();
//
_refreshDeviceListSubscription = EventBusUtil().instance.on<RefreshDeviceListEvent>().listen((event) async {
_requestTeamDeviceList();
});
}
@override
void onClose() {
super.onClose();
_refreshDeviceListSubscription.cancel();
}
void initData() async {
_requestTeamDeviceList();
}
///
void changeStatusFilter(int index) {
selectedStatusIndex.value = index;
_updateFilteredList();
}
/// selectedStatusIndex
void _updateFilteredList() {
List<StarCloudLock> filtered = [];
switch (selectedStatusIndex.value) {
case 0: //
filtered = deviceList;
break;
case 1: // 线
filtered = deviceList.where((device) => device.isOnline == true).toList();
break;
case 2: // 线
filtered = deviceList.where((device) => device.isOnline == false).toList();
break;
default:
filtered = deviceList;
}
filteredDeviceList.assignAll(filtered); //
}
void _requestTeamDeviceList() async {
showLoading();
await StarCloudSDK.instance.getDeviceList(
pageNo: 1,
pageSize: 999,
cloudUid: mainController.selectedTeam.value.teamCloudInfo!.uid!,
onSuccess: (StarCloudLockList list) {
filteredDeviceList.clear();
deviceList.clear();
for (var groupItem in list.groupList) {
for (var lock in groupItem.lockList) {
deviceList.add(lock);
if (lock.isOnline ?? false) {
onlineDeviceCount.value++;
} else {
offlineDeviceCount.value++;
}
}
}
//
_updateFilteredList();
hideLoading();
},
onError: (err) {
AppLogger.error('获取设备列表失败:${err}');
hideLoading();
},
);
}
}

View File

@ -5,8 +5,10 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:starwork_flutter/common/constant/app_colors.dart';
import 'package:starwork_flutter/common/constant/app_support_device_type.dart';
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart';
import 'package:starwork_flutter/extension/function_extension.dart';
import 'package:starwork_flutter/routes/app_routes.dart';
import 'package:starwork_flutter/views/device/deviceManage/device_manage_controller.dart';
class DeviceManageView extends GetView<DeviceManageController> {
@ -35,7 +37,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
horizontal: 10.w,
vertical: 10.h,
),
decoration: BoxDecoration(
decoration: const BoxDecoration(
color: Colors.white,
),
child: Column(
@ -57,7 +59,9 @@ class DeviceManageView extends GetView<DeviceManageController> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {},
onPressed: () {
Get.toNamed(AppRoutes.searchDevice);
},
child: Text(
'添加设备',
style: TextStyle(
@ -68,7 +72,14 @@ class DeviceManageView extends GetView<DeviceManageController> {
),
),
TextButton(
onPressed: () {},
onPressed: () {
Get.toNamed(
AppRoutes.deviceRemoveDevice,
arguments: {
AppViewParameterKeys.deviceList: controller.deviceList,
},
);
},
child: Text(
'删除设备',
style: TextStyle(
@ -79,7 +90,9 @@ class DeviceManageView extends GetView<DeviceManageController> {
),
),
TextButton(
onPressed: () {},
onPressed: () {
controller.showFunctionNotOpen();
},
child: Text(
'更多管理',
style: TextStyle(
@ -139,7 +152,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
children: [
GestureDetector(
onTap: () {
controller.selectedStatusIndex.value = 0;
controller.changeStatusFilter(0);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
@ -162,7 +175,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
),
GestureDetector(
onTap: () {
controller.selectedStatusIndex.value = 1;
controller.changeStatusFilter(1);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
@ -171,7 +184,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
'在线(${1})'.tr,
'在线(${controller.onlineDeviceCount.value})'.tr,
style: TextStyle(
fontSize: 12.sp,
color: controller.selectedStatusIndex == 1 ? Colors.white : Colors.grey[500],
@ -185,7 +198,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
),
GestureDetector(
onTap: () {
controller.selectedStatusIndex.value = 2;
controller.changeStatusFilter(2);
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 4.h),
@ -194,7 +207,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
'离线(${1})'.tr,
'离线(${controller.offlineDeviceCount.value})'.tr,
style: TextStyle(
fontSize: 12.sp,
color: controller.selectedStatusIndex == 2 ? Colors.white : Colors.grey[500],
@ -246,7 +259,7 @@ class DeviceManageView extends GetView<DeviceManageController> {
),
alignment: Alignment.center,
child: Text(
'${deviceType.label}(${0})',
'${deviceType.label}(${controller.deviceList.length})',
style: TextStyle(
fontSize: 10.sp,
color: Colors.black,
@ -262,78 +275,81 @@ class DeviceManageView extends GetView<DeviceManageController> {
}
_buildRightDeviceList() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //
crossAxisSpacing: 6.w, //
mainAxisSpacing: 6.h, //
childAspectRatio: 1.6, //
),
itemCount: 100,
itemBuilder: (context, index) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8.r)),
),
padding: EdgeInsets.symmetric(
horizontal: 10.w,
),
alignment: Alignment.centerLeft,
child: Obx(
() => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (controller.selectedDeviceType.value.iconImagePath != null)
Image(
image: AssetImage(controller.selectedDeviceType.value.iconImagePath!),
width: 26.w,
height: 26.w,
fit: BoxFit.contain,
gaplessPlayback: true,
//
filterQuality: FilterQuality.medium,
//
errorBuilder: (context, error, stackTrace) {
return Icon(
Icons.image_not_supported,
size: 26.sp,
color: Colors.grey,
);
},
),
return Obx(
() => Container(
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, //
crossAxisSpacing: 6.w, //
mainAxisSpacing: 6.h, //
childAspectRatio: 1.6, //
),
itemCount: controller.filteredDeviceList.length,
itemBuilder: (context, index) {
final lock = controller.filteredDeviceList[index];
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(8.r)),
),
padding: EdgeInsets.symmetric(
horizontal: 10.w,
),
alignment: Alignment.centerLeft,
child: Obx(
() => Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (controller.selectedDeviceType.value.iconImagePath != null)
Image(
image: AssetImage(controller.selectedDeviceType.value.iconImagePath!),
width: 26.w,
height: 26.w,
fit: BoxFit.contain,
gaplessPlayback: true,
//
filterQuality: FilterQuality.medium,
//
errorBuilder: (context, error, stackTrace) {
return Icon(
Icons.image_not_supported,
size: 26.sp,
color: Colors.grey,
);
},
),
SizedBox(
height: 6.h,
),
Text(
'TMH_5sdds5465a4sd5665$index',
style: TextStyle(
fontSize: 10.sp,
fontWeight: FontWeight.w400,
Text(
lock.lockName,
style: TextStyle(
fontSize: 10.sp,
fontWeight: FontWeight.w400,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
SizedBox(
height: 4.h,
),
Text(
'在线',
style: TextStyle(
color: Colors.green,
fontSize: 10.sp,
fontWeight: FontWeight.w500,
SizedBox(
height: 4.h,
),
)
],
Text(
lock.isOnline ?? false ? '在线' : '离线',
style: TextStyle(
color: lock.isOnline ?? false ? Colors.green : Colors.grey,
fontSize: 10.sp,
fontWeight: FontWeight.w500,
),
)
],
),
),
),
);
},
);
},
),
),
);
}

View File

@ -0,0 +1,10 @@
import 'package:get/get.dart';
import 'package:starwork_flutter/views/device/deviceManage/device_manage_controller.dart';
import 'package:starwork_flutter/views/device/removeDevice/remove_device_controller.dart';
class RemoveDeviceBinding implements Bindings {
@override
void dependencies() {
Get.lazyPut(() => RemoveDeviceController());
}
}

View File

@ -0,0 +1,104 @@
import 'package:get/get.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starcloud/sdk/sdk_lock_operate_extension.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/base/base_controller.dart';
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
import 'package:starwork_flutter/common/events/refresh_device_list_event.dart';
import 'package:starwork_flutter/common/utils/event_bus_util.dart';
import 'package:starwork_flutter/views/main/main_controller.dart';
class RemoveDeviceController extends BaseController {
//
RxList<StarCloudLock> selectedDevices = <StarCloudLock>[].obs;
RxList<StarCloudLock> deviceList = <StarCloudLock>[].obs;
final mainController = Get.find<MainController>();
final currentProgress = ''.obs; // "正在重置设备 1/3"
@override
void onInit() {
deviceList.value = (Get.arguments?[AppViewParameterKeys.deviceList] as List<StarCloudLock>?) ?? [];
super.onInit();
}
//
bool isSelected(StarCloudLock device) {
AppLogger.highlight('${selectedDevices.contains(device)}');
return selectedDevices.contains(device);
}
//
void toggleDevice(StarCloudLock device) {
if (isSelected(device)) {
selectedDevices.remove(device);
} else {
selectedDevices.add(device);
}
}
//
void selectAll(List<StarCloudLock> allDevices) {
selectedDevices.assignAll(allDevices);
}
//
void clearSelection() {
selectedDevices.clear();
}
//
bool isAllSelected(List<StarCloudLock> allDevices) {
return allDevices.isNotEmpty && selectedDevices.length == allDevices.length;
}
//
void removeDevice(RxList<StarCloudLock> selectedDevices) async {
showLoading();
var teamInfo = mainController.selectedTeam.value;
var teamCloudInfo = teamInfo.teamCloudInfo;
if (teamCloudInfo == null) {
showToast('没找到对应的星云账户信息');
hideLoading();
return;
}
if (selectedDevices.isEmpty) {
showToast('没有选择要重置的设备');
hideLoading();
return;
}
bool allSuccess = true; //
for (int i = 0; i < selectedDevices.length; i++) {
final device = selectedDevices[i];
try {
await StarCloudSDK.instance.resetLock(
lockId: device.lockId,
cloudUid: teamCloudInfo.uid,
onSuccess: () {
//
},
onError: (err) {
allSuccess = false; //
},
);
} catch (e) {
allSuccess = false;
}
}
// === ===
if (allSuccess) {
Future.delayed(1.seconds, () {
EventBusUtil().instance.fire(RefreshDeviceListEvent());
Get.back();
});
} else {
Future.delayed(2.seconds, () {});
}
hideLoading();
}
}

View File

@ -0,0 +1,159 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/common/constant/app_colors.dart';
import 'package:starwork_flutter/common/constant/app_images.dart';
import 'package:starwork_flutter/common/constant/app_view_parameter_keys.dart';
import 'package:starwork_flutter/common/widgets/custome_app_bar_wdiget.dart';
import 'package:starwork_flutter/extension/function_extension.dart';
import 'package:starwork_flutter/views/device/removeDevice/remove_device_controller.dart';
class RemoveDeviceView extends GetView<RemoveDeviceController> {
const RemoveDeviceView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.scaffoldBackgroundColor,
appBar: CustomAppBarWidget(
title: '选择设备'.tr,
actions: [
TextButton(
onPressed: () {
var allSelected = controller.isAllSelected(controller.deviceList);
if (allSelected) {
controller.clearSelection();
} else {
controller.selectAll(controller.deviceList);
}
},
child: Text(
'全选'.tr,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
),
)
],
),
body: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 10.h,
),
child: Column(
children: [
_buildDeviceList(),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
if (controller.selectedDevices.isEmpty) {
controller.showToast('请至少选中一个设备');
}
controller.removeDevice(controller.selectedDevices);
}.debounce(),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
padding: EdgeInsets.symmetric(vertical: 12.h),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.r)),
),
child: Text(
'删除'.tr,
style: TextStyle(
fontSize: 16.sp,
color: Colors.white,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
),
);
}
_buildDeviceList() {
return Expanded(
child: ListView.builder(
itemCount: controller.deviceList.length,
itemBuilder: (context, index) {
final device = controller.deviceList[index];
return GestureDetector(
onTap: () {
controller.toggleDevice(device);
},
child: Container(
margin: EdgeInsets.only(bottom: 10.h),
padding: EdgeInsets.symmetric(
horizontal: 10.w,
vertical: 4.h,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8.r),
),
child: Row(
children: [
Obx(
() => Checkbox(
value: controller.isSelected(device), //
shape: CircleBorder(
side: BorderSide(width: 0.4.w),
),
activeColor: Colors.blue,
onChanged: (value) {
controller.toggleDevice(device); //
},
),
),
Image(
image: const AssetImage(AppImages.iconLockTypeDoorLock),
width: 26.w,
height: 26.w,
fit: BoxFit.contain,
gaplessPlayback: true,
//
filterQuality: FilterQuality.medium,
//
errorBuilder: (context, error, stackTrace) {
return Icon(
Icons.image_not_supported,
size: 26.sp,
color: Colors.grey,
);
},
),
SizedBox(
width: 8.w,
),
Expanded(
child: Text(
device.lockName,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: Colors.black87,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
);
},
),
);
}
}

View File

@ -11,6 +11,8 @@ import 'package:starwork_flutter/base/app_permission.dart';
import 'package:starwork_flutter/base/base_controller.dart';
import 'package:starwork_flutter/common/constant/app_toast_messages.dart';
import 'package:starwork_flutter/common/constant/cache_keys.dart';
import 'package:starwork_flutter/common/events/refresh_device_list_event.dart';
import 'package:starwork_flutter/common/utils/event_bus_util.dart';
import 'package:starwork_flutter/common/utils/shared_preferences_utils.dart';
class SearchDeviceController extends BaseController {
@ -35,6 +37,7 @@ class SearchDeviceController extends BaseController {
void onClose() {
StarCloudSDK.instance.stopScan(onError: (err) {});
isSearching.value = false;
deviceList.clear();
super.onClose();
}
@ -121,40 +124,17 @@ class SearchDeviceController extends BaseController {
void connectingDevices(StarCloudScanResult device) async {
showLoading();
try {
var cacheStarCloudUserName = await SharedPreferencesUtils.getString(CacheKeys.starCloudUserName);
var cacheStarCloudPassword = await SharedPreferencesUtils.getString(CacheKeys.starCloudPassword);
var cacheStarCloudUid = await SharedPreferencesUtils.getString(CacheKeys.starCloudUid);
if (cacheStarCloudUserName == null || cacheStarCloudPassword == null || cacheStarCloudUid == null) {
await StarCloudSDK.instance.createCloudUser(
onError: (err) {
AppLogger.error('err:${err}');
hideLoading();
},
onSuccess: (userInfo) {
SharedPreferencesUtils.setString(CacheKeys.starCloudUserName, userInfo.username);
SharedPreferencesUtils.setString(CacheKeys.starCloudPassword, userInfo.password);
SharedPreferencesUtils.setString(CacheKeys.starCloudUid, userInfo.uid.toString());
},
);
}
StarCloudSDK.instance.setCloudAccounts(
[
CloudUserInfo(
username: cacheStarCloudUserName!,
password: cacheStarCloudPassword!,
uid: int.parse(cacheStarCloudUid!),
)
],
);
await StarCloudSDK.instance.pairDevice(
onError: (err) {
AppLogger.error('err:${err}');
AppLogger.error('连接设备时出现错误:${err}');
hideLoading();
},
onSuccess: (StarCloudLock lockInfo) {
AppLogger.highlight('lockInfo:${lockInfo.toString()}');
//
showSuccess(message: '设备添加成功'.tr);
hideLoading();
//
EventBusUtil().instance.fire(RefreshDeviceListEvent());
Get.back();
},
scanResult: device,

View File

@ -2,11 +2,16 @@ import 'package:carousel_slider/carousel_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starcloud/sdk/sdk_device_operate_extension.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/api/model/team/response/team_info_response.dart';
import 'package:starwork_flutter/api/service/team_api_service.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/base/app_permission.dart';
import 'package:starwork_flutter/base/base_controller.dart';
import 'package:starwork_flutter/common/events/refresh_device_list_event.dart';
import 'package:starwork_flutter/common/utils/event_bus_util.dart';
import 'package:starwork_flutter/routes/app_routes.dart';
import 'package:starwork_flutter/views/main/main_controller.dart';
@ -15,7 +20,6 @@ class HomeController extends BaseController {
final isOpenNotificationPermission = false.obs;
var carouselCurrentIndex = 0.obs;
//
@ -42,18 +46,14 @@ class HomeController extends BaseController {
//
void _initializeData() async {
showLoading();
//
isOpenNotificationPermission.value = await AppPermission.checkPermission(
permission: Permission.notification,
);
hideLoading();
//
EventBusUtil().instance.fire(RefreshDeviceListEvent());
}
//
void updateGradientColor(int index) {
if (index < gradientColors.length) {
@ -66,27 +66,8 @@ class HomeController extends BaseController {
//
Future<void> refreshHome() async {
//
// isLoading.value = true;
//
await Future.delayed(const Duration(seconds: 1));
//
// 1.
// 2.
// 3.
// 4.
// 5.
//
isOpenNotificationPermission.value = await AppPermission.checkPermission(
permission: Permission.notification,
);
//
// isLoading.value = false;
print('首页数据刷新完成');
_initializeData();
}
}

View File

@ -232,12 +232,18 @@ class HomeView extends GetView<HomeController> {
HomeCarouselAreaWidget(
carouselCurrentIndex: controller.carouselCurrentIndex,
),
SizedBox(height: 10.h),
HomeNotDeviceArea(),
Visibility(
child: SizedBox(height: 10.h),
visible: controller.mainController.deviceCountSize.value == 0,
),
Visibility(
visible: controller.mainController.deviceCountSize.value == 0,
child: HomeNotDeviceArea(),
),
HomeTeamNoticeRowWidget(),
HomeStatisticsRowWidget(
personCount: 0,
deviceCount: 0,
deviceCount: controller.mainController.deviceCountSize.value,
),
SizedBox(height: 10.h),
HomeFunctionListAreaWidget(),

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:starwork_flutter/routes/app_routes.dart';
class HomeStatisticsRowWidget extends StatelessWidget {
final int personCount;
@ -47,80 +48,85 @@ class HomeStatisticsRowWidget extends StatelessWidget {
}) {
return Expanded(
// 使Expanded让卡片自适应宽度
child: Container(
height: 62.h,
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(8.r),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// + +
Row(
children: [
//
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
// 使baseline对齐
textBaseline: TextBaseline.alphabetic,
// 线
children: [
Text(
count.toString(),
style: TextStyle(
fontSize: 20.sp, // text-5
fontWeight: FontWeight.bold,
color: textColor,
),
),
Text(
unit,
style: TextStyle(
fontSize: 12.sp, // text-3
color: textColor,
),
),
],
),
const Spacer(),
//
Container(
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
buttonText,
style: TextStyle(
fontSize: 10.sp, // text-2.5
fontWeight: FontWeight.w600,
color: textColor,
),
),
),
],
),
SizedBox(height: 4.h), // mt-1
//
Text(
label,
style: TextStyle(
fontSize: 10.sp, // text-2.5
color: textColor,
child: GestureDetector(
onTap: (){
Get.toNamed(AppRoutes.deviceManage);
},
child: Container(
height: 62.h,
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 10.h),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(8.r),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 4,
offset: const Offset(0, 2),
),
),
],
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// + +
Row(
children: [
//
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
// 使baseline对齐
textBaseline: TextBaseline.alphabetic,
// 线
children: [
Text(
count.toString(),
style: TextStyle(
fontSize: 20.sp, // text-5
fontWeight: FontWeight.bold,
color: textColor,
),
),
Text(
unit,
style: TextStyle(
fontSize: 12.sp, // text-3
color: textColor,
),
),
],
),
const Spacer(),
//
Container(
padding: EdgeInsets.symmetric(horizontal: 4.w, vertical: 4.h),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.5),
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
buttonText,
style: TextStyle(
fontSize: 10.sp, // text-2.5
fontWeight: FontWeight.w600,
color: textColor,
),
),
),
],
),
SizedBox(height: 4.h), // mt-1
//
Text(
label,
style: TextStyle(
fontSize: 10.sp, // text-2.5
color: textColor,
),
),
],
),
),
),
);

View File

@ -4,14 +4,19 @@ import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:starcloud/entity/star_cloud_lock_list.dart';
import 'package:starcloud/sdk/entity/cloud_user_info.dart';
import 'package:starcloud/sdk/sdk_device_operate_extension.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart';
import 'package:starwork_flutter/api/model/team/request/change_current_team_request.dart';
import 'package:starwork_flutter/api/model/team/response/team_info_response.dart';
import 'package:starwork_flutter/api/service/team_api_service.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/base/base_controller.dart';
import 'package:starwork_flutter/common/constant/app_images.dart';
import 'package:starwork_flutter/common/events/refresh_device_list_event.dart';
import 'package:starwork_flutter/common/utils/event_bus_util.dart';
import 'package:starwork_flutter/routes/app_routes.dart';
import 'package:starwork_flutter/views/home/home_controller.dart';
import 'package:starwork_flutter/views/home/home_view.dart';
@ -39,11 +44,32 @@ class MainController extends BaseController {
//
var selectedTeam = TeamInfoResponse().obs;
//
final deviceCountSize = 0.obs;
//
final teamCountSize = 0.obs;
//
late StreamSubscription _refreshDeviceListSubscription;
@override
void onInit() {
super.onInit();
///
requestAllTeamInfoList();
super.onInit();
//
_refreshDeviceListSubscription = EventBusUtil().instance.on<RefreshDeviceListEvent>().listen((event) {
_requestTeamDeviceList();
});
}
@override
void onClose() {
super.onClose();
_refreshDeviceListSubscription.cancel();
}
//
@ -115,13 +141,27 @@ class MainController extends BaseController {
if (myTeamList.isEmpty) {
// 使
Get.toNamed(AppRoutes.teamUseCaseSetting);
return;
}
selectedTeam.value = myTeamList.first;
//
if (selectedTeam.value.teamCloudInfo != null) {
StarCloudSDK.instance.setCloudAccounts([
CloudUserInfo(
username: selectedTeam.value.teamCloudInfo!.username!,
password: selectedTeam.value.teamCloudInfo!.password!,
uid: selectedTeam.value.teamCloudInfo!.uid!,
)
]);
_requestTeamDeviceList();
}
}
}
///
void requestChangeCurrentTeam(TeamInfoResponse teamInfo) async {
if (teamInfo.teamNo == selectedTeam.value.teamNo) return;
var teamNo = teamInfo.teamNo;
if (teamNo == null) {
return;
@ -132,19 +172,58 @@ class MainController extends BaseController {
),
);
if (changeCurrentTeamResponse.isSuccess) {
AppLogger.highlight('切换当前团队成功');
selectedTeam.value = teamInfo;
// teamInfo中的cloudInfo不为空
if (teamInfo.cloudInfo != null) {
if (teamInfo.teamCloudInfo != null) {
StarCloudSDK.instance.setCloudAccounts([
CloudUserInfo(
username: teamInfo.cloudInfo!.username!,
password: teamInfo.cloudInfo!.password!,
uid: teamInfo.cloudInfo!.uid!,
username: teamInfo.teamCloudInfo!.username!,
password: teamInfo.teamCloudInfo!.password!,
uid: teamInfo.teamCloudInfo!.uid!,
)
]);
_requestTeamDeviceList();
} else {
//
StarCloudSDK.instance.createCloudUser(
onSuccess: (CloudUserInfo userInfo) async {
var teamStarCloudAccountResponse = await teamApi.requestBindTeamStarCloudAccount(
request: BindTeamStarCloudAccountRequest(
teamNo: teamNo,
username: userInfo.username,
password: userInfo.password,
teamId: teamInfo.id!,
cloudUid: userInfo.uid,
),
);
if (teamStarCloudAccountResponse.isSuccess) {
StarCloudSDK.instance.setCloudAccounts([
CloudUserInfo(
username: userInfo.username,
password: userInfo.password,
uid: userInfo.uid,
)
]);
_requestTeamDeviceList();
}
},
onError: (err) {},
);
}
}
}
void _requestTeamDeviceList() async {
await StarCloudSDK.instance.getDeviceList(
pageNo: 1,
pageSize: 999,
cloudUid: selectedTeam.value.teamCloudInfo!.uid!,
onSuccess: (StarCloudLockList list) {
deviceCountSize.value = list.total;
},
onError: (err) {
AppLogger.error('获取设备列表失败:${err}');
},
);
}
}

View File

@ -1,8 +1,12 @@
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:starcloud/sdk/entity/cloud_user_info.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart';
import 'package:starwork_flutter/api/model/team/request/create_team_request.dart';
import 'package:starwork_flutter/api/model/team/response/scene_info_response.dart';
import 'package:starwork_flutter/api/service/team_api_service.dart';
import 'package:starwork_flutter/base/app_logger.dart';
import 'package:starwork_flutter/base/base_controller.dart';
class CreateTeamController extends BaseController {
@ -35,21 +39,46 @@ class CreateTeamController extends BaseController {
}
void requestCreateTeam() async {
showLoading();
var createTeamResponse = await teamApi.requestCreateTeam(
request: CreateTeamRequest(
teamName: teamNameInputController.text,
scene: selectedUseCase.value?.id ?? 0,
),
//
await StarCloudSDK.instance.createCloudUser(
onSuccess: (CloudUserInfo userInfo) async {
//
var createTeamResponse = await teamApi.requestCreateTeam(
request: CreateTeamRequest(
teamName: teamNameInputController.text,
scene: selectedUseCase.value?.id ?? 0,
),
);
if (createTeamResponse.isSuccess) {
var teamNo = createTeamResponse.data?.teamNo;
var teamId = createTeamResponse.data?.teamId;
var username = userInfo.username;
var cloudUid = userInfo.uid;
var password = userInfo.password;
if (teamNo != null && teamId != null) {
var bindTeamStarCloudAccountResponse = await teamApi.requestBindTeamStarCloudAccount(
request: BindTeamStarCloudAccountRequest(
teamId: teamId,
teamNo: teamNo,
username: username,
cloudUid: cloudUid,
password: password,
),
);
AppLogger.highlight('bindTeamStarCloudAccountResponse:${bindTeamStarCloudAccountResponse.toString()}');
if (bindTeamStarCloudAccountResponse.isSuccess) {
// 使 Get.back(result: true)
showSuccess();
//
Get.back(result: {'created': true});
}
}
} else {
//
showError(message: createTeamResponse.errorMsg!);
}
},
onError: (err) {},
);
if (createTeamResponse.isSuccess) {
// 使 Get.back(result: true)
showSuccess();
//
Get.back(result: {'created': true});
} else {
//
showError(message: createTeamResponse.errorMsg!);
}
}
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:starcloud/sdk/entity/cloud_user_info.dart';
import 'package:starcloud/sdk/starcloud.dart';
import 'package:starwork_flutter/api/model/team/request/bind_team_star_cloud_account_request.dart';
import 'package:starwork_flutter/api/model/team/request/create_team_request.dart';
import 'package:starwork_flutter/api/model/team/response/scene_info_response.dart';
import 'package:starwork_flutter/api/service/team_api_service.dart';
@ -39,46 +40,46 @@ class UseCaseSettingController extends BaseController {
}
void requestCreateTeam() async {
var cacheStarCloudUserName = await SharedPreferencesUtils.getString(CacheKeys.starCloudUserName);
var cacheStarCloudPassword = await SharedPreferencesUtils.getString(CacheKeys.starCloudPassword);
var cacheStarCloudUid = await SharedPreferencesUtils.getString(CacheKeys.starCloudUid);
if (cacheStarCloudUserName == null || cacheStarCloudPassword == null || cacheStarCloudUid == null) {
await StarCloudSDK.instance.createCloudUser(
onError: (err) {
AppLogger.error('err:${err}');
hideLoading();
},
onSuccess: (userInfo) {
SharedPreferencesUtils.setString(CacheKeys.starCloudUserName, userInfo.username);
SharedPreferencesUtils.setString(CacheKeys.starCloudPassword, userInfo.password);
SharedPreferencesUtils.setString(CacheKeys.starCloudUid, userInfo.uid.toString());
},
);
}
StarCloudSDK.instance.setCloudAccounts(
[
CloudUserInfo(
username: cacheStarCloudUserName!,
password: cacheStarCloudPassword!,
uid: int.parse(cacheStarCloudUid!),
)
],
//
await StarCloudSDK.instance.createCloudUser(
onSuccess: (CloudUserInfo userInfo) async {
//
var createTeamResponse = await teamApi.requestCreateTeam(
request: CreateTeamRequest(
teamName: teamNameInputController.text,
scene: selectedUseCase.value?.id ?? 0,
),
);
if (createTeamResponse.isSuccess) {
var teamNo = createTeamResponse.data?.teamNo;
var teamId = createTeamResponse.data?.teamId;
var username = userInfo.username;
var cloudUid = userInfo.uid;
var password = userInfo.password;
if (teamNo != null && teamId != null) {
var bindTeamStarCloudAccountResponse = await teamApi.requestBindTeamStarCloudAccount(
request: BindTeamStarCloudAccountRequest(
teamId: teamId,
teamNo: teamNo,
username: username,
cloudUid: cloudUid,
password: password,
),
);
if (bindTeamStarCloudAccountResponse.isSuccess) {
// 使 Get.back(result: true)
showSuccess();
//
Get.back(result: {'created': true});
}
}
} else {
//
showError(message: createTeamResponse.errorMsg!);
}
},
onError: (err) {},
);
var createTeamResponse = await teamApi.requestCreateTeam(
request: CreateTeamRequest(
teamName: teamNameInputController.text,
scene: selectedUseCase.value?.id ?? 0,
),
);
if (createTeamResponse.isSuccess) {
//
showSuccess();
Get.back();
} else {
//
showError(message: createTeamResponse.errorMsg!);
}
}
/// 9

View File

@ -121,6 +121,14 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
event_bus:
dependency: "direct main"
description:
name: event_bus
sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
fake_async:
dependency: transitive
description:

View File

@ -36,6 +36,8 @@ dependencies:
carousel_slider: ^5.1.1
# 气泡提示框
super_tooltip: ^2.0.8
# 事件总线
event_bus: ^2.0.1
# 星云sdk
starcloud:
path: ../starcloud-sdk-flutter