技术文档中心
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
  • 基础入门

    • Electron 学习指南
    • 窗口管理
    • 进程通信
    • 对话框
  • 进阶内容

    • 菜单和托盘
    • 打包发布
    • Electron 自动更新
  • 框架集成

    • React + Electron
    • Vue + Electron

对话框

文件对话框

打开文件

const { dialog } = require('electron');

// 单个文件
const result = await dialog.showOpenDialog({
  title: '选择文件',
  defaultPath: app.getPath('documents'),
  buttonLabel: '打开',
  properties: ['openFile'],
  filters: [
    { name: 'Images', extensions: ['jpg', 'png', 'gif'] },
    { name: 'Videos', extensions: ['mkv', 'avi', 'mp4'] },
    { name: 'All Files', extensions: ['*'] }
  ]
});

if (!result.canceled) {
  console.log('选择的文件:', result.filePaths[0]);
}

// 多个文件
const result = await dialog.showOpenDialog({
  properties: ['openFile', 'multiSelections']
});

console.log('选择的文件:', result.filePaths);

// 选择目录
const result = await dialog.showOpenDialog({
  properties: ['openDirectory']
});

// 选择文件和目录
const result = await dialog.showOpenDialog({
  properties: ['openFile', 'openDirectory', 'multiSelections']
});

保存文件

const result = await dialog.showSaveDialog({
  title: '保存文件',
  defaultPath: path.join(app.getPath('documents'), 'untitled.txt'),
  buttonLabel: '保存',
  filters: [
    { name: 'Text Files', extensions: ['txt'] },
    { name: 'Markdown Files', extensions: ['md'] },
    { name: 'All Files', extensions: ['*'] }
  ]
});

if (!result.canceled) {
  console.log('保存路径:', result.filePath);
  // 写入文件
  fs.writeFileSync(result.filePath, content);
}

消息框

基础消息框

// 信息提示
dialog.showMessageBox({
  type: 'info',
  title: '提示',
  message: '操作成功',
  detail: '文件已保存',
  buttons: ['确定']
});

// 警告
dialog.showMessageBox({
  type: 'warning',
  title: '警告',
  message: '文件可能已损坏',
  buttons: ['确定']
});

// 错误
dialog.showMessageBox({
  type: 'error',
  title: '错误',
  message: '操作失败',
  detail: '请检查网络连接',
  buttons: ['确定']
});

// 询问
dialog.showMessageBox({
  type: 'question',
  title: '确认',
  message: '确定要删除吗?',
  buttons: ['取消', '删除']
});

异步消息框

const result = await dialog.showMessageBox({
  type: 'question',
  title: '确认',
  message: '确定要退出吗?',
  detail: '未保存的更改将丢失',
  buttons: ['取消', '退出'],
  defaultId: 0,
  cancelId: 0
});

if (result.response === 1) {
  // 用户点击了"退出"
  app.quit();
}

带复选框的消息框

const result = await dialog.showMessageBox({
  type: 'question',
  title: '确认',
  message: '确定要删除吗?',
  checkboxLabel: '不再提示',
  checkboxChecked: false,
  buttons: ['取消', '删除']
});

console.log('点击的按钮:', result.response);
console.log('复选框状态:', result.checkboxChecked);

if (result.checkboxChecked) {
  // 保存用户选择
  store.set('dontAskAgain', true);
}

错误对话框

const { dialog } = require('electron');

// 显示错误对话框
dialog.showErrorBox('错误', '应用程序遇到了一个错误');

// 在应用启动前显示错误
app.on('ready', () => {
  if (someError) {
    dialog.showErrorBox('启动失败', '无法初始化应用程序');
    app.quit();
  }
});

证书信任对话框

const result = await dialog.showCertificateTrustDialog({
  certificate: certificate,
  message: '是否信任此证书?'
});

实战示例

文件编辑器保存确认

