Commit 7c60f91c authored by 张宏's avatar 张宏

2

parent 4b88b06f
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:smart_hotel_app/models/bo/user_info_bo.dart';
import 'auth_event.dart';
import 'auth_state.dart';
import '../../services/auth_service.dart';
......@@ -18,6 +17,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
on<AuthLoginRequested>(_onLoginRequested);
on<AuthTokenExpiredEvent>(_onTokenExpired);
on<AuthLogoutRequestedEvent>(_onLogoutRequested);
on<AuthFetchUserInfo>(_onFetchUserInfo);
}
Future<void> _onLoginRequested(
......@@ -28,10 +28,10 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
try {
await _authService.login(event.username, event.password);
const userInfo = UserInfoBO(
username: "管理员",
);
emit(const AuthSuccess(userInfo));
// 登录成功后获取用户信息
final userInfo = await _authService.getUserInfo();
emit(AuthSuccess(userInfo));
} catch (e) {
emit(AuthFailure(e.toString()));
}
......@@ -42,6 +42,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
Emitter<AuthState> emit,
) async {
await _storageService.deleteToken();
await _storageService.deleteUserInfo();
await _storageService.deleteClientId();
emit(const AuthTokenExpired());
}
......@@ -50,6 +52,24 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
Emitter<AuthState> emit,
) async {
await _storageService.deleteToken();
await _storageService.deleteUserInfo();
await _storageService.deleteClientId();
emit(const AuthLoggedOut());
}
Future<void> _onFetchUserInfo(
AuthFetchUserInfo event,
Emitter<AuthState> emit,
) async {
try {
final userInfo = await _authService.getUserInfo();
emit(AuthSuccess(userInfo));
} catch (e) {
// 获取用户信息失败,尝试从本地读取
final storedUserInfo = await _authService.getStoredUserInfo();
if (storedUserInfo != null) {
emit(AuthSuccess(storedUserInfo));
}
}
}
}
\ No newline at end of file
......@@ -27,3 +27,7 @@ class AuthTokenExpiredEvent extends AuthEvent {
class AuthLogoutRequestedEvent extends AuthEvent {
const AuthLogoutRequestedEvent();
}
class AuthFetchUserInfo extends AuthEvent {
const AuthFetchUserInfo();
}
......@@ -65,6 +65,9 @@ class _MyAppState extends State<MyApp> {
final expired = await _authService.isTokenExpired();
if (expired) {
_authBloc.add(const AuthTokenExpiredEvent());
} else {
// Token 有效,尝试恢复用户信息
_authBloc.add(const AuthFetchUserInfo());
}
}
}
......
......@@ -43,6 +43,7 @@ class AlertBasicInfoBO extends Equatable {
final int alertCount;
final String alertTime;
final String alertIcon;
final int alertDeviceId;
const AlertBasicInfoBO({
required this.alertId,
......@@ -54,6 +55,7 @@ class AlertBasicInfoBO extends Equatable {
required this.alertCount,
required this.alertTime,
required this.alertIcon,
required this.alertDeviceId,
});
factory AlertBasicInfoBO.fromJson(Map<String, dynamic> json) {
......@@ -67,6 +69,7 @@ class AlertBasicInfoBO extends Equatable {
alertCount: json['alertCount'] as int? ?? 0,
alertTime: json['alertTime'] as String? ?? '',
alertIcon: json['alertIcon'] as String? ?? '',
alertDeviceId: json['alertDeviceId'] as int? ?? 0,
);
}
......@@ -81,6 +84,7 @@ class AlertBasicInfoBO extends Equatable {
alertCount,
alertTime,
alertIcon,
alertDeviceId,
];
}
......
......@@ -85,3 +85,169 @@ class InspectionDeviceListBO extends Equatable {
@override
List<Object?> get props => [total, data];
}
// ==================== 设备拓扑图 - 设备详情 ====================
class DeviceDetailBO extends Equatable {
final DeviceBasicInfoBO basicInfo;
final LatestInspectionBO? latestInspection;
final List<InspectionItemBO> inspectionItems;
const DeviceDetailBO({
required this.basicInfo,
this.latestInspection,
required this.inspectionItems,
});
factory DeviceDetailBO.fromJson(Map<String, dynamic> json) {
final items = json['inspectionItems'] as List<dynamic>? ?? [];
return DeviceDetailBO(
basicInfo: DeviceBasicInfoBO.fromJson(
json['basicInfo'] as Map<String, dynamic>? ?? {}),
latestInspection: json['latestInspection'] != null
? LatestInspectionBO.fromJson(
json['latestInspection'] as Map<String, dynamic>)
: null,
inspectionItems: items
.map((e) =>
InspectionItemBO.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
@override
List<Object?> get props => [basicInfo, latestInspection, inspectionItems];
}
class DeviceBasicInfoBO extends Equatable {
final int deviceId;
final String deviceName;
final String deviceTypeName;
final String onlineStatus;
final String deviceModel;
final String areaName;
final String responsiblePerson;
final String lastInspectTime;
const DeviceBasicInfoBO({
required this.deviceId,
required this.deviceName,
required this.deviceTypeName,
required this.onlineStatus,
required this.deviceModel,
required this.areaName,
required this.responsiblePerson,
required this.lastInspectTime,
});
factory DeviceBasicInfoBO.fromJson(Map<String, dynamic> json) {
return DeviceBasicInfoBO(
deviceId: int.tryParse(json['deviceId']?.toString() ?? '') ?? 0,
deviceName: json['deviceName'] as String? ?? '',
deviceTypeName: json['deviceTypeName'] as String? ?? '',
onlineStatus: json['onlineStatus'] as String? ?? '',
deviceModel: json['deviceModel'] as String? ?? '',
areaName: json['areaName'] as String? ?? '',
responsiblePerson: json['responsiblePerson'] as String? ?? '',
lastInspectTime: json['lastInspectTime'] as String? ?? '',
);
}
@override
List<Object?> get props => [
deviceId,
deviceName,
deviceTypeName,
onlineStatus,
deviceModel,
areaName,
responsiblePerson,
lastInspectTime,
];
}
class LatestInspectionBO extends Equatable {
final int inspectId;
final String inspectorName;
final String inspectTime;
final String inspectStatus;
final String remark;
const LatestInspectionBO({
required this.inspectId,
required this.inspectorName,
required this.inspectTime,
required this.inspectStatus,
required this.remark,
});
factory LatestInspectionBO.fromJson(Map<String, dynamic> json) {
return LatestInspectionBO(
inspectId: int.tryParse(json['inspectId']?.toString() ?? '') ?? 0,
inspectorName: json['inspectorName'] as String? ?? '',
inspectTime: json['inspectTime'] as String? ?? '',
inspectStatus: json['inspectStatus'] as String? ?? '',
remark: json['remark'] as String? ?? '',
);
}
@override
List<Object?> get props => [
inspectId,
inspectorName,
inspectTime,
inspectStatus,
remark,
];
}
class InspectionItemBO extends Equatable {
final String name;
final String value;
final String status;
const InspectionItemBO({
required this.name,
required this.value,
required this.status,
});
factory InspectionItemBO.fromJson(Map<String, dynamic> json) {
return InspectionItemBO(
name: json['name'] as String? ?? '',
value: json['value'] as String? ?? '',
status: json['status'] as String? ?? '',
);
}
@override
List<Object?> get props => [name, value, status];
}
// ==================== 设备拓扑图 - 开始巡检 ====================
class InspectionStartResultBO extends Equatable {
final int inspectId;
final String inspectStatus;
final String inspectTime;
final int totalItems;
const InspectionStartResultBO({
required this.inspectId,
required this.inspectStatus,
required this.inspectTime,
required this.totalItems,
});
factory InspectionStartResultBO.fromJson(Map<String, dynamic> json) {
return InspectionStartResultBO(
inspectId: int.tryParse(json['inspectId']?.toString() ?? '') ?? 0,
inspectStatus: json['inspectStatus'] as String? ?? '',
inspectTime: json['inspectTime'] as String? ?? '',
totalItems: int.tryParse(json['totalItems']?.toString() ?? '') ?? 0,
);
}
@override
List<Object?> get props => [inspectId, inspectStatus, inspectTime, totalItems];
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
import 'inspection_device_bo.dart';
class InspectionHistoryListBO extends Equatable {
final int total;
final InspectionHistoryTabCountBO tabCount;
final InspectionHistoryDeviceInfoBO deviceInfo;
final List<InspectionHistoryRecordBO> rows;
const InspectionHistoryListBO({
required this.total,
required this.tabCount,
required this.deviceInfo,
required this.rows,
});
factory InspectionHistoryListBO.fromJson(Map<String, dynamic> json) {
final rows = json['rows'] as List<dynamic>? ?? [];
return InspectionHistoryListBO(
total: int.tryParse(json['total']?.toString() ?? '') ?? 0,
tabCount: InspectionHistoryTabCountBO.fromJson(
json['tabCount'] as Map<String, dynamic>? ?? {}),
deviceInfo: InspectionHistoryDeviceInfoBO.fromJson(
json['deviceInfo'] as Map<String, dynamic>? ?? {}),
rows: rows
.map((e) => InspectionHistoryRecordBO.fromJson(
e as Map<String, dynamic>))
.toList(),
);
}
@override
List<Object?> get props => [total, tabCount, deviceInfo, rows];
}
class InspectionHistoryTabCountBO extends Equatable {
final int total;
final int passCount;
final int warnCount;
final int failCount;
const InspectionHistoryTabCountBO({
required this.total,
required this.passCount,
required this.warnCount,
required this.failCount,
});
factory InspectionHistoryTabCountBO.fromJson(Map<String, dynamic> json) {
return InspectionHistoryTabCountBO(
total: int.tryParse(json['total']?.toString() ?? '') ?? 0,
passCount: int.tryParse(json['passCount']?.toString() ?? '') ?? 0,
warnCount: int.tryParse(json['warnCount']?.toString() ?? '') ?? 0,
failCount: int.tryParse(json['failCount']?.toString() ?? '') ?? 0,
);
}
@override
List<Object?> get props => [total, passCount, warnCount, failCount];
}
class InspectionHistoryDeviceInfoBO extends Equatable {
final int deviceId;
final String deviceName;
final String deviceIcon;
const InspectionHistoryDeviceInfoBO({
required this.deviceId,
required this.deviceName,
required this.deviceIcon,
});
factory InspectionHistoryDeviceInfoBO.fromJson(Map<String, dynamic> json) {
return InspectionHistoryDeviceInfoBO(
deviceId: int.tryParse(json['deviceId']?.toString() ?? '') ?? 0,
deviceName: json['deviceName'] as String? ?? '',
deviceIcon: json['deviceIcon'] as String? ?? '',
);
}
@override
List<Object?> get props => [deviceId, deviceName, deviceIcon];
}
class InspectionHistoryRecordBO extends Equatable {
final int inspectId;
final String inspectorName;
final String inspectTime;
final String inspectStatus;
final String remark;
final List<InspectionItemBO> itemsDetail;
const InspectionHistoryRecordBO({
required this.inspectId,
required this.inspectorName,
required this.inspectTime,
required this.inspectStatus,
required this.remark,
required this.itemsDetail,
});
factory InspectionHistoryRecordBO.fromJson(Map<String, dynamic> json) {
final items = json['itemsDetail'] as List<dynamic>? ?? [];
return InspectionHistoryRecordBO(
inspectId: int.tryParse(json['inspectId']?.toString() ?? '') ?? 0,
inspectorName: json['inspectorName'] as String? ?? '',
inspectTime: json['inspectTime'] as String? ?? '',
inspectStatus: json['inspectStatus'] as String? ?? '',
remark: json['remark'] as String? ?? '',
itemsDetail: items
.map((e) =>
InspectionItemBO.fromJson(e as Map<String, dynamic>))
.toList(),
);
}
@override
List<Object?> get props => [
inspectId,
inspectorName,
inspectTime,
inspectStatus,
remark,
itemsDetail,
];
}
\ No newline at end of file
import 'package:equatable/equatable.dart';
/**
* 业务对象
*/
class UserInfoBO extends Equatable {
final String? username;
final String? rolename;
final String? rolecode;
final int userId;
final String userName;
final String nickName;
final String phonenumber;
final String email;
final String sex;
final String avatarUrl;
final String deptName;
final String tenantId;
const UserInfoBO({
this.username,
this.rolename,
this.rolecode,
required this.userId,
required this.userName,
required this.nickName,
required this.phonenumber,
required this.email,
required this.sex,
required this.avatarUrl,
required this.deptName,
required this.tenantId,
});
factory UserInfoBO.fromJson(Map<String, dynamic> json) {
return UserInfoBO(
userId: int.tryParse(json['userId']?.toString() ?? '') ?? 0,
userName: json['userName'] as String? ?? '',
nickName: json['nickName'] as String? ?? '',
phonenumber: json['phonenumber'] as String? ?? '',
email: json['email'] as String? ?? '',
sex: json['sex'] as String? ?? '',
avatarUrl: json['avatarUrl'] as String? ?? '',
deptName: json['deptName'] as String? ?? '',
tenantId: json['tenantId'] as String? ?? '',
);
}
Map<String, dynamic> toJson() {
return {
'userId': userId,
'userName': userName,
'nickName': nickName,
'phonenumber': phonenumber,
'email': email,
'sex': sex,
'avatarUrl': avatarUrl,
'deptName': deptName,
'tenantId': tenantId,
};
}
@override
List<Object?> get props => [username, rolename, rolecode];
List<Object?> get props => [
userId,
userName,
nickName,
phonenumber,
email,
sex,
avatarUrl,
deptName,
tenantId,
];
}
\ No newline at end of file
import '../utils/http/response_model.dart';
import '../utils/http/dio_request.dart';
import '../models/bo/login_bo.dart';
import '../models/bo/user_info_bo.dart';
class AuthRepository {
Future<ResponseModel<LoginBO>> login(Map<String, dynamic> params) {
......@@ -10,4 +11,11 @@ class AuthRepository {
fromJsonT: (data) => LoginBO.fromJson(data as Map<String, dynamic>),
);
}
Future<ResponseModel<UserInfoBO>> getUserInfo() {
return DioRequest.instance.get<UserInfoBO>(
'/app/auth/user-info',
fromJsonT: (data) => UserInfoBO.fromJson(data as Map<String, dynamic>),
);
}
}
......@@ -15,4 +15,23 @@ class InspectionDeviceRepository {
InspectionDeviceListBO.fromJson(data as Map<String, dynamic>),
);
}
Future<ResponseModel<DeviceDetailBO>> getDetail(int deviceId) {
return DioRequest.instance.get<DeviceDetailBO>(
'/app/device/detail',
queryParameters: {'deviceId': deviceId},
fromJsonT: (data) =>
DeviceDetailBO.fromJson(data as Map<String, dynamic>),
);
}
Future<ResponseModel<InspectionStartResultBO>> startInspection(
Map<String, dynamic> params) {
return DioRequest.instance.post<InspectionStartResultBO>(
'/app/device/inspection/start',
data: params,
fromJsonT: (data) =>
InspectionStartResultBO.fromJson(data as Map<String, dynamic>),
);
}
}
\ No newline at end of file
import '../utils/http/response_model.dart';
import '../utils/http/dio_request.dart';
import '../models/bo/inspection_history_bo.dart';
class InspectionHistoryRepository {
Future<ResponseModel<InspectionHistoryListBO>> getList({
required int pageSize,
required int pageNum,
required int deviceId,
}) {
return DioRequest.instance.get<InspectionHistoryListBO>(
'/app/device/inspection/list',
queryParameters: {
'pageSize': pageSize,
'pageNum': pageNum,
'deviceId': deviceId,
},
fromJsonT: (data) =>
InspectionHistoryListBO.fromJson(data as Map<String, dynamic>),
);
}
}
\ No newline at end of file
import '../models/bo/login_bo.dart';
import '../models/bo/user_info_bo.dart';
import '../repositories/auth_repository.dart';
import '../utils/constants.dart';
import '../utils/storage/storage_service.dart';
......@@ -27,11 +28,28 @@ class AuthService {
expiryHours: Constants.tokenExpiryHours,
);
}
if (loginBO.clientId != null && loginBO.clientId!.isNotEmpty) {
await _storageService.saveClientId(loginBO.clientId!);
}
return loginBO;
}
throw Exception(result.msg);
}
Future<UserInfoBO> getUserInfo() async {
final result = await _authRepository.getUserInfo();
if (result.success && result.data != null) {
final userInfo = result.data!;
await _storageService.saveUserInfo(userInfo);
return userInfo;
}
throw Exception(result.msg);
}
Future<UserInfoBO?> getStoredUserInfo() async {
return _storageService.getUserInfo();
}
Future<bool> isTokenExpired() async {
return _storageService.isTokenExpired();
}
......
......@@ -10,10 +10,32 @@ class InspectionDeviceService {
Future<InspectionDeviceListBO> getList({
required int pageSize,
required int pageNum,
int? roomId
int? roomId,
}) async {
final result =
await _repository.getList(pageSize: pageSize, pageNum: pageNum, roomId: roomId);
final result = await _repository.getList(
pageSize: pageSize, pageNum: pageNum, roomId: roomId);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
Future<DeviceDetailBO> getDetail(int deviceId) async {
final result = await _repository.getDetail(deviceId);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
Future<InspectionStartResultBO> startInspection({
required int deviceId,
required int inspectorId,
}) async {
final result = await _repository.startInspection({
'deviceId': deviceId,
'inspectorId': inspectorId,
});
if (result.success && result.data != null) {
return result.data!;
}
......
import '../repositories/inspection_history_repository.dart';
import '../models/bo/inspection_history_bo.dart';
class InspectionHistoryService {
final InspectionHistoryRepository _repository;
InspectionHistoryService({required InspectionHistoryRepository repository})
: _repository = repository;
Future<InspectionHistoryListBO> getList({
required int pageSize,
required int pageNum,
required int deviceId,
}) async {
final result = await _repository.getList(
pageSize: pageSize,
pageNum: pageNum,
deviceId: deviceId,
);
if (result.success && result.data != null) {
return result.data!;
}
throw Exception(result.msg);
}
}
\ No newline at end of file
class Constants {
// ==================== 环境配置 ====================
static const String baseUrl = 'http://121.41.57.178:9090';
// static const String baseUrl = 'http://192.168.0.168:9090';
// static const String baseUrl = 'http://192.168.1.7:9090';
// ==================== 超时配置 ====================
static const int connectTimeout = 30000;
......
......@@ -22,6 +22,10 @@ class RequestInterceptor extends Interceptor {
final token = await _storageService.getToken();
if (token != null && token.isNotEmpty) {
headers['Authorization'] = 'Bearer $token';
final clientId = await _storageService.getClientId();
if (clientId != null && clientId.isNotEmpty) {
headers['clientid'] = clientId;
}
}
options.headers.addAll(headers);
......
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:smart_hotel_app/models/bo/user_info_bo.dart';
class StorageService {
static const String _keyToken = 'auth_token';
......@@ -6,6 +7,8 @@ class StorageService {
static const String _keyRememberUsername = 'remember_username';
static const String _keyRememberPassword = 'remember_password';
static const String _keyRememberEnabled = 'remember_enabled';
static const String _keyUserInfo = 'user_info';
static const String _keyClientId = 'client_id';
final FlutterSecureStorage _storage = const FlutterSecureStorage(
aOptions: AndroidOptions(
......@@ -48,6 +51,50 @@ class StorageService {
return token != null && token.isNotEmpty;
}
// ==================== 用户信息相关 ====================
Future<void> saveUserInfo(UserInfoBO userInfo) async {
final jsonStr =
'${userInfo.userId}|${userInfo.userName}|${userInfo.nickName}|${userInfo.phonenumber}|${userInfo.email}|${userInfo.sex}|${userInfo.avatarUrl}|${userInfo.deptName}|${userInfo.tenantId}';
await _storage.write(key: _keyUserInfo, value: jsonStr);
}
Future<UserInfoBO?> getUserInfo() async {
final value = await _storage.read(key: _keyUserInfo);
if (value == null || value.isEmpty) return null;
final parts = value.split('|');
if (parts.length < 9) return null;
return UserInfoBO(
userId: int.tryParse(parts[0]) ?? 0,
userName: parts[1],
nickName: parts[2],
phonenumber: parts[3],
email: parts[4],
sex: parts[5],
avatarUrl: parts[6],
deptName: parts[7],
tenantId: parts[8],
);
}
Future<void> deleteUserInfo() async {
await _storage.delete(key: _keyUserInfo);
}
// ==================== ClientId 相关 ====================
Future<void> saveClientId(String clientId) async {
await _storage.write(key: _keyClientId, value: clientId);
}
Future<String?> getClientId() async {
return await _storage.read(key: _keyClientId);
}
Future<void> deleteClientId() async {
await _storage.delete(key: _keyClientId);
}
// ==================== 记住密码相关 ====================
Future<void> saveRememberCredentials({
......
......@@ -136,8 +136,8 @@ class AbnormalDetailView extends StatelessWidget {
),
child: TextButton(
onPressed: () {
// TODO: 替换为真实的 deviceId
context.pushRoute(DeviceDetailRoute(deviceId: 0));
final deviceId = cubit.state.alarmInfo?.alertDeviceId ?? 0;
context.pushRoute(DeviceDetailRoute(deviceId: deviceId));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
......
......@@ -53,6 +53,7 @@ class AbnormalCubit extends Cubit<AbnormalState> {
temperatureSpots: trend.dataPoints.asMap().entries.map((entry) {
return FlSpot(entry.key.toDouble(), entry.value.value);
}).toList(),
alertDeviceId: basic.alertDeviceId,
);
}
......
......@@ -15,6 +15,7 @@ class AlarmInfo {
final String linkAction;
final String date;
final List<FlSpot> temperatureSpots;
final int alertDeviceId;
const AlarmInfo({
required this.alarmType,
......@@ -31,6 +32,7 @@ class AlarmInfo {
required this.linkAction,
required this.date,
required this.temperatureSpots,
required this.alertDeviceId,
});
}
......
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'inspection_device_state.dart';
import '../../../../services/inspection_device_service.dart';
class InspectionDeviceCubit extends Cubit<InspectionDeviceState> {
InspectionDeviceCubit({required String deviceId})
: super(InspectionDeviceState(
final InspectionDeviceService _service;
final int _deviceIdInt;
InspectionDeviceCubit({
required String deviceId,
required InspectionDeviceService service,
}) : _service = service,
_deviceIdInt = int.tryParse(deviceId) ?? 0,
super(InspectionDeviceState(
deviceId: deviceId,
deviceName: '',
deviceType: '',
......@@ -18,54 +26,49 @@ class InspectionDeviceCubit extends Cubit<InspectionDeviceState> {
inspectionItems: [],
icon: Icons.devices,
)) {
_loadDeviceData(deviceId);
loadDeviceData();
}
void _loadDeviceData(String deviceId) {
// TODO: 从API或本地数据库加载设备数据
// 这里使用模拟数据
final mockState = InspectionDeviceState(
deviceId: deviceId,
deviceName: '客厅主控网关',
deviceType: '智能网关',
deviceStatus: '正常',
deviceModel: 'SmartHub Pro V2',
installLocation: '客厅电视柜',
area: '客厅',
responsiblePerson: '张师傅',
lastInspectionTime: '2024-01-15 14:30:00',
inspectionHistory: [
InspectionHistoryItem(
inspectorName: '张师傅',
inspectionTime: '2024-01-15 14:30:00',
result: '通过',
remark: '设备运行正常,各项指标正常',
),
],
inspectionItems: [
InspectionItem(
name: '电源状态',
value: '正常',
result: '通过',
),
InspectionItem(
name: '网络连接',
value: '稳定',
result: '通过',
),
InspectionItem(
name: 'CPU使用率',
value: '23%',
result: '通过',
),
InspectionItem(
name: '内存占用',
value: '45%',
result: '通过',
),
],
icon: Icons.wifi,
Future<void> loadDeviceData() async {
emit(state.copyWith(isLoading: true, error: null));
try {
final detail = await _service.getDetail(_deviceIdInt);
final basicInfo = detail.basicInfo;
final historyList = <InspectionHistoryItem>[];
if (detail.latestInspection != null) {
historyList.add(InspectionHistoryItem(
inspectorName: detail.latestInspection!.inspectorName,
inspectionTime: detail.latestInspection!.inspectTime,
result: detail.latestInspection!.inspectStatus,
remark: detail.latestInspection!.remark,
));
}
final items = detail.inspectionItems.map((item) {
return InspectionItem(
name: item.name,
value: item.value,
result: item.status,
);
emit(mockState);
}).toList();
emit(state.copyWith(
isLoading: false,
deviceName: basicInfo.deviceName,
deviceType: basicInfo.deviceTypeName,
deviceStatus: basicInfo.onlineStatus,
deviceModel: basicInfo.deviceModel,
area: basicInfo.areaName,
installLocation: basicInfo.areaName,
responsiblePerson: basicInfo.responsiblePerson,
lastInspectionTime: basicInfo.lastInspectTime,
inspectionHistory: historyList,
inspectionItems: items,
icon: Icons.devices,
));
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
}
}
\ No newline at end of file
......@@ -14,6 +14,8 @@ class InspectionDeviceState extends Equatable {
final List<InspectionHistoryItem> inspectionHistory;
final List<InspectionItem> inspectionItems;
final IconData icon;
final bool isLoading;
final String? error;
const InspectionDeviceState({
required this.deviceId,
......@@ -28,6 +30,8 @@ class InspectionDeviceState extends Equatable {
required this.inspectionHistory,
required this.inspectionItems,
required this.icon,
this.isLoading = false,
this.error,
});
InspectionDeviceState copyWith({
......@@ -43,6 +47,8 @@ class InspectionDeviceState extends Equatable {
List<InspectionHistoryItem>? inspectionHistory,
List<InspectionItem>? inspectionItems,
IconData? icon,
bool? isLoading,
String? error,
}) {
return InspectionDeviceState(
deviceId: deviceId ?? this.deviceId,
......@@ -57,6 +63,8 @@ class InspectionDeviceState extends Equatable {
inspectionHistory: inspectionHistory ?? this.inspectionHistory,
inspectionItems: inspectionItems ?? this.inspectionItems,
icon: icon ?? this.icon,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
......@@ -74,6 +82,8 @@ class InspectionDeviceState extends Equatable {
inspectionHistory,
inspectionItems,
icon,
isLoading,
error,
];
}
......
......@@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/repositories/inspection_device_repository.dart';
import 'package:smart_hotel_app/services/inspection_device_service.dart';
import 'cubit/inspection_device_cubit.dart';
import 'cubit/inspection_device_state.dart';
import 'widget/device_overview_card.dart';
......@@ -19,7 +21,12 @@ class InspectionDeviceView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionDeviceCubit(deviceId: deviceId),
create: (_) => InspectionDeviceCubit(
deviceId: deviceId,
service: InspectionDeviceService(
repository: InspectionDeviceRepository(),
),
),
child: Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
......
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:smart_hotel_app/blocs/auth/auth_bloc.dart';
import 'package:smart_hotel_app/blocs/auth/auth_state.dart';
import 'package:smart_hotel_app/repositories/inspection_device_repository.dart';
import 'package:smart_hotel_app/routes/app_router.gr.dart';
import 'package:smart_hotel_app/services/inspection_device_service.dart';
class StartInspectionButton extends StatelessWidget {
final String deviceId;
......@@ -11,8 +16,15 @@ class StartInspectionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<AuthBloc, AuthState>(
builder: (context, authState) {
return GestureDetector(
onTap: () async {
final inspectorId =
authState is AuthSuccess ? authState.userInfo.userId : 0;
if (inspectorId == 0) return;
if (context.mounted) {
showDialog(
context: context,
barrierDismissible: false,
......@@ -44,13 +56,30 @@ class StartInspectionButton extends StatelessWidget {
),
),
);
await Future.delayed(const Duration(seconds: 3));
}
final service = InspectionDeviceService(
repository: InspectionDeviceRepository(),
);
try {
await service.startInspection(
deviceId: int.tryParse(deviceId) ?? 0,
inspectorId: inspectorId,
);
if (context.mounted) {
context.popRoute();
context.pushRoute(
InspectionHistoryRoute(deviceId: deviceId),
);
}
} catch (e) {
if (context.mounted) {
context.popRoute();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('巡检失败: $e')),
);
}
}
},
child: Container(
width: double.infinity,
......@@ -71,5 +100,7 @@ class StartInspectionButton extends StatelessWidget {
),
),
);
},
);
}
}
\ No newline at end of file
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'inspection_history_state.dart';
import '../../../../services/inspection_history_service.dart';
class InspectionHistoryCubit extends Cubit<InspectionHistoryState> {
InspectionHistoryCubit({required String deviceId})
: super(InspectionHistoryState(
final InspectionHistoryService _service;
final int _deviceIdInt;
InspectionHistoryCubit({
required String deviceId,
required InspectionHistoryService service,
}) : _service = service,
_deviceIdInt = int.tryParse(deviceId) ?? 0,
super(InspectionHistoryState(
deviceId: deviceId,
totalCount: 0,
passCount: 0,
......@@ -12,66 +20,45 @@ class InspectionHistoryCubit extends Cubit<InspectionHistoryState> {
failCount: 0,
records: [],
)) {
_loadHistoryData(deviceId);
loadHistoryData();
}
void _loadHistoryData(String deviceId) {
// TODO: 从API或本地数据库加载巡检历史数据
// 这里使用模拟数据
final mockRecords = [
InspectionRecord(
deviceName: '客厅主控网关',
inspectorName: '张师傅',
inspectionTime: '2024-01-15 14:30:00',
result: '通过',
remark: '设备运行正常,各项指标正常',
items: [
const InspectionItemRecord(name: '电源状态', result: '通过'),
const InspectionItemRecord(name: '网络连接', result: '通过'),
const InspectionItemRecord(name: 'CPU使用率', result: '通过'),
const InspectionItemRecord(name: '内存占用', result: '通过'),
],
icon: Icons.wifi,
),
InspectionRecord(
deviceName: '智能摄像头-门口',
inspectorName: '王师傅',
inspectionTime: '2024-01-15 10:00:00',
result: '警告',
remark: '画面有轻微噪点,可能需要清洁镜头',
items: [
const InspectionItemRecord(name: '画面清晰度', result: '警告'),
const InspectionItemRecord(name: '夜视功能', result: '通过'),
const InspectionItemRecord(name: '录像存储', result: '通过'),
const InspectionItemRecord(name: '云台控制', result: '通过'),
],
icon: Icons.videocam,
),
InspectionRecord(
deviceName: '厨房智能插座',
inspectorName: '李师傅',
inspectionTime: '2024-01-15 10:00:00',
result: '不通过',
remark: '插座过热保护触发,需更换',
items: [
const InspectionItemRecord(name: '温度检测', result: '不通过'),
const InspectionItemRecord(name: '负载功率', result: '警告'),
const InspectionItemRecord(name: '过流保护', result: '通过'),
const InspectionItemRecord(name: '电源开关', result: '通过'),
],
icon: Icons.power,
),
];
Future<void> loadHistoryData() async {
emit(state.copyWith(isLoading: true, error: null));
try {
final result = await _service.getList(
pageSize: 10,
pageNum: 1,
deviceId: _deviceIdInt,
);
final mockState = InspectionHistoryState(
deviceId: deviceId,
totalCount: mockRecords.length,
passCount: mockRecords.where((r) => r.result == '通过').length,
warningCount: mockRecords.where((r) => r.result == '警告').length,
failCount: mockRecords.where((r) => r.result == '不通过').length,
records: mockRecords,
final records = result.rows.map((row) {
return InspectionRecord(
deviceName: result.deviceInfo.deviceName,
inspectorName: row.inspectorName,
inspectionTime: row.inspectTime,
result: row.inspectStatus,
remark: row.remark,
items: row.itemsDetail
.map((item) => InspectionItemRecord(
name: item.name,
result: item.status,
))
.toList(),
icon: Icons.devices,
);
}).toList();
emit(mockState);
emit(state.copyWith(
isLoading: false,
totalCount: result.tabCount.total,
passCount: result.tabCount.passCount,
warningCount: result.tabCount.warnCount,
failCount: result.tabCount.failCount,
records: records,
));
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
}
}
\ No newline at end of file
......@@ -8,6 +8,8 @@ class InspectionHistoryState extends Equatable {
final int warningCount;
final int failCount;
final List<InspectionRecord> records;
final bool isLoading;
final String? error;
const InspectionHistoryState({
required this.deviceId,
......@@ -16,6 +18,8 @@ class InspectionHistoryState extends Equatable {
required this.warningCount,
required this.failCount,
required this.records,
this.isLoading = false,
this.error,
});
InspectionHistoryState copyWith({
......@@ -25,6 +29,8 @@ class InspectionHistoryState extends Equatable {
int? warningCount,
int? failCount,
List<InspectionRecord>? records,
bool? isLoading,
String? error,
}) {
return InspectionHistoryState(
deviceId: deviceId ?? this.deviceId,
......@@ -33,6 +39,8 @@ class InspectionHistoryState extends Equatable {
warningCount: warningCount ?? this.warningCount,
failCount: failCount ?? this.failCount,
records: records ?? this.records,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
......@@ -44,6 +52,8 @@ class InspectionHistoryState extends Equatable {
warningCount,
failCount,
records,
isLoading,
error,
];
}
......
......@@ -2,6 +2,8 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:smart_hotel_app/repositories/inspection_history_repository.dart';
import 'package:smart_hotel_app/services/inspection_history_service.dart';
import 'cubit/inspection_history_cubit.dart';
import 'cubit/inspection_history_state.dart';
import 'widget/statistics_card.dart';
......@@ -16,7 +18,12 @@ class InspectionHistoryView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => InspectionHistoryCubit(deviceId: deviceId),
create: (_) => InspectionHistoryCubit(
deviceId: deviceId,
service: InspectionHistoryService(
repository: InspectionHistoryRepository(),
),
),
child: Scaffold(
backgroundColor: const Color.fromRGBO(242, 243, 245, 1),
appBar: AppBar(
......
# 智慧酒店 App 接口对接方案
# 智慧酒店 App 接口对接方案
......@@ -97,8 +97,10 @@ class XxxCubit extends Cubit<XxxState> {
class XxxCubit extends Cubit<XxxState> {
final XxxService _service;
XxxCubit({required XxxService service})
: _service = service,
XxxCubit()
: _service = XxxService(
repository:XxxRepository(),
),
super(const XxxState()) {
loadData();
}
......@@ -197,26 +199,245 @@ class xxxBO extends Equatable {
| 设备巡检-设备列表 |GET | /app/device/inspection | 已对接 | 设备巡检 | 相关页面:DeviceList |
| 设备拓扑图 |GET | /app/device/detail | 对接 | 设备拓扑图 | 相关页面:InspectionDeviceView |
| 设备拓扑图 |GET | /app/device/detail | 对接 | 设备拓扑图 | 相关页面:InspectionDeviceView |
| 设备拓扑图-开始巡检 |POST | /app/device/inspection/start | 对接 | 设备拓扑图-开始巡检 | 相关页面:InspectionDeviceView |
| 设备拓扑图-开始巡检 |POST | /app/device/inspection/start | 对接 | 设备拓扑图-开始巡检 | 相关页面:InspectionDeviceView |
| 巡检记录 |GET | /app/device/inspection/list | 对接 | 设备拓扑图 | 相关页面:InspectionHistoryView |
| 巡检记录 |GET | /app/device/inspection/list | 对接 | 设备拓扑图 | 相关页面:InspectionHistoryView |
| 获取当前登录用户信息 |GET | /app/auth/user-info | 对接 | 获取当前登录用户信息 | |
| 获取当前登录用户信息 |GET | /app/auth/user-info | 对接 | 获取当前登录用户信息 | |
----
| 客房服务看板-客房状态总览-楼层 |GET | /app/room/floor/areas | 未对接 | 客房服务看板-客房状态总览-楼层 | |
| 客房服务看板-客房状态总览-房间list |GET | /app/room/status | 未对接 | 客房服务看板-客房状态总览-房间list | |
| 客房服务看板-送电 |POST | /app/room/device/power/on | 未对接 | 客房服务看板-送电 | |
| 客房服务看板-断电 |POST | /app/room/device/power/off | 未对接 | 客房服务看板-断电 | |
| 客房详情 |GET | /app/room/detail | 未对接 | 客房详情 | |
| 客房详情-房间总电源-switch开 |GET | /app/room/power/on | 未对接 | 客房详情-房间总电源-switch开 | |
| 客房详情-房间总电源-switch关 |GET | /app/room/power/off | 未对接 | 客房详情-房间总电源-switch关 | |
### 3.2 接口详细定义
### 3.2 接口详细定义
#### 客房服务看板-客房状态总览-楼层
**GET /app/room/floor/areas**
```
求体:
{
}
应 data:
{
"code": 0,
"msg": "string",
"data": [
{
"floorId": 0,
"areas": [ // 客房,大堂,过道等等
"string"
],
"rooms": [
{
"roomId": 0,
"roomName": "string"
}
]
}
]
}
```
#### 客房详情-房间总电源-switch关
**GET /app/room/power/off**
```
求体:
{
"roomId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"success": true,
"affectedDeviceCount": 0
}
}
```
#### 客房详情-房间总电源-switch开
**GET /app/room/power/on**
```
求体:
{
"roomId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"success": true,
"affectedDeviceCount": 0
}
}
```
#### 客房详情
**GET /app/room/detail**
```
求体:
{
"roomId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"roomBasicInfo": {
"roomId": 0,
"roomNumber": "string",
"occupancyStatus": "string",
"powerSupplyStatus": true,
"wifiOnlineStatus": "string"
},
"deviceStatusList": [
{
"deviceId": 0,
"deviceName": "string",
"deviceTypeName": "string",
"deviceTypeIcon": "string",
"powerStatus": "string",
"powerStatusText": "string",
"runStatus": "string",
"runStatusText": "string",
"deviceParams": {
"temperature": "string",
"targetTemperature": "string",
"mode": "string",
"fanSpeed": "string"
}
}
],
"deviceControl": {
"roomPowerControl": {
"roomId": 0,
"powerOn": true,
"description": "string"
},
"subDeviceControls": [
{
"deviceId": 0,
"deviceName": "string",
"deviceTypeName": "string",
"powerOn": true,
"currentParamDisplay": "string"
}
]
}
}
}
```
#### 客房服务看板-断电
**POST /app/room/device/power/off**
```
求体:
{
"deviceId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"success": true,
"affectedDeviceCount": 0
}
}
```
#### 客房服务看板-送电
**POST /app/room/device/power/on**
```
求体:
{
"deviceId": 0
}
应 data:
{
"code": 0,
"msg": "string",
"data": {
"success": true,
"affectedDeviceCount": 0
}
}
```
#### 客房服务看板-客房状态总览-房间list
**GET /app/room/status**
```
求体:
{
"roomId": "string", // 房间Id 可选
"floorId": "string", // 楼层Id 可选
"areaType": "string" // 区域类型 可选
}
应 data:
{
"code": 0,
"msg": "string",
"data": [
{
"roomId": 0,
"roomNumber": "string",
"occupancyStatus": "string",
"roomStatus": "string",
"cleanStatus": "string",
"devicePowerStatus": "string"
}
]
}
```
#### 登录
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment