Skip to content

Commit d784c99

Browse files
committed
feat(favorite, ui): 完善收藏功能,引入独立标签管理、国际化支持及全面集成测试
本次更新全面增强收藏功能,引入独立的标签管理系统,完善了多语言支持,并对UI组件进行了优化。同时,新增了大量集成测试以确保功能的稳定性和数据完整性。 ## 新增功能与优化 ### 1. 独立标签管理系统 - **核心功能**: - **独立标签库 (STORAGE_KEYS.TAGS)**: 实现标签的独立存储,不依赖收藏项,支持预先创建使用次数为0的标签。 - **标签CRUD操作**: 提供 `addTag()` (支持重复检测)、`renameTag()` (更新所有相关收藏和独立库)、`mergeTags()` (合并多个标签)、`deleteTag()` (从独立库和收藏中移除) 功能。 - **标签统计算法**: `getAllTags()` 方法返回独立标签库与收藏中使用标签的合集,并支持按使用次数降序排序。 - **导入导出优化**: 导出内容包含独立标签库,导入时自动重建,确保数据完整性。 - **类型转换工具 (TagTypeConverter)**: 解决API与UI格式不一致问题,提供 `toTagStatistics()`、`toAutoCompleteOptions()` 等转换方法,并支持多种排序。 - **错误处理增强**: 新增 `FavoriteTagError` 基类及 `FavoriteTagAlreadyExistsError`、`FavoriteTagNotFoundError` 等专用错误类型。 ### 2. 标签管理器UI组件 (TagManager.vue) - 提供完整的标签CRUD界面,包括数据表格展示、实时搜索、新增、重命名、合并、删除、分页功能。 - 技术栈:Naive UI (NDataTable, NModal, NPopconfirm),使用 `TagTypeConverter` 统一数据格式。 - 集成点:FavoriteManager.vue 中新增“标签管理”按钮以打开该对话框。 ### 3. 智能标签建议 (useTagSuggestions) - **智能自动完成**: 基于现有标签使用统计提供实时建议,热门标签优先,支持前缀匹配,并自动去重过滤。 - **API**: 提供 `loadTags()`、`filterTags()`、`getPopularTags()`、`getRecentTags()` 方法。 - 集成点:SaveFavoriteDialog.vue 中集成标签自动完成输入框。 ### 4. 收藏功能国际化与UI优化 - **国际化覆盖 (中英繁)**: - 新增 `favorites.dialog.*` (保存对话框)、`favorites.manager.*` (收藏管理器)、`favorites.manager.tagManager.*` (标签管理器)、`favorites.card.*` (收藏卡片) 等完整翻译键。 - 遵循翻译规范:使用全角标点、统一术语、简洁明了的提示信息。 - **UI组件优化**: - **SaveFavoriteDialog.vue**: - **标签自动完成集成**: 使用 `useTagSuggestions` 提供智能建议,支持回车添加,已选标签显示为可关闭 `Tag`,保存前自动注册标签到独立库。 - **智能预填充**: 根据模式(创建/保存/编辑)智能预填充标题和内容。 - **FavoriteManager.vue**: - 新增“标签管理”按钮。 - 网格布局优化 (使用 `NGrid` 替代 `flex`)。 - 工具栏固定,响应式设计。 - 移除冗余视图切换,简化布局。 - **FavoriteCard.vue**: - 卡片布局优化:使用 `NTag` 展示标签,显示元信息 (创建时间、使用次数、功能模式),操作按钮 (编辑、删除、应用),悬停效果。 - **用户体验提升**: 优化标签输入和管理流程,增强错误处理(如 `TAG_ALREADY_EXISTS` 静默忽略,其他错误显示友好提示)。 ### 5. 分类管理架构优化与国际化 - **架构优化**: - 分离关注点:默认分类创建从 Core 层移至 UI 层,Core 层不再硬编码分类名称,改为接受配置参数。 - 添加初始化标记防止重复创建默认分类。 - `deleteCategory` 现返回受影响的收藏数量。 - **国际化增强**: - 新增 `useFavoriteInitializer` composable 管理国际化分类。 - 完善中英繁三语言的分类翻译。 - 优化默认分类配置 (新增“学习研究”和“日常助手”)。 - 统一错误信息为英文。 - **代码组织**: - 测试文件迁移至标准目录 `packages/core/tests/`。 - 改进 `FavoriteManager` 组件生命周期管理,优化初始化流程。 ### 6. 全面集成测试 - 新增 `integration.test.ts` (742行,20个集成测试),覆盖以下场景: - CRUD 完整流程测试 (3个)。 - 历史记录转收藏测试 (5个,覆盖12种 PromptRecordType)。 - 标签和分类管理测试 (6个:重命名/合并/删除/排序/统计/级联)。 - 导入导出完整性测试 (2个)。 - 搜索和过滤功能测试 (4个)。 - `type-converter.test.ts` 包含16个测试,`tag-manager.test.ts` 包含23个测试。
1 parent 5bf02fa commit d784c99

