generated from dellevin/template
优化回收站
This commit is contained in:
@@ -67,6 +67,29 @@ class BookCharacterDao {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// 恢复已删除的角色
|
||||||
|
Future<int> restore(String id) => _wrap('restore', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
return await db.update(
|
||||||
|
'book_characters',
|
||||||
|
{'is_deleted': 0, 'updated_at': DateTime.now().toUtc().toIso8601String()},
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 获取已删除的角色
|
||||||
|
Future<List<BookCharacter>> getDeleted() => _wrap('getDeleted', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
final maps = await db.query(
|
||||||
|
'book_characters',
|
||||||
|
where: 'is_deleted = ?',
|
||||||
|
whereArgs: [1],
|
||||||
|
orderBy: 'updated_at DESC',
|
||||||
|
);
|
||||||
|
return maps.map((m) => BookCharacter.fromJson(m)).toList();
|
||||||
|
});
|
||||||
|
|
||||||
/// 获取书籍的角色数量
|
/// 获取书籍的角色数量
|
||||||
Future<int> getCount(String bookId) => _wrap('getCount', () async {
|
Future<int> getCount(String bookId) => _wrap('getCount', () async {
|
||||||
final db = await _dbHelper.database;
|
final db = await _dbHelper.database;
|
||||||
|
|||||||
@@ -67,6 +67,29 @@ class GameCharacterDao {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// 恢复已删除的角色
|
||||||
|
Future<int> restore(String id) => _wrap('restore', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
return await db.update(
|
||||||
|
'game_characters',
|
||||||
|
{'is_deleted': 0, 'updated_at': DateTime.now().toUtc().toIso8601String()},
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 获取已删除的角色
|
||||||
|
Future<List<GameCharacter>> getDeleted() => _wrap('getDeleted', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
final maps = await db.query(
|
||||||
|
'game_characters',
|
||||||
|
where: 'is_deleted = ?',
|
||||||
|
whereArgs: [1],
|
||||||
|
orderBy: 'updated_at DESC',
|
||||||
|
);
|
||||||
|
return maps.map((m) => GameCharacter.fromJson(m)).toList();
|
||||||
|
});
|
||||||
|
|
||||||
/// 获取游戏的角色数量
|
/// 获取游戏的角色数量
|
||||||
Future<int> getCount(String gameId) => _wrap('getCount', () async {
|
Future<int> getCount(String gameId) => _wrap('getCount', () async {
|
||||||
final db = await _dbHelper.database;
|
final db = await _dbHelper.database;
|
||||||
|
|||||||
@@ -67,6 +67,29 @@ class MovieCharacterDao {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// 恢复已删除的角色
|
||||||
|
Future<int> restore(String id) => _wrap('restore', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
return await db.update(
|
||||||
|
'movie_characters',
|
||||||
|
{'is_deleted': 0, 'updated_at': DateTime.now().toUtc().toIso8601String()},
|
||||||
|
where: 'id = ?',
|
||||||
|
whereArgs: [id],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// 获取已删除的角色
|
||||||
|
Future<List<MovieCharacter>> getDeleted() => _wrap('getDeleted', () async {
|
||||||
|
final db = await _dbHelper.database;
|
||||||
|
final maps = await db.query(
|
||||||
|
'movie_characters',
|
||||||
|
where: 'is_deleted = ?',
|
||||||
|
whereArgs: [1],
|
||||||
|
orderBy: 'updated_at DESC',
|
||||||
|
);
|
||||||
|
return maps.map((m) => MovieCharacter.fromJson(m)).toList();
|
||||||
|
});
|
||||||
|
|
||||||
/// 获取影视的角色数量
|
/// 获取影视的角色数量
|
||||||
Future<int> getCount(String movieId) => _wrap('getCount', () async {
|
Future<int> getCount(String movieId) => _wrap('getCount', () async {
|
||||||
final db = await _dbHelper.database;
|
final db = await _dbHelper.database;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import '../../models/data_models.dart';
|
import '../../models/data_models.dart';
|
||||||
import '../../providers/app_provider.dart';
|
import '../../providers/app_provider.dart';
|
||||||
import '../../utils/image_path_helper.dart';
|
|
||||||
import '../../utils/toast_util.dart';
|
import '../../utils/toast_util.dart';
|
||||||
import '../../widgets/fade_in_local_image.dart';
|
import '../../widgets/fade_in_local_image.dart';
|
||||||
import 'character_form_page.dart';
|
import 'character_form_page.dart';
|
||||||
@@ -51,9 +50,6 @@ class _MovieCharactersPageState extends State<MovieCharactersPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _delete(MovieCharacter c) async {
|
Future<void> _delete(MovieCharacter c) async {
|
||||||
if (c.imagePath != null && c.imagePath!.isNotEmpty) {
|
|
||||||
await ImagePathHelper.instance.deleteCharacterImages(c.id);
|
|
||||||
}
|
|
||||||
await context.read<AppProvider>().deleteMovieCharacter(c.id);
|
await context.read<AppProvider>().deleteMovieCharacter(c.id);
|
||||||
_loadCharacters();
|
_loadCharacters();
|
||||||
if (mounted) ToastUtil.show(context, '已删除');
|
if (mounted) ToastUtil.show(context, '已删除');
|
||||||
@@ -116,9 +112,6 @@ class _BookCharactersPageState extends State<BookCharactersPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _delete(BookCharacter c) async {
|
Future<void> _delete(BookCharacter c) async {
|
||||||
if (c.imagePath != null && c.imagePath!.isNotEmpty) {
|
|
||||||
await ImagePathHelper.instance.deleteCharacterImages(c.id);
|
|
||||||
}
|
|
||||||
await context.read<AppProvider>().deleteBookCharacter(c.id);
|
await context.read<AppProvider>().deleteBookCharacter(c.id);
|
||||||
_loadCharacters();
|
_loadCharacters();
|
||||||
if (mounted) ToastUtil.show(context, '已删除');
|
if (mounted) ToastUtil.show(context, '已删除');
|
||||||
@@ -181,9 +174,6 @@ class _GameCharactersPageState extends State<GameCharactersPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _delete(GameCharacter c) async {
|
Future<void> _delete(GameCharacter c) async {
|
||||||
if (c.imagePath != null && c.imagePath!.isNotEmpty) {
|
|
||||||
await ImagePathHelper.instance.deleteCharacterImages(c.id);
|
|
||||||
}
|
|
||||||
await context.read<AppProvider>().deleteGameCharacter(c.id);
|
await context.read<AppProvider>().deleteGameCharacter(c.id);
|
||||||
_loadCharacters();
|
_loadCharacters();
|
||||||
if (mounted) ToastUtil.show(context, '已删除');
|
if (mounted) ToastUtil.show(context, '已删除');
|
||||||
@@ -247,7 +237,7 @@ class _CharacterListScaffold extends StatelessWidget {
|
|||||||
description: c.description as String?,
|
description: c.description as String?,
|
||||||
imagePath: c.imagePath as String?,
|
imagePath: c.imagePath as String?,
|
||||||
onTap: () => onTap(c),
|
onTap: () => onTap(c),
|
||||||
onDelete: () => _confirmDelete(context, c),
|
onDelete: () => onDelete(c),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -276,41 +266,6 @@ class _CharacterListScaffold extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _confirmDelete(BuildContext context, dynamic c) {
|
|
||||||
final colors = Theme.of(context).colorScheme;
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (ctx) => AlertDialog(
|
|
||||||
backgroundColor: colors.surface,
|
|
||||||
elevation: 0,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
title: Text('确认删除', style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
|
||||||
content: Text('确定要删除该角色吗?',
|
|
||||||
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.6), height: 1.5)),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(ctx),
|
|
||||||
child: Text('取消', style: TextStyle(color: colors.onSurface.withValues(alpha: 0.6))),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(ctx);
|
|
||||||
onDelete(c);
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: colors.error,
|
|
||||||
foregroundColor: colors.onError,
|
|
||||||
elevation: 0,
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
|
||||||
),
|
|
||||||
child: const Text('删除'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
actionsPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CharacterTile extends StatelessWidget {
|
class _CharacterTile extends StatelessWidget {
|
||||||
@@ -335,61 +290,48 @@ class _CharacterTile extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
return Dismissible(
|
return ListTile(
|
||||||
key: ValueKey(name + imagePath.toString()),
|
onTap: onTap,
|
||||||
direction: DismissDirection.endToStart,
|
onLongPress: () => _showDeleteDialog(context),
|
||||||
background: Container(
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
color: colors.error,
|
leading: _buildAvatar(colors),
|
||||||
alignment: Alignment.centerRight,
|
title: Text(name, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
padding: const EdgeInsets.only(right: 20),
|
subtitle: Column(
|
||||||
child: Icon(Icons.delete_outline, color: colors.onError),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
confirmDismiss: (_) async {
|
if (aliases.isNotEmpty)
|
||||||
_showDeleteDialog(context);
|
Padding(
|
||||||
return false;
|
padding: const EdgeInsets.only(top: 2),
|
||||||
},
|
child: Text(aliases.join('、'),
|
||||||
child: ListTile(
|
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||||
onTap: onTap,
|
),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
if (tags.isNotEmpty)
|
||||||
leading: _buildAvatar(colors),
|
Padding(
|
||||||
title: Text(name, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
padding: const EdgeInsets.only(top: 6),
|
||||||
subtitle: Column(
|
child: Wrap(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
spacing: 4,
|
||||||
children: [
|
runSpacing: 4,
|
||||||
if (aliases.isNotEmpty)
|
children: tags.map((t) => Container(
|
||||||
Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||||
padding: const EdgeInsets.only(top: 2),
|
decoration: BoxDecoration(
|
||||||
child: Text(aliases.join('、'),
|
color: colors.surfaceContainerHighest,
|
||||||
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.4))),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
if (tags.isNotEmpty)
|
child: Text(t, style: TextStyle(fontSize: 11, color: colors.onSurface.withValues(alpha: 0.6))),
|
||||||
Padding(
|
)).toList(),
|
||||||
padding: const EdgeInsets.only(top: 6),
|
),
|
||||||
child: Wrap(
|
),
|
||||||
spacing: 4,
|
if (description != null && description!.isNotEmpty)
|
||||||
runSpacing: 4,
|
Padding(
|
||||||
children: tags.map((t) => Container(
|
padding: const EdgeInsets.only(top: 4),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
child: Text(description!,
|
||||||
decoration: BoxDecoration(
|
maxLines: 2,
|
||||||
color: colors.surfaceContainerHighest,
|
overflow: TextOverflow.ellipsis,
|
||||||
borderRadius: BorderRadius.circular(4),
|
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.4), height: 1.4)),
|
||||||
),
|
),
|
||||||
child: Text(t, style: TextStyle(fontSize: 11, color: colors.onSurface.withValues(alpha: 0.6))),
|
],
|
||||||
)).toList(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (description != null && description!.isNotEmpty)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4),
|
|
||||||
child: Text(description!,
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.4), height: 1.4)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
trailing: Icon(Icons.chevron_right, size: 18, color: colors.onSurface.withValues(alpha: 0.25)),
|
|
||||||
),
|
),
|
||||||
|
trailing: Icon(Icons.chevron_right, size: 18, color: colors.onSurface.withValues(alpha: 0.25)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class RecycleBinPage extends StatefulWidget {
|
|||||||
State<RecycleBinPage> createState() => _RecycleBinPageState();
|
State<RecycleBinPage> createState() => _RecycleBinPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _ItemType { movie, book, note, game, movieReview, bookReview, bookExcerpt, gameReview, person }
|
enum _ItemType { movie, book, note, game, movieReview, bookReview, bookExcerpt, gameReview, person, movieCharacter, bookCharacter, gameCharacter }
|
||||||
|
|
||||||
class _DeletedItem {
|
class _DeletedItem {
|
||||||
final _ItemType type;
|
final _ItemType type;
|
||||||
@@ -94,6 +94,30 @@ class _DeletedItem {
|
|||||||
icon = Icons.person_outline,
|
icon = Icons.person_outline,
|
||||||
typeLabel = '人物';
|
typeLabel = '人物';
|
||||||
|
|
||||||
|
_DeletedItem.movieCharacter(MovieCharacter c)
|
||||||
|
: type = _ItemType.movieCharacter,
|
||||||
|
id = c.id,
|
||||||
|
title = c.name,
|
||||||
|
subtitle = '删除于 ${c.updatedAt.year}.${c.updatedAt.month.toString().padLeft(2, '0')}.${c.updatedAt.day.toString().padLeft(2, '0')}',
|
||||||
|
icon = Icons.movie_outlined,
|
||||||
|
typeLabel = '影视角色';
|
||||||
|
|
||||||
|
_DeletedItem.bookCharacter(BookCharacter c)
|
||||||
|
: type = _ItemType.bookCharacter,
|
||||||
|
id = c.id,
|
||||||
|
title = c.name,
|
||||||
|
subtitle = '删除于 ${c.updatedAt.year}.${c.updatedAt.month.toString().padLeft(2, '0')}.${c.updatedAt.day.toString().padLeft(2, '0')}',
|
||||||
|
icon = Icons.menu_book_outlined,
|
||||||
|
typeLabel = '书籍角色';
|
||||||
|
|
||||||
|
_DeletedItem.gameCharacter(GameCharacter c)
|
||||||
|
: type = _ItemType.gameCharacter,
|
||||||
|
id = c.id,
|
||||||
|
title = c.name,
|
||||||
|
subtitle = '删除于 ${c.updatedAt.year}.${c.updatedAt.month.toString().padLeft(2, '0')}.${c.updatedAt.day.toString().padLeft(2, '0')}',
|
||||||
|
icon = Icons.sports_esports_outlined,
|
||||||
|
typeLabel = '游戏角色';
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RecycleBinPageState extends State<RecycleBinPage> {
|
class _RecycleBinPageState extends State<RecycleBinPage> {
|
||||||
@@ -122,6 +146,9 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
final bookExcerpts = await provider.getDeletedBookExcerpts();
|
final bookExcerpts = await provider.getDeletedBookExcerpts();
|
||||||
final gameReviews = await provider.getDeletedGameReviews();
|
final gameReviews = await provider.getDeletedGameReviews();
|
||||||
final people = await provider.getDeletedPeople();
|
final people = await provider.getDeletedPeople();
|
||||||
|
final movieCharacters = await provider.getDeletedMovieCharacters();
|
||||||
|
final bookCharacters = await provider.getDeletedBookCharacters();
|
||||||
|
final gameCharacters = await provider.getDeletedGameCharacters();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_allItems = [
|
_allItems = [
|
||||||
@@ -134,6 +161,9 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
for (final e in bookExcerpts) _DeletedItem.bookExcerpt(e),
|
for (final e in bookExcerpts) _DeletedItem.bookExcerpt(e),
|
||||||
for (final r in gameReviews) _DeletedItem.gameReview(r),
|
for (final r in gameReviews) _DeletedItem.gameReview(r),
|
||||||
for (final p in people) _DeletedItem.person(p),
|
for (final p in people) _DeletedItem.person(p),
|
||||||
|
for (final c in movieCharacters) _DeletedItem.movieCharacter(c),
|
||||||
|
for (final c in bookCharacters) _DeletedItem.bookCharacter(c),
|
||||||
|
for (final c in gameCharacters) _DeletedItem.gameCharacter(c),
|
||||||
];
|
];
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
});
|
});
|
||||||
@@ -191,27 +221,54 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
|
|
||||||
Widget _buildFilterRow() {
|
Widget _buildFilterRow() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final chips = <Widget>[
|
||||||
|
_filterChip('全部', null),
|
||||||
|
_filterChip('影视', _ItemType.movie),
|
||||||
|
_filterChip('书籍', _ItemType.book),
|
||||||
|
_filterChip('笔记', _ItemType.note),
|
||||||
|
_filterChip('游戏', _ItemType.game),
|
||||||
|
_filterChip('影评', _ItemType.movieReview),
|
||||||
|
_filterChip('书评', _ItemType.bookReview),
|
||||||
|
_filterChip('书摘', _ItemType.bookExcerpt),
|
||||||
|
_filterChip('游戏评价', _ItemType.gameReview),
|
||||||
|
_filterChip('人物', _ItemType.person),
|
||||||
|
_filterChip('影视角色', _ItemType.movieCharacter),
|
||||||
|
_filterChip('书籍角色', _ItemType.bookCharacter),
|
||||||
|
_filterChip('游戏角色', _ItemType.gameCharacter),
|
||||||
|
];
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(bottom: BorderSide(color: colors.outlineVariant, width: 0.5)),
|
border: Border(bottom: BorderSide(color: colors.outlineVariant, width: 0.5)),
|
||||||
),
|
),
|
||||||
child: Wrap(
|
child: ShaderMask(
|
||||||
spacing: 8,
|
shaderCallback: (Rect bounds) {
|
||||||
runSpacing: 8,
|
return const LinearGradient(
|
||||||
children: [
|
begin: Alignment.centerLeft,
|
||||||
_filterChip('全部', null),
|
end: Alignment.centerRight,
|
||||||
_filterChip('影视', _ItemType.movie),
|
colors: [
|
||||||
_filterChip('书籍', _ItemType.book),
|
Color(0x00FFFFFF),
|
||||||
_filterChip('笔记', _ItemType.note),
|
Color(0xFFFFFFFF),
|
||||||
_filterChip('游戏', _ItemType.game),
|
Color(0xFFFFFFFF),
|
||||||
_filterChip('影评', _ItemType.movieReview),
|
Color(0x00FFFFFF),
|
||||||
_filterChip('书评', _ItemType.bookReview),
|
],
|
||||||
_filterChip('书摘', _ItemType.bookExcerpt),
|
stops: [0.0, 0.04, 0.96, 1.0],
|
||||||
_filterChip('游戏评价', _ItemType.gameReview),
|
).createShader(bounds);
|
||||||
_filterChip('人物', _ItemType.person),
|
},
|
||||||
],
|
blendMode: BlendMode.dstIn,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for (var i = 0; i < chips.length; i++) ...[
|
||||||
|
if (i > 0) const SizedBox(width: 8),
|
||||||
|
chips[i],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -442,6 +499,15 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
case _ItemType.person:
|
case _ItemType.person:
|
||||||
await provider.restorePerson(item.id);
|
await provider.restorePerson(item.id);
|
||||||
if (mounted) ToastUtil.show(context, '人物已恢复');
|
if (mounted) ToastUtil.show(context, '人物已恢复');
|
||||||
|
case _ItemType.movieCharacter:
|
||||||
|
await provider.restoreMovieCharacter(item.id);
|
||||||
|
if (mounted) ToastUtil.show(context, '影视角色已恢复');
|
||||||
|
case _ItemType.bookCharacter:
|
||||||
|
await provider.restoreBookCharacter(item.id);
|
||||||
|
if (mounted) ToastUtil.show(context, '书籍角色已恢复');
|
||||||
|
case _ItemType.gameCharacter:
|
||||||
|
await provider.restoreGameCharacter(item.id);
|
||||||
|
if (mounted) ToastUtil.show(context, '游戏角色已恢复');
|
||||||
}
|
}
|
||||||
_loadDeletedItems();
|
_loadDeletedItems();
|
||||||
}
|
}
|
||||||
@@ -469,6 +535,12 @@ class _RecycleBinPageState extends State<RecycleBinPage> {
|
|||||||
await provider.permanentDeleteGameReview(item.id);
|
await provider.permanentDeleteGameReview(item.id);
|
||||||
case _ItemType.person:
|
case _ItemType.person:
|
||||||
await provider.permanentDeletePerson(item.id);
|
await provider.permanentDeletePerson(item.id);
|
||||||
|
case _ItemType.movieCharacter:
|
||||||
|
await provider.permanentDeleteMovieCharacter(item.id);
|
||||||
|
case _ItemType.bookCharacter:
|
||||||
|
await provider.permanentDeleteBookCharacter(item.id);
|
||||||
|
case _ItemType.gameCharacter:
|
||||||
|
await provider.permanentDeleteGameCharacter(item.id);
|
||||||
}
|
}
|
||||||
_loadDeletedItems();
|
_loadDeletedItems();
|
||||||
if (mounted) ToastUtil.show(context, '已彻底删除');
|
if (mounted) ToastUtil.show(context, '已彻底删除');
|
||||||
|
|||||||
@@ -962,6 +962,9 @@ class AppProvider extends ChangeNotifier {
|
|||||||
final deletedBookExcerpts = await getDeletedBookExcerpts();
|
final deletedBookExcerpts = await getDeletedBookExcerpts();
|
||||||
final deletedGameReviews = await getDeletedGameReviews();
|
final deletedGameReviews = await getDeletedGameReviews();
|
||||||
final deletedPeople = await getDeletedPeople();
|
final deletedPeople = await getDeletedPeople();
|
||||||
|
final deletedMovieCharacters = await getDeletedMovieCharacters();
|
||||||
|
final deletedBookCharacters = await getDeletedBookCharacters();
|
||||||
|
final deletedGameCharacters = await getDeletedGameCharacters();
|
||||||
|
|
||||||
// 先收集需要删除图片的 ID,再在事务中批量删除数据库记录
|
// 先收集需要删除图片的 ID,再在事务中批量删除数据库记录
|
||||||
final movieIds = deletedMovies.map((m) => m.id).toList();
|
final movieIds = deletedMovies.map((m) => m.id).toList();
|
||||||
@@ -969,6 +972,9 @@ class AppProvider extends ChangeNotifier {
|
|||||||
final noteIds = deletedNotes.map((n) => n.id).toList();
|
final noteIds = deletedNotes.map((n) => n.id).toList();
|
||||||
final gameIds = deletedGames.map((g) => g.id).toList();
|
final gameIds = deletedGames.map((g) => g.id).toList();
|
||||||
final personIds = deletedPeople.map((p) => p.id).toList();
|
final personIds = deletedPeople.map((p) => p.id).toList();
|
||||||
|
final movieCharacterIds = deletedMovieCharacters.map((c) => c.id).toList();
|
||||||
|
final bookCharacterIds = deletedBookCharacters.map((c) => c.id).toList();
|
||||||
|
final gameCharacterIds = deletedGameCharacters.map((c) => c.id).toList();
|
||||||
|
|
||||||
// 事务内批量删除数据库记录,保证原子性
|
// 事务内批量删除数据库记录,保证原子性
|
||||||
final db = await DatabaseHelper.instance.database;
|
final db = await DatabaseHelper.instance.database;
|
||||||
@@ -1009,6 +1015,15 @@ class AppProvider extends ChangeNotifier {
|
|||||||
await txn.delete('game_people', where: 'person_id = ?', whereArgs: [id]);
|
await txn.delete('game_people', where: 'person_id = ?', whereArgs: [id]);
|
||||||
await txn.delete('people', where: 'id = ?', whereArgs: [id]);
|
await txn.delete('people', where: 'id = ?', whereArgs: [id]);
|
||||||
}
|
}
|
||||||
|
for (final id in movieCharacterIds) {
|
||||||
|
await txn.delete('movie_characters', where: 'id = ?', whereArgs: [id]);
|
||||||
|
}
|
||||||
|
for (final id in bookCharacterIds) {
|
||||||
|
await txn.delete('book_characters', where: 'id = ?', whereArgs: [id]);
|
||||||
|
}
|
||||||
|
for (final id in gameCharacterIds) {
|
||||||
|
await txn.delete('game_characters', where: 'id = ?', whereArgs: [id]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 事务成功后,清理关联的图片文件(文件删除失败不影响数据一致性)
|
// 事务成功后,清理关联的图片文件(文件删除失败不影响数据一致性)
|
||||||
@@ -1027,6 +1042,15 @@ class AppProvider extends ChangeNotifier {
|
|||||||
for (final id in personIds) {
|
for (final id in personIds) {
|
||||||
await ImagePathHelper.instance.deletePersonImages(id);
|
await ImagePathHelper.instance.deletePersonImages(id);
|
||||||
}
|
}
|
||||||
|
for (final id in movieCharacterIds) {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
}
|
||||||
|
for (final id in bookCharacterIds) {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
}
|
||||||
|
for (final id in gameCharacterIds) {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
}
|
||||||
|
|
||||||
await loadMovies();
|
await loadMovies();
|
||||||
await loadBooks();
|
await loadBooks();
|
||||||
@@ -1482,6 +1506,23 @@ class AppProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 恢复已删除的影视角色
|
||||||
|
Future<void> restoreMovieCharacter(String id) async {
|
||||||
|
await _movieCharacterDao.restore(id);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 彻底删除影视角色
|
||||||
|
Future<void> permanentDeleteMovieCharacter(String id) async {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
await _movieCharacterDao.permanentDelete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取已删除的影视角色
|
||||||
|
Future<List<MovieCharacter>> getDeletedMovieCharacters() async {
|
||||||
|
return await _movieCharacterDao.getDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 书籍角色 ───
|
// ─── 书籍角色 ───
|
||||||
Future<List<BookCharacter>> getBookCharacters(String bookId) async {
|
Future<List<BookCharacter>> getBookCharacters(String bookId) async {
|
||||||
return await _bookCharacterDao.getByBookId(bookId);
|
return await _bookCharacterDao.getByBookId(bookId);
|
||||||
@@ -1506,6 +1547,23 @@ class AppProvider extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 恢复已删除的书籍角色
|
||||||
|
Future<void> restoreBookCharacter(String id) async {
|
||||||
|
await _bookCharacterDao.restore(id);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 彻底删除书籍角色
|
||||||
|
Future<void> permanentDeleteBookCharacter(String id) async {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
await _bookCharacterDao.permanentDelete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取已删除的书籍角色
|
||||||
|
Future<List<BookCharacter>> getDeletedBookCharacters() async {
|
||||||
|
return await _bookCharacterDao.getDeleted();
|
||||||
|
}
|
||||||
|
|
||||||
// ─── 游戏角色 ───
|
// ─── 游戏角色 ───
|
||||||
Future<List<GameCharacter>> getGameCharacters(String gameId) async {
|
Future<List<GameCharacter>> getGameCharacters(String gameId) async {
|
||||||
return await _gameCharacterDao.getByGameId(gameId);
|
return await _gameCharacterDao.getByGameId(gameId);
|
||||||
@@ -1529,4 +1587,21 @@ class AppProvider extends ChangeNotifier {
|
|||||||
await _gameCharacterDao.delete(id);
|
await _gameCharacterDao.delete(id);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 恢复已删除的游戏角色
|
||||||
|
Future<void> restoreGameCharacter(String id) async {
|
||||||
|
await _gameCharacterDao.restore(id);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 彻底删除游戏角色
|
||||||
|
Future<void> permanentDeleteGameCharacter(String id) async {
|
||||||
|
await ImagePathHelper.instance.deleteCharacterImages(id);
|
||||||
|
await _gameCharacterDao.permanentDelete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取已删除的游戏角色
|
||||||
|
Future<List<GameCharacter>> getDeletedGameCharacters() async {
|
||||||
|
return await _gameCharacterDao.getDeleted();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user