feat: add two-tab timetable UI and glass dock
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../app/app_controller.dart';
|
||||
import '../../app/app_theme.dart';
|
||||
import '../../data/json_schedule_import.dart';
|
||||
import '../../domain/course.dart';
|
||||
|
||||
class ImportPreview extends StatefulWidget {
|
||||
const ImportPreview({
|
||||
super.key,
|
||||
required this.draft,
|
||||
required this.controller,
|
||||
});
|
||||
final ImportDraft draft;
|
||||
final AppController controller;
|
||||
@override
|
||||
State<ImportPreview> createState() => _ImportPreviewState();
|
||||
}
|
||||
|
||||
class _ImportPreviewState extends State<ImportPreview> {
|
||||
bool _clearConfirmed = false, _busy = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final draft = widget.draft, empty = draft.courses.isEmpty;
|
||||
final allowed =
|
||||
!_busy && (!empty || (draft.explicitlyEmpty && _clearConfirmed));
|
||||
return SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(24, 8, 24, 28),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
'确认你的课表',
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w700),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'${draft.courses.length} 条课程安排 · ${draft.source == CourseSource.demo ? '示例体验' : '导入预览'}',
|
||||
style: const TextStyle(color: river),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
if (empty)
|
||||
Text(draft.explicitlyEmpty ? '此操作会清空当前课表。' : '没有识别到课程,当前课表保持不变。'),
|
||||
if (empty && draft.explicitlyEmpty)
|
||||
CheckboxListTile(
|
||||
value: _clearConfirmed,
|
||||
onChanged: (v) => setState(() => _clearConfirmed = v ?? false),
|
||||
title: const Text('确认清空当前课表'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
for (final c in draft.courses)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: SurfaceCard(
|
||||
padding: const EdgeInsets.all(14),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
c.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.w700),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'${c.teacher} · ${c.room}',
|
||||
style: const TextStyle(fontSize: 12),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
'周${const ['一', '二', '三', '四', '五', '六', '日'][c.weekday - 1]} · 第 ${c.startPeriod}–${c.endPeriod} 节',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: ink.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.controller.snapshot.settings.semester == null &&
|
||||
draft.source != CourseSource.demo)
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(bottom: 16),
|
||||
child: Text(
|
||||
'导入后请在设置中选择真实学期开始日,课表与提醒才会显示。',
|
||||
style: TextStyle(fontSize: 12, height: 1.6),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: FilledButton(
|
||||
onPressed: allowed
|
||||
? () async {
|
||||
setState(() => _busy = true);
|
||||
await widget.controller.applyImport(
|
||||
draft,
|
||||
confirmedEmpty: _clearConfirmed,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
if (widget.controller.lastError == null) {
|
||||
Navigator.pop(context, true);
|
||||
} else {
|
||||
setState(() => _busy = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(widget.controller.lastError!),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
: null,
|
||||
child: Text(
|
||||
_busy
|
||||
? '正在保存…'
|
||||
: draft.source == CourseSource.demo
|
||||
? '开始体验'
|
||||
: '确认导入',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Center(
|
||||
child: TextButton(
|
||||
onPressed: _busy ? null : () => Navigator.pop(context, false),
|
||||
child: const Text('取消'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user