人人都会AI编程

10.1 系统对话框:文件选择、保存、消息提示、错误弹窗

更新时间:2026-07-11

在桌面应用开发中,系统对话框是用户交互的关键一环——打开文件、保存文档、确认操作、显示警告,这些都需要调用操作系统原生的对话框,而不是用网页模拟的假弹窗。Tauri 通过 dialog 插件提供了完整的原生对话框支持,既可以在前端用 JavaScript/TypeScript 直接调用,也能在 Rust 后端通过命令触发,保持了一致的用户体验。

10.1.1 安装与配置

Tauri 2 将对话框功能抽离为独立插件,使用前需要安装:

前端依赖

npm install @tauri-apps/plugin-dialog

Rust 依赖
src-tauri/Cargo.toml 中添加:

[dependencies]
tauri-plugin-dialog = "2"

插件注册
src-tauri/src/lib.rstauri::Builder 中注册插件:

fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

前端使用时需要先导入并检查权限(Tauri 2 的权限模型需要显式声明)。在 src-tauri/capabilities/default.json 中增加对话框权限:

{
  "identifier": "default",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "dialog:default"
  ]
}

10.1.2 文件选择对话框

让用户选择本地文件或文件夹是高频需求。前端调用 open 函数,可以指定文件类型过滤器、多选、目录模式等。

示例:选择单个图片文件

import { open } from '@tauri-apps/plugin-dialog';

async function selectImage() {
  const file = await open({
    multiple: false,
    filters: [{
      name: '图片',
      extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp']
    }]
  });
  if (file) {
    console.log('选中文件路径:', file);
    // file 是完整路径字符串,不是 Web File 对象
  }
}

选择文件夹

const folder = await open({
  directory: true,
  multiple: false,
  title: '选择导出目录'
});

返回的结果在 Tauri 2 中是一个 stringstring[](多选时),而不是 Electron 的 File 对象。如果需要读取文件内容,需要结合 fs 插件或 Rust 命令来操作本地文件。

在 Rust 后端调用对话框
有时候你可能希望在后端直接弹出对话框,比如某个命令需要用户确认打开某个文件:

use tauri::Manager;
use tauri_plugin_dialog::DialogExt;

#[tauri::command]
async fn open_file_from_rust(app: tauri::AppHandle) -> Result<String, String> {
    let file_path = app.dialog()
        .file()
        .add_filter("文本文件", &["txt", "md"])
        .blocking_open_file(); // 或使用异步 .open_file()
    file_path.map(|p| p.to_string_lossy().into_owned())
        .ok_or_else(|| "用户取消了选择".into())
}

10.1.3 文件保存对话框

保存文件和打开文件对话框类似,但会提示用户输入或确认文件名。

前端调用

import { save } from '@tauri-apps/plugin-dialog';

async function saveNote() {
  const filePath = await save({
    defaultPath: '未命名.md',
    filters: [{
      name: 'Markdown',
      extensions: ['md']
    }]
  });
  if (filePath) {
    // 需要自行将内容写入该路径,例如通过 Rust 命令或 fs 插件
  }
}

默认保存路径可以动态指定,比如根据用户主目录或最近使用的文件夹。save 返回的是用户选定的文件完整路径,应用需负责后续的写入操作。

10.1.4 消息提示对话框

消息提示框用于向用户展示一条信息,并等待用户确认(只有一个确认按钮)。

import { message } from '@tauri-apps/plugin-dialog';

await message('文件保存成功!', {
  title: '提示',
  kind: 'info' // 'info' | 'warning' | 'error'
});

message 会阻塞前端 JavaScript 执行,直到用户点击“确定”。类型参数 kind 可传入 infowarningerror,不同平台会显示不同的图标。

10.1.5 错误弹窗与询问对话框

ask 函数可创建带“是/否”或“确认/取消”的对话框,非常适合处理危险操作前的确认。

import { ask } from '@tauri-apps/plugin-dialog';

async function deleteItem() {
  const yes = await ask('确定要删除此项目吗?此操作不可撤销。', {
    title: '危险操作',
    kind: 'warning',
    okLabel: '删除',
    cancelLabel: '取消'
  });
  if (yes) {
    // 执行删除逻辑
  }
}

Tauri 2 没有直接的 error 独立方法,实现错误弹窗通常用 message 并指定 kind: 'error',或者用 Rust 命令返回错误时由前端捕获并显示对话框。如果需要自定义错误弹窗的按钮,可以考虑用 ask 搭配错误样式,但建议保持系统原生行为。

在 Rust 中弹出消息框
如果 Rust 后端检测到严重错误需要立即通知用户,可以直接调用:

use tauri_plugin_dialog::DialogExt;

app.dialog()
    .message("无法连接到服务器,请检查网络。")
    .title("连接失败")
    .kind(tauri_plugin_dialog::MessageDialogKind::Error)
    .blocking_show();

但要注意,阻塞式对话框会暂停当前线程,因此在前端命令中调用 Rust 然后弹出对话框是更安全的方式。

10.1.6 实际应用中的注意事项

  1. 权限分离:在 Tauri 2 的权限模型中,对话框的调用同样需要权限声明。如果忘记配置 dialog:default,调用会静默失败或在控制台报错。
  2. 异步与阻塞:前端 API 是异步的,Rust 端提供 blocking_ 前缀的同步方法和异步方法。在 Tauri 命令中调用对话框时,尽量使用异步版本,避免阻塞主线程。
  3. 文件路径处理:对话框返回的路径是操作系统原生格式,Windows 下为 C:\...,macOS/Linux 为 Unix 路径。传递给 Rust 命令读写时无需转换,Tauri 内部会自动处理。
  4. 用户体验一致性:原生对话框的呈现完全符合用户的操作系统习惯,无需担心样式差异。这正是 Tauri 相比 Electron 自定义对话框的优势之一。
  5. 错误处理边界:如果用户在对话框中取消操作,open/save 返回 nullundefined,务必判空后再进行后续处理。

将这些对话框组合起来,你的 Tauri 应用就像是真正的本地程序,而不再是“网页套了层壳”。