feat: 统一自动化任务调度配置并增强聊天流恢复

This commit is contained in:
qzl
2026-03-24 18:19:33 +08:00
parent 23359c2d01
commit 389f5248fc
30 changed files with 1144 additions and 888 deletions
@@ -40,6 +40,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
String _scheduleType = 'daily';
String _timezone = 'Asia/Shanghai';
TimeOfDay _runAt = const TimeOfDay(hour: 8, minute: 0);
final Set<int> _selectedWeekdays = <int>{1};
String _contextSource = 'latest_chat';
String _contextWindowMode = 'day';
int _contextWindowCount = 2;
@@ -134,8 +135,8 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
_buildSectionTitle('计划配置'),
const SizedBox(height: AppSpacing.sm),
_buildInfoCard([
_buildInfoRow('周期', _scheduleLabel(job.scheduleType)),
_buildInfoRow('执行时间', _displayRunAt(job.runAt)),
_buildInfoRow('周期', _scheduleLabel(job.config.schedule.type)),
_buildInfoRow('执行时间', _displayRunAt(job.config.schedule)),
_buildInfoRow('时区', job.timezone),
_buildInfoRow('状态', job.isActive ? '已启用' : '未启用'),
]),
@@ -239,7 +240,7 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
children: [
_buildBadge(job.isSystem ? '系统预置' : '自定义'),
_buildBadge(job.isActive ? '已启用' : '未启用'),
_buildBadge(_scheduleLabel(job.scheduleType)),
_buildBadge(_scheduleLabel(job.config.schedule.type)),
],
),
],
@@ -319,6 +320,10 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
value: _scheduleLabel(_scheduleType),
onTap: _pickScheduleType,
),
if (_scheduleType == 'weekly') ...[
const SizedBox(height: AppSpacing.sm),
_buildWeekdaySelector(),
],
const SizedBox(height: AppSpacing.sm),
_buildPickerTile(
label: '执行时间',
@@ -536,6 +541,85 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
);
}
Widget _buildWeekdaySelector() {
const weekdayLabels = <int, String>{
1: '周一',
2: '周二',
3: '周三',
4: '周四',
5: '周五',
6: '周六',
7: '周日',
};
return Container(
padding: const EdgeInsets.all(AppSpacing.md),
decoration: BoxDecoration(
color: AppColors.white,
borderRadius: BorderRadius.circular(AppRadius.lg),
border: Border.all(color: AppColors.borderSecondary),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'执行日',
style: TextStyle(
color: AppColors.slate500,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: AppSpacing.sm),
Wrap(
spacing: AppSpacing.sm,
runSpacing: AppSpacing.sm,
children: weekdayLabels.entries.map((entry) {
final selected = _selectedWeekdays.contains(entry.key);
return AppPressable(
onTap: () {
setState(() {
if (selected) {
if (_selectedWeekdays.length > 1) {
_selectedWeekdays.remove(entry.key);
}
} else {
_selectedWeekdays.add(entry.key);
}
});
},
borderRadius: BorderRadius.circular(AppRadius.full),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: AppSpacing.md,
vertical: AppSpacing.sm,
),
decoration: BoxDecoration(
color: selected ? AppColors.blue50 : AppColors.white,
borderRadius: BorderRadius.circular(AppRadius.full),
border: Border.all(
color: selected
? AppColors.blue300
: AppColors.borderSecondary,
),
),
child: Text(
entry.value,
style: TextStyle(
color: selected ? AppColors.blue600 : AppColors.slate600,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
),
);
}).toList(),
),
],
),
);
}
Widget _buildToolWrap(List<String> tools) {
if (tools.isEmpty) {
return _buildTextBlock('未启用工具');
@@ -641,6 +725,9 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
if (picked != null) {
setState(() {
_scheduleType = picked;
if (_scheduleType == 'weekly' && _selectedWeekdays.isEmpty) {
_selectedWeekdays.add(1);
}
});
}
}
@@ -708,15 +795,10 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
return '$hour:$minute:00';
}
String _displayRunAt(String runAtRaw) {
try {
final dt = DateTime.parse(runAtRaw).toLocal();
final hour = dt.hour.toString().padLeft(2, '0');
final minute = dt.minute.toString().padLeft(2, '0');
return '$hour:$minute';
} catch (_) {
return runAtRaw;
}
String _displayRunAt(ScheduleConfigModel schedule) {
final hour = schedule.runAt.hour.toString().padLeft(2, '0');
final minute = schedule.runAt.minute.toString().padLeft(2, '0');
return '$hour:$minute';
}
String _scheduleLabel(String scheduleType) {
@@ -757,8 +839,6 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
final request = AutomationJobCreateRequest(
title: title,
scheduleType: _scheduleType,
runAt: _formatTime(_runAt),
timezone: _timezone,
status: 'active',
config: AutomationJobConfigModel(
@@ -769,6 +849,13 @@ class _JobDetailScreenState extends State<JobDetailScreen> {
windowMode: _contextWindowMode,
windowCount: _contextWindowCount,
),
schedule: ScheduleConfigModel(
type: _scheduleType,
runAt: ScheduleRunAtModel(hour: _runAt.hour, minute: _runAt.minute),
weekdays: _scheduleType == 'weekly'
? (_selectedWeekdays.toList()..sort())
: null,
),
),
);
final success = await _cubit.createJob(request);