2025-05-09 16:49:13 +08:00
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:image_picker/image_picker.dart';
|
|
|
|
|
|
|
|
|
|
class RepairPage extends StatefulWidget {
|
2025-06-02 14:37:05 +08:00
|
|
|
|
const RepairPage({super.key, required this.scrollController});
|
|
|
|
|
|
|
|
|
|
final ScrollController scrollController;
|
2025-05-09 16:49:13 +08:00
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
State<RepairPage> createState() => _RepairPageState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _RepairPageState extends State<RepairPage> {
|
|
|
|
|
final _formKey = GlobalKey<FormState>();
|
|
|
|
|
String? _repairType;
|
|
|
|
|
final _locationController = TextEditingController();
|
|
|
|
|
final _descriptionController = TextEditingController();
|
|
|
|
|
XFile? _photo;
|
|
|
|
|
|
|
|
|
|
final List<Map<String, String>> _repairRecords = [
|
|
|
|
|
{
|
|
|
|
|
"date": "2025/04/20",
|
|
|
|
|
"type": "水管漏水",
|
|
|
|
|
"location": "A棟 2F",
|
|
|
|
|
"status": "處理中",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"date": "2025/04/18",
|
|
|
|
|
"type": "網路異常",
|
|
|
|
|
"location": "C棟 5F",
|
|
|
|
|
"status": "已完成",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"date": "2025/04/15",
|
|
|
|
|
"type": "電力問題",
|
|
|
|
|
"location": "B棟 6F",
|
|
|
|
|
"status": "待處理",
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
Future<void> _pickImage() async {
|
|
|
|
|
final picker = ImagePicker();
|
|
|
|
|
final picked = await picker.pickImage(source: ImageSource.gallery);
|
|
|
|
|
if (picked != null) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_photo = picked;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _submitForm() {
|
|
|
|
|
if (_formKey.currentState!.validate() && _repairType != null) {
|
|
|
|
|
ScaffoldMessenger.of(
|
|
|
|
|
context,
|
|
|
|
|
).showSnackBar(const SnackBar(content: Text('報修已提交!')));
|
2025-06-02 14:37:05 +08:00
|
|
|
|
// 這裡可以擴充送出資料到伺服器的邏輯
|
2025-05-09 16:49:13 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void dispose() {
|
|
|
|
|
_locationController.dispose();
|
|
|
|
|
_descriptionController.dispose();
|
|
|
|
|
super.dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
2025-06-02 14:37:05 +08:00
|
|
|
|
return Container(
|
|
|
|
|
decoration: const BoxDecoration(
|
|
|
|
|
color: Color(0xFFF7F8FA),
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
child: Column(
|
2025-05-09 16:49:13 +08:00
|
|
|
|
children: [
|
2025-06-02 14:37:05 +08:00
|
|
|
|
// 標題列
|
|
|
|
|
Container(
|
|
|
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
|
|
|
decoration: const BoxDecoration(
|
|
|
|
|
color: Color(0xFF9EAF9F),
|
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
|
|
|
),
|
|
|
|
|
child: Row(
|
2025-05-09 16:49:13 +08:00
|
|
|
|
children: [
|
2025-06-02 14:37:05 +08:00
|
|
|
|
IconButton(
|
|
|
|
|
icon: const Icon(Icons.close, color: Colors.white),
|
|
|
|
|
onPressed: () => Navigator.pop(context),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
const SizedBox(width: 8),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
const Text(
|
2025-06-02 14:37:05 +08:00
|
|
|
|
'水電網路報修',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
color: Colors.white,
|
|
|
|
|
),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
Expanded(
|
|
|
|
|
child: ListView(
|
|
|
|
|
controller: widget.scrollController,
|
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
|
children: [
|
|
|
|
|
const Text('🛠️ 填寫報修單', style: TextStyle(fontSize: 16)),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
const SizedBox(height: 12),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
Form(
|
|
|
|
|
key: _formKey,
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
const Text(
|
|
|
|
|
'報修類別',
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
DropdownButtonFormField<String>(
|
|
|
|
|
value: _repairType,
|
|
|
|
|
items: const [
|
|
|
|
|
DropdownMenuItem(value: '電力問題', child: Text('電力問題')),
|
|
|
|
|
DropdownMenuItem(value: '水管漏水', child: Text('水管漏水')),
|
|
|
|
|
DropdownMenuItem(value: '網路異常', child: Text('網路異常')),
|
|
|
|
|
DropdownMenuItem(value: '其他', child: Text('其他')),
|
|
|
|
|
],
|
|
|
|
|
hint: const Text('請選擇'),
|
|
|
|
|
onChanged: (value) {
|
|
|
|
|
setState(() {
|
|
|
|
|
_repairType = value;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
validator: (value) => value == null ? '請選擇報修類別' : null,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
const Text(
|
|
|
|
|
'地點/房號',
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
TextFormField(
|
|
|
|
|
controller: _locationController,
|
|
|
|
|
decoration: const InputDecoration(hintText: '如:B棟 3F'),
|
|
|
|
|
validator:
|
|
|
|
|
(value) =>
|
|
|
|
|
value == null || value.isEmpty ? '請輸入地點' : null,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
const Text(
|
|
|
|
|
'問題描述',
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
TextFormField(
|
|
|
|
|
controller: _descriptionController,
|
|
|
|
|
maxLines: 3,
|
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
|
hintText: '請簡要描述問題...',
|
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
|
),
|
|
|
|
|
validator:
|
|
|
|
|
(value) =>
|
|
|
|
|
value == null || value.isEmpty
|
|
|
|
|
? '請輸入問題描述'
|
|
|
|
|
: null,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
const Text(
|
|
|
|
|
'照片上傳(選填)',
|
|
|
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
ElevatedButton(
|
|
|
|
|
onPressed: _pickImage,
|
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
|
backgroundColor: Colors.white,
|
|
|
|
|
),
|
|
|
|
|
child: const Text(
|
|
|
|
|
'選擇照片',
|
|
|
|
|
style: TextStyle(color: Colors.purple),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
if (_photo != null)
|
|
|
|
|
const Text(
|
|
|
|
|
'已選擇圖片',
|
|
|
|
|
style: TextStyle(color: Colors.green),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
|
ElevatedButton(
|
|
|
|
|
onPressed: _submitForm,
|
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
|
backgroundColor: Colors.green,
|
|
|
|
|
minimumSize: const Size.fromHeight(48),
|
|
|
|
|
),
|
|
|
|
|
child: const Text(
|
|
|
|
|
'送出報修',
|
|
|
|
|
style: TextStyle(color: Colors.white),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
2025-05-09 16:49:13 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
|
const Text('📋 已申報紀錄', style: TextStyle(fontSize: 16)),
|
2025-05-09 16:49:13 +08:00
|
|
|
|
const SizedBox(height: 12),
|
2025-06-02 14:37:05 +08:00
|
|
|
|
..._repairRecords.map(
|
|
|
|
|
(record) => Padding(
|
|
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
|
children: [
|
|
|
|
|
Text(
|
|
|
|
|
'${record["date"]} - ${record["type"]}',
|
|
|
|
|
style: const TextStyle(fontWeight: FontWeight.bold),
|
|
|
|
|
),
|
|
|
|
|
Text('地點:${record["location"]}|狀態:${record["status"]}'),
|
|
|
|
|
],
|
2025-05-09 16:49:13 +08:00
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-02 14:37:05 +08:00
|
|
|
|
|
|
|
|
|
// 浮層包裝元件,給 showModalBottomSheet 使用
|
|
|
|
|
class RepairPageWrapper extends StatelessWidget {
|
|
|
|
|
const RepairPageWrapper({super.key});
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
return DraggableScrollableSheet(
|
|
|
|
|
initialChildSize: 0.95,
|
|
|
|
|
minChildSize: 0.5,
|
|
|
|
|
maxChildSize: 1.0,
|
|
|
|
|
expand: false,
|
|
|
|
|
builder: (_, scrollController) {
|
|
|
|
|
return Container(
|
|
|
|
|
color: Colors.transparent,
|
|
|
|
|
child: RepairPage(scrollController: scrollController),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|