21 files changed

+3907
-163
lines changed

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ export * from './services/context/constants'
147147
// 导出收藏管理相关
148148
export { FavoriteManager } from './services/favorite/manager'
149149
export { FavoriteManagerElectronProxy } from './services/favorite/electron-proxy'
150+
export { TagTypeConverter } from './services/favorite/type-converter'
150151
export * from './services/favorite/types'
151152
export * from './services/favorite/errors'
152153

packages/core/src/services/favorite/electron-proxy.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ import {
55
FavoriteAlreadyExistsError,
66
FavoriteCategoryNotFoundError,
77
FavoriteValidationError,
8-
FavoriteStorageError
8+
FavoriteStorageError,
9+
FavoriteTagAlreadyExistsError,
10+
FavoriteTagNotFoundError,
11+
FavoriteTagError,
12+
FavoriteMigrationError,
13+
FavoriteImportExportError
914
} from './errors';
1015

1116
declare const window: {
@@ -48,6 +53,23 @@ export class FavoriteManagerElectronProxy implements IFavoriteManager {
4853
if (error.code === 'STORAGE_ERROR') {
4954
throw new FavoriteStorageError(error.message || '');
5055
}
56+
// 标签相关错误
57+
if (error.code === 'TAG_ALREADY_EXISTS') {
58+
throw new FavoriteTagAlreadyExistsError(error.tag || '');
59+
}
60+
if (error.code === 'TAG_NOT_FOUND') {
61+
throw new FavoriteTagNotFoundError(error.tag || '');
62+
}
63+
if (error.code === 'TAG_ERROR') {
64+
throw new FavoriteTagError(error.message || '', error.code);
65+
}
66+
// 数据迁移和导入导出错误
67+
if (error.code === 'MIGRATION_ERROR') {
68+
throw new FavoriteMigrationError(error.message || '', error.cause);
69+
}
70+
if (error.code === 'IMPORT_EXPORT_ERROR') {
71+
throw new FavoriteImportExportError(error.message || '', error.cause, error.details);
72+
}
5173
throw new FavoriteError(error.message || '未知错误');
5274
}
5375
}
@@ -100,7 +122,7 @@ export class FavoriteManagerElectronProxy implements IFavoriteManager {
100122
return this.invokeMethod('updateCategory', id, updates);
101123
}
102124

