169 lines
5.4 KiB
Dart
169 lines
5.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:table_calendar/table_calendar.dart'; // 建議引入此套件
|
|
import 'package:intl/intl.dart';
|
|
import './calendar_model.dart';
|
|
import './calendar_api.dart';
|
|
import './calendar_detail.dart';
|
|
|
|
class CalendarManager extends StatefulWidget {
|
|
final String userId;
|
|
const CalendarManager({required this.userId, super.key});
|
|
|
|
@override
|
|
State<CalendarManager> createState() => _CalendarManagerState();
|
|
}
|
|
|
|
class _CalendarManagerState extends State<CalendarManager> {
|
|
final CalendarApiService _apiService = CalendarApiService();
|
|
|
|
// 狀態控制
|
|
CalendarFormat _calendarFormat = CalendarFormat.month;
|
|
DateTime _focusedDay = DateTime.now();
|
|
DateTime? _selectedDay;
|
|
|
|
// 資料儲存:將事件按日期分類,方便日曆標點
|
|
Map<DateTime, List<CalendarEvent>> _eventsMap = {};
|
|
bool _isLoading = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_selectedDay = _focusedDay;
|
|
_fetchMonthEvents(_focusedDay);
|
|
}
|
|
|
|
// 核心:抓取整個月的資料並進行分類
|
|
Future<void> _fetchMonthEvents(DateTime month) async {
|
|
setState(() => _isLoading = true);
|
|
try {
|
|
final events = await _apiService.fetchEvents(widget.userId, month);
|
|
|
|
// 將 List 轉換為 Map<DateTime, List<Event>>
|
|
Map<DateTime, List<CalendarEvent>> newMap = {};
|
|
for (var event in events) {
|
|
if (event.startTime != null) {
|
|
// 只取日期部分作為 Key
|
|
final dateKey = DateTime(event.startTime!.year, event.startTime!.month, event.startTime!.day);
|
|
if (newMap[dateKey] == null) newMap[dateKey] = [];
|
|
newMap[dateKey]!.add(event);
|
|
}
|
|
}
|
|
|
|
setState(() {
|
|
_eventsMap = newMap;
|
|
_isLoading = false;
|
|
});
|
|
} catch (e) {
|
|
setState(() => _isLoading = false);
|
|
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("載入失敗: $e")));
|
|
}
|
|
}
|
|
|
|
// 獲取選定日期的行程
|
|
List<CalendarEvent> _getEventsForDay(DateTime day) {
|
|
final dateKey = DateTime(day.year, day.month, day.day);
|
|
return _eventsMap[dateKey] ?? [];
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('企業行事曆'),
|
|
actions: [
|
|
IconButton(icon: const Icon(Icons.today), onPressed: () => setState(() => _focusedDay = DateTime.now())),
|
|
],
|
|
),
|
|
body: Column(
|
|
children: [
|
|
// 1. 日曆組件
|
|
TableCalendar<CalendarEvent>(
|
|
firstDay: DateTime.utc(2020, 1, 1),
|
|
lastDay: DateTime.utc(2030, 12, 31),
|
|
focusedDay: _focusedDay,
|
|
calendarFormat: _calendarFormat,
|
|
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
|
|
eventLoader: _getEventsForDay, // 在有行程的日期顯示小點點
|
|
|
|
// 樣式設定
|
|
calendarStyle: CalendarStyle(
|
|
todayDecoration: BoxDecoration(color: Colors.blue.withOpacity(0.5), shape: BoxShape.circle),
|
|
selectedDecoration: const BoxDecoration(color: Colors.blue, shape: BoxShape.circle),
|
|
markerDecoration: const BoxDecoration(color: Colors.orange, shape: BoxShape.circle),
|
|
),
|
|
|
|
// 互動事件
|
|
onDaySelected: (selectedDay, focusedDay) {
|
|
setState(() {
|
|
_selectedDay = selectedDay;
|
|
_focusedDay = focusedDay;
|
|
});
|
|
},
|
|
onFormatChanged: (format) {
|
|
setState(() => _calendarFormat = format);
|
|
},
|
|
onPageChanged: (focusedDay) {
|
|
_focusedDay = focusedDay;
|
|
_fetchMonthEvents(focusedDay); // 切換月份時自動重新抓取 API
|
|
},
|
|
),
|
|
|
|
const Divider(height: 1),
|
|
|
|
// 2. 下方行程列表
|
|
Expanded(
|
|
child: _isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: _buildEventList(),
|
|
),
|
|
],
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
child: const Icon(Icons.add),
|
|
onPressed: () { /* 實重新增行程邏輯 */ },
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildEventList() {
|
|
final dayEvents = _getEventsForDay(_selectedDay!);
|
|
|
|
if (dayEvents.isEmpty) {
|
|
return Center(
|
|
child: Text(
|
|
"${DateFormat('MM/dd').format(_selectedDay!)} 沒有行程",
|
|
style: const TextStyle(color: Colors.grey),
|
|
),
|
|
);
|
|
}
|
|
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.all(12),
|
|
itemCount: dayEvents.length,
|
|
itemBuilder: (context, index) {
|
|
final event = dayEvents[index];
|
|
return Card(
|
|
elevation: 2,
|
|
margin: const EdgeInsets.only(bottom: 10),
|
|
child: ListTile(
|
|
leading: Container(
|
|
width: 4,
|
|
height: 40,
|
|
decoration: BoxDecoration(
|
|
color: event.levelColor,
|
|
borderRadius: BorderRadius.circular(2),
|
|
),
|
|
),
|
|
title: Text(event.title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
subtitle: Text(event.timeRangeText),
|
|
trailing: const Icon(Icons.chevron_right),
|
|
onTap: () => Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (_) => CalendarDetail(event: event))
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
} |