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 type { Meta, StoryObj } from "@storybook/vue3";
|
||||||
|
|
||||||
import { VDialog } from ".";
|
import { Dialog, VDialog } from ".";
|
||||||
import { VButton } from "../button";
|
import { VButton } from "../button";
|
||||||
|
|
||||||
const meta: Meta<typeof VDialog> = {
|
const meta: Meta<typeof VDialog> = {
|
||||||
|
@ -12,8 +12,11 @@ const meta: Meta<typeof VDialog> = {
|
||||||
height: 400,
|
height: 400,
|
||||||
setup() {
|
setup() {
|
||||||
const showDialog = () => {
|
const showDialog = () => {
|
||||||
|
Dialog.success({
|
||||||
|
title: "Hi",
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
args.visible = true;
|
type: args.type,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -24,7 +27,6 @@ const meta: Meta<typeof VDialog> = {
|
||||||
template: `
|
template: `
|
||||||
<div style="height: 400px">
|
<div style="height: 400px">
|
||||||
<VButton @click="showDialog" >点击显示Dialog</VButton>
|
<VButton @click="showDialog" >点击显示Dialog</VButton>
|
||||||
<VDialog v-bind="args" @onCancel="args.visible = false" />
|
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Type as ButtonType } from "@/components/button/interface";
|
import type { DialogProps, Type } from "@/components/dialog/interface";
|
||||||
import type { Type } from "@/components/dialog/interface";
|
|
||||||
import { markRaw, ref, type Component, type Raw } from "vue";
|
import { markRaw, ref, type Component, type Raw } from "vue";
|
||||||
import {
|
import {
|
||||||
IconCheckboxCircle,
|
IconCheckboxCircle,
|
||||||
|
@ -12,20 +11,7 @@ import {
|
||||||
import { VButton } from "../button";
|
import { VButton } from "../button";
|
||||||
import { VModal } from "../modal";
|
import { VModal } from "../modal";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(defineProps<DialogProps>(), {
|
||||||
defineProps<{
|
|
||||||
type?: Type;
|
|
||||||
title?: string;
|
|
||||||
description?: string;
|
|
||||||
confirmText?: string;
|
|
||||||
confirmType?: ButtonType;
|
|
||||||
showCancel?: boolean;
|
|
||||||
cancelText?: string;
|
|
||||||
visible?: boolean;
|
|
||||||
onConfirm?: () => void;
|
|
||||||
onCancel?: () => void;
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "提示",
|
title: "提示",
|
||||||
description: "",
|
description: "",
|
||||||
|
@ -40,8 +26,7 @@ const props = withDefaults(
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "update:visible", visible: boolean): void;
|
(event: "update:visible", visible: boolean): void;
|
||||||
|
@ -102,6 +87,7 @@ const handleClose = () => {
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="450"
|
:width="450"
|
||||||
:layer-closable="true"
|
:layer-closable="true"
|
||||||
|
:data-unique-id="uniqueId"
|
||||||
@close="handleCancel()"
|
@close="handleCancel()"
|
||||||
>
|
>
|
||||||
<div class="flex justify-between items-start py-2 mb-2">
|
<div class="flex justify-between items-start py-2 mb-2">
|
||||||
|
|
|
@ -19,17 +19,32 @@ const defaultProps: DialogProps = {
|
||||||
visible: false,
|
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 dialog: DialogEntry = (userProps: DialogProps) => {
|
||||||
const props = {
|
const props = {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
...userProps,
|
...userProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
let container = document.body.querySelector(".dialog-container");
|
const container = getOrCreateContainer();
|
||||||
if (!container) {
|
|
||||||
container = document.createElement("div");
|
if (
|
||||||
container.className = "dialog-container";
|
props.uniqueId &&
|
||||||
document.body.appendChild(container);
|
container.querySelector(`[data-unique-id="${props.uniqueId}"]`)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { vnode, container: hostContainer } = createVNodeComponent(
|
const { vnode, container: hostContainer } = createVNodeComponent(
|
||||||
|
@ -37,19 +52,21 @@ const dialog: DialogEntry = (userProps: DialogProps) => {
|
||||||
props
|
props
|
||||||
);
|
);
|
||||||
|
|
||||||
if (hostContainer.firstElementChild) {
|
hostContainer.firstElementChild &&
|
||||||
container.appendChild(hostContainer.firstElementChild);
|
container.appendChild(hostContainer.firstElementChild);
|
||||||
}
|
|
||||||
|
|
||||||
if (vnode.component?.props) {
|
vnode.component?.props && (vnode.component.props.visible = true);
|
||||||
vnode.component.props.visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vnode?.props) {
|
if (vnode?.props) {
|
||||||
// close emit
|
|
||||||
|
|
||||||
vnode.props.onClose = () => {
|
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);
|
render(null, hostContainer);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,7 @@ export interface DialogProps {
|
||||||
showCancel?: boolean;
|
showCancel?: boolean;
|
||||||
confirmText?: string;
|
confirmText?: string;
|
||||||
cancelText?: string;
|
cancelText?: string;
|
||||||
|
uniqueId?: string;
|
||||||
onConfirm?: () => void;
|
onConfirm?: () => void;
|
||||||
onCancel?: () => 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"
|
class="modal-wrapper"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
v-bind="$attrs"
|
||||||
@keyup.esc.stop="handleClose()"
|
@keyup.esc.stop="handleClose()"
|
||||||
>
|
>
|
||||||
<transition
|
<transition
|
||||||
|
|
Loading…
Reference in New Issue