mirror of https://github.com/halo-dev/halo
feat: add support for setting uniqueId in Dialog to prevent duplicate creation (#6737)
#### What type of PR is this? /area ui /kind feature /milestone 2.20.x #### What this PR does / why we need it: Dialog API 支持传入 uniqueId,以限制同一时间仅打开一个。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/6724 #### Does this PR introduce a user-facing change? ```release-note Dialog API 支持传入 uniqueId,以限制同一时间仅打开一个。 ```pull/6736/head^2
parent
56804c9be1
commit
c80ceb460d
|
@ -1,6 +1,6 @@
|
|||
import type { Meta, StoryObj } from "@storybook/vue3";
|
||||
|
||||
import { VDialog } from ".";
|
||||
import { Dialog, VDialog } from ".";
|
||||
import { VButton } from "../button";
|
||||
|
||||
const meta: Meta<typeof VDialog> = {
|
||||
|
@ -12,8 +12,11 @@ const meta: Meta<typeof VDialog> = {
|
|||
height: 400,
|
||||
setup() {
|
||||
const showDialog = () => {
|
||||
// @ts-ignore
|
||||
args.visible = true;
|
||||
Dialog.success({
|
||||
title: "Hi",
|
||||
// @ts-ignore
|
||||
type: args.type,
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
@ -24,7 +27,6 @@ const meta: Meta<typeof VDialog> = {
|
|||
template: `
|
||||
<div style="height: 400px">
|
||||
<VButton @click="showDialog" >点击显示Dialog</VButton>
|
||||
<VDialog v-bind="args" @onCancel="args.visible = false" />
|
||||
</div>
|
||||
`,
|
||||
}),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts" setup>
|
||||
import type { Type as ButtonType } from "@/components/button/interface";
|
||||
import type { Type } from "@/components/dialog/interface";
|
||||
import type { DialogProps, Type } from "@/components/dialog/interface";
|
||||
import { markRaw, ref, type Component, type Raw } from "vue";
|
||||
import {
|
||||
IconCheckboxCircle,
|
||||
|
@ -12,36 +11,22 @@ import {
|
|||
import { VButton } from "../button";
|
||||
import { VModal } from "../modal";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type?: Type;
|
||||
title?: string;
|
||||
description?: string;
|
||||
confirmText?: string;
|
||||
confirmType?: ButtonType;
|
||||
showCancel?: boolean;
|
||||
cancelText?: string;
|
||||
visible?: boolean;
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
}>(),
|
||||
{
|
||||
type: "info",
|
||||
title: "提示",
|
||||
description: "",
|
||||
confirmText: "确定",
|
||||
confirmType: "primary",
|
||||
showCancel: true,
|
||||
cancelText: "取消",
|
||||
visible: false,
|
||||
onConfirm: () => {
|
||||
return;
|
||||
},
|
||||
onCancel: () => {
|
||||
return;
|
||||
},
|
||||
}
|
||||
);
|
||||
const props = withDefaults(defineProps<DialogProps>(), {
|
||||
type: "info",
|
||||
title: "提示",
|
||||
description: "",
|
||||
confirmText: "确定",
|
||||
confirmType: "primary",
|
||||
showCancel: true,
|
||||
cancelText: "取消",
|
||||
visible: false,
|
||||
onConfirm: () => {
|
||||
return;
|
||||
},
|
||||
onCancel: () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: "update:visible", visible: boolean): void;
|
||||
|
@ -102,6 +87,7 @@ const handleClose = () => {
|
|||
:visible="visible"
|
||||
:width="450"
|
||||
:layer-closable="true"
|
||||
:data-unique-id="uniqueId"
|
||||
@close="handleCancel()"
|
||||
>
|
||||
<div class="flex justify-between items-start py-2 mb-2">
|
||||
|
|
|
@ -19,17 +19,32 @@ const defaultProps: DialogProps = {
|
|||
visible: false,
|
||||
};
|
||||
|
||||
const DIALOG_CONTAINER_CLASS = ".dialog-container";
|
||||
const MODAL_WRAPPER_CLASS = ".modal-wrapper";
|
||||
|
||||
function getOrCreateContainer() {
|
||||
let container = document.body.querySelector(DIALOG_CONTAINER_CLASS);
|
||||
if (!container) {
|
||||
container = document.createElement("div");
|
||||
container.className = "dialog-container";
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
const dialog: DialogEntry = (userProps: DialogProps) => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
...userProps,
|
||||
};
|
||||
|
||||
let container = document.body.querySelector(".dialog-container");
|
||||
if (!container) {
|
||||
container = document.createElement("div");
|
||||
container.className = "dialog-container";
|
||||
document.body.appendChild(container);
|
||||
const container = getOrCreateContainer();
|
||||
|
||||
if (
|
||||
props.uniqueId &&
|
||||
container.querySelector(`[data-unique-id="${props.uniqueId}"]`)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { vnode, container: hostContainer } = createVNodeComponent(
|
||||
|
@ -37,19 +52,21 @@ const dialog: DialogEntry = (userProps: DialogProps) => {
|
|||
props
|
||||
);
|
||||
|
||||
if (hostContainer.firstElementChild) {
|
||||
hostContainer.firstElementChild &&
|
||||
container.appendChild(hostContainer.firstElementChild);
|
||||
}
|
||||
|
||||
if (vnode.component?.props) {
|
||||
vnode.component.props.visible = true;
|
||||
}
|
||||
vnode.component?.props && (vnode.component.props.visible = true);
|
||||
|
||||
if (vnode?.props) {
|
||||
// close emit
|
||||
|
||||
vnode.props.onClose = () => {
|
||||
container?.remove();
|
||||
const modals = container.querySelectorAll(MODAL_WRAPPER_CLASS);
|
||||
|
||||
if (modals.length > 1) {
|
||||
hostContainer.firstElementChild?.remove();
|
||||
} else {
|
||||
container.remove();
|
||||
}
|
||||
|
||||
render(null, hostContainer);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,13 +11,7 @@ export interface DialogProps {
|
|||
showCancel?: boolean;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
uniqueId?: string;
|
||||
onConfirm?: () => void;
|
||||
onCancel?: () => void;
|
||||
}
|
||||
|
||||
type useDialogOptions = Omit<DialogProps, "visible" | "title"> & {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type useDialogUserOptions = Omit<useDialogOptions, "type" | "visible">;
|
||||
|
|
|
@ -126,6 +126,7 @@ watch(
|
|||
class="modal-wrapper"
|
||||
role="dialog"
|
||||
tabindex="0"
|
||||
v-bind="$attrs"
|
||||
@keyup.esc.stop="handleClose()"
|
||||
>
|
||||
<transition
|
||||
|
|
Loading…
Reference in New Issue