1,fix提测问题:操作记录部分bug
2,新增操作记录—查看导出记录页面 3,新增导出锁记录API对接 4,新增批量导出操作记录API对接
This commit is contained in:
parent
b237d827ac
commit
50f0cc554a
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class _BatchExportLogPageState extends State<BatchExportLogPage>
|
||||
logic.showToast('请选择锁'.tr);
|
||||
return;
|
||||
}
|
||||
Get.toNamed(Routers.exportSuccessPage);
|
||||
logic.exportLockRecordsRequest();
|
||||
}),
|
||||
],
|
||||
),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -0,0 +1 @@
|
||||
|
||||
@ -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';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1 @@
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -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]}'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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 = [];
|
||||
}
|
||||
@ -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'; //导出锁操作记录
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user