测试块编辑器笔记

This commit is contained in:
DelLevin-Home
2026-06-26 05:53:40 +08:00
parent 22797603f5
commit f7ef50a677
20 changed files with 3743 additions and 1 deletions

View File

@@ -8,6 +8,8 @@ import '../pages/movies/movie_detail_page.dart';
import '../pages/book/book_detail_page.dart';
import '../pages/note/note_detail_page.dart';
import '../pages/movies/douban_webview_page.dart';
import '../pages/note_plus/note_plus_form_page.dart';
import '../pages/note_plus/note_plus_detail_page.dart';
/// 路由生成器
class AppRouter {
@@ -64,6 +66,20 @@ class AppRouter {
}
return SlideUpPageRoute(page: DoubanWebViewPage(url: url));
case '/note-plus-form':
final id = settings.arguments is String ? settings.arguments as String : null;
if (id == null) {
return _buildUnknownRoute(settings.name);
}
return SlideUpPageRoute(page: NotePlusFormPage(documentId: id));
case '/note-plus-detail':
final id = settings.arguments is String ? settings.arguments as String : null;
if (id == null) {
return _buildUnknownRoute(settings.name);
}
return SlideUpPageRoute(page: NotePlusDetailPage(documentId: id));
default:
return _buildUnknownRoute(settings.name);
}

View File

