Files
oneapp_docs/basic_uis/ui_business.md

550 lines
14 KiB
Markdown
Raw Permalink Normal View History

2025-09-24 14:08:54 +08:00
# UI Business - 业务UI组件模块文档
## 模块概述
`ui_business` 是 OneApp 基础UI模块群中的业务UI组件库提供与具体业务场景相关的UI组件和工具。该模块包含账户相关组件、订单相关组件、同意书组件等业务特定的界面元素和交互逻辑。
### 基本信息
- **模块名称**: ui_business
- **模块路径**: oneapp_basic_uis/ui_business
- **类型**: Flutter Package Module
- **主要功能**: 业务UI组件、账户组件、订单组件、同意书组件
### 核心特性
- **账户UI组件**: 提供登录、短信验证、证书拍照等账户相关UI
- **订单UI组件**: 订单相关的业务界面组件
- **同意书组件**: 用户协议和同意书相关UI
- **国际化支持**: 支持中英文多语言
- **表单验证**: 集成表单验证和数据处理
- **路由守卫**: 业务路由保护和权限控制
## 目录结构
```
ui_business/
├── lib/
│ ├── ui_account.dart # 账户相关组件导出
│ ├── ui_order.dart # 订单相关组件导出
│ ├── ui_consent.dart # 同意书相关组件导出
│ ├── ui_common.dart # 通用业务组件导出
│ ├── route_guard.dart # 路由守卫
│ ├── customer_care.dart # 客服相关
│ ├── poi_to_car.dart # POI到车功能
│ ├── src/ # 源代码目录
│ │ ├── ui_account/ # 账户相关组件
│ │ │ ├── widgets/ # 账户UI组件
│ │ │ │ ├── sms_auth/ # 短信验证组件
│ │ │ │ ├── check_spin/ # 验证转动组件
│ │ │ │ ├── certificates_photo/ # 证书拍照
│ │ │ │ └── time_picker/ # 时间选择器
│ │ │ ├── models/ # 账户数据模型
│ │ │ ├── mgmt/ # 账户管理
│ │ │ └── ui_config.dart # UI配置
│ │ ├── ui_order/ # 订单相关组件
│ │ └── constants/ # 常量定义
│ ├── generated/ # 生成的国际化文件
│ └── l10n/ # 国际化资源
├── assets/ # 资源文件
└── pubspec.yaml # 依赖配置
```
## 核心架构组件
### 1. 短信验证组件 (SmsAuthWidget)
提供短信验证码输入和验证功能的完整UI组件
```dart
/// 短信验证组件
class SmsAuthWidget extends StatelessWidget {
/// 构造器
///
/// [verificationType] 验证码类型1-登录注册 2-重置密码 3-设置/重置spin
/// [callback] 事件回调
/// [formKey] 表单键
/// [onSubmitBtnLockChanged] 确认按钮锁定回调
/// [smsCallbackOverride] 覆盖默认操作
/// [lockPhoneEdit] 是否锁定手机号修改
SmsAuthWidget({
required this.verificationType,
required this.formKey,
required this.callback,
required this.lockPhoneEdit,
this.onSubmitBtnLockChanged,
this.smsCallbackOverride,
Key? key,
}) : super(key: key);
/// 默认超时时间
static const defaultTimeout = 60;
/// 类型1-登录注册 2-重置密码 3-设置/重置spin
final String verificationType;
/// 表单键
final SmsAuthFormKey formKey;
/// 结果回调
final void Function(SmsAuthResult result) callback;
/// 确认按钮锁定状态变化回调
final OnSubmitBtnLockChanged? onSubmitBtnLockChanged;
/// 短信校验覆盖回调
final SmsCallbackOverride? smsCallbackOverride;
/// 是否锁定手机号修改
final bool lockPhoneEdit;
@override
Widget build(BuildContext context) => BlocProvider(
create: (ctx) => _bloc = SmsAuthBloc(smsCallbackOverride)
..add(const SmsAuthEvent.getLocalProfile()),
child: BlocConsumer<SmsAuthBloc, SmsAuthState>(
listener: _pageListener,
builder: _createLayout,
),
);
}
```
### 2. 回调类型定义
```dart
/// 确认按钮锁定状态变化回调
typedef OnSubmitBtnLockChanged = void Function(bool locked);
/// 用于覆盖短信校验接口的回调
typedef SmsCallbackOverride = Future<bool> Function(
PhoneNumber phone,
VerificationCode smsCode,
);
```
### 3. 手机号掩码扩展
```dart
extension _PhoneStringExtension on String {
/// 手机号掩码显示中间4位用*替换
String get maskPhone {
final buffer = StringBuffer();
int i = 0;
for (final c in characters) {
if ([3, 4, 5, 6].contains(i)) {
buffer.write('*');
} else {
buffer.write(c);
}
i++;
}
return buffer.toString();
}
}
```
### 4. 通用项目组件 (ItemAComponent)
来自general_ui_component模块的通用组件
```dart
/// 通用项目组件A
class ItemAComponent extends StatelessWidget {
ItemAComponent({
required this.title,
required this.logoImg,
required this.onItemAClick,
required this.index,
this.size,
});
/// Logo图片URL
String logoImg = '';
/// 标题文本
String title = '';
/// 点击回调
OnItemAClick onItemAClick;
/// 索引
int index;
/// 自定义尺寸
double? size;
@override
Widget build(BuildContext context) {
return InkWell(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Column(
children: [
Stack(
alignment: Alignment.center,
children: [
Container(
width: width(designWidgetWidth: 48),
height: width(designWidgetWidth: 48),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: OneColors.bgcSub2,
),
),
SizedBox(
height: size ?? width(designWidgetWidth: 40),
width: size ?? width(designWidgetWidth: 40),
child: CacheImageComponent(
imageUrl: logoImg,
),
),
],
),
Padding(
padding: EdgeInsets.only(top: 5.0),
child: Text(
title,
style: OneTextStyle.content(
color: const Color(0xFF7C7F81),
),
),
)
],
),
onTap: () => onItemAClick(index),
);
}
}
typedef OnItemAClick = Function(int index);
```
## 使用指南
### 1. 短信验证组件使用
```dart
import 'package:ui_business/ui_account.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
late SmsAuthFormKey _formKey;
bool _submitBtnLocked = true;
@override
void initState() {
super.initState();
_formKey = SmsAuthFormKey();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('短信验证')),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
SmsAuthWidget(
verificationType: '1', // 登录注册类型
formKey: _formKey,
lockPhoneEdit: false,
callback: _handleSmsResult,
onSubmitBtnLockChanged: (locked) {
setState(() {
_submitBtnLocked = locked;
});
},
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _submitBtnLocked ? null : _handleSubmit,
child: Text('确认'),
),
],
),
),
);
}
void _handleSmsResult(SmsAuthResult result) {
switch (result.state) {
case SmsAuthResultEnum.success:
// 验证成功,处理登录逻辑
print('验证成功UUID: ${result.uuid}');
Navigator.pushReplacementNamed(context, '/home');
break;
case SmsAuthResultEnum.failed:
// 验证失败
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('验证失败,请重试')),
);
break;
}
}
void _handleSubmit() {
// 触发表单验证和提交
_formKey.controllerList.forEach((controller) => controller());
}
}
```
### 2. 自定义短信验证回调
```dart
// 自定义短信验证逻辑
Future<bool> customSmsVerification(
PhoneNumber phone,
VerificationCode smsCode
) async {
try {
// 调用自定义API进行验证
final response = await MyApiService.verifySms(
phone: phone.value,
code: smsCode.value,
);
return response.success;
} catch (e) {
print('短信验证异常: $e');
return false;
}
}
// 使用自定义验证回调
SmsAuthWidget(
verificationType: '1',
formKey: _formKey,
lockPhoneEdit: false,
callback: _handleSmsResult,
smsCallbackOverride: customSmsVerification, // 使用自定义验证
)
```
### 3. 通用组件列表使用
```dart
import 'package:general_ui_component/ItemAComponent.dart';
class ServiceListPage extends StatelessWidget {
final List<ServiceItem> services = [
ServiceItem('充电服务', 'assets/images/charging.png'),
ServiceItem('维修保养', 'assets/images/maintenance.png'),
ServiceItem('道路救援', 'assets/images/rescue.png'),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('服务列表')),
body: GridView.builder(
padding: EdgeInsets.all(16.0),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
crossAxisSpacing: 16.0,
mainAxisSpacing: 16.0,
),
itemCount: services.length,
itemBuilder: (context, index) {
final service = services[index];
return ItemAComponent(
title: service.name,
logoImg: service.iconUrl,
index: index,
onItemAClick: _handleServiceClick,
);
},
),
);
}
void _handleServiceClick(int index) {
final service = services[index];
print('点击了服务: ${service.name}');
// 处理服务点击逻辑
}
}
```
### 4. 国际化支持
```dart
import 'package:ui_business/generated/l10n.dart';
class LocalizedWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(S.of(context).smsVerificationPage_hint_code),
Text(S.of(context).smsVerificationPage_btn_get),
],
);
}
}
```
## 依赖配置
### pubspec.yaml 关键依赖
```yaml
dependencies:
flutter:
sdk: flutter
# 基础UI组件
ui_basic:
path: ../ui_basic
# 基础工具
basic_utils:
path: ../../oneapp_basic_utils/basic_utils
# 模块化路由
basic_modular:
path: ../../oneapp_basic_utils/basic_modular
# 账户服务
clr_account:
path: ../../oneapp_account/clr_account
# 状态管理
flutter_bloc: ^8.0.0
# 国际化
flutter_localizations:
sdk: flutter
intl: ^0.17.0
dev_dependencies:
# 国际化代码生成
intl_utils: ^2.8.0
```
## 国际化配置
### l10n/intl_zh.arb (中文)
```json
{
"smsVerificationPage_hint_code": "请输入验证码",
"smsVerificationPage_btn_get": "获取验证码",
"smsVerificationsPage_error_invalidCode": "验证码格式不正确"
}
```
### l10n/intl_en.arb (英文)
```json
{
"smsVerificationPage_hint_code": "Please enter verification code",
"smsVerificationPage_btn_get": "Get Code",
"smsVerificationsPage_error_invalidCode": "Invalid verification code format"
}
```
## 表单验证
### SmsAuthFormKey 使用
```dart
class CustomFormPage extends StatefulWidget {
@override
_CustomFormPageState createState() => _CustomFormPageState();
}
class _CustomFormPageState extends State<CustomFormPage> {
late SmsAuthFormKey _formKey;
@override
void initState() {
super.initState();
_formKey = SmsAuthFormKey();
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
// 表单字段
ElevatedButton(
onPressed: () {
if (_formKey.validate()) {
_formKey.save();
// 使用表单数据
print('手机号: ${_formKey.phone}');
print('短信码: ${_formKey.sms}');
}
},
child: Text('提交'),
),
],
),
);
}
}
```
## 最佳实践
### 1. 错误处理
```dart
// 推荐:完整的错误处理
void _handleSmsResult(SmsAuthResult result) {
switch (result.state) {
case SmsAuthResultEnum.success:
// 成功处理
_navigateToNextPage(result);
break;
case SmsAuthResultEnum.failed:
// 失败处理,显示用户友好的错误信息
_showErrorMessage('验证失败,请检查验证码是否正确');
break;
case SmsAuthResultEnum.timeout:
// 超时处理
_showErrorMessage('验证超时,请重新获取验证码');
break;
}
}
```
### 2. 资源管理
```dart
// 推荐:及时释放资源
@override
void dispose() {
_formKey.dispose();
super.dispose();
}
```
### 3. 无障碍支持
```dart
// 推荐:添加语义标签
Semantics(
label: '验证码输入框',
hint: '请输入6位数字验证码',
child: SmsAuthWidget(
// 配置参数
),
)
```
## 问题排查
### 常见问题
1. **验证码收不到**: 检查手机号格式和网络连接
2. **倒计时不工作**: 确认CountDownButton状态管理正确
3. **国际化文本不显示**: 检查l10n配置和S.of(context)使用
### 调试技巧
- 使用Flutter Inspector查看Widget树
- 检查BLoC状态变化日志
- 验证表单验证逻辑