103-
async deleteCategory(id: string): Promise<void> {
125+
async deleteCategory(id: string): Promise<number> {
104126
return this.invokeMethod('deleteCategory', id);
105127
}
106128

@@ -134,6 +156,10 @@ export class FavoriteManagerElectronProxy implements IFavoriteManager {
134156
return this.invokeMethod('getAllTags');
135157
}
136158

159+
async addTag(tag: string): Promise<void> {
160+
return this.invokeMethod('addTag', tag);
161+
}
162+
137163
async renameTag(oldTag: string, newTag: string): Promise<number> {
138164
return this.invokeMethod('renameTag', oldTag, newTag);
139165
}

packages/core/src/services/favorite/errors.ts

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,85 @@ export class FavoriteError extends Error {
1111

1212
export class FavoriteNotFoundError extends FavoriteError {
1313
constructor(id: string) {
14-
super(`收藏项未找到: ${id}`, 'FAVORITE_NOT_FOUND');
14+
super(`Favorite not found: ${id}`, 'FAVORITE_NOT_FOUND');
1515
this.name = 'FavoriteNotFoundError';
1616
}
1717
}
1818

1919
export class FavoriteAlreadyExistsError extends FavoriteError {
2020
constructor(content: string) {
21-
super(`收藏项已存在: ${content.slice(0, 50)}...`, 'FAVORITE_ALREADY_EXISTS');
21+
super(`Favorite already exists: ${content.slice(0, 50)}...`, 'FAVORITE_ALREADY_EXISTS');
2222
this.name = 'FavoriteAlreadyExistsError';
2323
}
2424
}
2525

2626
export class FavoriteCategoryNotFoundError extends FavoriteError {
2727
constructor(id: string) {
28-
super(`分类未找到: ${id}`, 'CATEGORY_NOT_FOUND');
28+
super(`Category not found: ${id}`, 'CATEGORY_NOT_FOUND');
2929
this.name = 'FavoriteCategoryNotFoundError';
3030
}
3131
}
3232

3333
export class FavoriteValidationError extends FavoriteError {
3434
constructor(message: string) {
35-
super(`验证错误: ${message}`, 'VALIDATION_ERROR');
35+
super(`Validation error: ${message}`, 'VALIDATION_ERROR');
3636
this.name = 'FavoriteValidationError';
3737
}
3838
}
3939

4040
export class FavoriteStorageError extends FavoriteError {
41-
constructor(message: string) {
42-
super(`存储错误: ${message}`, 'STORAGE_ERROR');
41+
constructor(message: string, public cause?: Error) {
42+
super(`Storage error: ${message}`, 'STORAGE_ERROR');
4343
this.name = 'FavoriteStorageError';
4444
}
45+
}
46+
47+
/**
48+
* 标签相关错误
49+
*/
50+
export class FavoriteTagError extends FavoriteError {
51+
constructor(message: string, code: string = 'TAG_ERROR') {
52+
super(message, code);
53+
this.name = 'FavoriteTagError';
54+
}
55+
}
56+
57+
/**
58+
* 标签已存在错误
59+
*/
60+
export class FavoriteTagAlreadyExistsError extends FavoriteTagError {
61+
constructor(tag: string) {
62+
super(`Tag already exists: ${tag}`, 'TAG_ALREADY_EXISTS');
63+
this.name = 'FavoriteTagAlreadyExistsError';
64+
}
65+
}
66+
67+
/**
68+
* Tag not found error
69+
*/
70+
export class FavoriteTagNotFoundError extends FavoriteTagError {
71+
constructor(tag: string) {
72+
super(`Tag not found: ${tag}`, 'TAG_NOT_FOUND');
73+
this.name = 'FavoriteTagNotFoundError';
74+
}
75+
}
76+
77+
/**
78+
* Data migration error
79+
*/
80+
export class FavoriteMigrationError extends FavoriteError {
81+
constructor(message: string, public cause?: Error) {
82+
super(`Migration error: ${message}`, 'MIGRATION_ERROR');
83+
this.name = 'FavoriteMigrationError';
84+
}
85+
}
86+
87+
/**
88+
* 导入导出错误
89+
*/
90+
export class FavoriteImportExportError extends FavoriteError {
91+
constructor(message: string, public cause?: Error, public details?: string[]) {
92+
super(message, 'IMPORT_EXPORT_ERROR');
93+
this.name = 'FavoriteImportExportError';
94+
}
4595
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
export * from './types';
22
export * from './errors';
33
export * from './manager';
4-
export type { FavoritePrompt, FavoriteCategory, FavoriteStats, IFavoriteManager } from './types';
4+
export * from './type-converter';
5+
export type {
6+
FavoritePrompt,
7+
FavoriteCategory,
8+
FavoriteStats,
9+
FavoriteTag,
10+
TagStatistics,
11+
IFavoriteManager
12+
} from './types';

0 commit comments

Comments
 (0)