Files
pj_supermarket_cloud/lib/pages/setting/widgets/setting-version-setting.dart
T
2026-02-04 13:22:59 +08:00

408 lines
13 KiB
Dart

import 'dart:convert';
import 'package:background_downloader/background_downloader.dart';
import 'package:common_utils/common_utils.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'dart:io';
import 'package:permission_handler/permission_handler.dart';
import 'package:external_path/external_path.dart';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:forui/forui.dart';
import 'package:get/get.dart';
import 'package:pj_supermarket_cloud/common/index.dart';
class VersionManageController extends GetxController {
static const String _tag = "版本管理 Controller";
final appC = Get.find<AppControl>();
RxInt currentPage = RxInt(1);
RxInt totalPage = RxInt(0);
RxInt pageSize = RxInt(3);
RxList<ServiceAppVersion> versionList = RxList();
@override
void onInit() async {
super.onInit();
await getVersionList();
}
@override
void onClose() {}
/// 搜索
Future<void> doPage({int? page}) async {
if (page != null) currentPage.value = page;
await getVersionList(isLoading: true);
}
/// 获取版本列表
Future<void> getVersionList({bool isLoading = false}) async {
if (isLoading) {
EasyLoading.show(
status: '查询中...',
maskType: EasyLoadingMaskType.black,
);
}
try {
final params = {
"appUniqueCode": Constants.deviceUniqueCode,
};
final data = await AppApi.openCardList(params);
logger.d("|$_tag|获取版本列表成功:${json.encode(data)}");
if (data == null || ObjectUtil.isEmptyList(data.records)) {
return;
}
versionList.value = data.records!;
totalPage.value = data.total;
} finally {
if (isLoading) {
Future.delayed(const Duration(seconds: 1), () {
EasyLoading.dismiss();
});
}
}
}
/// 远程下载apk
Future<void> doDownload(String url) async {
if (url.isEmpty) return;
String downUrl = url;
if (!url.startsWith("http") && !url.startsWith("https")) {
downUrl = "${Constants.ossUrl}/api/admin/sys-file/oss/file?fileName=$url";
}
logger.d("|$_tag|远程下载apk,url:$downUrl");
try {
EasyLoading.show(
status: '下载中...',
maskType: EasyLoadingMaskType.black,
);
if (Platform.isAndroid) {
var hasStorage = await Permission.storage.request().isGranted;
if (!hasStorage) {
EasyLoading.showError("存储权限未授权,无法下载");
return;
}
if (await Permission.manageExternalStorage.isDenied) {
final g = await Permission.manageExternalStorage.request();
hasStorage = hasStorage || g.isGranted;
}
final ext = AppFileTool.getFileExtension(downUrl);
final useExt = ObjectUtil.isEmpty(ext) ? ".apk" : ext;
final now = DateTime.now();
final dateStr = DateUtil.formatDate(
now,
format: DateFormats.full,
);
final fileName =
"pj_xfj_${dateStr}_${now.millisecondsSinceEpoch}$useExt";
final task = DownloadTask(
url: downUrl,
filename: fileName,
updates: Updates.statusAndProgress,
baseDirectory: BaseDirectory.applicationDocuments,
);
final result = await FileDownloader().download(
task,
onProgress: (progress) {
logger.d("|$_tag|下载进度:$progress");
EasyLoading.showProgress(
progress,
status: "(${(progress * 100).toStringAsFixed(2)}%)下载中...",
);
},
onStatus: (status) => {logger.d("|$_tag|下载状态:$status")},
);
if (result.status == TaskStatus.complete) {
final movedPath = await FileDownloader().moveToSharedStorage(
task,
SharedStorage.downloads,
mimeType: useExt == ".apk"
? "application/vnd.android.package-archive"
: "application/octet-stream",
);
if (ObjectUtil.isNotEmpty(movedPath)) {
logger.i("|$_tag|下载完成:$movedPath");
EasyLoading.showSuccess("下载完成");
} else {
EasyLoading.showError("移动到下载目录失败");
}
} else {
EasyLoading.showError("下载失败");
}
} else {
EasyLoading.showInfo("当前平台不支持系统下载");
}
} catch (e) {
logger.e("|$_tag|远程下载apk,下载失败:$e");
EasyLoading.showError("下载失败");
} finally {
EasyLoading.dismiss();
}
}
}
Widget settingVersionSetting(BuildContext context) {
final versionC = Get.put<VersionManageController>(
VersionManageController(),
);
return <Widget>[
buildSettingVersionSettingSearch(context),
buildSettingVersionSettingList(context).expanded(),
Obx(
() => appPage(
context,
pageNo: versionC.currentPage.value,
pageSize: versionC.pageSize.value,
total: versionC.totalPage.value,
onPageChanged: (pageNo) => versionC.doPage(page: pageNo),
),
)
].toColumn();
}
// 版本更新搜索区域
Widget buildSettingVersionSettingSearch(BuildContext context) {
final versionC = Get.find<VersionManageController>();
final theme = FTheme.of(context);
return <Widget>[
const Spacer(),
FButton(
prefix: FIcon(
FAssets.icons.search,
size: theme.typography.base.fontSize,
),
label: Text(
'搜索',
style: theme.typography.base.copyWith(
color: theme.colorScheme.secondary,
),
),
onPress: () => versionC.doPage(),
),
].toRow();
}
// 版本更新列表区域
Widget buildSettingVersionSettingList(BuildContext context) {
final versionC = Get.find<VersionManageController>();
return Obx(() {
if (versionC.versionList.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.info_outline, size: 48, color: Colors.grey),
const SizedBox(height: 10),
Text("暂无版本信息", style: TextStyle(color: Colors.grey[600])),
],
),
);
}
return GridView.builder(
padding: const EdgeInsets.all(10),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
childAspectRatio: 6,
),
itemCount: versionC.versionList.length,
itemBuilder: (context, index) {
final item = versionC.versionList[index];
return buildSettingVersionSettingCardCopy(context, item);
},
);
});
}
Widget buildSettingVersionSettingCardCopy(
BuildContext context,
ServiceAppVersion item,
) {
final versionC = Get.find<VersionManageController>();
final theme = FTheme.of(context);
return <Widget>[
<Widget>[
Text(
"${AppPlatformEnum.values.fromCode(item.platform ?? '')?.title ?? '未知平台'} - ${item.versionName ?? '未知版本'}",
style: theme.typography.lg.copyWith(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
),
SizedBox(width: 20.w),
FBadge(
label: Text(
AppVersionStatusEnum.values.fromCode(item.status ?? '')?.title ??
'未知状态',
style: theme.typography.sm.copyWith(height: 1, color: Colors.white),
).padding(),
),
].toRow(),
SizedBox(height: 10.w),
Text(
"更新内容",
style: theme.typography.base.copyWith(fontWeight: FontWeight.bold),
),
SizedBox(height: 10.w),
Text(
item.updateContent ?? '暂无更新内容',
style: theme.typography.base.copyWith(color: Colors.grey),
),
SizedBox(height: 10.w),
<Widget>[
FIcon(
FAssets.icons.sendHorizontal,
size: theme.typography.base.fontSize,
),
SizedBox(width: 10.w),
Text(
item.downloadUrl ?? '未知下载地址',
style: theme.typography.sm.copyWith(color: Colors.grey),
),
const Spacer(),
FIcon(
FAssets.icons.arrowDownToLine,
size: theme.typography.base.fontSize,
),
]
.toRow()
.padding(horizontal: 20.w, vertical: 15.w)
.decorated(
color: theme.colorScheme.secondary,
border: Border.all(
color: theme.colorScheme.primary,
width: 1,
),
borderRadius: theme.style.borderRadius,
)
.onTap(() => versionC.doDownload(item.downloadUrl ?? '')),
]
.toColumn(crossAxisAlignment: CrossAxisAlignment.start)
.padding(horizontal: 20.w, vertical: 15.w)
.decorated(
color: theme.colorScheme.muted,
border: Border.all(
color: theme.colorScheme.primary,
width: 1,
),
borderRadius: theme.style.borderRadius,
);
}
// 版本管理卡片
Widget buildSettingVersionSettingCard(
BuildContext context,
ServiceAppVersion item,
) {
final theme = FTheme.of(context);
return Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
"${item.platform ?? '未知平台'} - ${item.versionName ?? '未知版本'}",
style:
theme.typography.lg.copyWith(fontWeight: FontWeight.bold),
overflow: TextOverflow.ellipsis,
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: item.status == '已发布'
? Colors.green.withOpacity(0.1)
: Colors.grey.withOpacity(0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
item.status ?? '未知状态',
style: theme.typography.sm.copyWith(
color: item.status == '已发布' ? Colors.green : Colors.grey,
),
),
),
],
),
const SizedBox(height: 8),
Row(
children: [
Text("Build: ${item.versionCode ?? '-'}",
style: theme.typography.base.copyWith(color: Colors.grey)),
const SizedBox(width: 10),
if (item.isForce == true) _buildTag(context, "强制更新", Colors.red),
if (item.isSilent == true)
_buildTag(context, "静默更新", Colors.orange),
],
),
const Divider(height: 20),
Text("更新内容:",
style: theme.typography.base.copyWith(fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Text(
item.updateContent ?? '暂无更新内容',
style: theme.typography.base.copyWith(color: Colors.grey[800]),
),
if (item.downloadUrl != null && item.downloadUrl!.isNotEmpty) ...[
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(4),
),
child: Row(
children: [
const Icon(Icons.link, size: 16, color: Colors.blue),
const SizedBox(width: 5),
Expanded(
child: Text(
item.downloadUrl!,
style: theme.typography.sm.copyWith(color: Colors.blue),
overflow: TextOverflow.ellipsis,
),
),
],
),
),
],
],
),
);
}
Widget _buildTag(BuildContext context, String text, Color color) {
return Container(
margin: const EdgeInsets.only(left: 8),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
border: Border.all(color: color),
borderRadius: BorderRadius.circular(4),
),
child: Text(text, style: TextStyle(color: color, fontSize: 10)),
);
}