系统对话框 #

一、对话框概述 #

1.1 对话框类型 #

类型 说明 使用场景
文件选择 选择文件或目录 打开文件
保存对话框 选择保存位置 保存文件
消息对话框 显示消息 确认、警告
确认对话框 确认操作 删除确认

1.2 安装插件 #

bash
pnpm add @tauri-apps/plugin-dialog
rust
// src-tauri/src/lib.rs
tauri::Builder::default()
    .plugin(tauri_plugin_dialog::init())
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

1.3 权限配置 #

json
// src-tauri/capabilities/default.json
{
    "permissions": [
        "dialog:default",
        "dialog:allow-open",
        "dialog:allow-save",
        "dialog:allow-message",
        "dialog:allow-ask",
        "dialog:allow-confirm"
    ]
}

二、文件选择对话框 #

2.1 基本使用 #

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

async function selectFile() {
    const selected = await open({
        multiple: false,
        directory: false,
    });

    if (selected) {
        console.log('Selected file:', selected);
    }
}

2.2 多文件选择 #

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

async function selectMultipleFiles() {
    const selected = await open({
        multiple: true,
        directory: false,
    });

    if (selected) {
        // selected 是数组
        console.log('Selected files:', selected);
    }
}

2.3 选择目录 #

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

async function selectDirectory() {
    const selected = await open({
        directory: true,
        multiple: false,
    });

    if (selected) {
        console.log('Selected directory:', selected);
    }
}

2.4 文件过滤器 #

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

async function selectImageFile() {
    const selected = await open({
        multiple: false,
        filters: [
            {
                name: 'Images',
                extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp'],
            },
            {
                name: 'All Files',
                extensions: ['*'],
            },
        ],
    });

    return selected;
}

2.5 设置默认路径 #

typescript
import { open } from '@tauri-apps/plugin-dialog';
import { homeDir } from '@tauri-apps/api/path';

async function selectFromHome() {
    const home = await homeDir();
    
    const selected = await open({
        defaultPath: home,
        multiple: false,
    });

    return selected;
}

2.6 设置标题 #

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

async function selectWithCustomTitle() {
    const selected = await open({
        title: '选择要打开的文档',
        multiple: false,
    });

    return selected;
}

三、保存对话框 #

3.1 基本使用 #

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

async function selectSaveLocation() {
    const path = await save({
        defaultPath: 'untitled.txt',
    });

    if (path) {
        console.log('Save to:', path);
    }
}

3.2 设置过滤器 #

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

async function saveAsImage() {
    const path = await save({
        defaultPath: 'image.png',
        filters: [
            {
                name: 'PNG Image',
                extensions: ['png'],
            },
            {
                name: 'JPEG Image',
                extensions: ['jpg', 'jpeg'],
            },
        ],
    });

    return path;
}

3.3 完整保存示例 #

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

async function saveFile(content: string, defaultName: string = 'untitled.txt') {
    const path = await save({
        defaultPath: defaultName,
        filters: [
            {
                name: 'Text File',
                extensions: ['txt'],
            },
            {
                name: 'All Files',
                extensions: ['*'],
            },
        ],
    });

    if (path) {
        await writeTextFile(path, content);
        return path;
    }

    return null;
}

四、消息对话框 #

4.1 基本消息 #

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

async function showMessage() {
    await message('操作已完成', {
        title: '提示',
        kind: 'info',
    });
}

4.2 消息类型 #

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

// 信息
await message('这是一条信息', { kind: 'info' });

// 警告
await message('这是一条警告', { kind: 'warning' });

// 错误
await message('发生错误', { kind: 'error' });

4.3 错误处理示例 #

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

async function handleOperation() {
    try {
        // 执行操作
        await performOperation();
        
        await message('操作成功完成', {
            title: '成功',
            kind: 'info',
        });
    } catch (error) {
        await message(`操作失败: ${error}`, {
            title: '错误',
            kind: 'error',
        });
    }
}

五、确认对话框 #

5.1 基本确认 #

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

async function confirmAction() {
    const confirmed = await ask('确定要执行此操作吗?', {
        title: '确认',
        kind: 'warning',
    });

    if (confirmed) {
        console.log('用户确认');
    } else {
        console.log('用户取消');
    }
}

5.2 删除确认 #

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

async function confirmDelete(fileName: string): Promise<boolean> {
    return await ask(`确定要删除 "${fileName}" 吗?此操作无法撤销。`, {
        title: '确认删除',
        kind: 'warning',
        okLabel: '删除',
        cancelLabel: '取消',
    });
}

