feat: 完成 Flutter 版智能电网 IoT 管理应用初始化搭建
This commit is contained in:
commit
f61b7bf90b
120 files changed
+15229
No files matched your search
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../core/network/api_client.dart';
|
||||
import '../../../core/network/api_response.dart';
|
||||
import '../../../core/providers.dart';
|
||||
import 'project_models.dart';
|
||||
|
||||
final projectApiProvider = Provider<ProjectApi>(
|
||||
(ref) => ProjectApi(ref.watch(apiClientProvider)),
|
||||
);
|
||||
|
||||
/// 智电改造方案接口,对应后端 `/api/project-plans`。
|
||||
///
|
||||
/// 说明:uni-app 版项目模块使用本地 mock(`api/mock.ts`),字段与后端契约完全不一致;
|
||||
/// Flutter 版按用户确认改为直连后端真实接口。
|
||||
class ProjectApi {
|
||||
ProjectApi(this._client);
|
||||
|
||||
final ApiClient _client;
|
||||
|
||||
Future<ApiResponse<ProjectPlanPage>> list({
|
||||
int page = 1,
|
||||
int pageSize = 100,
|
||||
String? keyword,
|
||||
int? customerId,
|
||||
int? status,
|
||||
String? registerStartDate,
|
||||
String? registerEndDate,
|
||||
}) => _client.get<ProjectPlanPage>(
|
||||
'/project-plans',
|
||||
query: {
|
||||
'page': page,
|
||||
'pageSize': pageSize,
|
||||
'keyword': keyword,
|
||||
'customerId': customerId,
|
||||
'status': status,
|
||||
'registerStartDate': registerStartDate,
|
||||
'registerEndDate': registerEndDate,
|
||||
},
|
||||
parse: (d) {
|
||||
final m = (d as Map).cast<String, dynamic>();
|
||||
return ProjectPlanPage(
|
||||
list: (m['list'] as List? ?? [])
|
||||
.map((e) => ProjectPlan.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(),
|
||||
total: (m['total'] as num?)?.toInt() ?? 0,
|
||||
page: (m['page'] as num?)?.toInt() ?? 1,
|
||||
pageSize: (m['pageSize'] as num?)?.toInt() ?? pageSize,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Future<ApiResponse<ProjectPlan>> detail(int id) => _client.get<ProjectPlan>(
|
||||
'/project-plans/$id',
|
||||
parse: (d) => ProjectPlan.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
Future<ApiResponse<ProjectPlan>> create(Map<String, dynamic> body) =>
|
||||
_client.post<ProjectPlan>(
|
||||
'/project-plans',
|
||||
body: body,
|
||||
parse: (d) => ProjectPlan.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
Future<ApiResponse<ProjectPlan>> update(int id, Map<String, dynamic> body) =>
|
||||
_client.put<ProjectPlan>(
|
||||
'/project-plans/$id',
|
||||
body: body,
|
||||
parse: (d) => ProjectPlan.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
Future<ApiResponse<void>> delete(int id) => _client.delete<void>('/project-plans/$id');
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/painting.dart';
|
||||
|
||||
import '../../../core/theme/app_colors.dart';
|
||||
import '../../../core/utils/id_util.dart';
|
||||
|
||||
/// 方案状态:0 草稿 1 已确认。
|
||||
abstract final class ProjectPlanStatus {
|
||||
static const draft = 0;
|
||||
static const confirmed = 1;
|
||||
}
|
||||
|
||||
const projectStatusMap = <int, ({String text, ChipTone tone})>{
|
||||
0: (text: '草稿', tone: ChipTone.warn),
|
||||
1: (text: '已确认', tone: ChipTone.primary),
|
||||
};
|
||||
|
||||
String projectStatusText(int? s) => projectStatusMap[s]?.text ?? '未知';
|
||||
|
||||
ChipTone projectStatusTone(int? s) => projectStatusMap[s]?.tone ?? ChipTone.muted;
|
||||
|
||||
/// 列表卡片左侧色条颜色。
|
||||
Color projectStatusBarColor(int? status) => switch (status) {
|
||||
0 => AppColors.statusWarn,
|
||||
1 => AppColors.primary,
|
||||
_ => AppColors.borderRule,
|
||||
};
|
||||
|
||||
/// 项目方案设备明细,对应后端 `project_plan_detail` 表。
|
||||
class ProjectPlanDetail {
|
||||
const ProjectPlanDetail({
|
||||
required this.id,
|
||||
required this.planId,
|
||||
required this.customerId,
|
||||
required this.equipName,
|
||||
required this.quantity,
|
||||
this.equipId,
|
||||
this.smartDeviceId,
|
||||
this.remark,
|
||||
});
|
||||
|
||||
final int id;
|
||||
final int planId;
|
||||
final int customerId;
|
||||
final String equipName;
|
||||
final num quantity;
|
||||
final int? equipId;
|
||||
final int? smartDeviceId;
|
||||
final String? remark;
|
||||
|
||||
factory ProjectPlanDetail.fromJson(Map<String, dynamic> json) => ProjectPlanDetail(
|
||||
id: toIntOr(json['id'], 0),
|
||||
planId: toIntOr(json['planId'], 0),
|
||||
customerId: toIntOr(json['customerId'], 0),
|
||||
equipName: json['equipName'] as String? ?? '',
|
||||
quantity: (json['quantity'] as num?) ?? 1,
|
||||
equipId: toIntOrNull(json['equipId']),
|
||||
smartDeviceId: toIntOrNull(json['smartDeviceId']),
|
||||
remark: json['remark'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'equipName': equipName,
|
||||
'quantity': quantity,
|
||||
if (equipId != null) 'equipId': equipId,
|
||||
if (smartDeviceId != null) 'smartDeviceId': smartDeviceId,
|
||||
if (remark != null && remark!.isNotEmpty) 'remark': remark,
|
||||
};
|
||||
|
||||
ProjectPlanDetail copyWith({String? equipName, num? quantity, String? remark}) =>
|
||||
ProjectPlanDetail(
|
||||
id: id,
|
||||
planId: planId,
|
||||
customerId: customerId,
|
||||
equipName: equipName ?? this.equipName,
|
||||
quantity: quantity ?? this.quantity,
|
||||
equipId: equipId,
|
||||
smartDeviceId: smartDeviceId,
|
||||
remark: remark ?? this.remark,
|
||||
);
|
||||
}
|
||||
|
||||
/// 智电改造方案,与后端 `/api/project-plans` 契约对齐。
|
||||
class ProjectPlan {
|
||||
const ProjectPlan({
|
||||
required this.id,
|
||||
required this.planNo,
|
||||
required this.customerId,
|
||||
required this.status,
|
||||
this.registrar,
|
||||
this.registerTime,
|
||||
this.planDesc,
|
||||
this.planFileUrl,
|
||||
this.createdAt,
|
||||
this.details = const [],
|
||||
});
|
||||
|
||||
final int id;
|
||||
final String planNo;
|
||||
final int customerId;
|
||||
|
||||
/// 方案登记人(业务员姓名文本)
|
||||
final String? registrar;
|
||||
|
||||
/// 方案登记时间(DATEONLY,`YYYY-MM-DD`)
|
||||
final String? registerTime;
|
||||
final String? planDesc;
|
||||
|
||||
/// 方案最终文件 URL
|
||||
final String? planFileUrl;
|
||||
final String? createdAt;
|
||||
|
||||
/// 设备明细,仅详情接口返回
|
||||
final List<ProjectPlanDetail> details;
|
||||
final int status;
|
||||
|
||||
factory ProjectPlan.fromJson(Map<String, dynamic> json) => ProjectPlan(
|
||||
id: toIntOr(json['id'], 0),
|
||||
planNo: json['planNo'] as String? ?? '',
|
||||
customerId: toIntOr(json['customerId'], 0),
|
||||
registrar: json['registrar'] as String?,
|
||||
registerTime: json['registerTime'] as String?,
|
||||
planDesc: json['planDesc'] as String?,
|
||||
planFileUrl: json['planFileUrl'] as String?,
|
||||
createdAt: json['created_at'] as String?,
|
||||
details: (json['details'] as List? ?? [])
|
||||
.map((e) => ProjectPlanDetail.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(),
|
||||
status: toIntOr(json['status'], 0),
|
||||
);
|
||||
|
||||
/// 设备明细合计数量。
|
||||
num get detailQuantityTotal =>
|
||||
details.fold<num>(0, (sum, d) => sum + d.quantity);
|
||||
|
||||
String get displayTitle => planNo.isNotEmpty ? planNo : '方案 #$id';
|
||||
}
|
||||
|
||||
class ProjectPlanPage {
|
||||
const ProjectPlanPage({
|
||||
required this.list,
|
||||
required this.total,
|
||||
required this.page,
|
||||
required this.pageSize,
|
||||
});
|
||||
|
||||
final List<ProjectPlan> list;
|
||||
final int total;
|
||||
final int page;
|
||||
final int pageSize;
|
||||
}
|
||||
Reference in new issue
Block a user