1,fix提测问题:操作记录部分bug

2,新增操作记录—查看导出记录页面
3,新增导出锁记录API对接
4,新增批量导出操作记录API对接
This commit is contained in:
Daisy 2024-06-21 09:15:38 +08:00
parent b237d827ac
commit 50f0cc554a
15 changed files with 360 additions and 17 deletions

View File

@ -1,6 +1,48 @@
import 'dart:io';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/batchExportLog/batchExportLog_state.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecord_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
class BatchExportLogLogic extends BaseGetXController {
BatchExportLogState state = BatchExportLogState();
//-
Future<void> exportLockRecordsRequest() async {
final ExportRecordEntity entity =
await ApiRepository.to.batchExportLockRecords(
lockIds: state.lockIdList,
startDate:
DateTime.tryParse(state.beginTime.value)!.millisecondsSinceEpoch,
endDate: DateTime.tryParse(state.endTime.value)!.millisecondsSinceEpoch,
);
if (entity.errorCode!.codeIsSuccessful) {
final String url = entity.data!.fileUrl!;
//
final String filePath = await downloadAndSaveFile(url);
Get.toNamed(Routers.exportSuccessPage,
arguments: <String, String>{'filePath': filePath});
} else {
throw 'Export failed with error code ${entity.errorCode}';
}
}
//
Future<String> downloadAndSaveFile(String url) async {
final http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/exported_file.pdf');
await file.writeAsBytes(response.bodyBytes);
return file.path;
} else {
throw 'Failed to download file';
}
}
}

View File

@ -75,7 +75,7 @@ class _BatchExportLogPageState extends State<BatchExportLogPage>
logic.showToast('请选择锁'.tr);
return;
}
Get.toNamed(Routers.exportSuccessPage);
logic.exportLockRecordsRequest();
}),
],
),

View File

@ -141,8 +141,9 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
context: context,
builder: (BuildContext context) {
return ExportRecordDialog(
onExport: () {
Get.toNamed(Routers.exportSuccessPage);
onExport: (String filePath) {
Get.toNamed(Routers.exportSuccessPage,
arguments: <String, String>{'filePath': filePath});
},
);
},
@ -280,7 +281,7 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'$formattedTime ${timelineData.username!.isNotEmpty ? "${timelineData.username}" : ""}${timelineData.recordTypeName}',
'$formattedTime ${timelineData.username!.isNotEmpty ? "${timelineData.username}操作" : ""}${timelineData.recordTypeName}',
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.black,

View File

@ -1,9 +1,17 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecord_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
import 'package:star_lock/tools/commonDataManage.dart';
import 'package:star_lock/tools/pickers/pickers.dart';
import 'package:star_lock/tools/pickers/time_picker/model/date_mode.dart';
import 'package:star_lock/tools/pickers/time_picker/model/pduration.dart';
@ -13,7 +21,7 @@ import 'package:star_lock/tools/submitBtn.dart';
class ExportRecordDialog extends StatelessWidget {
const ExportRecordDialog({required this.onExport, Key? key})
: super(key: key);
final VoidCallback onExport;
final Function(String) onExport;
@override
Widget build(BuildContext context) {
@ -40,7 +48,7 @@ class ExportRecordDialog extends StatelessWidget {
class _DerivedRecordWidget extends StatefulWidget {
const _DerivedRecordWidget({required this.onExport, Key? key})
: super(key: key);
final VoidCallback onExport;
final Function(String) onExport;
@override
__DerivedRecordWidgetState createState() => __DerivedRecordWidgetState();
@ -190,9 +198,38 @@ class __DerivedRecordWidgetState extends State<_DerivedRecordWidget> {
return;
}
//
exportLockRecordsRequest();
}
//
widget.onExport();
//-
Future<void> exportLockRecordsRequest() async {
final ExportRecordEntity entity = await ApiRepository.to.exportLockRecords(
lockId: CommonDataManage().currentKeyInfo.lockId ?? 0,
startDate: startDate!.millisecondsSinceEpoch,
endDate: endDate!.millisecondsSinceEpoch,
);
if (entity.errorCode!.codeIsSuccessful) {
final String url = entity.data!.fileUrl!;
//
final String filePath = await downloadAndSaveFile(url);
widget.onExport(filePath);
// return url;
} else {
throw 'Export failed with error code ${entity.errorCode}';
}
}
//
Future<String> downloadAndSaveFile(String url) async {
final http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final Directory directory = await getApplicationDocumentsDirectory();
final File file = File('${directory.path}/exported_file.xlsx');
await file.writeAsBytes(response.bodyBytes);
return file.path;
} else {
throw 'Failed to download file';
}
}
}

View File

@ -0,0 +1,40 @@
class ExportRecordEntity {
ExportRecordEntity(
{this.errorCode, this.description, this.errorMsg, this.data});
ExportRecordEntity.fromJson(Map<String, dynamic> json) {
errorCode = json['errorCode'];
description = json['description'];
errorMsg = json['errorMsg'];
data = json['data'] != null ? Data.fromJson(json['data']) : null;
}
int? errorCode;
String? description;
String? errorMsg;
Data? data;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['errorCode'] = errorCode;
data['description'] = description;
data['errorMsg'] = errorMsg;
if (this.data != null) {
data['data'] = this.data!.toJson();
}
return data;
}
}
class Data {
Data({this.fileUrl});
Data.fromJson(Map<String, dynamic> json) {
fileUrl = json['fileUrl'];
}
String? fileUrl;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['fileUrl'] = fileUrl;
return data;
}
}

