From d87205effa88e6632c2560d2d92b3f733dec77dc Mon Sep 17 00:00:00 2001 From: joe Date: Sun, 8 Mar 2026 01:51:07 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A3=B2=E4=B8=8A=E5=85=A5=E5=8A=9B?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=20basic=20UI=20=E6=A7=8B=E7=AF=89=E5=AE=8C?= =?UTF-8?q?=E4=BA=86\n\n-=20Dashboard=E3=83=BB=E5=90=88=E8=A8=88=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=82=A8=E3=83=AA=E3=82=A2=EF=BC=88=E3=83=80=E3=83=83?= =?UTF-8?q?=E3=82=B7=E3=83=A5=E3=83=9C=E3=83=BC=E3=83=89=EF=BC=89\n-=20?= =?UTF-8?q?=E5=95=86=E5=93=81=E6=A4=9C=E7=B4=A2=20TextField=20=E3=81=AE?= =?UTF-8?q?=E6=94=B9=E5=96=84\n-=20ExpansionTile=20=E5=86=85=E3=83=AA?= =?UTF-8?q?=E3=82=B9=E3=83=88=E6=A7=8B=E9=80=A0=E3=81=AE=E3=83=AA=E3=83=95?= =?UTF-8?q?=E3=82=A1=E3=82=AF=E3=82=BF\n-=20=E4=BF=9D=E5=AD=98=E3=83=80?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E6=A9=9F=E8=83=BD=E3=81=AE?= =?UTF-8?q?=E6=94=B9=E4=BF=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/screens/sales_screen.dart | 213 +++++++++++++++++++++++++--------- 1 file changed, 155 insertions(+), 58 deletions(-) diff --git a/lib/screens/sales_screen.dart b/lib/screens/sales_screen.dart index 6c90720..8f35ace 100644 --- a/lib/screens/sales_screen.dart +++ b/lib/screens/sales_screen.dart @@ -1,4 +1,4 @@ -// Version: 1.0.0 +// Version: 1.0.1 - Sprint 4-M2 実装開始 import 'package:flutter/material.dart'; /// 売上入力画面(レジモードの主戦場)(Material Design テンプレート) @@ -12,85 +12,182 @@ class SalesScreen extends StatelessWidget { title: const Text('売上入力'), actions: [ IconButton( - icon: const Icon(Icons.receipt_long), + icon: const Icon(Icons.save), onPressed: () => _showSaveDialog(context), ), ], ), - body: ListView( - padding: const EdgeInsets.all(16), + body: Column( children: [ - // レジモードのメイン表示エリア - Card( - margin: EdgeInsets.zero, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // 合計金額表示 - Text( - '合計:¥0', - style: const TextStyle( - fontSize: 32, - fontWeight: FontWeight.bold, - color: Colors.black87, + // ダッシュボードエリア(合計金額表示) + Padding( + padding: const EdgeInsets.all(16), + child: Card( + elevation: 4, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // タイトル + Text( + 'レジモード', + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), ), - ), - const SizedBox(height: 16), + const SizedBox(height: 8), - // 税率/税額表示 - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('税別:¥0'), - Text('税込:¥0', style: const TextStyle(fontWeight: FontWeight.bold)), - ], - ), - ], + // 合計金額表示 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '合計', + style: TextStyle( + fontSize: 18, + color: Colors.grey.shade600, + ), + ), + const Icon(Icons.payments, size: 32), + ], + ), + const SizedBox(height: 4), + + // 合計金額(大きな表示) + Text( + '¥0', + style: const TextStyle( + fontSize: 48, + fontWeight: FontWeight.bold, + color: Colors.teal, + ), + ), + const SizedBox(height: 16), + + // 税率/税額表示 + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('税別', style: TextStyle(fontSize: 14)), + Text('¥0'), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text('税込', style: TextStyle(fontWeight: FontWeight.bold)), + Text('¥0', style: const TextStyle(fontWeight: FontWeight.bold)), + ], + ), + ], + ), + ], + ), ), ), ), - const SizedBox(height: 16), - // 商品入力エリア - TextField( - decoration: const InputDecoration( - labelText: '商品検索', - hintText: 'JAN コードまたは商品名を入力', - prefixIcon: Icon(Icons.search), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TextField( + decoration: InputDecoration( + labelText: '商品検索', + hintText: 'JAN コードまたは商品名を入力して選択', + prefixIcon: const Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + ), + filled: true, + ), ), - onChanged: (value) { /* TODO: 商品検索 */ }, ), + const SizedBox(height: 8), - const SizedBox(height: 16), - - // 商品リスト - Card( - margin: EdgeInsets.zero, - child: ExpansionTile( - title: const Text('売上商品'), + // 売上商品リスト(ダミーデータ用) + Expanded( + child: ListView( children: [ - ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - itemCount: 0, // デモ用 - itemBuilder: (context, index) => Card( - margin: const EdgeInsets.symmetric(vertical: 4), - child: ListTile( - leading: CircleAvatar( - backgroundColor: Colors.orange.shade100, - child: Icon(Icons.store, color: Colors.orange), - ), - title: Text('商品${index + 1}'), - subtitle: Text('数量:0 pcs / 単価:¥0'), + Card( + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), + child: ExpansionTile( + title: Text( + '📦 売上商品', + style: TextStyle(fontWeight: FontWeight.bold), ), + subtitle: const Text('商品を登録'), + children: [ + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: 0, // TODO: 実際のデータに差し替える + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Card( + child: ListTile( + leading: CircleAvatar( + backgroundColor: Colors.orange.shade100, + child: Icon(Icons.store, color: Colors.orange), + ), + title: Text('商品${index + 1}'), + subtitle: const Row( + children: [ + SizedBox(width: 8), + Icon(Icons.remove_circle_outline), + SizedBox(width: 4), + Text('数量:0 pcs / 単価:¥0'), + ], + ), + ), + ), + ); + }, + ), + ], ), ), ], ), ), + + const SizedBox(height: 16), + ], + ), + ); + } + + /// 保存ダイアログ表示(TODO: DatabaseHelper.insertSales を呼び出す) + void _showSaveDialog(BuildContext context) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('売上データを保存'), + content: const Text( + '入力した商品情報を販売アシストに保存します。\n\n DatabaseHelper.insertSales を呼び出す予定です。', + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('キャンセル'), + ), + ElevatedButton( + onPressed: () { + // TODO: DatabaseHelper.insertSales(context) 呼び出し + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('売上データ保存処理中...'), + duration: Duration(seconds: 2), + ), + ); + Navigator.pop(context); + }, + child: const Text('保存'), + ), ], ), );