// 使用
async function deleteFile(path: string) {
    const fileName = path.split('/').pop() || path;
    const confirmed = await confirmDelete(fileName);
    
    if (confirmed) {
        // 执行删除
        await remove(path);
    }
}

5.3 退出确认 #

typescript
import { ask } from '@tauri-apps/plugin-dialog';
import { getCurrentWindow } from '@tauri-apps/api/window';

async function setupCloseConfirmation() {
    const window = getCurrentWindow();

    await window.onCloseRequested(async (event) => {
        const confirmed = await ask('确定要退出吗?未保存的更改将丢失。', {
            title: '退出确认',
            kind: 'warning',
            okLabel: '退出',
            cancelLabel: '取消',
        });

        if (!confirmed) {
            event.preventDefault();
        }
    });
}

六、自定义按钮 #

6.1 自定义按钮文本 #

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

async function customButtons() {
    const result = await ask('是否保存更改?', {
        title: '保存',
        kind: 'info',
        okLabel: '保存',
        cancelLabel: '不保存',
    });

    return result;
}

6.2 三选一对话框 #

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

async function saveBeforeClose(): Promise<'save' | 'discard' | 'cancel'> {
    const save = await confirm('是否保存更改?', {
        title: '保存',
        kind: 'info',
        okLabel: '保存',
        cancelLabel: '不保存',
    });

    if (save) {
        return 'save';
    }

    const discard = await confirm('确定放弃更改吗?', {
        title: '确认',
        kind: 'warning',
        okLabel: '放弃',
        cancelLabel: '取消',
    });

    return discard ? 'discard' : 'cancel';
}

七、React 封装 #

7.1 对话框 Hook #

typescript
// hooks/useDialog.ts
import { open, save, message, ask } from '@tauri-apps/plugin-dialog';

export function useDialog() {
    const selectFile = async (options?: Parameters<typeof open>[0]) => {
        return open(options);
    };

    const selectDirectory = async () => {
        return open({ directory: true });
    };

    const selectSavePath = async (defaultName?: string) => {
        return save({ defaultPath: defaultName });
    };

    const showMessage = async (msg: string, title?: string) => {
        await message(msg, { title });
    };

    const showError = async (msg: string, title?: string) => {
        await message(msg, { title, kind: 'error' });
    };

    const showWarning = async (msg: string, title?: string) => {
        await message(msg, { title, kind: 'warning' });
    };

    const confirm = async (msg: string, title?: string) => {
        return ask(msg, { title, kind: 'warning' });
    };

    return {
        selectFile,
        selectDirectory,
        selectSavePath,
        showMessage,
        showError,
        showWarning,
        confirm,
    };
}

7.2 使用 Hook #

tsx
import { useDialog } from '../hooks/useDialog';

function FileEditor() {
    const { selectFile, selectSavePath, confirm, showError } = useDialog();

    const handleOpen = async () => {
        const path = await selectFile({
            filters: [{ name: 'Text', extensions: ['txt'] }],
        });

        if (path) {
            // 打开文件
        }
    };

    const handleSave = async () => {
        const path = await selectSavePath('untitled.txt');

        if (path) {
            // 保存文件
        }
    };

    const handleDelete = async () => {
        const confirmed = await confirm('确定要删除吗?');
        if (confirmed) {
            // 删除
        }
    };

    return (
        <div>
            <button onClick={handleOpen}>打开</button>
            <button onClick={handleSave}>保存</button>
            <button onClick={handleDelete}>删除</button>
        </div>
    );
}

八、最佳实践 #

8.1 错误处理 #

typescript
async function safeSelectFile() {
    try {
        const path = await open();
        return path;
    } catch (error) {
        console.error('Failed to open dialog:', error);
        return null;
    }
}

8.2 用户友好提示 #

typescript
async function saveWithConfirmation(content: string) {
    const path = await save({
        defaultPath: 'untitled.txt',
        title: '保存文件',
    });

    if (!path) {
        // 用户取消
        return false;
    }

    try {
        await writeTextFile(path, content);
        await message('文件保存成功', { title: '成功', kind: 'info' });
        return true;
    } catch (error) {
        await message(`保存失败: ${error}`, { title: '错误', kind: 'error' });
        return false;
    }
}

九、总结 #

9.1 核心要点 #

要点 说明
文件选择 open() 函数
保存对话框 save() 函数
消息对话框 message() 函数
确认对话框 ask() 函数
过滤器 filters 选项

9.2 下一步 #

现在你已经掌握了系统对话框,接下来让我们学习 系统托盘,了解如何创建系统托盘应用!

最后更新:2026-03-28