# Dialog 对话框组件

基于 Svelte 构建的全面对话框系统，提供具有拖拽功能、操作按钮和高级生命周期管理的灵活模态对话框。

## 特性

- **多种对话框类型**：Dialog、CommonDialog 和 DialogBoard 组件
- **拖拽功能**：可通过标题栏拖拽移动的对话框
- **操作按钮**：可配置的按钮操作，自动处理结果
- **关闭确认**：关闭前的可选确认对话框
- **模态管理**：基于堆栈的对话框管理，带有遮罩层
- **响应式设计**：自动调整大小和定位
- **无障碍访问**：ARIA 支持和键盘导航
- **生命周期钩子**：带结果处理的 onClose 回调
- **全局对话框 API**：窗口级别的对话框管理
- **可定制样式**：灵活的主题和大小选项

## 安装

```bash
npm install @ticatec/uniface-element
```

```typescript
import Dialog, { CommonDialog, DialogBoard } from '@ticatec/uniface-element/Dialog';
```

或者在同一项目中使用：
```typescript
import Dialog, { CommonDialog, DialogBoard } from '$lib/dialog';
```

## 组件概览

### Dialog
具有完全自定义选项的基础对话框组件。

### CommonDialog
具有内置确认/取消操作的简化对话框。

### DialogBoard
对话框容器和管理系统。

## 设置

### 1. 将 DialogBoard 添加到您的应用

首先，将 DialogBoard 组件添加到您的主应用布局中：

```svelte
<!-- App.svelte 或 +layout.svelte -->
<script>
  import { DialogBoard } from '$lib/dialog';
</script>

<!-- 您的应用内容 -->
<main>
  <!-- 您的路由和组件 -->
</main>

<!-- 对话框容器（应放在最后） -->
<DialogBoard />
```

### 2. 初始化全局对话框 API

DialogBoard 在挂载时会自动初始化全局 `window.Dialog` API。

## Dialog 组件

### 基本用法

```svelte
<script>
  import Dialog from '$lib/dialog';
  import { ModalResult } from '$lib/dialog/ModalResult';
  
  let showDialog = false;
  
  const actions = [
    {
      label: '保存',
      type: 'primary',
      handler: async () => {
        // 保存逻辑
        console.log('正在保存...');
        return true; // 返回 true 关闭对话框
      }
    }
  ];
  
  function handleClose(result) {
    console.log('对话框关闭，结果：', result);
    showDialog = false;
  }
</script>

{#if showDialog}
  <Dialog 
    title="我的对话框" 
    {actions} 
    onClose={handleClose}
    width="500px"
    height="300px"
  >
    <p>对话框内容放在这里...</p>
  </Dialog>
{/if}
```

### 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `title` | `string` | `''` | 显示在标题栏的对话框标题 |
| `width` | `string` | `'unset'` | 对话框宽度（CSS 值） |
| `height` | `string` | `'unset'` | 对话框高度（CSS 值） |
| `actions` | `ButtonActions \| null` | `null` | 操作按钮数组 |
| `closeConfirm` | `DialogCloseConfirm \| null` | `null` | 关闭前的确认函数 |
| `content$style` | `string` | `''` | 内容区域的自定义样式 |
| `closeAction` | `ButtonAction \| null` | `null` | 自定义关闭按钮配置 |
| `onClose` | `OnClose \| null` | `null` | 对话框关闭时的回调 |
| `dialog$style` | `string` | `''` | 对话框容器的自定义样式 |

### 方法

| 方法 | 描述 |
|--------|-------------|
| `close(result: ModalResult)` | 程序化关闭对话框并返回结果 |

## CommonDialog 组件

具有内置确认/取消模式的简化对话框。

### 基本用法

```svelte
<script>
  import { CommonDialog } from '$lib/dialog';
  
  let showDialog = false;
  
  async function handleConfirm() {
    // 您的确认逻辑
    console.log('已确认！');
    return true; // 返回 true 关闭对话框
  }
  
  function handleClose(result) {
    console.log('对话框结果：', result);
    showDialog = false;
  }
</script>

{#if showDialog}
  <CommonDialog 
    title="确认操作"
    confirmHandler={handleConfirm}
    onClose={handleClose}
    width="400px"
  >
    <p>您确定要继续吗？</p>
  </CommonDialog>
{/if}
```

### 属性

| 属性 | 类型 | 默认值 | 描述 |
|------|------|---------|-------------|
| `title` | `string` | `''` | 对话框标题 |
| `width` | `string` | `'unset'` | 对话框宽度 |
| `height` | `string` | `'unset'` | 对话框高度 |
| `closeConfirm` | `DialogCloseConfirm \| null` | `null` | 关闭确认函数 |
| `content$style` | `string` | `''` | 内容区域样式 |
| `enableConfirm` | `boolean` | `true` | 启用/禁用确认按钮 |
| `confirmHandler` | `(() => Promise<any>) \| null` | `null` | 确认按钮处理器 |
| `confirmText` | `string \| null` | `null` | 自定义确认按钮文本 |
| `onClose` | `OnClose \| null` | `null` | 关闭回调 |

