406 lines
15 KiB
Dart
406 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:superport/services/mock_data_service.dart';
|
|
import 'package:superport/utils/constants.dart';
|
|
import 'package:superport/screens/common/main_layout.dart';
|
|
import 'package:superport/screens/common/custom_widgets.dart';
|
|
import 'package:superport/screens/common/widgets/pagination.dart';
|
|
import 'package:superport/screens/common/theme_tailwind.dart';
|
|
import 'package:superport/screens/common/widgets/category_autocomplete_field.dart';
|
|
|
|
/// 물품 관리(등록) 화면
|
|
/// 이름, 제조사, 대분류, 중분류, 소분류만 등록/조회 가능
|
|
class GoodsListScreen extends StatefulWidget {
|
|
const GoodsListScreen({super.key});
|
|
|
|
@override
|
|
State<GoodsListScreen> createState() => _GoodsListScreenState();
|
|
}
|
|
|
|
class _GoodsListScreenState extends State<GoodsListScreen> {
|
|
final MockDataService _dataService = MockDataService();
|
|
late List<_GoodsItem> _goodsList;
|
|
int _currentPage = 1;
|
|
final int _pageSize = 10;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_loadGoods();
|
|
}
|
|
|
|
void _loadGoods() {
|
|
final allEquipments = _dataService.getAllEquipmentIns();
|
|
final goodsSet = <String, _GoodsItem>{};
|
|
for (final equipmentIn in allEquipments) {
|
|
final eq = equipmentIn.equipment;
|
|
final key =
|
|
'${eq.manufacturer}|${eq.name}|${eq.category}|${eq.subCategory}|${eq.subSubCategory}';
|
|
goodsSet[key] = _GoodsItem(
|
|
name: eq.name,
|
|
manufacturer: eq.manufacturer,
|
|
category: eq.category,
|
|
subCategory: eq.subCategory,
|
|
subSubCategory: eq.subSubCategory,
|
|
);
|
|
}
|
|
setState(() {
|
|
_goodsList = goodsSet.values.toList();
|
|
});
|
|
}
|
|
|
|
void _showAddGoodsDialog() async {
|
|
final result = await showDialog<_GoodsItem>(
|
|
context: context,
|
|
builder: (context) => _GoodsFormDialog(),
|
|
);
|
|
if (result != null) {
|
|
setState(() {
|
|
_goodsList.add(result);
|
|
});
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(const SnackBar(content: Text('물품이 등록되었습니다.')));
|
|
}
|
|
}
|
|
|
|
void _showEditGoodsDialog(int index) async {
|
|
final result = await showDialog<_GoodsItem>(
|
|
context: context,
|
|
builder: (context) => _GoodsFormDialog(item: _goodsList[index]),
|
|
);
|
|
if (result != null) {
|
|
setState(() {
|
|
_goodsList[index] = result;
|
|
});
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(const SnackBar(content: Text('물품 정보가 수정되었습니다.')));
|
|
}
|
|
}
|
|
|
|
void _deleteGoods(int index) {
|
|
showDialog(
|
|
context: context,
|
|
builder:
|
|
(context) => AlertDialog(
|
|
title: const Text('삭제 확인'),
|
|
content: const Text('이 물품 정보를 삭제하시겠습니까?'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: const Text('취소'),
|
|
),
|
|
TextButton(
|
|
onPressed: () {
|
|
setState(() {
|
|
_goodsList.removeAt(index);
|
|
});
|
|
Navigator.pop(context);
|
|
ScaffoldMessenger.of(
|
|
context,
|
|
).showSnackBar(const SnackBar(content: Text('물품이 삭제되었습니다.')));
|
|
},
|
|
child: const Text('삭제'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final maxContentWidth = screenWidth > 1200 ? 1200.0 : screenWidth - 32;
|
|
final int totalCount = _goodsList.length;
|
|
final int startIndex = (_currentPage - 1) * _pageSize;
|
|
final int endIndex =
|
|
(startIndex + _pageSize) > totalCount
|
|
? totalCount
|
|
: (startIndex + _pageSize);
|
|
final pagedGoods = _goodsList.sublist(startIndex, endIndex);
|
|
|
|
return MainLayout(
|
|
title: '물품 관리',
|
|
currentRoute: Routes.goods,
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh),
|
|
onPressed: _loadGoods,
|
|
color: Colors.grey,
|
|
),
|
|
],
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
PageTitle(
|
|
title: '물품 목록',
|
|
width: maxContentWidth - 32,
|
|
rightWidget: ElevatedButton.icon(
|
|
onPressed: _showAddGoodsDialog,
|
|
icon: const Icon(Icons.add),
|
|
label: const Text('추가'),
|
|
style: AppThemeTailwind.primaryButtonStyle,
|
|
),
|
|
),
|
|
Expanded(
|
|
child: DataTableCard(
|
|
width: maxContentWidth - 32,
|
|
child:
|
|
pagedGoods.isEmpty
|
|
? const Center(child: Text('등록된 물품이 없습니다.'))
|
|
: SingleChildScrollView(
|
|
scrollDirection: Axis.horizontal,
|
|
child: Container(
|
|
constraints: BoxConstraints(
|
|
minWidth: maxContentWidth - 64,
|
|
),
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: DataTable(
|
|
columns: const [
|
|
DataColumn(label: Text('번호')),
|
|
DataColumn(label: Text('이름')),
|
|
DataColumn(label: Text('제조사')),
|
|
DataColumn(label: Text('대분류')),
|
|
DataColumn(label: Text('중분류')),
|
|
DataColumn(label: Text('소분류')),
|
|
DataColumn(label: Text('관리')),
|
|
],
|
|
rows: List.generate(pagedGoods.length, (i) {
|
|
final item = pagedGoods[i];
|
|
final realIndex = startIndex + i;
|
|
return DataRow(
|
|
cells: [
|
|
DataCell(Text('${realIndex + 1}')),
|
|
DataCell(Text(item.name)),
|
|
DataCell(Text(item.manufacturer)),
|
|
DataCell(Text(item.category)),
|
|
DataCell(Text(item.subCategory)),
|
|
DataCell(Text(item.subSubCategory)),
|
|
DataCell(
|
|
Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.edit,
|
|
color: AppThemeTailwind.primary,
|
|
),
|
|
onPressed:
|
|
() => _showEditGoodsDialog(
|
|
realIndex,
|
|
),
|
|
),
|
|
IconButton(
|
|
icon: const Icon(
|
|
Icons.delete,
|
|
color: AppThemeTailwind.danger,
|
|
),
|
|
onPressed:
|
|
() => _deleteGoods(realIndex),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (totalCount > _pageSize)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
child: Pagination(
|
|
totalCount: totalCount,
|
|
currentPage: _currentPage,
|
|
pageSize: _pageSize,
|
|
onPageChanged: (page) {
|
|
setState(() {
|
|
_currentPage = page;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 물품 데이터 모델 (이름, 제조사, 대중소분류)
|
|
class _GoodsItem {
|
|
final String name;
|
|
final String manufacturer;
|
|
final String category;
|
|
final String subCategory;
|
|
final String subSubCategory;
|
|
|
|
_GoodsItem({
|
|
required this.name,
|
|
required this.manufacturer,
|
|
required this.category,
|
|
required this.subCategory,
|
|
required this.subSubCategory,
|
|
});
|
|
}
|
|
|
|
/// 물품 등록/수정 폼 다이얼로그
|
|
class _GoodsFormDialog extends StatefulWidget {
|
|
final _GoodsItem? item;
|
|
const _GoodsFormDialog({this.item});
|
|
@override
|
|
State<_GoodsFormDialog> createState() => _GoodsFormDialogState();
|
|
}
|
|
|
|
class _GoodsFormDialogState extends State<_GoodsFormDialog> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
late String _name;
|
|
late String _manufacturer;
|
|
late String _category;
|
|
late String _subCategory;
|
|
late String _subSubCategory;
|
|
|
|
late final MockDataService _dataService;
|
|
late final List<String> _manufacturerList;
|
|
late final List<String> _nameList;
|
|
late final List<String> _categoryList;
|
|
late final List<String> _subCategoryList;
|
|
late final List<String> _subSubCategoryList;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_name = widget.item?.name ?? '';
|
|
_manufacturer = widget.item?.manufacturer ?? '';
|
|
_category = widget.item?.category ?? '';
|
|
_subCategory = widget.item?.subCategory ?? '';
|
|
_subSubCategory = widget.item?.subSubCategory ?? '';
|
|
_dataService = MockDataService();
|
|
_manufacturerList = _dataService.getAllManufacturers();
|
|
_nameList = _dataService.getAllEquipmentNames();
|
|
_categoryList = _dataService.getAllCategories();
|
|
_subCategoryList = _dataService.getAllSubCategories();
|
|
_subSubCategoryList = _dataService.getAllSubSubCategories();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Dialog(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
child: ConstrainedBox(
|
|
constraints: const BoxConstraints(maxWidth: 420),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(24.0),
|
|
child: Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
widget.item == null ? '신상품 등록' : '신상품 정보 수정',
|
|
style: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
FormFieldWrapper(
|
|
label: '이름',
|
|
isRequired: true,
|
|
child: CategoryAutocompleteField(
|
|
hintText: '이름을 입력 또는 선택하세요',
|
|
value: _name,
|
|
items: _nameList,
|
|
isRequired: true,
|
|
onSelect: (v) => setState(() => _name = v),
|
|
),
|
|
),
|
|
FormFieldWrapper(
|
|
label: '제조사',
|
|
isRequired: true,
|
|
child: CategoryAutocompleteField(
|
|
hintText: '제조사를 입력 또는 선택하세요',
|
|
value: _manufacturer,
|
|
items: _manufacturerList,
|
|
isRequired: true,
|
|
onSelect: (v) => setState(() => _manufacturer = v),
|
|
),
|
|
),
|
|
FormFieldWrapper(
|
|
label: '대분류',
|
|
isRequired: true,
|
|
child: CategoryAutocompleteField(
|
|
hintText: '대분류를 입력 또는 선택하세요',
|
|
value: _category,
|
|
items: _categoryList,
|
|
isRequired: true,
|
|
onSelect: (v) => setState(() => _category = v),
|
|
),
|
|
),
|
|
FormFieldWrapper(
|
|
label: '중분류',
|
|
isRequired: true,
|
|
child: CategoryAutocompleteField(
|
|
hintText: '중분류를 입력 또는 선택하세요',
|
|
value: _subCategory,
|
|
items: _subCategoryList,
|
|
isRequired: true,
|
|
onSelect: (v) => setState(() => _subCategory = v),
|
|
),
|
|
),
|
|
FormFieldWrapper(
|
|
label: '소분류',
|
|
isRequired: true,
|
|
child: CategoryAutocompleteField(
|
|
hintText: '소분류를 입력 또는 선택하세요',
|
|
value: _subSubCategory,
|
|
items: _subSubCategoryList,
|
|
isRequired: true,
|
|
onSelect: (v) => setState(() => _subSubCategory = v),
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('취소'),
|
|
),
|
|
const SizedBox(width: 8),
|
|
ElevatedButton(
|
|
style: AppThemeTailwind.primaryButtonStyle,
|
|
onPressed: () {
|
|
if (_formKey.currentState?.validate() ?? false) {
|
|
Navigator.of(context).pop(
|
|
_GoodsItem(
|
|
name: _name,
|
|
manufacturer: _manufacturer,
|
|
category: _category,
|
|
subCategory: _subCategory,
|
|
subSubCategory: _subSubCategory,
|
|
),
|
|
);
|
|
}
|
|
},
|
|
child: Text(widget.item == null ? '등록' : '수정'),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|