From b2217ae2be3e4b59ff5bfc950d359f07d3d11208 Mon Sep 17 00:00:00 2001 From: LingandRX Date: Wed, 7 May 2025 22:52:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(category):=20=E6=96=B0=E5=A2=9E=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E5=8A=9F=E8=83=BD=E5=B9=B6=E9=87=8D=E6=9E=84=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=B1=8F=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CategoryScreen 屏幕用于显示分类- 新增 ItemCategory 模型类用于分类数据管理 - 新增 ItemCategoryRepository 用于分类数据持久化 - 新增 item_category_table 创建分类表结构 -重构 HomeScreen 底部导航栏,增加分类选项 - 重命名相关屏幕文件,统一命名规范 - 调整 ItemScreen 以适应新增的分类功能- 更新 SQLiteHelper 以支持分类表创建和默认分类插入 - 删除 StatisticsScreen 屏幕 --- lib/database/item_category_table.dart | 13 +++++ lib/database/sqlite_helper.dart | 4 ++ lib/models/item_category_model.dart | 54 ++++++++++++++++++ lib/models/item_model.dart | 2 +- lib/repository/item_category_repository.dart | 57 +++++++++++++++++++ .../category_screens/category_screen.dart | 42 ++++++++++++++ lib/screens/home_screen.dart | 10 +++- .../{ => item_screens}/item_list_screen.dart | 8 +-- ...{add_item_screen.dart => item_screen.dart} | 54 +++++++++--------- .../widgets/item_is_use_selector.dart | 2 +- .../item_screens/widgets/submit_button.dart | 7 ++- lib/screens/statistics_screen.dart | 15 ----- 12 files changed, 214 insertions(+), 54 deletions(-) create mode 100644 lib/database/item_category_table.dart create mode 100644 lib/models/item_category_model.dart create mode 100644 lib/repository/item_category_repository.dart create mode 100644 lib/screens/category_screens/category_screen.dart rename lib/screens/{ => item_screens}/item_list_screen.dart (88%) rename lib/screens/item_screens/{add_item_screen.dart => item_screen.dart} (75%) delete mode 100644 lib/screens/statistics_screen.dart diff --git a/lib/database/item_category_table.dart b/lib/database/item_category_table.dart new file mode 100644 index 0000000..f330da7 --- /dev/null +++ b/lib/database/item_category_table.dart @@ -0,0 +1,13 @@ +const String createItemCategoryTable = ''' +CREATE TABLE item_category ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + description TEXT, + created_at TEXT DEFAULT (datetime('now')), + updated_at TEXT DEFAULT (datetime('now')) +); +'''; + +const String insertDefaultCategory = ''' +INSERT INTO item_category (name, description) VALUES ('默认分类', '系统默认分类'); +'''; diff --git a/lib/database/sqlite_helper.dart b/lib/database/sqlite_helper.dart index d83a582..bf33dba 100644 --- a/lib/database/sqlite_helper.dart +++ b/lib/database/sqlite_helper.dart @@ -3,6 +3,8 @@ import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; +import 'item_category_table.dart'; + class DatabaseHelper { // 数据库名称 static final _databaseName = "my_database.db"; @@ -35,5 +37,7 @@ class DatabaseHelper { Future _onCreate(Database db, int version) async { await db.execute(createItemTable); + await db.execute(createItemCategoryTable); + await db.execute(insertDefaultCategory); } } \ No newline at end of file diff --git a/lib/models/item_category_model.dart b/lib/models/item_category_model.dart new file mode 100644 index 0000000..997db5e --- /dev/null +++ b/lib/models/item_category_model.dart @@ -0,0 +1,54 @@ +class ItemCategory { + final int? id; + final String name; + final String? description; + final String? createdAt; + final String? updatedAt; + + ItemCategory({ + this.id, + required this.name, + this.description, + this.createdAt, + this.updatedAt, + }); + + // fromMap — 数据库 map -> Dart 对象 + factory ItemCategory.fromMap(Map map) { + return ItemCategory( + id: map['id'] as int?, + name: map['name'] as String, + description: map['description'] as String?, + createdAt: map['created_at'] as String?, + updatedAt: map['updated_at'] as String?, + ); + } + + // toMap — Dart 对象 -> 数据库 map + Map toMap() { + return { + 'id': id, + 'name': name, + 'description': description, + 'created_at': createdAt, + 'updated_at': updatedAt, + }; + } + + // copyWith — 方便局部更新 + ItemCategory copyWith({ + int? id, + String? name, + String? description, + String? createdAt, + String? updatedAt, + }) { + return ItemCategory( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + ); + } +} diff --git a/lib/models/item_model.dart b/lib/models/item_model.dart index fb1cbe5..30aaab0 100644 --- a/lib/models/item_model.dart +++ b/lib/models/item_model.dart @@ -1,4 +1,4 @@ -import 'package:item_tracker/screens/item_screens/add_item_screen.dart'; +import 'package:item_tracker/screens/item_screens/item_screen.dart'; class Item { int? id; diff --git a/lib/repository/item_category_repository.dart b/lib/repository/item_category_repository.dart new file mode 100644 index 0000000..9a49cea --- /dev/null +++ b/lib/repository/item_category_repository.dart @@ -0,0 +1,57 @@ +import 'package:sqflite/sqflite.dart'; + +import '../models/item_category_model.dart'; + +class ItemCategoryRepository { + final Database db; + + ItemCategoryRepository(this.db); + + // 插入 (Create) + Future insert(ItemCategory category) async { + return await db.insert( + 'item_category', + category.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + // 查询全部 (Read All) + Future> getAll() async { + final List> maps = await db.query('item_category'); + return maps.map((map) => ItemCategory.fromMap(map)).toList(); + } + + // 查询单个 (Read One by id) + Future getById(int id) async { + final List> maps = await db.query( + 'item_category', + where: 'id = ?', + whereArgs: [id], + limit: 1, + ); + if (maps.isNotEmpty) { + return ItemCategory.fromMap(maps.first); + } + return null; + } + + // 更新 (Update) + Future update(ItemCategory category) async { + return await db.update( + 'item_category', + category.toMap(), + where: 'id = ?', + whereArgs: [category.id], + ); + } + + // 删除 (Delete) + Future delete(int id) async { + return await db.delete( + 'item_category', + where: 'id = ?', + whereArgs: [id], + ); + } +} diff --git a/lib/screens/category_screens/category_screen.dart b/lib/screens/category_screens/category_screen.dart new file mode 100644 index 0000000..ee9b456 --- /dev/null +++ b/lib/screens/category_screens/category_screen.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +class CategoryScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + const items = 20; + + return Scaffold( + appBar: AppBar( + title: Text('分类'), + ), + body: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: List.generate( + items, + (index) => ItemWidget(text: 'Item $index'), + ), + ), + ), + ); + }, + ), + ); + } +} + +class ItemWidget extends StatelessWidget { + const ItemWidget({super.key, required this.text}); + + final String text; + + @override + Widget build(BuildContext context) { + return Card(child: SizedBox(height: 100, child: Center(child: Text(text)))); + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 5011309..1925e54 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:item_tracker/screens/item_list_screen.dart'; -import 'package:item_tracker/screens/statistics_screen.dart'; +import 'package:item_tracker/screens/category_screens/category_screen.dart'; +import 'package:item_tracker/screens/item_screens/item_list_screen.dart'; import 'package:item_tracker/screens/my_screen.dart'; // 新增导入 class HomeScreen extends StatefulWidget { @@ -12,7 +12,7 @@ class _HomeScreenState extends State { int _selectedIndex = 0; static List _widgetOptions = [ ItemListScreen(), - StatisticsScreen(), + CategoryScreen(), MyScreen(), ]; @@ -37,6 +37,10 @@ class _HomeScreenState extends State { icon: Icon(Icons.add_business_sharp), label: '首页', ), + BottomNavigationBarItem( + icon: Icon(Icons.add_business_sharp), + label: '分类', + ), BottomNavigationBarItem( icon: Icon(Icons.person), label: '我的', diff --git a/lib/screens/item_list_screen.dart b/lib/screens/item_screens/item_list_screen.dart similarity index 88% rename from lib/screens/item_list_screen.dart rename to lib/screens/item_screens/item_list_screen.dart index d7e15f8..389db0e 100644 --- a/lib/screens/item_list_screen.dart +++ b/lib/screens/item_screens/item_list_screen.dart @@ -1,10 +1,8 @@ import 'package:flutter/material.dart'; import 'package:item_tracker/provider/item_provider.dart'; -import 'package:item_tracker/screens/item_screens/add_item_screen.dart'; +import 'package:item_tracker/screens/item_screens/item_screen.dart'; import 'package:provider/provider.dart'; -import 'item_screens/detail_item_screen.dart'; - class ItemListScreen extends StatefulWidget { @override _ItemListScreenState createState() => _ItemListScreenState(); @@ -52,7 +50,7 @@ class _ItemListScreenState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => AddItemScreen(itemToEdit: item), + builder: (context) => ItemScreen(itemToEdit: item, ), ), ); }, @@ -63,7 +61,7 @@ class _ItemListScreenState extends State { onPressed: () async { final shouldRefresh = await Navigator.push( context, - MaterialPageRoute(builder: (context) => AddItemScreen()), + MaterialPageRoute(builder: (context) => ItemScreen()), ); print('shouldRefresh${shouldRefresh}'); diff --git a/lib/screens/item_screens/add_item_screen.dart b/lib/screens/item_screens/item_screen.dart similarity index 75% rename from lib/screens/item_screens/add_item_screen.dart rename to lib/screens/item_screens/item_screen.dart index a17ed0c..c7f0542 100644 --- a/lib/screens/item_screens/add_item_screen.dart +++ b/lib/screens/item_screens/item_screen.dart @@ -40,18 +40,16 @@ extension ItemIsUseX on ItemIsUse { } } -class AddItemScreen extends StatefulWidget { +class ItemScreen extends StatefulWidget { final Item? itemToEdit; - const AddItemScreen({Key? key, this.itemToEdit}) : super(key: key); + const ItemScreen({Key? key, this.itemToEdit}) : super(key: key); @override _FromTestRouteSate createState() => _FromTestRouteSate(); - - } -class _FromTestRouteSate extends State { +class _FromTestRouteSate extends State { String _name = ''; String _description = ''; String _location = ''; @@ -126,32 +124,34 @@ class _FromTestRouteSate extends State { }, ), SizedBox(height: 28.0), - SubmitButton(onPressed: () { - if (_formKey.currentState!.validate()) { - final newItem = Item( - name: _name, - description: _description, - purchaseDate: _selectedDate, - isInUse: _itemIsUse, - price: _price, - ); + SubmitButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + final newItem = Item( + name: _name, + description: _description, + purchaseDate: _selectedDate, + isInUse: _itemIsUse, + price: _price, + ); - print(newItem.toMap()); + print(newItem.toMap()); - // 获取 ItemProvider 并添加物品 - Provider.of(context, listen: false) - .addItem(newItem); + // 获取 ItemProvider 并添加物品 + Provider.of(context, listen: false) + .addItem(newItem); - // 弹窗提示添加成功 - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('提交成功'), - duration: Duration(seconds: 1), - )); + // 弹窗提示添加成功 + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: Text('提交成功'), + duration: Duration(seconds: 1), + )); - // 确保只有在表单验证成功后才调用 Navigator.pop - Navigator.pop(context, true); - } - }) + // 确保只有在表单验证成功后才调用 Navigator.pop + Navigator.pop(context, true); + } + }, + isEdit: widget.itemToEdit != null) ], ), ); diff --git a/lib/screens/item_screens/widgets/item_is_use_selector.dart b/lib/screens/item_screens/widgets/item_is_use_selector.dart index 97b697e..7b03d91 100644 --- a/lib/screens/item_screens/widgets/item_is_use_selector.dart +++ b/lib/screens/item_screens/widgets/item_is_use_selector.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../add_item_screen.dart'; +import '../item_screen.dart'; class ItemIsUseSelector extends StatelessWidget { final ItemIsUse? selected; diff --git a/lib/screens/item_screens/widgets/submit_button.dart b/lib/screens/item_screens/widgets/submit_button.dart index b4611fb..9f63a22 100644 --- a/lib/screens/item_screens/widgets/submit_button.dart +++ b/lib/screens/item_screens/widgets/submit_button.dart @@ -2,15 +2,18 @@ import 'package:flutter/material.dart'; class SubmitButton extends StatelessWidget { final VoidCallback onPressed; - const SubmitButton({required this.onPressed}); + final bool isEdit; + + const SubmitButton({required this.onPressed, this.isEdit = false}); @override Widget build(BuildContext context) { + final buttonText = isEdit ? '保存' : '提交'; return ElevatedButton( onPressed: onPressed, child: Padding( padding: const EdgeInsets.all(16.0), - child: Text('提交'), + child: Text('${buttonText}'), ), style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), diff --git a/lib/screens/statistics_screen.dart b/lib/screens/statistics_screen.dart deleted file mode 100644 index bc5c373..0000000 --- a/lib/screens/statistics_screen.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:flutter/material.dart'; - -class StatisticsScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('统计信息'), - ), - body: Center( - child: Text('这里将显示统计信息'), - ), - ); - } -} \ No newline at end of file