add chart code
This commit is contained in:
+143
-29
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import './leave_model.dart';
|
||||
import './leave_api.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../services/person_picker_dialog.dart';
|
||||
|
||||
class LeaveForm extends StatefulWidget {
|
||||
final String userId;
|
||||
@@ -13,34 +14,92 @@ class LeaveForm extends StatefulWidget {
|
||||
|
||||
class _LeaveFormState extends State<LeaveForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String _selectedType = '事假';
|
||||
String _agentId = '';
|
||||
String _note = '';
|
||||
DateTime _start = DateTime.now();
|
||||
DateTime _end = DateTime.now().add(const Duration(hours: 8));
|
||||
final LeaveApiService _leaveApi = LeaveApiService();
|
||||
|
||||
final List<String> _types = ['事假', '病假', '特休', '婚假', '喪假'];
|
||||
// 狀態變數
|
||||
List<LeaveType> _dbLeaveTypes = [];
|
||||
LeaveType? _selectedType;
|
||||
bool _isLoadingTypes = true;
|
||||
|
||||
Map<String, dynamic>? _selectedAgent; // 改為這個
|
||||
DateTime _startDate = DateTime.now();
|
||||
TimeOfDay _startTime = const TimeOfDay(hour: 09, minute: 00);
|
||||
DateTime _endDate = DateTime.now();
|
||||
TimeOfDay _endTime = const TimeOfDay(hour: 18, minute: 00);
|
||||
String _note = '';
|
||||
|
||||
Future<void> _pickDateTime(bool isStart) async {
|
||||
final date = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: isStart ? _startDate : _endDate,
|
||||
firstDate: DateTime(2020),
|
||||
lastDate: DateTime(2030),
|
||||
);
|
||||
if (date == null) return;
|
||||
|
||||
final time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: isStart ? _startTime : _endTime,
|
||||
);
|
||||
if (time == null) return;
|
||||
|
||||
setState(() {
|
||||
if (isStart) {
|
||||
_startDate = date; _startTime = time;
|
||||
} else {
|
||||
_endDate = date; _endTime = time;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _submit() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (_formKey.currentState!.validate() && _selectedType != null && _selectedAgent != null) {
|
||||
_formKey.currentState!.save();
|
||||
|
||||
// 合併日期時間
|
||||
final start = DateTime(_startDate.year, _startDate.month, _startDate.day, _startTime.hour, _startTime.minute);
|
||||
final end = DateTime(_endDate.year, _endDate.month, _endDate.day, _endTime.hour, _endTime.minute);
|
||||
|
||||
final newLeave = Leave(
|
||||
billNo: '', // API 端生成
|
||||
personId: widget.userId,
|
||||
agentId: _agentId,
|
||||
leaveType: _selectedType,
|
||||
startTime: _start,
|
||||
endTime: _end,
|
||||
agentId: _selectedAgent!['id'].toString(), // 確保轉為 String
|
||||
leaveType: _selectedType!.id, // 傳送 ID 給後端
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
days: 1.0, // 簡化處理,實際可依 start/end 計算
|
||||
hours: 8.0,
|
||||
leaveNote: _note,
|
||||
);
|
||||
|
||||
await LeaveApiService().createLeave(newLeave);
|
||||
//if (success && mounted) {
|
||||
// Navigator.pop(context, true);
|
||||
//}
|
||||
if (mounted) {
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadInitialData();
|
||||
}
|
||||
|
||||
// 從 API 載入假別
|
||||
Future<void> _loadInitialData() async {
|
||||
try {
|
||||
final types = await _leaveApi.fetchLeaveTypes();
|
||||
setState(() {
|
||||
_dbLeaveTypes = types;
|
||||
// 預設選取第一筆(如有資料)
|
||||
if (_dbLeaveTypes.isNotEmpty) {
|
||||
_selectedType = _dbLeaveTypes.first;
|
||||
}
|
||||
_isLoadingTypes = false;
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _isLoadingTypes = false);
|
||||
// 實務上應加入錯誤處理提示
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,32 +107,62 @@ class _LeaveFormState extends State<LeaveForm> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('新增請假申請')),
|
||||
body: Form(
|
||||
body: _isLoadingTypes
|
||||
? const Center(child: CircularProgressIndicator()) // 載入中顯示轉圈
|
||||
:Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
DropdownButtonFormField<String>(
|
||||
// 假別選擇 (顯示名稱,存入代碼)
|
||||
DropdownButtonFormField<LeaveType>(
|
||||
value: _selectedType,
|
||||
decoration: const InputDecoration(labelText: '請假類別'),
|
||||
items: _types.map((t) => DropdownMenuItem(value: t, child: Text(t))).toList(),
|
||||
onChanged: (v) => setState(() => _selectedType = v!),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: '代理人工號'),
|
||||
validator: (v) => v!.isEmpty ? '必填' : null,
|
||||
onSaved: (v) => _agentId = v!,
|
||||
decoration: const InputDecoration(labelText: '請假類別', border: OutlineInputBorder()),
|
||||
// 將 API 取得的資料轉換為選單項目
|
||||
items: _dbLeaveTypes.map((t) => DropdownMenuItem(
|
||||
value: t,
|
||||
child: Text(t.name) // 顯示 leavetype_name
|
||||
)).toList(),
|
||||
onChanged: (v) => setState(() => _selectedType = v),
|
||||
validator: (v) => v == null ? '請選擇假別' : null,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// 代理人開窗
|
||||
// 在 _LeaveFormState 內部的 Widget Tree 中
|
||||
ListTile(
|
||||
title: const Text('開始時間'),
|
||||
subtitle: Text(DateFormat('yyyy/MM/dd HH:mm').format(_start)),
|
||||
trailing: const Icon(Icons.calendar_today),
|
||||
title: const Text('代理人'),
|
||||
// 顯示已選擇的代理人姓名與 ID,若無則顯示提示
|
||||
subtitle: Text(_selectedAgent == null
|
||||
? '請點擊選擇代理人'
|
||||
: '${_selectedAgent!['name']} (${_selectedAgent!['id']})'),
|
||||
trailing: const Icon(Icons.person_add_alt_1, color: Colors.blue),
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
onTap: () async {
|
||||
// 這裡簡化,實務上可串接 showDatePicker + showTimePicker
|
||||
// 呼叫獨立的彈窗組件
|
||||
final Map<String, dynamic>? result = await showDialog<Map<String, dynamic>>(
|
||||
context: context,
|
||||
builder: (context) => const PersonPickerDialog(title: '查詢代理人'),
|
||||
);
|
||||
|
||||
// 如果使用者有選取人員(result 不為 null),則更新 UI 狀態
|
||||
if (result != null) {
|
||||
setState(() {
|
||||
_selectedAgent = result;
|
||||
// 這裡 result 的內容為 {'id': '...', 'name': '...', 'dept': '...'}
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
// 起訖時間選擇區
|
||||
_buildTimePickerTile('開始時間', _startDate, _startTime, true),
|
||||
const SizedBox(height: 10),
|
||||
_buildTimePickerTile('結束時間', _endDate, _endTime, false),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(labelText: '事由說明'),
|
||||
@@ -91,4 +180,29 @@ class _LeaveFormState extends State<LeaveForm> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimePickerTile(String label, DateTime date, TimeOfDay time, bool isStart) {
|
||||
final format = DateFormat('yyyy/MM/dd');
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontSize: 14, color: Colors.blueGrey)),
|
||||
const SizedBox(height: 5),
|
||||
InkWell(
|
||||
onTap: () => _pickDateTime(isStart),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 15),
|
||||
decoration: BoxDecoration(border: Border.all(color: Colors.grey.shade400), borderRadius: BorderRadius.circular(4)),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.access_time, size: 20, color: Colors.blue),
|
||||
const SizedBox(width: 10),
|
||||
Text('${format.format(date)} ${time.format(context)}', style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user