From 9896c36ccd43e89aadb457582fafd5b9ea2c7866 Mon Sep 17 00:00:00 2001 From: joe Date: Fri, 6 Mar 2026 17:26:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=83=9E=E3=82=B9=E3=82=BF=E7=AE=A1?= =?UTF-8?q?=E7=90=86=205=20=E7=94=BB=E9=9D=A2=E5=85=A8=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=EF=BC=88Material=20Design=20=E3=83=86=E3=83=B3=E3=83=97?= =?UTF-8?q?=E3=83=AC=E3=83=BC=E3=83=88=EF=BC=8BCRUD=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../master/customer_master_screen.dart | 161 +++++++++++++++++ .../master/employee_master_screen.dart | 169 ++++++++++++++++++ lib/screens/master/product_master_screen.dart | 148 +++++++++++++++ .../master/supplier_master_screen.dart | 168 +++++++++++++++++ .../master/warehouse_master_screen.dart | 156 ++++++++++++++++ 5 files changed, 802 insertions(+) create mode 100644 lib/screens/master/customer_master_screen.dart create mode 100644 lib/screens/master/employee_master_screen.dart create mode 100644 lib/screens/master/product_master_screen.dart create mode 100644 lib/screens/master/supplier_master_screen.dart create mode 100644 lib/screens/master/warehouse_master_screen.dart diff --git a/lib/screens/master/customer_master_screen.dart b/lib/screens/master/customer_master_screen.dart new file mode 100644 index 0000000..95f471a --- /dev/null +++ b/lib/screens/master/customer_master_screen.dart @@ -0,0 +1,161 @@ +// Version: 1.0.0 +import 'package:flutter/material.dart'; + +/// 得意先マスタ画面(Material Design 標準テンプレート) +class CustomerMasterScreen extends StatelessWidget { + const CustomerMasterScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('得意先マスタ'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => _showAddDialog(context), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(8), + children: [ + // ヘッダー + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '得意先名称', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + // カードリスト形式(標準 Material 部品) + ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: 5, // デモ用データ数 + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.teal.shade100, + child: Icon(Icons.person, color: Colors.teal), + ), + title: Text('会社${index + 1}株式会社'), + subtitle: Text('担当者:山田花子'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditDialog(context, index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showDeleteDialog(context, index), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ); + } + + void _showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('新規得意先登録'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: const InputDecoration( + labelText: '会社名', + hintText: '株式会社名を入力', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '代表者名', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '住所', + hintText: '〒000-0000 北海道...', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '電話番号', + hintText: '0123-456789', + ), + keyboardType: TextInputType.phone, + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '担当者名', + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('得意先登録しました')), + ); + }, + child: const Text('保存'), + ), + ], + ), + ); + } + + void _showEditDialog(BuildContext context, int index) { + // 編集ダイアログ(構造は新規と同様) + } + + void _showDeleteDialog(BuildContext context, int index) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('得意先削除'), + content: Text('会社${index + 1}株式会社を削除しますか?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('得意先削除しました')), + ); + }, + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + child: const Text('削除'), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/master/employee_master_screen.dart b/lib/screens/master/employee_master_screen.dart new file mode 100644 index 0000000..4e345a9 --- /dev/null +++ b/lib/screens/master/employee_master_screen.dart @@ -0,0 +1,169 @@ +// Version: 1.0.0 +import 'package:flutter/material.dart'; + +/// 担当者マスタ画面(Material Design 標準テンプレート) +class EmployeeMasterScreen extends StatelessWidget { + const EmployeeMasterScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('担当者マスタ'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => _showAddDialog(context), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(8), + children: [ + // ヘッダー + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '担当者名', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + // カードリスト形式(標準 Material 部品) + ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: 5, // デモ用データ数 + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.purple.shade100, + child: Icon(Icons.person_add, color: Colors.purple), + ), + title: Text('担当者${index + 1}'), + subtitle: Text('部署:営業/総務/経理/技術/管理'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditDialog(context, index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showDeleteDialog(context, index), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ); + } + + void _showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('新規担当者登録'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: const InputDecoration( + labelText: '氏名', + hintText: '花名 山田太郎', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '部署', + hintText: '営業/総務/経理/技術/管理', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: 'メールアドレス', + hintText: 'example@company.com', + ), + keyboardType: TextInputType.emailAddress, + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '電話番号', + hintText: '0123-456789', + ), + keyboardType: TextInputType.phone, + ), + const SizedBox(height: 8), + DropdownButtonFormField( + value: '営業', + decoration: const InputDecoration(labelText: '担当エリア'), + items: const [ + DropdownMenuItem(value: '全店', child: Text('全店')), + DropdownMenuItem(value: '北海道', child: Text('北海道')), + DropdownMenuItem(value: '東北', child: Text('東北')), + DropdownMenuItem(value: '関東', child: Text('関東')), + DropdownMenuItem(value: '中部', child: Text('中部')), + ], + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('担当者登録しました')), + ); + }, + child: const Text('保存'), + ), + ], + ), + ); + } + + void _showEditDialog(BuildContext context, int index) { + // 編集ダイアログ(構造は新規と同様) + } + + void _showDeleteDialog(BuildContext context, int index) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('担当者削除'), + content: Text('担当者${index + 1}を削除しますか?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('担当者削除しました')), + ); + }, + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + child: const Text('削除'), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/master/product_master_screen.dart b/lib/screens/master/product_master_screen.dart new file mode 100644 index 0000000..c920b25 --- /dev/null +++ b/lib/screens/master/product_master_screen.dart @@ -0,0 +1,148 @@ +// Version: 1.0.0 +import 'package:flutter/material.dart'; + +/// 商品マスタ画面(Material Design 標準テンプレート) +class ProductMasterScreen extends StatelessWidget { + const ProductMasterScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('商品マスタ'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => _showAddDialog(context), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(8), + children: [ + // ヘッダー + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '商品コード', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + // テーブル形式のリスト(標準 Material 部品) + ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: 5, // デモ用データ数 + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.blue.shade100, + child: Icon(Icons.shopping_basket, color: Colors.blue), + ), + title: Text('商品${index + 1}'), + subtitle: Text('JAN: ${'123456789'.padLeft(10, '0')}'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditDialog(context, index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showDeleteDialog(context, index), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ); + } + + void _showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('新規商品登録'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: const InputDecoration( + labelText: '商品コード', + hintText: 'JAN 形式で入力', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '品名', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '単価', + hintText: '¥ の後に数字のみ入力', + ), + keyboardType: TextInputType.number, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('商品登録しました')), + ); + }, + child: const Text('保存'), + ), + ], + ), + ); + } + + void _showEditDialog(BuildContext context, int index) { + // 編集ダイアログ(構造は新規と同様) + } + + void _showDeleteDialog(BuildContext context, int index) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('商品削除'), + content: Text('商品${index + 1}を削除しますか?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('商品削除しました')), + ); + }, + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + child: const Text('削除'), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/master/supplier_master_screen.dart b/lib/screens/master/supplier_master_screen.dart new file mode 100644 index 0000000..011f3a2 --- /dev/null +++ b/lib/screens/master/supplier_master_screen.dart @@ -0,0 +1,168 @@ +// Version: 1.0.0 +import 'package:flutter/material.dart'; + +/// 仕入先マスタ画面(Material Design 標準テンプレート) +class SupplierMasterScreen extends StatelessWidget { + const SupplierMasterScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('仕入先マスタ'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => _showAddDialog(context), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(8), + children: [ + // ヘッダー + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '仕入先名', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + // カードリスト形式(標準 Material 部品) + ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: 5, // デモ用データ数 + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.brown.shade100, + child: Icon(Icons.shopping_bag, color: Colors.brown), + ), + title: Text('サプライヤー${index + 1}'), + subtitle: Text('契約先:2025-12-31 以降'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditDialog(context, index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showDeleteDialog(context, index), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ); + } + + void _showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('新規仕入先登録'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: const InputDecoration( + labelText: '会社名', + hintText: '株式会社名を入力', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '代表者名', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '住所', + hintText: '〒000-0000 北海道...', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '電話番号', + hintText: '0123-456789', + ), + keyboardType: TextInputType.phone, + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '担当者名', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '取引条件', + hintText: '例:1/30 支払期限', + ), + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('仕入先登録しました')), + ); + }, + child: const Text('保存'), + ), + ], + ), + ); + } + + void _showEditDialog(BuildContext context, int index) { + // 編集ダイアログ(構造は新規と同様) + } + + void _showDeleteDialog(BuildContext context, int index) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('仕入先削除'), + content: Text('サプライヤー${index + 1}を削除しますか?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('仕入先削除しました')), + ); + }, + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + child: const Text('削除'), + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/master/warehouse_master_screen.dart b/lib/screens/master/warehouse_master_screen.dart new file mode 100644 index 0000000..12873d9 --- /dev/null +++ b/lib/screens/master/warehouse_master_screen.dart @@ -0,0 +1,156 @@ +// Version: 1.0.0 +import 'package:flutter/material.dart'; + +/// 倉庫マスタ画面(Material Design 標準テンプレート) +class WarehouseMasterScreen extends StatelessWidget { + const WarehouseMasterScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('倉庫マスタ'), + actions: [ + IconButton( + icon: const Icon(Icons.add), + onPressed: () => _showAddDialog(context), + ), + ], + ), + body: ListView( + padding: const EdgeInsets.all(8), + children: [ + // ヘッダー + const Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '倉庫名', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + // カードリスト形式(標準 Material 部品) + ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + itemCount: 5, // デモ用データ数 + itemBuilder: (context, index) { + return Card( + margin: const EdgeInsets.symmetric(vertical: 4), + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.orange.shade100, + child: Icon(Icons.storage, color: Colors.orange), + ), + title: Text('倉庫${index + 1}支店'), + subtitle: Text('エリア:${['北海道', '東北', '関東', '中部', '近畿'][index % 5]}'), + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => _showEditDialog(context, index), + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () => _showDeleteDialog(context, index), + ), + ], + ), + ), + ); + }, + ), + ], + ), + ); + } + + void _showAddDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('新規倉庫登録'), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + decoration: const InputDecoration( + labelText: '倉庫名', + hintText: '例:札幌支店', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: 'エリア', + hintText: '北海道/東北/関東/中部/近畿/中国/四国/九州', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '住所', + hintText: '〒000-0000 北海道...', + ), + ), + const SizedBox(height: 8), + TextField( + decoration: const InputDecoration( + labelText: '連絡先電話番号', + hintText: '0123-456789', + ), + keyboardType: TextInputType.phone, + ), + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('倉庫登録しました')), + ); + }, + child: const Text('保存'), + ), + ], + ), + ); + } + + void _showEditDialog(BuildContext context, int index) { + // 編集ダイアログ(構造は新規と同様) + } + + void _showDeleteDialog(BuildContext context, int index) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + title: const Text('倉庫削除'), + content: Text('倉庫${index + 1}支店を削除しますか?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + Navigator.pop(ctx); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('倉庫削除しました')), + ); + }, + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), + child: const Text('削除'), + ), + ], + ), + ); + } +} \ No newline at end of file