feat: 新增勘察派单功能及拜访详情页操作
新增客户空间拓扑API、勘察明细模型,重构勘察接口,新增拜访详情页操作逻辑及新建勘察派单页面
This commit is contained in:
1 parent
7ad604ff8b
commit
83df770eaa
10 files changed
+2788
-529
No files matched your search
@@ -9,23 +9,6 @@ final surveyApiProvider = Provider<SurveyApi>(
|
||||
(ref) => SurveyApi(ref.watch(apiClientProvider)),
|
||||
);
|
||||
|
||||
/// SOP 回执增量保存结果。
|
||||
class SopSaveResult {
|
||||
const SopSaveResult({required this.updated, required this.skipped, required this.sopResults});
|
||||
|
||||
final int updated;
|
||||
final List<int> skipped;
|
||||
final List<SurveySopResult> sopResults;
|
||||
|
||||
factory SopSaveResult.fromJson(Map<String, dynamic> json) => SopSaveResult(
|
||||
updated: (json['updated'] as num?)?.toInt() ?? 0,
|
||||
skipped: (json['skipped'] as List? ?? []).map((e) => (e as num).toInt()).toList(),
|
||||
sopResults: (json['sopResults'] as List? ?? [])
|
||||
.map((e) => SurveySopResult.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 勘察工单接口,对应 uni-app 版 `api/survey.ts`。
|
||||
class SurveyApi {
|
||||
SurveyApi(this._client);
|
||||
@@ -77,6 +60,14 @@ class SurveyApi {
|
||||
parse: (d) => SurveyItem.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
/// 新建勘察派单:workerId 可省略,后端默认落「待指派(0)」。
|
||||
Future<ApiResponse<SurveyItem>> create(Map<String, dynamic> body) =>
|
||||
_client.post<SurveyItem>(
|
||||
'/survey-orders',
|
||||
body: body,
|
||||
parse: (d) => SurveyItem.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
Future<ApiResponse<SurveyItem>> update(int id, Map<String, dynamic> body) =>
|
||||
_client.put<SurveyItem>(
|
||||
'/survey-orders/$id',
|
||||
@@ -84,12 +75,15 @@ class SurveyApi {
|
||||
parse: (d) => SurveyItem.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
|
||||
/// 按 SOP 检查项增量保存现场回执;
|
||||
/// 仅现场勘察中(status=1)且工单对当前用户可见时后端才放行。
|
||||
Future<ApiResponse<SopSaveResult>> saveSopResults(int id, List<SopResultPayload> results) =>
|
||||
_client.put<SopSaveResult>(
|
||||
'/survey-orders/$id/sop-results',
|
||||
body: {'results': results.map((e) => e.toJson()).toList()},
|
||||
parse: (d) => SopSaveResult.fromJson((d as Map).cast<String, dynamic>()),
|
||||
);
|
||||
/// 全量替换工单勘察明细(新增/编辑/删除统一走此接口,返回最新明细)。
|
||||
Future<ApiResponse<List<SurveyOrderDetail>>> saveDetails(
|
||||
int id,
|
||||
List<SurveyOrderDetail> details,
|
||||
) => _client.put<List<SurveyOrderDetail>>(
|
||||
'/survey-orders/$id/details',
|
||||
body: {'details': details.map((e) => e.toJson()).toList()},
|
||||
parse: (d) => (d as List? ?? [])
|
||||
.map((e) => SurveyOrderDetail.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
@@ -94,69 +94,62 @@ class SurveyWorker {
|
||||
);
|
||||
}
|
||||
|
||||
class SurveySopResult {
|
||||
const SurveySopResult({
|
||||
required this.id,
|
||||
required this.surveyOrderId,
|
||||
required this.sopItemId,
|
||||
required this.checked,
|
||||
required this.itemName,
|
||||
this.itemDesc = '',
|
||||
this.receiptType = '',
|
||||
this.receiptRequire = '',
|
||||
this.required = false,
|
||||
this.receiptValue = const [],
|
||||
this.receiptUrls = const [],
|
||||
/// 勘察明细:一条 = 客户空间拓扑中的 建筑 + 楼层 + 房间(选填) + 现场照片 + 备注。
|
||||
class SurveyOrderDetail {
|
||||
const SurveyOrderDetail({
|
||||
this.id,
|
||||
this.surveyOrderId,
|
||||
this.buildingId,
|
||||
this.buildingName = '',
|
||||
this.floorNo = '',
|
||||
this.roomId,
|
||||
this.roomName = '',
|
||||
this.photos = const [],
|
||||
this.photoUrls = const [],
|
||||
this.remark = '',
|
||||
});
|
||||
|
||||
final int id;
|
||||
final int surveyOrderId;
|
||||
final int sopItemId;
|
||||
final bool checked;
|
||||
final String itemName;
|
||||
final String itemDesc;
|
||||
final String receiptType;
|
||||
final String receiptRequire;
|
||||
final bool required;
|
||||
final int? id;
|
||||
final int? surveyOrderId;
|
||||
final int? buildingId;
|
||||
|
||||
/// 回执内容:photo/meter 类为照片对象 key 数组,form 类为单元素文本值数组。
|
||||
final List<String> receiptValue;
|
||||
/// 建筑名称(冗余存储,客户拓扑变更后仍可读)。
|
||||
final String buildingName;
|
||||
|
||||
/// 详情接口附带:照片类回执的预签名访问 URL(与 receiptValue 顺序一致)。
|
||||
final List<String> receiptUrls;
|
||||
/// 楼层:正数为地上、负数为地下,如 `"1"` / `"-1"`。
|
||||
final String floorNo;
|
||||
final int? roomId;
|
||||
final String roomName;
|
||||
|
||||
/// 现场照片对象 key。
|
||||
final List<String> photos;
|
||||
|
||||
/// 详情接口附带:照片预签名访问 URL(与 photos 顺序一致)。
|
||||
final List<String> photoUrls;
|
||||
final String remark;
|
||||
|
||||
factory SurveySopResult.fromJson(Map<String, dynamic> json) => SurveySopResult(
|
||||
id: toIntOr(json['id'], 0),
|
||||
surveyOrderId: toIntOr(json['surveyOrderId'], 0),
|
||||
sopItemId: toIntOr(json['sopItemId'], 0),
|
||||
checked: json['checked'] == true,
|
||||
itemName: json['itemName'] as String? ?? '',
|
||||
itemDesc: json['itemDesc'] as String? ?? '',
|
||||
receiptType: json['receiptType'] as String? ?? '',
|
||||
receiptRequire: json['receiptRequire'] as String? ?? '',
|
||||
required: json['required'] == true,
|
||||
receiptValue: (json['receiptValue'] as List? ?? []).map((e) => e.toString()).toList(),
|
||||
receiptUrls: (json['receiptUrls'] as List? ?? []).map((e) => e.toString()).toList(),
|
||||
factory SurveyOrderDetail.fromJson(Map<String, dynamic> json) => SurveyOrderDetail(
|
||||
id: toIntOrNull(json['id']),
|
||||
surveyOrderId: toIntOrNull(json['surveyOrderId']),
|
||||
buildingId: toIntOrNull(json['buildingId']),
|
||||
buildingName: json['buildingName'] as String? ?? '',
|
||||
floorNo: json['floorNo'] == null ? '' : json['floorNo'].toString(),
|
||||
roomId: toIntOrNull(json['roomId']),
|
||||
roomName: json['roomName'] as String? ?? '',
|
||||
photos: (json['photos'] as List? ?? []).map((e) => e.toString()).toList(),
|
||||
photoUrls: (json['photoUrls'] as List? ?? []).map((e) => e.toString()).toList(),
|
||||
remark: json['remark'] as String? ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
/// 逐项回执的增量提交体:只传需要变更的 SOP 项,未传项保持原值。
|
||||
class SopResultPayload {
|
||||
const SopResultPayload({required this.sopItemId, this.checked, this.receiptValue, this.remark});
|
||||
|
||||
final int sopItemId;
|
||||
final bool? checked;
|
||||
final List<String>? receiptValue;
|
||||
final String? remark;
|
||||
|
||||
/// 提交体:只带定位要素与照片/备注,行 id 由后端全量重建。
|
||||
Map<String, dynamic> toJson() => {
|
||||
'sopItemId': sopItemId,
|
||||
if (checked != null) 'checked': checked,
|
||||
if (receiptValue != null) 'receiptValue': receiptValue,
|
||||
if (remark != null) 'remark': remark,
|
||||
'buildingId': buildingId,
|
||||
'buildingName': buildingName,
|
||||
'floorNo': floorNo,
|
||||
'roomId': roomId,
|
||||
'roomName': roomName,
|
||||
'photos': photos,
|
||||
'remark': remark,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -204,7 +197,7 @@ class SurveyItem {
|
||||
this.reviewedAt,
|
||||
this.customer,
|
||||
this.worker,
|
||||
this.sopResults,
|
||||
this.details,
|
||||
});
|
||||
|
||||
final int id;
|
||||
@@ -251,8 +244,8 @@ class SurveyItem {
|
||||
final String salesmanName;
|
||||
final String salesmanPhone;
|
||||
|
||||
// 仅详情接口返回
|
||||
final List<SurveySopResult>? sopResults;
|
||||
// 仅详情接口返回:勘察明细(建筑/楼层/房间 + 现场照片)
|
||||
final List<SurveyOrderDetail>? details;
|
||||
|
||||
factory SurveyItem.fromJson(Map<String, dynamic> json) => SurveyItem(
|
||||
id: toIntOr(json['id'], 0),
|
||||
@@ -300,10 +293,10 @@ class SurveyItem {
|
||||
workerName: json['workerName'] as String? ?? '',
|
||||
salesmanName: json['salesmanName'] as String? ?? '',
|
||||
salesmanPhone: json['salesmanPhone'] as String? ?? '',
|
||||
sopResults: json['sopResults'] == null
|
||||
details: json['details'] == null
|
||||
? null
|
||||
: (json['sopResults'] as List)
|
||||
.map((e) => SurveySopResult.fromJson((e as Map).cast<String, dynamic>()))
|
||||
: (json['details'] as List)
|
||||
.map((e) => SurveyOrderDetail.fromJson((e as Map).cast<String, dynamic>()))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
|
||||
Reference in new issue
Block a user