chore(project): 初始化项目基础配置文件

- 添加 CodeGraph、Android 和通用 gitignore 配置
- 创建项目元数据文件跟踪 Flutter 项目属性
- 添加 Codex AI 指导文档 AGENTS.md 说明项目架构
- 配置代码分析选项 analysis_options.yaml
- 设置 Android 应用清单权限和 Kiosk 模式配置
- 实现中英文国际化支持 AppLocalizations
- 配置 GoRouter 应用路由导航
- 创建明亮工业控制风格的主题配置 AppTheme
This commit is contained in:
Developer
2026-06-04 11:19:44 +08:00
commit 5d28bf631b
85 changed files with 21423 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/localization/app_localizations.dart';
import '../../../core/theme/app_theme.dart';
import '../../../shared/widgets/common_button.dart';
import '../models/program.dart';
import '../providers/programs_provider.dart';
/// 程序表单弹窗
/// 用于新增和编辑程序
class ProgramFormDialog extends ConsumerStatefulWidget {
final Program? program;
const ProgramFormDialog({super.key, this.program});
@override
ConsumerState<ProgramFormDialog> createState() => _ProgramFormDialogState();
}
class _ProgramFormDialogState extends ConsumerState<ProgramFormDialog> {
final _formKey = GlobalKey<FormState>();
late TextEditingController _codeController;
late TextEditingController _nameController;
bool _isEnabled = true;
bool _isSaving = false;
@override
void initState() {
super.initState();
_codeController = TextEditingController(text: widget.program?.code ?? '');
_nameController = TextEditingController(text: widget.program?.name ?? '');
_isEnabled = widget.program?.status == 1;
}
@override
void dispose() {
_codeController.dispose();
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final isEditing = widget.program != null;
return AlertDialog(
title: Text(
isEditing
? (l10n?.editProgram ?? '编辑程序')
: (l10n?.addProgram ?? '新增程序'),
),
content: SizedBox(
width: 400,
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 编号输入
TextFormField(
controller: _codeController,
decoration: InputDecoration(
labelText: l10n?.programCode ?? '编号',
hintText: '例如: P001',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return '请输入编号';
}
return null;
},
),
const SizedBox(height: 16),
// 名称输入
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: l10n?.programName ?? '名称',
hintText: '请输入程序名称',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return '请输入名称';
}
return null;
},
),
const SizedBox(height: 16),
// 状态开关
Row(
children: [
Text(
'状态',
style: TextStyle(color: AppTheme.textPrimary),
),
const Spacer(),
Switch(
value: _isEnabled,
onChanged: (value) {
setState(() {
_isEnabled = value;
});
},
activeColor: AppTheme.successColor,
),
Text(
_isEnabled ? '启用' : '停用',
style: TextStyle(
color: _isEnabled ? AppTheme.successColor : AppTheme.idleColor,
),
),
],
),
],
),
),
),
actions: [
TextButton(
onPressed: _isSaving ? null : () => Navigator.of(context).pop(),
child: Text(l10n?.cancel ?? '取消'),
),
CommonButton(
text: l10n?.save ?? '保存',
icon: Icons.save,
type: ButtonType.primary,
isLoading: _isSaving,
onPressed: _isSaving ? null : () => _saveProgram(context, ref, l10n),
),
],
);
}
/// 保存程序
Future<void> _saveProgram(
BuildContext context,
WidgetRef ref,
AppLocalizations? l10n,
) async {
if (!_formKey.currentState!.validate()) return;
setState(() {
_isSaving = true;
});
final notifier = ref.read(programsProvider.notifier);
final now = DateTime.now().toString().substring(0, 10);
final program = Program(
id: widget.program?.id,
code: _codeController.text.trim(),
name: _nameController.text.trim(),
createdAt: widget.program?.createdAt ?? now,
status: _isEnabled ? 1 : 0,
);
bool success;
if (widget.program != null) {
success = await notifier.updateProgram(program);
} else {
success = await notifier.addProgram(program);
}
setState(() {
_isSaving = false;
});
if (success) {
Navigator.of(context).pop();
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('保存失败,请检查编号是否重复'),
backgroundColor: AppTheme.errorColor,
),
);
}
}
}