204 lines
7.1 KiB
Dart
204 lines
7.1 KiB
Dart
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:forui/forui.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/common/index.dart';
|
|
|
|
import 'package:pj_supermarket_cloud/pages/setting/widgets/index.dart';
|
|
import 'package:pj_supermarket_cloud/pages/setting/widgets/setting_components.dart';
|
|
|
|
import 'index.dart';
|
|
|
|
class SettingPage extends GetView<SettingController> {
|
|
const SettingPage({super.key});
|
|
|
|
/// 沿用原有业务设置的设备绑定要求,本地配置与诊断入口继续允许未绑定访问。
|
|
static const _requiresBinding = {
|
|
SettingMenuType.syncSetting,
|
|
SettingMenuType.paySetting,
|
|
SettingMenuType.faceSetting,
|
|
SettingMenuType.productSetting,
|
|
SettingMenuType.facePluginSetting,
|
|
SettingMenuType.versionSetting,
|
|
};
|
|
|
|
// 主视图
|
|
Widget _buildView() {
|
|
final context = Get.context!;
|
|
final width = Get.context!.sizes.width;
|
|
final theme = FTheme.of(context);
|
|
return <Widget>[
|
|
_buildMenu().width(width * 0.2).height(double.infinity),
|
|
FDivider(
|
|
axis: Axis.vertical,
|
|
style: theme.dividerStyles.verticalStyle.copyWith(
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
),
|
|
Obx(() => _buildDetail(context, controller.cuurentMenuType.value))
|
|
.marginSymmetric(horizontal: 10.w)
|
|
.expanded(),
|
|
]
|
|
.toRow(crossAxisAlignment: CrossAxisAlignment.stretch)
|
|
.height(double.infinity);
|
|
}
|
|
|
|
Widget _buildMenu() {
|
|
final theme = FTheme.of(Get.context!);
|
|
return Obx(
|
|
() => controller.menuList
|
|
.map((v) {
|
|
final isSelect = controller.cuurentMenuType.value == v.type;
|
|
return <Widget>[
|
|
FIcon(
|
|
v.prefixIcon,
|
|
color: isSelect
|
|
? theme.colorScheme.secondary
|
|
: theme.colorScheme.primary,
|
|
),
|
|
Flexible(
|
|
// 窄屏放大字体时只约束长菜单标题,不能把确认图标挤出侧栏。
|
|
child: Text(
|
|
v.title,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: theme.typography.xl.copyWith(
|
|
color: isSelect
|
|
? theme.colorScheme.secondary
|
|
: theme.colorScheme.primary,
|
|
),
|
|
).paddingHorizontal(20.w),
|
|
),
|
|
isSelect
|
|
? FIcon(
|
|
FAssets.icons.check,
|
|
color: theme.colorScheme.secondary,
|
|
)
|
|
: SizedBox.shrink(),
|
|
]
|
|
.toRow(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
)
|
|
.decorated(
|
|
color: isSelect
|
|
? theme.colorScheme.primary
|
|
: theme.colorScheme.secondary,
|
|
border: Border.all(
|
|
color: theme.colorScheme.primary,
|
|
width: 1,
|
|
),
|
|
borderRadius: theme.style.borderRadius,
|
|
)
|
|
.onTap(() => controller.doMenu(v));
|
|
})
|
|
.toList()
|
|
.toColumnSpace(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
space: 10.h,
|
|
)
|
|
.height(double.infinity),
|
|
);
|
|
}
|
|
|
|
/// 绑定校验的等待、失败与未绑定均显示明确状态,避免用空组件掩盖访问原因。
|
|
Widget _buildDetail(BuildContext context, SettingMenuType type) {
|
|
return FutureBuilder<bool>(
|
|
future: controller.checkIsBind(),
|
|
builder: (context, snapshot) {
|
|
if (snapshot.connectionState != ConnectionState.done) {
|
|
return Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const CircularProgressIndicator(),
|
|
SizedBox(height: 16.h),
|
|
Text('正在读取设备配置…', style: FTheme.of(context).typography.base),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
if (snapshot.hasError) {
|
|
return _accessNotice(
|
|
message: '设备配置读取失败,请重试',
|
|
actionLabel: '重试',
|
|
// 重建当前设置页以重新创建读取 Future,不能继续复用失败的快照。
|
|
onTap: () => controller.update(['setting']),
|
|
);
|
|
}
|
|
if (snapshot.data != true && _requiresBinding.contains(type)) {
|
|
return _accessNotice(
|
|
message: '设备尚未绑定商户,请先在“基本信息”中完成绑定',
|
|
actionLabel: '前往基本信息',
|
|
onTap: () => controller.openMenu(SettingMenuType.baseInfo),
|
|
);
|
|
}
|
|
switch (type) {
|
|
case SettingMenuType.baseInfo:
|
|
return settingBaseInfo(context);
|
|
case SettingMenuType.backup:
|
|
return settingBackup(context);
|
|
case SettingMenuType.syncSetting:
|
|
return settingSync(context);
|
|
case SettingMenuType.paySetting:
|
|
return settingPay(context);
|
|
case SettingMenuType.offlineUserSetting:
|
|
return settingOfflineUser(context);
|
|
case SettingMenuType.faceSetting:
|
|
return settingFace(context);
|
|
case SettingMenuType.mealPeriodSetting:
|
|
return const SettingMealPeriods();
|
|
case SettingMenuType.productSetting:
|
|
return settingProduct(context);
|
|
case SettingMenuType.logPreview:
|
|
return const SettingLogPreview();
|
|
case SettingMenuType.facePluginSetting:
|
|
return settingFacePlugin(context);
|
|
case SettingMenuType.versionSetting:
|
|
return settingVersionSetting(context);
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
/// 复用设置页空状态与操作按钮,为未绑定和读取失败提供可执行的恢复入口。
|
|
Widget _accessNotice({
|
|
required String message,
|
|
required String actionLabel,
|
|
required VoidCallback onTap,
|
|
}) =>
|
|
Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SettingEmpty(message: message),
|
|
SizedBox(height: 16.h),
|
|
SettingActionButton(
|
|
label: actionLabel, onTap: onTap, primary: true),
|
|
],
|
|
),
|
|
);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GetBuilder<SettingController>(
|
|
id: "setting",
|
|
builder: (_) {
|
|
// FScaffold 不会创建 Material;正文与恢复按钮的 InkWell 需要统一的涟漪宿主。
|
|
return Material(
|
|
type: MaterialType.transparency,
|
|
child: FScaffold(
|
|
// 标题与返回入口由终端外壳顶栏统一提供;设置模块自身的分区菜单
|
|
// 保留在内容区并直接贴紧顶栏。
|
|
content: _buildView(),
|
|
contentPad: true,
|
|
resizeToAvoidBottomInset: false,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|