h-1.flutter.4/lib/screens/master/employee_master_screen.dart

214 lines
No EOL
7.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Version: 1.7 - 担当者マスタ画面DB 連携実装)
import 'package:flutter/material.dart';
/// 担当者マスタ管理画面CRUD 機能付き)
class EmployeeMasterScreen extends StatefulWidget {
const EmployeeMasterScreen({super.key});
@override
State<EmployeeMasterScreen> createState() => _EmployeeMasterScreenState();
}
final _employeeDialogKey = GlobalKey();
class _EmployeeMasterScreenState extends State<EmployeeMasterScreen> {
List<Map<String, dynamic>> _employees = [];
bool _loading = true;
@override
void initState() {
super.initState();
_loadEmployees();
}
Future<void> _loadEmployees() async {
setState(() => _loading = true);
try {
// デモデータ(実際には DatabaseHelper 経由)
final demoData = [
{'id': 1, 'name': '山田太郎', 'department': '営業', 'email': 'yamada@example.com', 'phone': '03-1234-5678'},
{'id': 2, 'name': '田中花子', 'department': '総務', 'email': 'tanaka@example.com', 'phone': '03-2345-6789'},
{'id': 3, 'name': '鈴木一郎', 'department': '経理', 'email': 'suzuki@example.com', 'phone': '03-3456-7890'},
];
setState(() => _employees = demoData);
} catch (e) {
if (mounted) ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('読み込みエラー:$e'), backgroundColor: Colors.red),
);
} finally {
setState(() => _loading = false);
}
}
Future<void> _addEmployee() async {
final employee = <String, dynamic>{
'id': DateTime.now().millisecondsSinceEpoch,
'name': '',
'department': '',
'email': '',
'phone': '',
};
final result = await showDialog<Map<String, dynamic>>(
context: context,
builder: (context) => _EmployeeDialogState(
Dialog(
child: SingleChildScrollView(
padding: EdgeInsets.zero,
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 200),
child: EmployeeForm(employee: employee),
),
),
),
),
);
if (result != null && mounted) {
setState(() => _employees.add(result));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('担当者登録完了'), backgroundColor: Colors.green),
);
}
}
Future<void> _editEmployee(int id) async {
final employee = _employees.firstWhere((e) => e['id'] == id);
final edited = await showDialog<Map<String, dynamic>>(
context: context,
builder: (context) => _EmployeeDialogState(
Dialog(
child: SingleChildScrollView(
padding: EdgeInsets.zero,
child: ConstrainedBox(
constraints: const BoxConstraints(minHeight: 200),
child: EmployeeForm(employee: employee),
),
),
),
),
);
if (edited != null && mounted) {
final index = _employees.indexWhere((e) => e['id'] == id);
setState(() => _employees[index] = edited);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('担当者更新完了'), backgroundColor: Colors.green),
);
}
}
Future<void> _deleteEmployee(int id) async {
final confirmed = await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('担当者削除'),
content: Text('この担当者を実際に削除しますか?'),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('キャンセル')),
ElevatedButton(
onPressed: () => Navigator.pop(context, true),
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: const Text('削除'),
),
],
),
);
if (confirmed == true) {
setState(() {
_employees.removeWhere((e) => e['id'] == id);
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('担当者削除完了'), backgroundColor: Colors.green),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('/M5. 担当者マスタ'),
actions: [
IconButton(icon: const Icon(Icons.refresh), onPressed: _loadEmployees),
IconButton(icon: const Icon(Icons.add), onPressed: _addEmployee),
],
),
body: _loading ? const Center(child: CircularProgressIndicator()) :
_employees.isEmpty ? Center(child: Text('担当者データがありません')) :
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: _employees.length,
itemBuilder: (context, index) {
final employee = _employees[index];
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(backgroundColor: Colors.purple.shade50, child: Icon(Icons.person_add, color: Colors.purple)),
title: Text(employee['name'] ?? '未入力'),
subtitle: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text('部署:${employee['department']}'),
if (employee['email'] != null) Text('Email: ${employee['email']}'),
]),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(icon: const Icon(Icons.edit), onPressed: () => _editEmployee(employee['id'] as int)),
IconButton(icon: const Icon(Icons.delete), onPressed: () => _deleteEmployee(employee['id'] as int)),
],
),
),
);
},
),
);
}
}
/// 担当者フォーム部品
class EmployeeForm extends StatelessWidget {
final Map<String, dynamic> employee;
const EmployeeForm({super.key, required this.employee});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(decoration: InputDecoration(labelText: '氏名 *'), controller: TextEditingController(text: employee['name'] ?? '')),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
decoration: InputDecoration(labelText: '部署', hintText: '営業/総務/経理/技術/管理'),
value: employee['department'] != null ? (employee['department'] as String?) : null,
items: ['営業', '総務', '経理', '技術', '管理'].map((dep) => DropdownMenuItem(value: dep, child: Text(dep))).toList(),
onChanged: (v) { employee['department'] = v; },
),
const SizedBox(height: 8),
TextField(decoration: InputDecoration(labelText: 'メールアドレス'), controller: TextEditingController(text: employee['email'] ?? ''), keyboardType: TextInputType.emailAddress),
const SizedBox(height: 8),
TextField(decoration: InputDecoration(labelText: '電話番号', hintText: '0123-456789'), controller: TextEditingController(text: employee['phone'] ?? ''), keyboardType: TextInputType.phone),
const SizedBox(height: 24),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [TextButton(onPressed: () => Navigator.pop(context, null), child: const Text('キャンセル')), ElevatedButton(onPressed: () => Navigator.pop(context, employee), child: const Text('保存'))],
),
],
);
}
}
/// 担当者ダイアログ表示ヘルパークラス(削除用)
class _EmployeeDialogState extends StatelessWidget {
final Dialog dialog;
const _EmployeeDialogState(this.dialog);
@override
Widget build(BuildContext context) {
return dialog;
}
}