Skip to page content

Dialog 对话框插件

Quasar 对话框非常适合让用户选择某个操作或一系列操作,也可以用来向用户展示重要信息,或要求用户做出决定。

从 UI 角度来看,对话框可以理解为一种浮动的模态窗口,只覆盖屏幕的一部分。因此对话框应该只用于快速的用户交互操作。

TIP

对话框也可以作为 Vue 文件模板中的组件使用(适用于复杂场景,如特定的表单组件、可选选项等)。详情请参阅 QDialog 页面。

使用 Quasar 插件形式的对话框相比 QDialog 组件的优势在于:插件可以在 Vue 环境之外调用,且不需要您管理模板。但相应地,其自定义能力不如组件形式灵活。

不过,您也可以提供一个自定义组件让 Dialog 插件来渲染(参见下方的"调用自定义组件"章节),这是一种很好的方式来避免在 Vue 模板中内联对话框(同时也有助于更好地组织项目文件和复用对话框)。

通过 QDialog 插件,您可以编程式地创建以下三种类型的对话框:

  1. 提示输入对话框——要求用户在输入框中填写某些数据。
  2. 选项选择对话框——让用户通过单选按钮或开关(单选)或复选框(多选)来选择选项。
  3. 简单确认对话框——用户可以取消或确认某个操作。

要创建第 1 种提示输入表单,需要在 opts 对象中使用 prompt 属性。

要创建第 2 种选项选择表单,需要在 opts 对象中使用 options 属性。

内置组件

在 Vue 文件外部

import { Dialog } from 'quasar'
(Object) Dialog.create({ ... })

// 在 Vue 文件内部
import { useQuasar } from 'quasar'

setup () {
  const $q = useQuasar()
  $q.dialog({ ... }) // 返回 Object
}

请查看 API 卡片了解返回的 Object 的具体内容。

用法

TIP

查看以下所有示例时,建议同时打开浏览器控制台观察输出。

WARNING

这并不是 Dialog 插件全部功能的完整列表。更多功能请查阅 API 章节。

基础



关闭原因
v2.28+

onCancel 回调会收到本次关闭的原因:cancel(点击了取消按钮)、backdrop(点击了背景遮罩)、escape(ESC 键)或 programmatic(通过代码隐藏,包括应用路由变化触发的关闭)。

onDismiss 回调会收到同样的原因,但当对话框通过 OK 关闭时例外——此时它收到的是 onOk 回调所收到的那份 payload。

当调用自定义组件时(见下文各章节),该原因会镜像你组件 hide 事件的 payload:useDialogPluginComponent 组合式函数会替你发出上述这些值,而手写的组件则自行决定它的 payload(也可以不带)。

原生属性

您还可以为内部的 QInput 或 QOptionGroup 组件传递原生 HTML 属性,如下例所示。

用户输入验证

系统提供了基础的验证机制,在用户填写的值不符合预期之前,将无法提交对话框(点击/触摸"确定"或按 ENTER)。

进度展示

使用 HTML

如果指定了 html: true 属性,您可以在标题和消息中使用 HTML。请注意这可能导致 XSS 攻击,请务必自行对消息内容进行安全过滤。

调用自定义组件

您也可以调用自己的自定义组件,而不仅限于使用 Dialog 插件自带的默认组件。但此时您需要自行处理所有逻辑(包括自定义组件的 props)。

这个功能实际上是 Dialog 插件的精华所在。它可以帮助您保持其他 Vue 组件模板的整洁,轻松地将对话框功能分离和复用。

触发自定义组件


/**
 * 这种方式也可以在 Vue 组件外部使用
 */

import { Dialog } from 'quasar'
import CustomComponent from '..path.to.component..'

Dialog.create({
  component: CustomComponent,

  // 转发给自定义组件的 props
  componentProps: {
    text: 'something',
    persistent: true,
    // ...更多 props...
  }
}).onOk(() => {
  console.log('OK')
}).onCancel(reason => {
  // reason(Quasar v2.28+)为 'cancel'、'backdrop'、
  // 'escape' 或 'programmatic'
  console.log('Cancel', reason)
}).onDismiss(() => {
  console.log('Called on OK or Cancel')
})

使用选项式 API 的等价写法是直接使用 this.$q.dialog({ ... })。

WARNING

您的自定义组件必须遵循下面描述的接口规范,才能完美接入 Dialog 插件。请注意代码中的 “REQUIRED”(必需)注释,以下仅为最基础的示例骨架。

编写自定义组件

使用 “script setup” 的 SFC 组合式 API 写法

我们将使用 useDialogPluginComponent 组合式函数。

