Skip to content

Commit 00d7793

Browse files
committed
feat: add favorites import/export functionality and enhance pages store with chat page clearing
1 parent 1e65bee commit 00d7793

3 files changed

Lines changed: 221 additions & 29 deletions

File tree

src/renderer/src/components/settings/DataManagement.tsx

Lines changed: 168 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@ import {
1111
Table,
1212
Checkbox,
1313
Input,
14-
Tag
14+
Tag,
15+
Dropdown,
16+
MenuProps
1517
} from 'antd'
1618
import {
1719
DownloadOutlined,
1820
UploadOutlined,
1921
DeleteOutlined,
2022
ExclamationCircleOutlined,
21-
SelectOutlined
23+
SelectOutlined,
24+
SettingOutlined,
25+
MessageOutlined,
26+
StarOutlined,
27+
MoreOutlined
2228
} from '@ant-design/icons'
2329
import { useSettingsStore } from '../../stores/settingsStore'
2430
import { usePagesStore } from '../../stores/pagesStore'
31+
import { useFavoritesStore } from '../../stores/favoritesStore'
2532
import { clearAllStores } from '../../stores/useAppStores'
2633
import { clearStoreState } from '../../stores/persistence/storeConfig'
2734
import { useMessagesStore } from '../../stores/messagesStore'
@@ -38,7 +45,8 @@ const { Text, Paragraph } = Typography
3845

3946
export default function DataManagement() {
4047
const { importSettings, exportSettings } = useSettingsStore()
41-
const { pages, folders, importPages, importFolders, clearAllPages } = usePagesStore()
48+
const { pages, folders, importPages, importFolders, clearAllPages, clearChatPages } = usePagesStore()
49+
const { items: favoriteItems, folders: favoriteFolders, importFavorites, importFavoriteFolders, clearAllFavorites } = useFavoritesStore()
4250
const [importing, setImporting] = useState(false)
4351
const [selectiveImportModal, setSelectiveImportModal] = useState(false)
4452
const [selectableChatItems, setSelectableChatItems] = useState<SelectableChatItem[]>([])
@@ -61,6 +69,10 @@ export default function DataManagement() {
6169
settings,
6270
pages,
6371
folders,
72+
favorites: {
73+
items: favoriteItems,
74+
folders: favoriteFolders
75+
},
6476
version: '1.0.0',
6577
exportTime: Date.now()
6678
}
@@ -129,6 +141,31 @@ export default function DataManagement() {
129141
}
130142
}
131143

