feat: 添加自动化任务(automation_jobs)功能模块
This commit is contained in:
@@ -1,7 +1,18 @@
|
||||
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 {
|
||||
@@ -12,27 +23,88 @@ class FeaturesScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
bool _dailyReminderEnabled = true;
|
||||
bool _dailySummaryEnabled = false;
|
||||
bool _weeklyReportEnabled = true;
|
||||
bool _weeklyDigestEnabled = false;
|
||||
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 SettingsPageScaffold(
|
||||
title: '周期计划',
|
||||
onBack: () => context.pop(),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildSectionTitle('每日'),
|
||||
const SizedBox(height: 8),
|
||||
_buildDailyList(),
|
||||
const SizedBox(height: 16),
|
||||
_buildSectionTitle('每周'),
|
||||
const SizedBox(height: 8),
|
||||
_buildWeeklyList(),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -48,122 +120,95 @@ class _FeaturesScreenState extends State<FeaturesScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDailyList() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildFeatureCard(
|
||||
icon: Icons.alarm,
|
||||
iconColor: const Color(0xFF14B8A6),
|
||||
iconBg: const Color(0xFFECFEFF),
|
||||
iconBorder: const Color(0xFFC9F4F2),
|
||||
title: '会议提醒',
|
||||
subtitle: '每次会议前 15 分钟提醒',
|
||||
value: _dailyReminderEnabled,
|
||||
onChanged: (v) => setState(() => _dailyReminderEnabled = v),
|
||||
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),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildFeatureCard(
|
||||
icon: Icons.summarize,
|
||||
iconColor: const Color(0xFF2563EB),
|
||||
iconBg: const Color(0xFFEEF6FF),
|
||||
iconBorder: const Color(0xFFDCEAFF),
|
||||
title: '每日摘要',
|
||||
subtitle: '每天 18:00 发送当日摘要',
|
||||
value: _dailySummaryEnabled,
|
||||
onChanged: (v) => setState(() => _dailySummaryEnabled = v),
|
||||
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);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWeeklyList() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
_buildFeatureCard(
|
||||
icon: Icons.calendar_view_week,
|
||||
iconColor: AppColors.success,
|
||||
iconBg: const Color(0xFFECFDF5),
|
||||
iconBorder: const Color(0xFFCDEEDC),
|
||||
title: '周报生成',
|
||||
subtitle: '每周一自动生成周报',
|
||||
value: _weeklyReportEnabled,
|
||||
onChanged: (v) => setState(() => _weeklyReportEnabled = v),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildFeatureCard(
|
||||
icon: Icons.article,
|
||||
iconColor: AppColors.warning,
|
||||
iconBg: const Color(0xFFFFF7ED),
|
||||
iconBorder: const Color(0xFFFDE6CD),
|
||||
title: '每周摘要',
|
||||
subtitle: '每周日发送本周活动汇总',
|
||||
value: _weeklyDigestEnabled,
|
||||
onChanged: (v) => setState(() => _weeklyDigestEnabled = v),
|
||||
),
|
||||
],
|
||||
);
|
||||
String _buildSubtitle(AutomationJobModel job) {
|
||||
final statusText = job.isActive ? '已启用' : '未启用';
|
||||
final sourceText = job.isSystem ? '系统预置' : '自定义';
|
||||
return '$sourceText • $statusText';
|
||||
}
|
||||
|
||||
Widget _buildFeatureCard({
|
||||
required IconData icon,
|
||||
required Color iconColor,
|
||||
required Color iconBg,
|
||||
required Color iconBorder,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required bool value,
|
||||
required ValueChanged<bool> onChanged,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: iconBg,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: iconBorder),
|
||||
),
|
||||
child: Icon(icon, size: 18, color: iconColor),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate900,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
subtitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: AppColors.slate500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AppToggleSwitch(value: value, onChanged: onChanged),
|
||||
],
|
||||
),
|
||||
Widget _buildCreateButton() {
|
||||
return AppButton(
|
||||
text: '创建任务',
|
||||
onPressed: () async {
|
||||
await context.push(AppRoutes.settingsJobNew);
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
_cubit.loadJobs();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user