This commit is contained in:
DelLevin-Home
2026-06-20 16:47:06 +08:00
parent 6c372c6589
commit 58be9f1d9d
6 changed files with 58 additions and 15 deletions

View File

@@ -263,7 +263,7 @@ class _BookFormPageState extends State<BookFormPage> {
icon: Icons.category_outlined, icon: Icons.category_outlined,
onTap: () async { onTap: () async {
final provider = context.read<AppProvider>(); final provider = context.read<AppProvider>();
final tags = await provider.getTags('book_genre'); final tags = await provider.getTags('book_genre', excludeHidden: true);
final existingNames = tags.map((t) => t['name'] as String).toList(); final existingNames = tags.map((t) => t['name'] as String).toList();
if (mounted) { if (mounted) {
_showMultiValueDialog( _showMultiValueDialog(

View File

@@ -539,7 +539,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
icon: Icons.category_outlined, icon: Icons.category_outlined,
onTap: () async { onTap: () async {
final provider = context.read<AppProvider>(); final provider = context.read<AppProvider>();
final tags = await provider.getTags('movie_genre'); final tags = await provider.getTags('movie_genre', excludeHidden: true);
final existingNames = tags.map((t) => t['name'] as String).toList(); final existingNames = tags.map((t) => t['name'] as String).toList();
if (mounted) { if (mounted) {
_showMultiValueDialog( _showMultiValueDialog(

View File

@@ -267,20 +267,22 @@ class _TagManagementPageState extends State<TagManagementPage> {
final count = _usageCounts[name] ?? 0; final count = _usageCounts[name] ?? 0;
final tagId = tag['id'] as String; final tagId = tag['id'] as String;
final isNew = tagId == _newlyAddedTagId; final isNew = tagId == _newlyAddedTagId;
final isHidden = (tag['is_hidden'] as int?) == 1;
return GestureDetector( return GestureDetector(
key: ValueKey(tagId), key: ValueKey(tagId),
onTap: () => _showTagMenu(tag), onTap: () => _showTagMenu(tag),
onLongPress: () => _showTagMenu(tag), onLongPress: () => _showTagMenu(tag),
child: Opacity(
opacity: isHidden ? 0.4 : 1.0,
child: isNew child: isNew
? _NewTagHighlight( ? _NewTagHighlight(child: _tagChipContent(name, count, colors, isHidden: isHidden))
child: _tagChipContent(name, count, colors), : _tagChipContent(name, count, colors, isHidden: isHidden),
) ),
: _tagChipContent(name, count, colors),
); );
} }
Widget _tagChipContent(String name, int count, ColorScheme colors) { Widget _tagChipContent(String name, int count, ColorScheme colors, {bool isHidden = false}) {
return Container( return Container(
padding: const EdgeInsets.only(left: 14, right: 10, top: 8, bottom: 8), padding: const EdgeInsets.only(left: 14, right: 10, top: 8, bottom: 8),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -293,7 +295,10 @@ class _TagManagementPageState extends State<TagManagementPage> {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text(name, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: colors.onSurface)), Text(name, style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w500, color: colors.onSurface,
decoration: isHidden ? TextDecoration.lineThrough : null,
)),
if (count > 0) ...[ if (count > 0) ...[
const SizedBox(width: 6), const SizedBox(width: 6),
Container( Container(
@@ -341,6 +346,7 @@ class _TagManagementPageState extends State<TagManagementPage> {
void _showTagMenu(Map<String, dynamic> tag) { void _showTagMenu(Map<String, dynamic> tag) {
final colors = Theme.of(context).colorScheme; final colors = Theme.of(context).colorScheme;
final name = tag['name'] as String; final name = tag['name'] as String;
final isHidden = (tag['is_hidden'] as int?) == 1;
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@@ -361,9 +367,28 @@ class _TagManagementPageState extends State<TagManagementPage> {
width: double.infinity, width: double.infinity,
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(12)), decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(12)),
child: Text(name, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface)), child: Row(
children: [
Expanded(child: Text(name, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface))),
if (isHidden) Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(color: colors.outlineVariant, borderRadius: BorderRadius.circular(4)),
child: Text('已隐藏', style: TextStyle(fontSize: 11, color: colors.onSurface.withValues(alpha: 0.5))),
),
],
),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_menuAction(
isHidden ? Icons.visibility_outlined : Icons.visibility_off_outlined,
isHidden ? '取消隐藏' : '隐藏',
colors,
() async {
Navigator.pop(ctx);
await context.read<AppProvider>().toggleTagHidden(tag['id'] as String);
await _loadTags(_currentType);
},
),
_menuAction(Icons.edit_outlined, '重命名', colors, () { _menuAction(Icons.edit_outlined, '重命名', colors, () {
Navigator.pop(ctx); Navigator.pop(ctx);
_showRenameDialog(tag); _showRenameDialog(tag);

View File

@@ -653,11 +653,16 @@ class AppProvider extends ChangeNotifier {
// ========== 标签管理方法 ========== // ========== 标签管理方法 ==========
Future<List<Map<String, dynamic>>> getTags(String type) async { Future<List<Map<String, dynamic>>> getTags(String type, {bool excludeHidden = false}) async {
if (_useRemote) { if (_useRemote) {
return await ServerDataService.instance.getTags(type.isEmpty ? null : type); return await ServerDataService.instance.getTags(type.isEmpty ? null : type);
} }
return await _tagDao.getTagsByType(type); return await _tagDao.getTagsByType(type, excludeHidden: excludeHidden);
}
Future<void> toggleTagHidden(String tagId) async {
await _tagDao.toggleHidden(tagId);
notifyListeners();
} }
Future<String> addTag(String name, String type) async { Future<String> addTag(String name, String type) async {

View File

@@ -57,7 +57,7 @@ class DatabaseHelper {
return await openDatabase( return await openDatabase(
path, path,
version: 16, version: 17,
onCreate: _createDB, onCreate: _createDB,
onUpgrade: _onUpgrade, onUpgrade: _onUpgrade,
); );
@@ -140,6 +140,9 @@ class DatabaseHelper {
await db.execute('ALTER TABLE books ADD COLUMN cover_offset REAL DEFAULT 0'); await db.execute('ALTER TABLE books ADD COLUMN cover_offset REAL DEFAULT 0');
} }
} }
if (oldVersion < 17) {
await db.execute('ALTER TABLE tags ADD COLUMN is_hidden INTEGER NOT NULL DEFAULT 0');
}
} }
/// 升级books表到V11添加ISBN和出版时间字段 /// 升级books表到V11添加ISBN和出版时间字段
@@ -207,6 +210,7 @@ class DatabaseHelper {
name TEXT NOT NULL, name TEXT NOT NULL,
type TEXT NOT NULL, type TEXT NOT NULL,
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
is_hidden INTEGER NOT NULL DEFAULT 0,
UNIQUE(name, type) UNIQUE(name, type)
) )
'''); ''');
@@ -631,6 +635,7 @@ class DatabaseHelper {
name TEXT NOT NULL, name TEXT NOT NULL,
type TEXT NOT NULL, type TEXT NOT NULL,
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
is_hidden INTEGER NOT NULL DEFAULT 0,
UNIQUE(name, type) UNIQUE(name, type)
) )
'''); ''');

View File

@@ -16,10 +16,12 @@ class TagDao {
} }
/// 获取指定类型的所有标签,按名称排序 /// 获取指定类型的所有标签,按名称排序
Future<List<Map<String, dynamic>>> getTagsByType(String type) => _wrap('getTagsByType', () async { /// [excludeHidden] 为 true 时,过滤掉隐藏标签
Future<List<Map<String, dynamic>>> getTagsByType(String type, {bool excludeHidden = false}) => _wrap('getTagsByType', () async {
final db = await _dbHelper.database; final db = await _dbHelper.database;
final where = excludeHidden ? 'type = ? AND is_hidden = 0' : 'type = ?';
return await db.query('tags', return await db.query('tags',
where: 'type = ?', where: where,
whereArgs: [type], whereArgs: [type],
orderBy: 'name ASC'); orderBy: 'name ASC');
}); });
@@ -122,6 +124,12 @@ class TagDao {
await db.delete('tags', where: 'id = ?', whereArgs: [tagId]); await db.delete('tags', where: 'id = ?', whereArgs: [tagId]);
}); });
/// 切换标签的隐藏状态
Future<void> toggleHidden(String tagId) => _wrap('toggleHidden', () async {
final db = await _dbHelper.database;
await db.rawUpdate('UPDATE tags SET is_hidden = 1 - is_hidden WHERE id = ?', [tagId]);
});
/// 确保标签存在(用于替换操作) /// 确保标签存在(用于替换操作)
Future<void> _ensureTagExists(Transaction txn, String name, String type) async { Future<void> _ensureTagExists(Transaction txn, String name, String type) async {
final existing = await txn.query('tags', final existing = await txn.query('tags',