View File

@ -1,13 +1,19 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportSuccess/exportSuccess_logic.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportSuccess/exportSuccess_state.dart';
import 'package:star_lock/tools/NativeInteractionTool.dart';
import 'package:star_lock/tools/commonDataManage.dart';
import 'package:star_lock/tools/submitBtn.dart';
import 'package:star_lock/tools/titleAppBar.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:open_file/open_file.dart';
class ExportSuccessPage extends StatefulWidget {
const ExportSuccessPage({Key? key}) : super(key: key);
@ -76,7 +82,20 @@ class _ExportSuccessPageState extends State<ExportSuccessPage> with RouteAware {
SubmitBtn(
btnName: '立即查看'.tr,
onClick: () {
Get.toNamed(Routers.viewExportRecordPage);
// OpenFile.open(state.getFilePath.value);
NativeInteractionTool()
.loadNativeFileShare(shareText: state.getFilePath.value);
// openFile(state.getFilePath.value);
// previewFile();
// Get.toNamed(Routers.viewExportRecordPage,
// arguments: <String, Object>{
// 'filePath': state.getFilePath.value,
// });
// Get.toNamed(Routers.webviewShowPage, arguments: <String, Object>{
// 'url': state.getFilePath.value,
// 'title': '查看操作记录'.tr
// });
}),
SizedBox(
height: 20.h,
@ -91,6 +110,34 @@ class _ExportSuccessPageState extends State<ExportSuccessPage> with RouteAware {
);
}
// Future<void> openFile(String filePath) async {
// final File file = File(filePath);
// if (await file.exists()) {
// await launchUrl(Uri.parse('file://$filePath'));
// } else {
// throw 'File not found';
// }
// }
Future<void> previewFile() async {
//
final Directory appDocDir = await getApplicationDocumentsDirectory();
final String appDocPath = appDocDir.path;
final String filePath = '$appDocPath/exported_file.xlsx';
//
final File file = File(filePath);
if (await file.exists()) {
// 使url_launcher打开文件
final bool launched = await launchUrl(Uri.parse(filePath));
if (!launched) {
throw 'Could not launch $filePath';
}
} else {
print('File does not exist');
}
}
Future<void> _openModalBottomSheet() async {
showModalBottomSheet(
context: context,

View File

@ -1 +1,10 @@
class ExportSuccessState {}
import 'package:get/get.dart';
class ExportSuccessState {
ExportSuccessState() {
final Map map = Get.arguments;
getFilePath.value = map['filePath'];
}
RxString getFilePath = ''.obs;
}

View File

@ -0,0 +1,6 @@
import 'package:star_lock/main/lockDetail/doorLockLog/viewExportRecord/viewExportRecord_state.dart';
import 'package:star_lock/tools/baseGetXController.dart';
class ViewExportRecordLogic extends BaseGetXController {
ViewExportRecordState state = ViewExportRecordState();
}

View File

@ -0,0 +1,93 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:excel/excel.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/viewExportRecord/viewExportRecord_logic.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/viewExportRecord/viewExportRecord_state.dart';
import 'package:star_lock/tools/titleAppBar.dart';
class ViewExportRecordPage extends StatefulWidget {
const ViewExportRecordPage({Key? key}) : super(key: key);
@override
State<ViewExportRecordPage> createState() => _ViewExportRecordPageState();
}
class _ViewExportRecordPageState extends State<ViewExportRecordPage>
with RouteAware {
final ViewExportRecordLogic logic = Get.put(ViewExportRecordLogic());
final ViewExportRecordState state = Get.find<ViewExportRecordLogic>().state;
@override
void initState() {
super.initState();
loadExcel();
}
Future<void> loadExcel() async {
try {
final Directory appDocDir = await getApplicationDocumentsDirectory();
final String appDocPath = appDocDir.path;
final String filePath = '$appDocPath/exported_file.xlsx';
final Uint8List bytes = File(filePath).readAsBytesSync();
final Excel excel = Excel.decodeBytes(bytes);
for (final String table in excel.tables.keys) {
for (final List<Data?> row in excel.tables[table]!.rows) {
setState(() {
state.excelData.add(row);
});
}
}
} catch (e) {
print('Error reading Excel: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.mainBackgroundColor,
appBar: TitleAppBar(
barTitle: '查看导出记录'.tr,
haveBack: true,
backgroundColor: AppColors.mainColor,
),
body: Center(
child: state.excelData.isEmpty
? const CircularProgressIndicator()
: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: DataTable(
columns: List.generate(
state.excelData[0].length,
(int index) => DataColumn(
label: Text('${state.excelData[0][index]}'),
),
),
rows: List.generate(
state.excelData.length - 1,
(int index) => DataRow(
cells: List.generate(
state.excelData[index + 1].length,
(int cellIndex) => DataCell(
Text('${state.excelData[index + 1][cellIndex]}'),
),
),
),
),
),
),
),
),
);
}
}

View File

@ -0,0 +1,11 @@
import 'package:get/get.dart';
class ViewExportRecordState {
ViewExportRecordState() {
final Map map = Get.arguments;
getFilePath.value = map['filePath'];
}
RxString getFilePath = ''.obs;
List<List<dynamic>> excelData = [];
}

View File

@ -257,4 +257,5 @@ abstract class Api {
final String keyNoticeTemplateURL = '/key/getNoticeTemplate'; //
final String keyNoticeSubmitURL = '/key/noticeSubmit'; //
final String lockUpdateLockInfo = '/lock/updateLockInfo'; //
final String exportLockRecordsURL = '/lockRecords/export'; //
}

View File

@ -397,8 +397,12 @@ class ApiProvider extends BaseProvider {
}));
//
Future<Response> getStarLockListInfo(int pageNo, int pageSize,
{bool isUnShowLoading = true, int? keyId,}) =>
Future<Response> getStarLockListInfo(
int pageNo,
int pageSize, {
bool isUnShowLoading = true,
int? keyId,
}) =>
post(
getStarLockInfoURL.toUrl,
jsonEncode(<String, dynamic>{
@ -1460,9 +1464,7 @@ class ApiProvider extends BaseProvider {
//
Future<Response> getTransferLockListData(String searchStr) =>
post(transferLockListURL.toUrl, jsonEncode({
"searchStr":searchStr
}));
post(transferLockListURL.toUrl, jsonEncode({"searchStr": searchStr}));
//
Future<Response> transferLockConfirmInfoData(
@ -2284,6 +2286,34 @@ class ApiProvider extends BaseProvider {
'fwVersion': fwVersion,
}),
);
Future<Response<dynamic>> exportLockRecords(
int lockId,
int startDate,
int endDate,
) =>
post(
exportLockRecordsURL.toUrl,
jsonEncode(<String, dynamic>{
'lockId': lockId,
'startDate': startDate,
'endDate': endDate,
}),
);
Future<Response<dynamic>> batchExportLockRecords(
List<int> lockIds,
int startDate,
int endDate,
) =>
post(
exportLockRecordsURL.toUrl,
jsonEncode(<String, dynamic>{
'lockIds': lockIds,
'startDate': startDate,
'endDate': endDate,
}),
);
}
extension ExtensionString on String {

View File

@ -4,6 +4,7 @@ import 'package:star_lock/login/login/app_get_version.dart';
import 'package:star_lock/login/selectCountryRegion/common/countryRegionEntity.dart';
import 'package:star_lock/main/lockDetail/authorizedAdmin/authorizedAdmin/notice_template_entity.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_entity.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecord_entity.dart';
import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyEntity.dart';
import 'package:star_lock/main/lockDetail/electronicKey/electronicKeyList/entity/ElectronicKeyListEntity.dart';
import 'package:star_lock/main/lockDetail/electronicKey/massSendElectronicKey/massSendLockGroupList/lockUserList/lockUserList_entity.dart';
@ -1653,7 +1654,8 @@ class ApiRepository {
}
//
Future<TransferSmartLockEntity> getTransferLockListData({required String searchStr}) async {
Future<TransferSmartLockEntity> getTransferLockListData(
{required String searchStr}) async {
final res = await apiProvider.getTransferLockListData(searchStr);
return TransferSmartLockEntity.fromJson(res.body);
}
@ -2298,4 +2300,26 @@ class ApiRepository {
await apiProvider.getLockUpdateLockInfo(lockId, fwVersion);
return UpdateLockInfoEntity.fromJson(res.body);
}
//
Future<ExportRecordEntity> exportLockRecords({
required int lockId,
required int startDate,
required int endDate,
}) async {
final Response<dynamic> res =
await apiProvider.exportLockRecords(lockId, startDate, endDate);
return ExportRecordEntity.fromJson(res.body);
}
//
Future<ExportRecordEntity> batchExportLockRecords({
required List<int> lockIds,
required int startDate,
required int endDate,
}) async {
final Response<dynamic> res =
await apiProvider.batchExportLockRecords(lockIds, startDate, endDate);
return ExportRecordEntity.fromJson(res.body);
}
}