@@ -39,7 +39,7 @@ class DatabaseHelper {
return await openDatabase(
path,
version: 17,
version: 21,
onCreate: _createDB,
onUpgrade: _onUpgrade,
);
@@ -125,6 +125,33 @@ class DatabaseHelper {
if (oldVersion < 17) {
await db.execute('ALTER TABLE tags ADD COLUMN is_hidden INTEGER NOT NULL DEFAULT 0');
}
if (oldVersion < 18) {
await _createNotePlusTable(db);
}
if (oldVersion < 19) {
await _createNotePlusTable(db);
}
if (oldVersion < 20) {
// 确保 note_plus 表有 parent_id 列(从旧版 folder 迁移)
try {
final cols = await db.rawQuery('PRAGMA table_info(note_plus)');
if (!cols.any((col) => col['name'] == 'parent_id')) {
if (cols.any((col) => col['name'] == 'folder')) {
await db.execute("ALTER TABLE note_plus RENAME COLUMN folder TO parent_id");
} else {
await db.execute("ALTER TABLE note_plus ADD COLUMN parent_id TEXT DEFAULT ''");
}
}
} catch (_) {}
}
if (oldVersion < 21) {
try {
final cols = await db.rawQuery('PRAGMA table_info(note_plus)');
if (!cols.any((col) => col['name'] == 'sort_index')) {
await db.execute("ALTER TABLE note_plus ADD COLUMN sort_index INTEGER DEFAULT 0");
}
} catch (_) {}
}
}
/// 升级books表到V11添加ISBN和出版时间字段
@@ -639,6 +666,27 @@ class DatabaseHelper {
is_deleted INTEGER DEFAULT 0
)
''');
// Note Plus 块编辑器文档表
await _createNotePlusTable(db);
}
/// 创建 Note Plus 文档表
Future<void> _createNotePlusTable(Database db) async {
await db.execute('''
CREATE TABLE IF NOT EXISTS note_plus (
id TEXT PRIMARY KEY,
title TEXT DEFAULT '',
parent_id TEXT DEFAULT '',
sort_index INTEGER DEFAULT 0,
blocks_json TEXT NOT NULL,
tags TEXT,
images TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
is_deleted INTEGER DEFAULT 0
)
''');
}
// 关闭数据库

View File

@@ -0,0 +1,134 @@
import 'package:flutter/foundation.dart';
import '../../models/note_plus_models.dart';
import '../database_helper.dart';
/// Note Plus 块文档数据访问对象
class NotePlusDao {
final DatabaseHelper _dbHelper = DatabaseHelper.instance;
Future<T> _wrap<T>(String op, Future<T> Function() fn) async {
try {
return await fn();
} catch (e) {
debugPrint('[NotePlusDao] $op error: $e');
rethrow;
}
}
// 获取所有未删除的文档
Future<List<NotePlusDocument>> getAll() => _wrap('getAll', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'note_plus',
where: 'is_deleted = ?',
whereArgs: [0],
orderBy: 'sort_index ASC, updated_at DESC',
);
return List.generate(maps.length, (i) => NotePlusDocument.fromJson(maps[i]));
});
// 分页查询
Future<List<NotePlusDocument>> getPaged({int limit = 20, int offset = 0}) =>
_wrap('getPaged', () async {
final db = await _dbHelper.database;
final maps = await db.query('note_plus', where: 'is_deleted = 0',
orderBy: 'sort_index ASC, updated_at DESC', limit: limit, offset: offset);
return List.generate(maps.length, (i) => NotePlusDocument.fromJson(maps[i]));
});
// 根据ID获取
Future<NotePlusDocument?> getById(String id) => _wrap('getById', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'note_plus',
where: 'id = ? AND is_deleted = ?',
whereArgs: [id, 0],
);
if (maps.isEmpty) return null;
return NotePlusDocument.fromJson(maps.first);
});
// 插入
Future<int> insert(NotePlusDocument doc) => _wrap('insert', () async {
final db = await _dbHelper.database;
return await db.insert('note_plus', doc.toJson());
});
// 更新
Future<int> update(NotePlusDocument doc) => _wrap('update', () async {
final db = await _dbHelper.database;
return await db.update(
'note_plus',
doc.toJson(),
where: 'id = ?',
whereArgs: [doc.id],
);
});
// 软删除
Future<int> delete(String id) => _wrap('delete', () async {
final db = await _dbHelper.database;
return await db.update(
'note_plus',
{'is_deleted': 1, 'updated_at': DateTime.now().toIso8601String()},
where: 'id = ?',
whereArgs: [id],
);
});
// ========== 回收站 ==========
Future<List<NotePlusDocument>> getDeleted() => _wrap('getDeleted', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'note_plus',
where: 'is_deleted = ?',
whereArgs: [1],
orderBy: 'sort_index ASC, updated_at DESC',
);
return List.generate(maps.length, (i) => NotePlusDocument.fromJson(maps[i]));
});
Future<int> restore(String id) => _wrap('restore', () async {
final db = await _dbHelper.database;
return await db.update(
'note_plus',
{'is_deleted': 0, 'updated_at': DateTime.now().toIso8601String()},
where: 'id = ?',
whereArgs: [id],
);
});
Future<int> permanentDelete(String id) => _wrap('permanentDelete', () async {
final db = await _dbHelper.database;
return await db.delete(
'note_plus',
where: 'id = ?',
whereArgs: [id],
);
});
// 搜索
Future<List<NotePlusDocument>> search(String query) => _wrap('search', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'note_plus',
where: '(title LIKE ? OR blocks_json LIKE ? OR tags LIKE ?) AND is_deleted = ?',
whereArgs: ['%$query%', '%$query%', '%$query%', 0],
orderBy: 'sort_index ASC, updated_at DESC',
);
return List.generate(maps.length, (i) => NotePlusDocument.fromJson(maps[i]));
});
// 根据标签筛选
Future<List<NotePlusDocument>> getByTag(String tag) => _wrap('getByTag', () async {
final db = await _dbHelper.database;
final List<Map<String, dynamic>> maps = await db.query(
'note_plus',
where: 'tags LIKE ? AND is_deleted = ?',
whereArgs: ['%$tag%', 0],
orderBy: 'sort_index ASC, updated_at DESC',
);
return List.generate(maps.length, (i) => NotePlusDocument.fromJson(maps[i]));
});
}

View File

@@ -89,6 +89,10 @@ class UserPrefs {
bool get showNoteTab => prefs.getBool('showNoteTab') ?? true;
Future<bool> setShowNoteTab(bool value) => prefs.setBool('showNoteTab', value);
/// 是否显示 Note Plus 标签(默认关闭)
bool get showNotePlusTab => prefs.getBool('showNotePlusTab') ?? false;
Future<bool> setShowNotePlusTab(bool value) => prefs.setBool('showNotePlusTab', value);
/// 默认启动标签 (0: 影视, 1: 阅读, 2: 笔记)
int get defaultMainTabIndex => prefs.getInt('defaultMainTabIndex') ?? 0;
Future<bool> setDefaultMainTabIndex(int value) => prefs.setInt('defaultMainTabIndex', value);