接口配置
-模型名称 (modelName)
-{{ m.desc }}
-运行参数
-- 正在配置 - {{ - activeTab === 'reading' ? '心得生成' : '文本总结' - }} - 专用模型。 -
-diff --git a/db.sqlite b/db.sqlite index 5cb6826..8e23c17 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/electron-builder.yml b/electron-builder.yml index aec582c..d389699 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -1,4 +1,4 @@ -appId: com.electron.app +appId: com.reading_book productName: read_books directories: buildResources: build diff --git a/package.json b/package.json index a71818b..4a2824c 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,8 @@ { "name": "read_books", "version": "1.0.0", - "description": "An Electron application with Vue and TypeScript", + "description": "让每一篇阅读者享受阅读与思考的乐趣", "main": "./out/main/index.js", - "author": "example.com", - "homepage": "https://electron-vite.org", "scripts": { "format": "prettier --write .", "lint": "eslint --cache .", diff --git a/src/main/index.ts b/src/main/index.ts index e6cf66a..856380e 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, ipcMain, shell } from 'electron' +import { app, BrowserWindow, shell } from 'electron' import { join } from 'path' import { electronApp, is, optimizer } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' @@ -8,11 +8,11 @@ import { createIPCHandler } from 'electron-trpc/main' import { initDB } from '@main/db/data-source' function createWindow(): void { - // Create the browser window. const mainWindow = new BrowserWindow({ width: 1200, height: 800, show: false, + title: '读书心得助手', resizable: false, autoHideMenuBar: true, ...(process.platform === 'linux' ? { icon } : {}), @@ -38,8 +38,6 @@ function createWindow(): void { return { action: 'deny' } }) - // HMR for renderer base on electron-vite cli. - // Load the remote URL for development or the local html file for production. if (is.dev && process.env['ELECTRON_RENDERER_URL']) { mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) } else { @@ -47,41 +45,22 @@ function createWindow(): void { } } -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. app.whenReady().then(async () => { await initDB() - // Set app user model id for windows - electronApp.setAppUserModelId('com.electron') + electronApp.setAppUserModelId('com.reading_book') - // Default open or close DevTools by F12 in development - // and ignore CommandOrControl + R in production. - // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window) }) - - // IPC test - ipcMain.on('ping', () => console.log('pong')) - createWindow() app.on('activate', function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) - -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and require them here. diff --git a/src/main/manager/readingReflectionsTaskManager.ts b/src/main/manager/readingReflectionsTaskManager.ts index 0056832..f5b7611 100644 --- a/src/main/manager/readingReflectionsTaskManager.ts +++ b/src/main/manager/readingReflectionsTaskManager.ts @@ -4,9 +4,16 @@ import { readingReflectionGraph } from '@main/services/ai/graph/readingReflectio import { AppDataSource } from '@main/db/data-source' import { ReadingReflectionTaskBatch } from '@main/db/entities/ReadingReflectionTaskBatch' import { ReadingReflectionTaskItem } from '@main/db/entities/ReadingReflectionTaskItem' +import Store from 'electron-store' +import { CONFIG_STORE_KEY } from '@rpc/constants/store_key' +import { Notification } from 'electron' export const readingReflectionTaskEvent = new EventEmitter() +// 兼容性处理获取 Store 构造函数 +const StoreClass = (Store as any).default || Store +const store = new StoreClass({ encryptionKey: CONFIG_STORE_KEY }) + class TaskManager { private limit = pLimit(2) private batchRepo = AppDataSource.getRepository(ReadingReflectionTaskBatch) @@ -113,6 +120,7 @@ class TaskManager { result?: any ) { const displayId = total === 1 ? taskId : `${taskId}-${index}` + //发送 tRPC 实时事件(驱动前端 UI 进度条) readingReflectionTaskEvent.emit('readingReflectionTaskProgress', { taskId: displayId, progress, @@ -120,6 +128,44 @@ class TaskManager { statusText: `[任务${index + 1}/${total}] ${status}`, // 传描述文字 result }) + // 2. 添加任务状态通知判断 + this.handleNotification(status, progress, total, index) + } + /** + * 内部私有方法:处理通知逻辑 + */ + private handleNotification(status: string, progress: number, total: number, index: number) { + // 从 electron-store 获取用户偏好 + const config = store.get('notification') || { + masterSwitch: true, + taskCompleted: true, + taskFailed: true + } + + // 如果总开关关闭,直接拦截 + if (!config.masterSwitch) return + + // 场景 A: 任务全部完成 (100%) + if (progress === 100 && config.taskCompleted) { + // 只有当所有子任务都完成,或者当前是单任务时才弹出 + // 如果是批量任务,你可以选择在最后一个子任务完成时通知 + if (index + 1 === total) { + new Notification({ + title: '🎉 读书心得已生成', + body: total > 1 ? `共 ${total} 篇心得已全部处理完成。` : '您的书籍心得已准备就绪。', + silent: config.silentMode + }).show() + } + } + + // 场景 B: 任务失败 (假设你传入的 status 是 'FAILED') + if (status === 'FAILED' && config.taskFailed) { + new Notification({ + title: '❌ 任务生成失败', + body: `第 ${index + 1} 项任务执行异常,请检查网络或 API 余额。`, + silent: config.silentMode + }).show() + } } } diff --git a/src/main/services/ai/state/readingReflectionState.ts b/src/main/services/ai/state/readingReflectionState.ts index a735f06..9861676 100644 --- a/src/main/services/ai/state/readingReflectionState.ts +++ b/src/main/services/ai/state/readingReflectionState.ts @@ -1,5 +1,5 @@ import { Annotation } from '@langchain/langgraph' -import { Occupation } from '@shared/types/reflections' +import { Occupation } from '@shared/types/IReadingReflectionTask' export const ReadingReflectionState = Annotation.Root({ // 输入任务 diff --git a/src/renderer/components.d.ts b/src/renderer/components.d.ts index 7a6b3d8..5bf6f5d 100644 --- a/src/renderer/components.d.ts +++ b/src/renderer/components.d.ts @@ -12,6 +12,7 @@ export {} declare module 'vue' { export interface GlobalComponents { AButton: typeof import('@arco-design/web-vue')['Button'] + ACheckbox: typeof import('@arco-design/web-vue')['Checkbox'] ACollapse: typeof import('@arco-design/web-vue')['Collapse'] ACollapseItem: typeof import('@arco-design/web-vue')['CollapseItem'] ActiveMenu: typeof import('./src/components/ActiveMenu.vue')['default'] @@ -23,6 +24,7 @@ declare module 'vue' { AOption: typeof import('@arco-design/web-vue')['Option'] ASelect: typeof import('@arco-design/web-vue')['Select'] ASlider: typeof import('@arco-design/web-vue')['Slider'] + ASwitch: typeof import('@arco-design/web-vue')['Switch'] ATabPane: typeof import('@arco-design/web-vue')['TabPane'] ATabs: typeof import('@arco-design/web-vue')['Tabs'] ATag: typeof import('@arco-design/web-vue')['Tag'] diff --git a/src/renderer/index.html b/src/renderer/index.html index e3b0ca1..ca3ac66 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -1,17 +1,17 @@ -
- -