<template>
  <q-dialog ref="dialogRef" @hide="onDialogHide">
    <q-card class="q-dialog-plugin">
      <!--
        ...内容
        ... 可以使用 q-card-section 来组织内容
      -->

      <!-- 按钮示例 -->
      <q-card-actions align="right">
        <q-btn color="primary" label="OK" @click="onOKClick" />
        <q-btn color="primary" label="Cancel" @click="onDialogCancel" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script setup>
  import { useDialogPluginComponent } from "quasar";

  const props = defineProps({
    // ...您的自定义 props
  });

  defineEmits([
    // 必需;需要指定组件通过 useDialogPluginComponent() 发出的事件
    ...useDialogPluginComponent.emits,
  ]);

  const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
  // dialogRef      - 应用到 QDialog 的 Vue ref
  // onDialogHide   - 用作 QDialog @hide 事件的处理函数;
  //                    请直接绑定它(不要包裹),这样它才能接收到
  //                    QDialog 的事件,并把关闭原因转发给链式的
  //                    onCancel/onDismiss 回调(Quasar v2.28+)
  // onDialogOK     - 以"确认"结果关闭对话框的函数
  //                    示例:onDialogOK() - 无数据负载
  //                    示例:onDialogOK({ /*...*/ }) - 带数据负载
  // onDialogCancel - 以"取消"结果关闭对话框的函数

  // 以下是示例代码(非必需)
  function onOKClick() {
    // 点击确认时,必须调用 onDialogOK(可选传入数据负载)
    onDialogOK();
    // 或带数据负载:onDialogOK({ ... })
    // ...同时会自动隐藏对话框
  }
</script>

如果您想使用对象形式定义 emits,则(需要 Quasar v2.2.5+):

defineEmits({
  // 必需;需要指定组件通过 useDialogPluginComponent() 发出的事件
  ...useDialogPluginComponent.emitsObject,

  // ...您自己的定义
})

使用 “script” 的 SFC 组合式 API 写法

我们将使用 useDialogPluginComponent 组合式函数。

<template>
  <!-- 注意这里的 dialogRef -->
  <q-dialog ref="dialogRef" @hide="onDialogHide">
    <q-card class="q-dialog-plugin">
      <!--
        ...内容
        ... 可以使用 q-card-section 来组织内容
      -->

      <!-- 按钮示例 -->
      <q-card-actions align="right">
        <q-btn color="primary" label="OK" @click="onOKClick" />
        <q-btn color="primary" label="Cancel" @click="onCancelClick" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script>
  import { useDialogPluginComponent } from "quasar";

  export default {
    props: {
      // ...您的自定义 props
    },

    emits: [
      // 必需;需要指定组件通过 useDialogPluginComponent() 发出的事件
      ...useDialogPluginComponent.emits,
    ],

    setup() {
      // 必需;必须在 setup() 中调用
      const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent();
      // dialogRef      - 应用到 QDialog 的 Vue ref
      // onDialogHide   - 用作 QDialog @hide 事件的处理函数;
      //                    请直接绑定它(不要包裹),这样它才能接收到
      //                    QDialog 的事件,并把关闭原因转发给链式的
      //                    onCancel/onDismiss 回调(Quasar v2.28+)
      // onDialogOK     - 以"确认"结果关闭对话框的函数
      //                    示例:onDialogOK() - 无数据负载
      //                    示例:onDialogOK({ /*...*/ }) - 带数据负载
      // onDialogCancel - 以"取消"结果关闭对话框的函数

      return {
        // 以下是必需的;
        // 需要将 useDialogPluginComponent() 返回的内容
        // 注入到 vue 作用域中以供模板使用
        dialogRef,
        onDialogHide,

        // 模板中使用的其他方法;
        // 以下是示例代码(非必需)
        onOKClick() {
          // 点击确认时,必须调用 onDialogOK(可选传入数据负载)
          onDialogOK();
          // 或带数据负载:onDialogOK({ ... })
          // ...同时会自动隐藏对话框
        },

        // 直接透传 onDialogCancel
        onCancelClick: onDialogCancel,
      };
    },
  };
</script>

如果您想使用对象形式定义 emits,则(需要 Quasar v2.2.5+):

emits: {
  // 必需;需要指定组件通过 useDialogPluginComponent() 发出的事件
  ...useDialogPluginComponent.emitsObject,

  // ...您自己的定义
}

使用 “script” 的 SFC 选项式 API 写法

