feat(category): 新增分类功能并重构相关屏幕
- 新增 CategoryScreen 屏幕用于显示分类- 新增 ItemCategory 模型类用于分类数据管理 - 新增 ItemCategoryRepository 用于分类数据持久化 - 新增 item_category_table 创建分类表结构 -重构 HomeScreen 底部导航栏,增加分类选项 - 重命名相关屏幕文件,统一命名规范 - 调整 ItemScreen 以适应新增的分类功能- 更新 SQLiteHelper 以支持分类表创建和默认分类插入 - 删除 StatisticsScreen 屏幕
This commit is contained in:
parent
b7d1cdc62e
commit
b2217ae2be
13
lib/database/item_category_table.dart
Normal file
13
lib/database/item_category_table.dart
Normal file
@ -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 ('默认分类', '系统默认分类');
|
||||
''';
|
||||
@ -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<void> _onCreate(Database db, int version) async {
|
||||
await db.execute(createItemTable);
|
||||
await db.execute(createItemCategoryTable);
|
||||
await db.execute(insertDefaultCategory);
|
||||
}
|
||||
}
|
||||
54
lib/models/item_category_model.dart
Normal file
54
lib/models/item_category_model.dart
Normal file
@ -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<String, dynamic> 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<String, dynamic> 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
57
lib/repository/item_category_repository.dart
Normal file
57
lib/repository/item_category_repository.dart
Normal file
@ -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<int> insert(ItemCategory category) async {
|
||||
return await db.insert(
|
||||
'item_category',
|
||||
category.toMap(),
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
|
||||
// 查询全部 (Read All)
|
||||
Future<List<ItemCategory>> getAll() async {
|
||||
final List<Map<String, dynamic>> maps = await db.query('item_category');
|
||||
return maps.map((map) => ItemCategory.fromMap(map)).toList();
|
||||
}
|
||||
|
||||
// 查询单个 (Read One by id)
|
||||
Future<ItemCategory?> getById(int id) async {
|
||||
final List<Map<String, dynamic>> maps = await db.query(
|
||||
'item_category',
|
||||
where: 'id = ?',
|
||||
whereArgs: [id],
|
||||
limit: 1,
|
||||
);
|
||||
if (maps.isNotEmpty) {
|
||||
return ItemCategory.fromMap(maps.first);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新 (Update)
|
||||
Future<int> update(ItemCategory category) async {
|
||||
return await db.update(
|
||||
'item_category',
|
||||
category.toMap(),
|
||||
where: 'id = ?',
|
||||
whereArgs: [category.id],
|
||||
);
|
||||
}
|
||||
|
||||
// 删除 (Delete)
|
||||
Future<int> delete(int id) async {
|
||||
return await db.delete(
|
||||
'item_category',
|
||||
where: 'id = ?',
|
||||
whereArgs: [id],
|
||||
);
|
||||
}
|
||||
}
|
||||
42
lib/screens/category_screens/category_screen.dart
Normal file
42
lib/screens/category_screens/category_screen.dart
Normal file
@ -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))));
|
||||
}
|
||||
}
|
||||
@ -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<HomeScreen> {
|
||||
int _selectedIndex = 0;
|
||||
static List<Widget> _widgetOptions = <Widget>[
|
||||
ItemListScreen(),
|
||||
StatisticsScreen(),
|
||||
CategoryScreen(),
|
||||
MyScreen(),
|
||||
];
|
||||
|
||||
@ -37,6 +37,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
icon: Icon(Icons.add_business_sharp),
|
||||
label: '首页',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.add_business_sharp),
|
||||
label: '分类',
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.person),
|
||||
label: '我的',
|
||||
|
||||
@ -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<ItemListScreen> {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddItemScreen(itemToEdit: item),
|
||||
builder: (context) => ItemScreen(itemToEdit: item, ),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -63,7 +61,7 @@ class _ItemListScreenState extends State<ItemListScreen> {
|
||||
onPressed: () async {
|
||||
final shouldRefresh = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => AddItemScreen()),
|
||||
MaterialPageRoute(builder: (context) => ItemScreen()),
|
||||
);
|
||||
|
||||
print('shouldRefresh${shouldRefresh}');
|
||||
@ -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<AddItemScreen> {
|
||||
class _FromTestRouteSate extends State<ItemScreen> {
|
||||
String _name = '';
|
||||
String _description = '';
|
||||
String _location = '';
|
||||
@ -126,32 +124,34 @@ class _FromTestRouteSate extends State<AddItemScreen> {
|
||||
},
|
||||
),
|
||||
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<ItemProvider>(context, listen: false)
|
||||
.addItem(newItem);
|
||||
// 获取 ItemProvider 并添加物品
|
||||
Provider.of<ItemProvider>(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)
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -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;
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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('这里将显示统计信息'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user