feat(dialog): 实现对话框主题与拖拽功能

This commit is contained in:
MoeSnowyFox
2025-10-02 02:29:36 +08:00
parent 7d1ceda958
commit 0a20ee299d
5 changed files with 485 additions and 145 deletions

View File

@@ -5,6 +5,7 @@ import {
ipcMain,
Menu,
nativeImage,
nativeTheme,
screen,
shell,
Tray,
@@ -749,6 +750,108 @@ ipcMain.handle('stop-backend', async () => {
return stopBackend()
})
// 获取当前主题信息
ipcMain.handle('get-theme-info', async () => {
try {
const appRoot = getAppRoot()
const configPath = path.join(appRoot, 'config', 'frontend_config.json')
let themeMode = 'system'
let themeColor = 'blue'
// 尝试从配置文件读取主题设置
if (fs.existsSync(configPath)) {
try {
const configData = fs.readFileSync(configPath, 'utf8')
const config = JSON.parse(configData)
themeMode = config.themeMode || 'system'
themeColor = config.themeColor || 'blue'
} catch (error) {
log.warn('读取主题配置失败,使用默认值:', error)
}
}
// 检测系统主题
const systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
// 确定实际使用的主题
let actualTheme = themeMode
if (themeMode === 'system') {
actualTheme = systemTheme
}
const themeColors: Record<string, string> = {
blue: '#1677ff',
purple: '#722ed1',
cyan: '#13c2c2',
green: '#52c41a',
magenta: '#eb2f96',
pink: '#eb2f96',
red: '#ff4d4f',
orange: '#fa8c16',
yellow: '#fadb14',
volcano: '#fa541c',
geekblue: '#2f54eb',
lime: '#a0d911',
gold: '#faad14',
}
return {
themeMode,
themeColor,
actualTheme,
systemTheme,
isDark: actualTheme === 'dark',
primaryColor: themeColors[themeColor] || themeColors.blue
}
} catch (error) {
log.error('获取主题信息失败:', error)
return {
themeMode: 'system',
themeColor: 'blue',
actualTheme: 'light',
systemTheme: 'light',
isDark: false,
primaryColor: '#1677ff'
}
}
})
// 获取对话框专用的主题信息
ipcMain.handle('get-theme', async () => {
try {
const appRoot = getAppRoot()
const configPath = path.join(appRoot, 'config', 'frontend_config.json')
let themeMode = 'system'
// 尝试从配置文件读取主题设置
if (fs.existsSync(configPath)) {
try {
const configData = fs.readFileSync(configPath, 'utf8')
const config = JSON.parse(configData)
themeMode = config.themeMode || 'system'
} catch (error) {
log.warn('读取主题配置失败,使用默认值:', error)
}
}
// 检测系统主题
const systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
// 确定实际使用的主题
let actualTheme = themeMode
if (themeMode === 'system') {
actualTheme = systemTheme
}
return actualTheme
} catch (error) {
log.error('获取对话框主题失败:', error)
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
}
})
// 全局存储对话框窗口引用和回调
let dialogWindows = new Map<string, BrowserWindow>()
let dialogCallbacks = new Map<string, (result: boolean) => void>()
@@ -769,15 +872,19 @@ function createQuestionDialog(questionData: any): Promise<boolean> {
messageId: messageId
}
// 创建对话框窗口
// 获取主窗口的尺寸用于全屏显示
let windowBounds = { width: 800, height: 600, x: 100, y: 100 }
if (mainWindow && !mainWindow.isDestroyed()) {
windowBounds = mainWindow.getBounds()
}
// 创建对话框窗口 - 小尺寸可拖动窗口
const dialogWindow = new BrowserWindow({
width: 450,
height: 200,
minWidth: 350,
minHeight: 150,
maxWidth: 600,
maxHeight: 400,
resizable: true,
width: 400,
height: 140,
x: windowBounds.x + (windowBounds.width - 400) / 2, // 居中显示
y: windowBounds.y + (windowBounds.height - 200) / 2,
resizable: false, // 不允许改变大小
minimizable: false,
maximizable: false,
alwaysOnTop: true,
@@ -803,19 +910,8 @@ function createQuestionDialog(questionData: any): Promise<boolean> {
const dialogUrl = `file://${path.join(__dirname, '../public/dialog.html')}?data=${encodedData}`
dialogWindow.loadURL(dialogUrl)
// 窗口准备好后显示并居中
// 窗口准备好后显示
dialogWindow.once('ready-to-show', () => {
// 计算居中位置
if (mainWindow && !mainWindow.isDestroyed()) {
const mainBounds = mainWindow.getBounds()
const dialogBounds = dialogWindow.getBounds()
const x = Math.round(mainBounds.x + (mainBounds.width - dialogBounds.width) / 2)
const y = Math.round(mainBounds.y + (mainBounds.height - dialogBounds.height) / 2)
dialogWindow.setPosition(x, y)
} else {
dialogWindow.center()
}
dialogWindow.show()
dialogWindow.focus()
})
@@ -830,7 +926,7 @@ function createQuestionDialog(questionData: any): Promise<boolean> {
}
})
log.info(`对话框窗口已创建: ${messageId}`)
log.info(`全屏对话框窗口已创建: ${messageId}`)
})
}
@@ -867,16 +963,16 @@ ipcMain.handle('dialog-response', async (_event, messageId: string, choice: bool
return true
})
// 调整对话框窗口大小
ipcMain.handle('resize-dialog-window', async (_event, height: number) => {
// 移动对话框窗口
ipcMain.handle('move-window', async (_event, deltaX: number, deltaY: number) => {
// 获取当前活动的对话框窗口(最后创建的)
const dialogWindow = Array.from(dialogWindows.values()).pop()
if (dialogWindow && !dialogWindow.isDestroyed()) {
const bounds = dialogWindow.getBounds()
dialogWindow.setBounds({
...bounds,
height: Math.max(150, Math.min(400, height))
})
const currentBounds = dialogWindow.getBounds()
dialogWindow.setPosition(
currentBounds.x + deltaX,
currentBounds.y + deltaY
)
}
})

View File

@@ -67,6 +67,11 @@ contextBridge.exposeInMainWorld('electronAPI', {
showQuestionDialog: (questionData: any) => ipcRenderer.invoke('show-question-dialog', questionData),
dialogResponse: (messageId: string, choice: boolean) => ipcRenderer.invoke('dialog-response', messageId, choice),
resizeDialogWindow: (height: number) => ipcRenderer.invoke('resize-dialog-window', height),
moveWindow: (deltaX: number, deltaY: number) => ipcRenderer.invoke('move-window', deltaX, deltaY),
// 主题信息获取
getThemeInfo: () => ipcRenderer.invoke('get-theme-info'),
getTheme: () => ipcRenderer.invoke('get-theme'),
// 监听下载进度
onDownloadProgress: (callback: (progress: any) => void) => {