## 全局对话框 API

使用全局 `window.Dialog` API 从应用的任何地方显示对话框。

### 显示对话框

```svelte
<script>
  import MyDialogContent from './MyDialogContent.svelte';
  
  function openDialog() {
    window.Dialog.showModal(MyDialogContent, {
      title: '我的对话框',
      data: { /* 初始数据 */ },
      onSave: (data) => console.log('已保存：', data)
    });
  }
</script>

<button on:click={openDialog}>打开对话框</button>
```

### 创建对话框内容组件

```svelte
<!-- MyDialogContent.svelte -->
<script>
  import Dialog from '$lib/dialog';
  import { ModalResult } from '$lib/dialog/ModalResult';
  
  // 从 showModal 传递的属性
  export let title = '对话框';
  export let data = {};
  export let onSave = null;
  
  const actions = [
    {
      label: '保存',
      type: 'primary',
      handler: async () => {
        // 验证逻辑
        if (!data.name) {
          alert('姓名是必填的');
          return false; // 不关闭对话框
        }
        
        // 保存逻辑
        await onSave?.(data);
        return true; // 关闭对话框
      }
    }
  ];
  
  const closeConfirm = async () => {
    if (hasChanges) {
      return await window.MessageBox.showConfirm(
        '您有未保存的更改。确定要关闭吗？',
        '确认关闭'
      ) === ModalResult.Ok;
    }
    return true;
  };
</script>

<Dialog {title} {actions} {closeConfirm} width="600px">
  <!-- 您的对话框内容 -->
  <form>
    <input bind:value={data.name} placeholder="姓名" />
    <!-- 更多表单字段 -->
  </form>
</Dialog>
```

## 操作按钮

### 按钮配置

```typescript
type ButtonAction = {
  label: string;
  type?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  handler?: (event: MouseEvent) => Promise<boolean> | boolean;
};

type ButtonActions = (ButtonAction | null)[];
```

### 示例

```svelte
<script>
  const actions = [
    {
      label: '删除',
      type: 'danger',
      handler: async () => {
        const confirmed = await confirmDelete();
        if (confirmed) {
          await deleteItem();
          return true; // 关闭对话框
        }
        return false; // 保持对话框打开
      }
    },
    null, // 分隔符
    {
      label: '保存',
      type: 'primary',
      disabled: !isValid,
      handler: async () => {
        await saveData();
        return true;
      }
    }
  ];
</script>
```

## 关闭确认

为有未保存更改的情况添加关闭确认：

```svelte
<script>
  let originalData = JSON.stringify(data);
  
  const closeConfirm = async () => {
    const hasChanges = JSON.stringify(data) !== originalData;
    if (hasChanges) {
      return await window.MessageBox.showConfirm(
        '您有未保存的更改。仍要关闭吗？',
        '确认关闭'
      ) === ModalResult.Ok;
    }
    return true;
  };
</script>

<Dialog {closeConfirm} {/* 其他属性 */}>
  <!-- 内容 -->
</Dialog>
```

## 模态结果

```typescript
enum ModalResult {
  Cancel = 0, // 用户取消或关闭对话框
  Ok = 1      // 用户确认或完成操作
}
```

### 处理结果

```svelte
<script>
  function handleDialogClose(result) {
    if (result === ModalResult.Ok) {
      console.log('用户确认');
    } else {
      console.log('用户取消');
    }
  }
</script>
```

## 对话框大小和定位

### 响应式大小

```svelte
<!-- 固定大小 -->
<Dialog width="500px" height="400px">

<!-- 响应式大小 -->
<Dialog width="90vw" height="80vh">

<!-- 自动大小，带最大约束 -->
<Dialog 
  width="min(800px, 90vw)" 
  height="min(600px, 80vh)"
>
```

### 自定义样式

```svelte
<Dialog 
  dialog$style="border-radius: 12px; box-shadow: 0 20px 40px rgba(0,0,0,0.3);"
  content$style="padding: 2rem; background: #f9f9f9;"
>
```

## 高级示例

### 带验证的表单对话框

