AlertDialog 确认对话框

需要用户明确回答的确认对话框。

<script setup lang="ts">
  import { ref } from 'vue'
  import { AlertDialog, Button, Stack, Text } from '@hina-ui/vue'

  const removed = ref(false)

  async function remove() {
    await new Promise(resolve => setTimeout(resolve, 800))
    removed.value = true
  }
</script>

<template>
  <Stack gap="sm" align="start">
    <AlertDialog
      title="删除这篇文章?"
      description="删除后无法恢复,文章下的评论也会一并删除。"
      tone="danger"
      confirm-text="删除"
      @confirm="remove"
    >
      <Button variant="outline" tone="danger">删除文章</Button>
    </AlertDialog>
    <Text v-if="removed" tone="muted" size="sm">文章已删除。</Text>
  </Stack>
</template>
vue

用法

import { AlertDialog } from '@hina-ui/vue'
ts

确认对话框只问一个问题:title 是问题本身,description 补充后果,底部固定一对按钮,文字可以用 cancelText 与 confirmText 替换。默认插槽是触发器。点击遮罩不会关闭它,按 Esc 或者点「取消」才会;它没有右上角的关闭按钮,回答本身就是关闭的方式。

<script setup lang="ts">
  import { ref } from 'vue'
  import { AlertDialog, Button, Stack, Text } from '@hina-ui/vue'

  const published = ref(false)
</script>

<template>
  <Stack gap="sm" align="start">
    <AlertDialog
      title="发布这篇文章?"
      description="发布之后所有人都能看到它。"
      confirm-text="发布"
      cancel-text="再想想"
      @confirm="published = true"
    >
      <Button variant="outline" tone="neutral">发布</Button>
    </AlertDialog>
    <Text v-if="published" tone="muted" size="sm">文章已发布。</Text>
  </Stack>
</template>
vue

示例

危险操作

tone="danger" 把「确定」换成危险色,用于删除这类不可撤销的操作。

<script setup lang="ts">
  import { AlertDialog, Button } from '@hina-ui/vue'
</script>

<template>
  <AlertDialog
    title="注销账号?"
    description="账号与所有数据会在 30 天后永久删除,期间登录即可撤销。"
    tone="danger"
    confirm-text="注销"
  >
    <Button variant="outline" tone="danger">注销账号</Button>
  </AlertDialog>
</template>
vue

确认倒计时

confirmDelay 指定确认前的等待秒数,默认为 0。每次打开时重新计时,期间确认按钮禁用并显示剩余秒数;结束后恢复原文案与可点击状态,取消和 Esc 在等待期间仍可用。

打开期间修改 confirmDelay 会按新值重新计时,设为 0 立即解除等待。正小数向上取整,非正数或非有限值按 0 处理。确认失败后可以直接重试。

<script setup lang="ts">
  import { AlertDialog, Button } from '@hina-ui/vue'
</script>

<template>
  <AlertDialog
    title="确认操作?"
    description="倒计时结束后,确认按钮可以点击。"
    tone="danger"
    confirm-text="确认"
    :confirm-delay="3"
  >
    <Button variant="outline" tone="neutral">确认倒计时</Button>
  </AlertDialog>
</template>
vue

异步确认

onConfirm 接收确认处理函数,也可以通过 @confirm 传入。返回 Promise 或 PromiseLike 时,对话框等待其完成再关闭:期间「确定」显示加载指示,「取消」与 Esc 都不可用。

同步抛错或异步拒绝由组件内部捕获,并通过 error 事件传出原始错误。对话框保持打开并恢复按钮,可以直接重试;使用 @error 更新错误提示。

<script setup lang="ts">
  import { ref } from 'vue'
  import { AlertDialog, Button, Stack, Switch, Text } from '@hina-ui/vue'

  const fail = ref(false)
  const status = ref('')

  async function archive() {
    status.value = ''
    await new Promise(resolve => setTimeout(resolve, 1000))
    if (fail.value) {
      throw new Error('归档失败,请稍后再试。')
    }
    status.value = '已归档。'
  }
  function handleError(error: unknown) {
    status.value = error instanceof Error ? error.message : String(error)
  }