<template>
  <q-dialog ref="dialog" @hide="onDialogHide">
    <q-card class="q-dialog-plugin">
      <!--
        ...内容
        ... 可以使用 q-card-section 来组织内容
      -->

      <!-- 按钮示例 -->
      <q-card-actions align="right">
        <q-btn color="primary" label="OK" @click="onOKClick" />
        <q-btn color="primary" label="Cancel" @click="onCancelClick" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script>
  export default {
    props: {
      // ...您的自定义 props
    },

    emits: [
      // 必需
      "ok",
      "hide",
    ],

    data() {
      return {
        dismissReason: null
      };
    },

    methods: {
      // 以下方法是必需的
      // (不要更改方法名 --> "show")
      show() {
        this.dismissReason = null;
        this.$refs.dialog.show();
      },

      // 以下方法是必需的
      // (不要更改方法名 --> "hide")
      hide() {
        this.$refs.dialog.hide();
      },

      onDialogHide(evt) {
        // 当 QDialog 触发 "hide" 事件时必须发出此事件;
        // 该 payload 会作为关闭原因传递给插件链式的
        // onCancel/onDismiss 回调(Quasar v2.28+)
        this.$emit(
          "hide",
          this.dismissReason !== null
            ? this.dismissReason
            : evt === undefined
              ? "programmatic"
              : evt.type.indexOf("key") === 0
                ? "escape"
                : "backdrop"
        );
      },

      onOKClick() {
        // 点击确认时,必须在隐藏 QDialog 之前
        // 发出 "ok" 事件(可选传入数据负载)
        this.$emit("ok");
        // 或带数据负载:this.$emit('ok', { ... })

        // 然后隐藏对话框
        this.hide();
      },

      onCancelClick() {
        // 记录原因,然后隐藏对话框
        this.dismissReason = "cancel";
        this.hide();
      },
    },
  };
</script>

上面这套关闭原因的处理(dismissReason 与 hide 的 payload)是可选的:直接写 this.$emit('hide') 依然可用,只是链式的 onCancel/onDismiss 回调将收不到原因。useDialogPluginComponent 组合式函数会替你处理好这一切。

示例:异步提交

内置对话框在点击 OK 按钮时会立即关闭。当提交必须等待一个异步操作(例如保存到服务器)时,请改为调用一个自定义组件:没有任何机制强制你立刻调用 onDialogOK(),因此你可以让对话框保持打开、提交按钮处于 loading 状态,仅在操作成功时才关闭它,失败时则保持打开并显示错误。

请注意下面的 :persistent="submitting"。它可以防止用户在操作仍在进行时关闭对话框(点击背景遮罩或按 ESC)。

<template>
  <q-dialog ref="dialogRef" :persistent="submitting" @hide="onDialogHide">
    <q-card class="q-dialog-plugin">
      <q-card-section>
        <q-input
          v-model="name"
          label="Name"
          :disable="submitting"
          :error="error !== null"
          :error-message="error"
        />
      </q-card-section>

      <q-card-actions align="right">
        <q-btn
          flat
          color="primary"
          label="Cancel"
          :disable="submitting"
          @click="onDialogCancel"
        />
        <q-btn
          color="primary"
          label="Save"
          :loading="submitting"
          @click="onSaveClick"
        />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script setup>
  import { ref } from 'vue'
  import { useDialogPluginComponent } from 'quasar'

  defineEmits([
    // 必需;需要指定组件通过 useDialogPluginComponent() 发出的事件
    ...useDialogPluginComponent.emits
  ])

  const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
    useDialogPluginComponent()

  const name = ref('')
  const submitting = ref(false)
  const error = ref(null)

  async function onSaveClick() {
    submitting.value = true
    error.value = null

    try {
      // 替换成你自己的异步操作(fetch/axios 等)
      const result = await api.save({ name: name.value })

      // 现在才以 "ok" 结果关闭对话框;
      // 这份 payload 会传给链式的 onOk() 回调,
      // 对话框会自动隐藏
      onDialogOK(result)
    } catch (err) {
      // 对话框保持打开;显示错误
      error.value = err.message
    } finally {
      submitting.value = false
    }
  }
</script>

调用方无需做任何改动:

$q.dialog({
  component: SaveDialog
}).onOk(result => {
  // 上面 onDialogOK() 传入的 payload
  console.log('saved', result)
})

Cordova/Capacitor 返回按钮

Quasar 默认会处理返回按钮的行为,使其在有对话框打开时关闭对话框,而不是执行默认的返回上一页操作(那样的用户体验并不好)。

但如果您希望禁用此行为,可以编辑 /quasar.config 文件:


// quasar.config file
return {
  framework: {
    config: {
      capacitor: {
        // Quasar 会在按下手机返回按钮时处理应用退出。
        backButtonExit: true/false/'*'/['/login', '/home', '/my-page'],

        // 另一方面,以下配置会完全
        // 禁用 Quasar 的返回按钮管理。
        backButton: true/false
      }
    }
  }
}