215 lines
6.4 KiB
Dart
215 lines
6.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import '../../../../core/di/injection.dart';
|
|
import '../../../../core/router/app_routes.dart';
|
|
import '../../../../core/theme/design_tokens.dart';
|
|
import '../../../../shared/widgets/app_button.dart';
|
|
import '../../../../shared/widgets/app_loading_indicator.dart';
|
|
import '../../../../shared/widgets/app_pressable.dart';
|
|
import '../../../../shared/widgets/app_toggle_switch.dart';
|
|
import '../../../../shared/widgets/toast/toast.dart';
|
|
import '../../../../shared/widgets/toast/toast_type.dart';
|
|
import '../../data/models/automation_job_model.dart';
|
|
import '../../data/services/automation_jobs_api.dart';
|
|
import '../../presentation/cubits/automation_jobs_cubit.dart';
|
|
import '../widgets/settings_page_scaffold.dart';
|
|
|
|
class FeaturesScreen extends StatefulWidget {
|
|
const FeaturesScreen({super.key});
|
|
|
|
@override
|
|
State<FeaturesScreen> createState() => _FeaturesScreenState();
|
|
}
|
|
|
|
class _FeaturesScreenState extends State<FeaturesScreen> {
|
|
late final AutomationJobsCubit _cubit;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_cubit = AutomationJobsCubit(sl<AutomationJobsApi>());
|
|
_cubit.loadJobs();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_cubit.close();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider.value(
|
|
value: _cubit,
|
|
child: SettingsPageScaffold(
|
|
title: '周期计划',
|
|
onBack: () => context.pop(),
|
|
body: BlocBuilder<AutomationJobsCubit, AutomationJobsState>(
|
|
builder: (context, state) {
|
|
if (state.isLoading) {
|
|
return const Center(child: AppLoadingIndicator());
|
|
}
|
|
if (state.error != null) {
|
|
return Center(child: Text(state.error!));
|
|
}
|
|
return _buildJobList(state);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildJobList(AutomationJobsState state) {
|
|
final dailyJobs = state.dailyJobs;
|
|
final weeklyJobs = state.weeklyJobs;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildSectionTitle('每日'),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
if (dailyJobs.isEmpty)
|
|
_buildEmptyHint('暂无每日计划')
|
|
else
|
|
...dailyJobs.map(_buildJobCard),
|
|
const SizedBox(height: AppSpacing.lg),
|
|
_buildSectionTitle('每周'),
|
|
const SizedBox(height: AppSpacing.sm),
|
|
if (weeklyJobs.isEmpty)
|
|
_buildEmptyHint('暂无每周计划')
|
|
else
|
|
...weeklyJobs.map(_buildJobCard),
|
|
if (state.canCreateMore) ...[
|
|
const SizedBox(height: AppSpacing.lg),
|
|
_buildCreateButton(),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildEmptyHint(String text) {
|
|
return Container(
|
|
width: double.infinity,
|
|
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.white,
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
border: Border.all(color: AppColors.borderSecondary),
|
|
),
|
|
child: Text(
|
|
text,
|
|
style: const TextStyle(
|
|
color: AppColors.slate500,
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSectionTitle(String title) {
|
|
return Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 13,
|
|
fontWeight: FontWeight.w600,
|
|
color: AppColors.slate500,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildJobCard(AutomationJobModel job) {
|
|
return AppPressable(
|
|
onTap: () async {
|
|
await context.push(AppRoutes.settingsJobDetail(job.id));
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
_cubit.loadJobs();
|
|
},
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
child: Container(
|
|
margin: const EdgeInsets.only(bottom: AppSpacing.sm),
|
|
padding: const EdgeInsets.all(AppSpacing.lg),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.white,
|
|
borderRadius: BorderRadius.circular(AppRadius.lg),
|
|
border: Border.all(color: AppColors.borderSecondary),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: AppSpacing.xxl + AppSpacing.lg,
|
|
height: AppSpacing.xxl + AppSpacing.lg,
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceTertiary,
|
|
borderRadius: BorderRadius.circular(AppRadius.md),
|
|
),
|
|
child: const Icon(
|
|
Icons.auto_awesome,
|
|
size: AppSpacing.lg,
|
|
color: AppColors.blue500,
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.md),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
job.title,
|
|
style: const TextStyle(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: AppSpacing.xs),
|
|
Text(
|
|
_buildSubtitle(job),
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
color: AppColors.slate500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: AppSpacing.sm),
|
|
AppToggleSwitch(
|
|
value: job.isActive,
|
|
onChanged: (next) {
|
|
if (job.isSystem) {
|
|
Toast.show(context, '系统预置任务状态不可修改', type: ToastType.info);
|
|
return;
|
|
}
|
|
_cubit.updateJobStatus(id: job.id, enabled: next);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
String _buildSubtitle(AutomationJobModel job) {
|
|
final statusText = job.isActive ? '已启用' : '未启用';
|
|
final sourceText = job.isSystem ? '系统预置' : '自定义';
|
|
return '$sourceText • $statusText';
|
|
}
|
|
|
|
Widget _buildCreateButton() {
|
|
return AppButton(
|
|
text: '创建任务',
|
|
onPressed: () async {
|
|
await context.push(AppRoutes.settingsJobNew);
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
_cubit.loadJobs();
|
|
},
|
|
);
|
|
}
|
|
}
|