</script>

<template>
  <Stack gap="md" align="start">
    <Switch v-model="fail">模拟失败</Switch>
    <AlertDialog
      title="归档这个项目?"
      description="归档后项目变为只读,可以随时恢复。"
      confirm-text="归档"
      @confirm="archive"
      @error="handleError"
    >
      <Button variant="outline" tone="neutral">归档项目</Button>
      <template #content>
        <Text v-if="status" tone="muted" size="sm">{{ status }}</Text>
      </template>
    </AlertDialog>
  </Stack>
</template>
vue

受控

open 支持双向绑定。省略默认插槽时不渲染触发器,对话框只能从外部打开。

<script setup lang="ts">
  import { ref } from 'vue'
  import { AlertDialog, Button, Inline } from '@hina-ui/vue'

  const open = ref(false)
</script>

<template>
  <Inline gap="sm">
    <Button variant="outline" tone="neutral" @click="open = true">从外部打开</Button>
    <AlertDialog v-model:open="open" title="离开当前页面?" description="未保存的修改会丢失。" />
  </Inline>
</template>
vue

尺寸与位置

size 默认 sm,宽 384 像素,md 为 448 像素;placement 与 Dialog 相同,不设置时宽屏居中、窄屏贴底。

<script setup lang="ts">
  import { AlertDialog, Button, Inline } from '@hina-ui/vue'
</script>

<template>
  <Inline gap="sm">
    <AlertDialog title="清空回收站?" description="回收站里的 12 个文件会被永久删除。" size="md">
      <Button variant="outline" tone="neutral">md 尺寸</Button>
    </AlertDialog>
    <AlertDialog
      title="清空回收站?"
      description="回收站里的 12 个文件会被永久删除。"
      placement="center"
    >
      <Button variant="outline" tone="neutral">始终居中</Button>
    </AlertDialog>
    <AlertDialog
      title="清空回收站?"
      description="回收站里的 12 个文件会被永久删除。"
      placement="bottom"
    >
      <Button variant="outline" tone="neutral">始终贴底</Button>
    </AlertDialog>
  </Inline>
</template>
vue

行为

  • 打开时焦点落在「取消」按钮上,误按回车不会执行操作;关闭后焦点回到触发器。
  • 点击遮罩不关闭;Esc 关闭,等同于取消。
  • 打开期间页面停止滚动,焦点被限制在面板内。
  • 「确定」的处理函数返回 Promise 时,对话框在其结束前处于忙碌状态,成功后关闭;失败时触发 error 并保持打开。

无障碍

  • 面板是 role="alertdialog",标题与说明分别关联到 aria-labelledby 与 aria-describedby。
  • 忙碌期间面板带有 aria-busy。
  • 按钮文字取自当前语言,可以替换。

API

Props

属性
类型
默认值
说明
title
string
—
必填。问题本身
description
string
—
补充说明
confirmText
string
当前语言
「确定」按钮的文字
confirmDelay
number
0
每次打开后的确认等待秒数
cancelText
string
当前语言
「取消」按钮的文字
tone
'accent' | 'danger'
'accent'
「确定」按钮的色调
size
'sm' | 'md'
'sm'
面板的最大宽度
placement
'center' | 'bottom'
—
不设置时随屏幕宽度变化
onConfirm
() => unknown
—
确认处理函数,可返回 Promise 或 PromiseLike
open
boolean
—
是否打开,支持双向绑定
class
string
—
追加至面板的类名

插槽

插槽
说明
default
触发器
content
说明与按钮之间的内容

事件

事件
参数
说明
confirm
—
点击「确定」;处理函数返回 Promise 时等待其完成
cancel
—
点击「取消」
error
unknown
确认失败的原始错误,弹窗保持打开