feat(desktop): ✨ 优化一些逻辑
1. 优化通知配置 2. 优化命名规范 3. 优化代码逻辑
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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({
|
||||
// 输入任务
|
||||
|
||||
Reference in New Issue
Block a user