144+
// 单独导出收藏
145+
const handleExportFavorites = () => {
146+
try {
147+
const favoritesData = {
148+
type: 'favorites-only',
149+
items: favoriteItems,
150+
folders: favoriteFolders,
151+
version: '1.0.0',
152+
exportTime: Date.now()
153+
}
154+
const blob = new Blob([JSON.stringify(favoritesData, null, 2)], { type: 'application/json' })
155+
const url = URL.createObjectURL(blob)
156+
const link = document.createElement('a')
157+
158+
link.href = url
159+
link.download = `ai-chat-favorites-${getTimestamp()}.json`
160+
document.body.appendChild(link)
161+
link.click()
162+
document.body.removeChild(link)
163+
URL.revokeObjectURL(url)
164+
} catch (error) {
165+
message.error('收藏导出失败')
166+
}
167+
}
168+
132169
const handleImport = (file: File) => {
133170
setImporting(true)
134171

@@ -151,7 +188,17 @@ export default function DataManagement() {
151188
importFolders(parsedData.folders)
152189
}
153190

154-
message.success('所有数据导入成功(包含设置和聊天历史)')
191+
// 导入收藏数据
192+
if (parsedData.favorites) {
193+
if (parsedData.favorites.items && parsedData.favorites.items.length > 0) {
194+
importFavorites(parsedData.favorites.items)
195+
}
196+
if (parsedData.favorites.folders && parsedData.favorites.folders.length > 0) {
197+
importFavoriteFolders(parsedData.favorites.folders)
198+
}
199+
}
200+
201+
message.success('所有数据导入成功(包含设置、聊天历史和收藏)')
155202
} else if (parsedData.type === 'settings-only') {
156203
// 只有设置数据
157204
importSettings(parsedData.settings)
@@ -167,6 +214,16 @@ export default function DataManagement() {
167214
}
168215

169216
message.success('聊天记录导入成功')
217+
} else if (parsedData.type === 'favorites-only') {
218+
// 只有收藏数据
219+
if (parsedData.items && parsedData.items.length > 0) {
220+
importFavorites(parsedData.items)
221+
}
222+
if (parsedData.folders && parsedData.folders.length > 0) {
223+
importFavoriteFolders(parsedData.folders)
224+
}
225+
226+
message.success('收藏数据导入成功')
170227
} else if (
171228
parsedData.settings &&
172229
parsedData.pages !== undefined &&
@@ -352,7 +409,10 @@ export default function DataManagement() {
352409
// 第二步:清除 messagesStore 中的流式消息状态
353410
useMessagesStore.setState({ streamingMessages: {} })
354411

355-
// 第三步:清除 IndexedDB 中的所有持久化数据
412+
// 第三步:清除收藏数据
413+
clearAllFavorites()
414+
415+
// 第四步:清除 IndexedDB 中的所有持久化数据
356416
await Promise.all([
357417
clearStoreState('settings-store'),
358418
clearStoreState('messages-store'),
@@ -361,7 +421,8 @@ export default function DataManagement() {
361421
clearStoreState('ui-store'),
362422
clearStoreState('object-store'),
363423
clearStoreState('crosstab-store'),
364-
clearStoreState('ai-tasks-store')
424+
clearStoreState('ai-tasks-store'),
425+
clearStoreState('favorites-store')
365426
// pages 和 folders 已经在 clearAllPages() 中处理了
366427
])
367428

@@ -385,15 +446,14 @@ export default function DataManagement() {
385446
cancelText: '取消',
386447
onOk: async () => {
387448
try {
388-
// 清除聊天相关的存储
389-
clearAllPages()
449+
// 清除聊天相关的存储(保留settings页面)
450+
clearChatPages()
390451

391-
// 清除相关的持久化数据
452+
// 清除相关的持久化数据(仅清除与聊天记录直接相关的存储)
392453
await Promise.all([
393454
clearStoreState('messages-store'),
394-
clearStoreState('search-store'),
395-
clearStoreState('tabs-store'),
396-
clearStoreState('ui-store')
455+
clearStoreState('search-store')
456+
// 注意:不清除 tabs-store 和 ui-store,因为它们包含settings页面状态
397457
])
398458

399459
// 重置消息存储的流式消息状态
@@ -436,6 +496,79 @@ export default function DataManagement() {
436496
})
437497
}
438498

499+
// 单独清空收藏
500+
const handleResetFavorites = () => {
501+
modal.confirm({
502+
title: '确认清空收藏',
503+
icon: <ExclamationCircleOutlined />,
504+
content: '这将清除所有收藏项和收藏文件夹,但保留您的设置和聊天记录。此操作不可恢复,确定要继续吗?',
505+
okText: '确定清空',
506+
okType: 'danger',
507+
cancelText: '取消',
508+
onOk: async () => {
509+
try {
510+
// 清除收藏数据
511+
clearAllFavorites()
512+
513+
// 清除收藏相关的持久化数据
514+
await clearStoreState('favorites-store')
515+
516+
message.success('收藏已清空')
517+
} catch (error) {
518+
console.error('清空收藏失败:', error)
519+
message.error('清空收藏失败')
520+
}
521+
}
522+
})
523+
}
524+
525+
// 导出下拉菜单项(仅包含部分导出选项)
526+
const exportMenuItems: MenuProps['items'] = [
527+
{
528+
key: 'settings',
529+
label: '仅导出设置',
530+
icon: <SettingOutlined />,
531+
onClick: handleExportSettings
532+
},
533+
{
534+
key: 'chats',
535+
label: '仅导出聊天记录',
536+
icon: <MessageOutlined />,
537+
onClick: handleExportChats
538+
},
539+
{
540+
key: 'favorites',
541+
label: '仅导出收藏',
542+
icon: <StarOutlined />,
543+
onClick: handleExportFavorites
544+
}
545+
]
546+
547+
// 重置下拉菜单项(仅包含部分重置选项)
548+
const resetMenuItems: MenuProps['items'] = [
549+
{
550+
key: 'settings',
551+
label: '仅清空设置',
552+
icon: <SettingOutlined />,
553+
onClick: handleResetSettings,
554+
danger: true
555+
},
556+
{
557+
key: 'chats',
558+
label: '仅清空聊天记录',
559+
icon: <MessageOutlined />,
560+
onClick: handleResetChats,
561+
danger: true
562+
},
563+
{
564+
key: 'favorites',
565+
label: '仅清空收藏',
566+
icon: <StarOutlined />,
567+
onClick: handleResetFavorites,
568+
danger: true
569+
}
570+
]
571+
439572
const getCurrentDataSize = () => {
440573
try {
441574
const settings = exportSettings()
@@ -444,6 +577,10 @@ export default function DataManagement() {
444577
settings,
445578
pages,
446579
folders,
580+
favorites: {
581+
items: favoriteItems,
582+
folders: favoriteFolders
583+
},
447584
version: '1.0.0',
448585
exportTime: Date.now()
449586
}
@@ -453,9 +590,17 @@ export default function DataManagement() {
453590
const chatsSize = (
454591
JSON.stringify({ type: 'chats-only', pages, folders, version: '1.0.0' }).length / 1024
455592
).toFixed(2)
593+
const favoritesSize = (
594+
JSON.stringify({
595+
type: 'favorites-only',
596+
items: favoriteItems,
597+
folders: favoriteFolders,
598+
version: '1.0.0'
599+
}).length / 1024
600+
).toFixed(2)
456601
const totalSize = (JSON.stringify(allData).length / 1024).toFixed(2)
457602

458-
return `总计: ${totalSize} KB (设置: ${settingsSize} KB, 聊天: ${chatsSize} KB)`
603+
return `总计: ${totalSize} KB (设置: ${settingsSize} KB, 聊天: ${chatsSize} KB, 收藏: ${favoritesSize} KB)`
459604
} catch {
460605
return '计算中...'
461606
}
@@ -483,16 +628,13 @@ export default function DataManagement() {
483628
<Button icon={<DownloadOutlined />} onClick={handleExportAll} type="primary" ghost>
484629
导出所有数据
485630
</Button>
486-
<Button icon={<DownloadOutlined />} onClick={handleExportSettings} type="default">
487-
仅导出设置
488-
</Button>
489-
<Button icon={<DownloadOutlined />} onClick={handleExportChats} type="default">
490-
仅导出聊天记录
491-
</Button>
631+
<Dropdown menu={{ items: exportMenuItems }} placement="bottomLeft">
632+
<Button icon={<MoreOutlined />} type="default" style={{ padding: '4px 8px' }} />
633+
</Dropdown>
492634
</Space>
493635
<div>
494636
<Text type="secondary" style={{ fontSize: '12px' }}>
495-
您可以选择导出所有数据,或者分别导出设置和聊天记录
637+
您可以选择导出所有数据,或者分别导出设置、聊天记录和收藏
496638
</Text>
497639
</div>
498640
</div>
@@ -520,7 +662,7 @@ export default function DataManagement() {
520662
</Upload>
521663
<div>
522664
<Text type="secondary" style={{ fontSize: '12px' }}>
523-
支持导入完整数据文件、设置文件或聊天记录文件,系统会自动识别文件类型进行相应处理
665+
支持导入完整数据文件、设置文件、聊天记录文件或收藏文件,系统会自动识别文件类型进行相应处理
524666
</Text>
525667
</div>
526668
</div>
@@ -552,19 +694,16 @@ export default function DataManagement() {
552694
<Text strong>重置数据</Text>
553695
</div>
554696
<Space direction="horizontal" size="small" wrap style={{ marginBottom: '4px' }}>
555-
<Button icon={<DeleteOutlined />} danger type="default" onClick={handleResetAll}>
697+
<Button icon={<DeleteOutlined />} onClick={handleResetAll} danger type="default">
556698
重置所有数据
557699
</Button>
558-
<Button icon={<DeleteOutlined />} danger type="default" onClick={handleResetSettings}>
559-
仅清空设置
560-
</Button>
561-
<Button icon={<DeleteOutlined />} danger type="default" onClick={handleResetChats}>
562-
仅清空聊天记录
563-
</Button>
700+
<Dropdown menu={{ items: resetMenuItems }} placement="bottomLeft">
701+
<Button icon={<MoreOutlined />} danger type="default" style={{ padding: '4px 8px' }} />
702+
</Dropdown>
564703
</Space>
565704
<div>
566705
<Text type="secondary" style={{ fontSize: '12px' }}>
567-
您可以选择重置所有数据,或者分别清空聊天记录、重置设置配置
706+
您可以选择重置所有数据,或者分别清空聊天记录、收藏或重置设置配置
568707
</Text>
569708
</div>
570709
</div>

src/renderer/src/stores/favoritesStore.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ export interface FavoritesActions {
9696
textFragmentCount: number
9797
folderCount: number
9898
}
99+
100+
// 导入导出
101+
importFavorites: (items: FavoriteItem[]) => void
102+
importFavoriteFolders: (folders: FavoriteFolder[]) => void
103+
clearAllFavorites: () => void
99104
}
100105

101106
const initialState: FavoritesState = {
@@ -604,6 +609,39 @@ export const useFavoritesStore = create<FavoritesState & FavoritesActions>()(
604609
textFragmentCount: items.filter((i) => i.type === 'text-fragment').length,
605610
folderCount: get().folders.length
606611
}
612+
},
613+
614+
// ==================== 导入导出 ====================
615+
616+
importFavorites: (items) => {
617+
try {
618+
set((state) => {
619+
state.items = items
620+
})
621+
} catch (error) {
622+
handleStoreError('favoritesStore', 'importFavorites', error)
623+
}
624+
},
625+
626+
importFavoriteFolders: (folders) => {
627+
try {
628+
set((state) => {
629+
state.folders = folders
630+
})
631+
} catch (error) {
632+
handleStoreError('favoritesStore', 'importFavoriteFolders', error)
633+
}
634+
},
635+
636+
clearAllFavorites: () => {
637+
try {
638+
set((state) => {
639+
state.items = []
640+
state.folders = []
641+
})
642+
} catch (error) {
643+
handleStoreError('favoritesStore', 'clearAllFavorites', error)
644+
}
607645
}
608646
})),
609647
createFavoritesPersistConfig('favorites-store', 1)

0 commit comments

Comments
 (0)