```svelte
<!-- UserFormDialog.svelte -->
<script>
  import Dialog from '$lib/dialog';
  import FormField from '$lib/form-field';
  import TextEditor from '$lib/text-editor';
  
  export let user = { name: '', email: '' };
  export let onSave = null;
  
  let errors = {};
  
  function validate() {
    errors = {};
    if (!user.name) errors.name = '姓名是必填的';
    if (!user.email) errors.email = '邮箱是必填的';
    if (user.email && !user.email.includes('@')) errors.email = '邮箱格式无效';
    return Object.keys(errors).length === 0;
  }
  
  const actions = [
    {
      label: '保存用户',
      type: 'primary',
      handler: async () => {
        if (!validate()) return false;
        
        try {
          await onSave?.(user);
          return true;
        } catch (error) {
          console.error('保存失败：', error);
          return false;
        }
      }
    }
  ];
</script>

<Dialog title="用户详情" {actions} width="500px">
  <div style="padding: 1rem;">
    <FormField label="姓名" error={errors.name}>
      <TextEditor bind:value={user.name} />
    </FormField>
    
    <FormField label="邮箱" error={errors.email}>
      <TextEditor bind:value={user.email} type="email" />
    </FormField>
  </div>
</Dialog>
```

### 主从详情对话框

```svelte
<!-- ProjectDialog.svelte -->
<script>
  import Dialog from '$lib/dialog';
  import DataTable from '$lib/data-table';
  
  export let project = { name: '', tasks: [] };
  
  let selectedTask = null;
  
  const actions = [
    {
      label: '添加任务',
      type: 'secondary',
      handler: () => {
        project.tasks = [...project.tasks, { name: '新任务', status: '待处理' }];
        return false; // 保持对话框打开
      }
    },
    {
      label: '保存项目',
      type: 'primary',
      handler: async () => {
        await saveProject(project);
        return true;
      }
    }
  ];
  
  const taskColumns = [
    { title: '任务名称', field: 'name' },
    { title: '状态', field: 'status' }
  ];
</script>

<Dialog title="项目详情" {actions} width="800px" height="600px">
  <div style="display: flex; height: 100%; gap: 1rem; padding: 1rem;">
    <!-- 项目信息 -->
    <div style="flex: 1;">
      <h3>项目信息</h3>
      <FormField label="项目名称">
        <TextEditor bind:value={project.name} />
      </FormField>
    </div>
    
    <!-- 任务列表 -->
    <div style="flex: 2;">
      <h3>任务</h3>
      <DataTable 
        list={project.tasks}
        columns={taskColumns}
        bind:selectedRow={selectedTask}
      />
    </div>
  </div>
</Dialog>
```

## 样式定制

### CSS 变量

```css
.uniface-dialog {
  --dialog-background: white;
  --dialog-border-radius: 8px;
  --dialog-shadow: 0 10px 30px rgba(0,0,0,0.2);
  --title-bar-background: #f5f5f5;
  --title-bar-color: #333;
}
```

### 自定义对话框样式

```scss
.custom-dialog {
  .uniface-dialog-box {
    border-radius: 12px;
    overflow: hidden;
    
    .title-bar {
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    
    .dialog-content {
      background: #fafafa;
    }
  }
}
```

## 无障碍访问

- **ARIA 标签**：为屏幕阅读器提供适当的标签
- **键盘导航**：通过 Tab 在按钮和表单元素间切换
- **焦点管理**：对话框打开时自动聚焦，关闭时恢复焦点
- **Escape 键**：使用 Escape 键关闭对话框
- **模态行为**：将焦点限制在对话框内

## 最佳实践

### 1. 对话框大小指南
- 移动端：使用 `width="95vw"` 制作移动友好的对话框
- 桌面端：优先使用固定宽度如 `"500px"` 或 `"800px"`
- 内容驱动：使用 `"auto"` 并带有最大宽度约束

### 2. 操作按钮模式
- 主要操作放在右侧
- 破坏性操作使用 `type="danger"`
- 使用分隔符（`null`）分组相关操作

### 3. 表单验证
- 在允许对话框关闭前验证
- 显示字段级错误
- 表单无效时禁用提交按钮

### 4. 数据管理
- 使用响应式语句启用/禁用按钮
- 为有更改的表单实现关闭确认
- 传递数据持久化的回调

### 5. 错误处理
- 在操作处理器中处理异步错误
- 显示用户友好的错误消息
- 保存失败时保持对话框打开

## 类型定义

```typescript
interface DialogCloseConfirm {
  (): Promise<boolean>;
}

interface ButtonAction {
  label: string;
  type?: 'primary' | 'secondary' | 'danger';
  disabled?: boolean;
  handler?: (event: MouseEvent) => Promise<boolean> | boolean;
}

type ButtonActions = (ButtonAction | null)[];

type OnClose = (result: ModalResult) => void;

interface IDialog {
  showModal: (component: any, params: any) => void;
}
```

## 迁移指南

### 从 v1.x 到 v2.x
- 您的应用现在需要 `DialogBoard`
- 全局对话框 API 会自动初始化
- 模态结果现在基于枚举而不是字符串

## 注意事项

- 对话框由 DialogBoard 组件自动管理
- 可以同时打开多个对话框（堆叠）
- 标题栏默认启用拖拽功能
- 对话框系统使用 Svelte 过渡实现平滑动画
- 操作按钮处理器应返回 `true` 关闭对话框，返回 `false` 保持打开