// main.js
ipcMain.handle('confirm-save', async (event, fileName) => {
  const result = await dialog.showMessageBox(BrowserWindow.fromWebContents(event.sender), {
    type: 'question',
    title: '保存更改',
    message: `是否保存对 ${fileName} 的更改?`,
    detail: '如果不保存,更改将丢失',
    buttons: ['不保存', '取消', '保存'],
    defaultId: 2,
    cancelId: 1
  });

  return result.response;
  // 0: 不保存
  // 1: 取消
  // 2: 保存
});

// preload.js
contextBridge.exposeInMainWorld('dialogAPI', {
  confirmSave: (fileName) => ipcRenderer.invoke('confirm-save', fileName)
});

// renderer.js
async function closeFile() {
  if (isModified) {
    const response = await window.dialogAPI.confirmSave(currentFileName);
    
    if (response === 1) {
      // 取消
      return;
    } else if (response === 2) {
      // 保存
      await saveFile();
    }
  }
  
  // 关闭文件
  closeCurrentFile();
}

导入导出功能

// 导入文件
ipcMain.handle('import-file', async () => {
  const result = await dialog.showOpenDialog({
    title: '导入文件',
    properties: ['openFile'],
    filters: [
      { name: 'JSON Files', extensions: ['json'] },
      { name: 'All Files', extensions: ['*'] }
    ]
  });

  if (result.canceled) {
    return { success: false, canceled: true };
  }

  try {
    const content = await fs.promises.readFile(result.filePaths[0], 'utf-8');
    const data = JSON.parse(content);
    return { success: true, data };
  } catch (error) {
    dialog.showErrorBox('导入失败', error.message);
    return { success: false, error: error.message };
  }
});

// 导出文件
ipcMain.handle('export-file', async (event, data) => {
  const result = await dialog.showSaveDialog({
    title: '导出文件',
    defaultPath: 'export.json',
    filters: [
      { name: 'JSON Files', extensions: ['json'] }
    ]
  });

  if (result.canceled) {
    return { success: false, canceled: true };
  }

  try {
    await fs.promises.writeFile(
      result.filePath,
      JSON.stringify(data, null, 2),
      'utf-8'
    );
    
    dialog.showMessageBox({
      type: 'info',
      title: '成功',
      message: '导出成功',
      buttons: ['确定']
    });
    
    return { success: true };
  } catch (error) {
    dialog.showErrorBox('导出失败', error.message);
    return { success: false, error: error.message };
  }
});

批量操作确认

ipcMain.handle('confirm-batch-delete', async (event, count) => {
  const result = await dialog.showMessageBox({
    type: 'warning',
    title: '批量删除',
    message: `确定要删除 ${count} 个项目吗?`,
    detail: '此操作无法撤销',
    buttons: ['取消', '删除'],
    defaultId: 0,
    cancelId: 0
  });

  return result.response === 1;
});

自定义对话框

// 创建自定义对话框窗口
function createCustomDialog(parentWindow) {
  const dialog = new BrowserWindow({
    parent: parentWindow,
    modal: true,
    width: 400,
    height: 300,
    frame: false,
    resizable: false,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,
      contextIsolation: true
    }
  });

  dialog.loadFile('dialog.html');
  
  return new Promise((resolve) => {
    ipcMain.once('dialog-response', (event, response) => {
      dialog.close();
      resolve(response);
    });
  });
}

// 使用
const response = await createCustomDialog(mainWindow);
console.log('用户选择:', response);

最佳实践

  1. 使用异步 API:避免阻塞主进程
  2. 提供清晰的提示:让用户明白操作的后果
  3. 合理的默认值:设置合适的 defaultId 和 cancelId
  4. 错误处理:捕获并显示友好的错误信息
  5. 记住用户选择:使用复选框让用户选择不再提示
  6. 父窗口关联:模态对话框应该关联到父窗口
最近更新: 2026/2/6 15:39
Contributors: hailong
Prev
进程通信