diff --git a/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx b/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx
index 49f5780aae..54ea019199 100644
--- a/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx
+++ b/packages/devui-vue/devui/modal/__tests__/modal.spec.tsx
@@ -4,7 +4,7 @@ import DModal from '../src/modal';
import DModalHeader from '../src/components/header';
import DModalFooter from '../src/components/footer';
import DIcon from '../../icon/src/icon';
-import { useNamespace } from '../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared';
import { wait } from '../../shared/utils/';
const ns = useNamespace('modal', true);
diff --git a/packages/devui-vue/devui/modal/index.ts b/packages/devui-vue/devui/modal/index.ts
index fbc3467196..9c68b4bc9a 100644
--- a/packages/devui-vue/devui/modal/index.ts
+++ b/packages/devui-vue/devui/modal/index.ts
@@ -11,7 +11,7 @@ export * from './src/modal-types';
export { Modal, ModalHeader, ModalBody, ModalFooter };
export default {
- title: 'Modal 弹窗',
+ title: 'Modal 模态框',
category: '反馈',
status: '100%',
install(app: App): void {
diff --git a/packages/devui-vue/devui/modal/src/components/body.tsx b/packages/devui-vue/devui/modal/src/components/body.tsx
index a095ae2233..785b501cde 100644
--- a/packages/devui-vue/devui/modal/src/components/body.tsx
+++ b/packages/devui-vue/devui/modal/src/components/body.tsx
@@ -1,5 +1,5 @@
import { defineComponent } from 'vue';
-import { useNamespace } from '../../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared';
export default defineComponent({
name: 'DModalBody',
diff --git a/packages/devui-vue/devui/modal/src/components/footer.tsx b/packages/devui-vue/devui/modal/src/components/footer.tsx
index f24d1e027d..50d77d1ee7 100644
--- a/packages/devui-vue/devui/modal/src/components/footer.tsx
+++ b/packages/devui-vue/devui/modal/src/components/footer.tsx
@@ -1,5 +1,5 @@
import { defineComponent } from 'vue';
-import { useNamespace } from '../../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared';
export default defineComponent({
name: 'DModalFooter',
diff --git a/packages/devui-vue/devui/modal/src/components/header.tsx b/packages/devui-vue/devui/modal/src/components/header.tsx
index 1ed9937dca..d15f67aa66 100644
--- a/packages/devui-vue/devui/modal/src/components/header.tsx
+++ b/packages/devui-vue/devui/modal/src/components/header.tsx
@@ -1,5 +1,5 @@
import { defineComponent } from 'vue';
-import { useNamespace } from '../../../shared/hooks/use-namespace';
+import { useNamespace } from '@devui/shared';
export default defineComponent({
name: 'DModalHeader',
diff --git a/packages/devui-vue/devui/modal/src/components/modal-icons.tsx b/packages/devui-vue/devui/modal/src/components/modal-icons.tsx
new file mode 100644
index 0000000000..fd6cfa8d7a
--- /dev/null
+++ b/packages/devui-vue/devui/modal/src/components/modal-icons.tsx
@@ -0,0 +1,19 @@
+export function CloseIcon(): JSX.Element {
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/packages/devui-vue/devui/modal/src/composables/use-modal.ts b/packages/devui-vue/devui/modal/src/composables/use-modal.ts
index b8cab8c255..7e33ddfbd5 100644
--- a/packages/devui-vue/devui/modal/src/composables/use-modal.ts
+++ b/packages/devui-vue/devui/modal/src/composables/use-modal.ts
@@ -1,8 +1,32 @@
-import { onMounted, onUnmounted, watch } from 'vue';
+import { onUnmounted, watch, ref } from 'vue';
import { ModalProps, EmitEventFn, UseModal } from '../modal-types';
import { lockScroll } from '../../../shared/utils/lock-scroll';
+import { useNamespace, focusStack } from '@devui/shared'
+import type { FoucusLayer } from '@devui/shared'
+import { ZINDEX } from '../const'
export function useModal(props: ModalProps, emit: EmitEventFn): UseModal {
+ let lockScrollCb: () => void;
+ const ns = useNamespace('modal')
+ const overlayZIndex = ref('')
+ const modalZIndex = ref('')
+ const paused = ref(false)
+ const layer: FoucusLayer = {
+ pause() {
+ paused.value = true
+ },
+ resume() {
+ paused.value = false
+ }
+ }
+
+ const removeAdditions = () => {
+ lockScrollCb?.()
+ props.escapable && window.removeEventListener('keyup', onKeydown)
+ setTimeout(() => {
+ focusStack.remove(layer)
+ });
+ }
function close(): void {
emit('update:modelValue', false);
emit('close');
@@ -12,44 +36,37 @@ export function useModal(props: ModalProps, emit: EmitEventFn): UseModal {
}
function onKeydown(event: KeyboardEvent) {
- if (event.code === 'Escape') {
+ if (event.code === 'Escape' && !paused.value) {
execClose();
}
}
- onMounted(() => {
- if (props.escapable) {
- window.addEventListener('keydown', onKeydown);
- }
- });
-
- onUnmounted(() => {
- if (props.escapable) {
- window.addEventListener('keydown', onKeydown);
- }
- });
-
- return { execClose };
-}
+ function calculateZIndex() {
+ const modalDomsLength = document.querySelectorAll(`.${ns.b()}`).length
+ overlayZIndex.value = `calc(var(--devui-z-index-modal, 1050) + ${(modalDomsLength - 1) * ZINDEX} - 1)`
+ modalZIndex.value = `calc(var(--devui-z-index-modal, 1050) + ${(modalDomsLength - 1) * ZINDEX})`
+ }
-export function useModalRender(props: ModalProps): void {
- let lockScrollCb: () => void;
- const removeBodyAdditions = () => {
- lockScrollCb?.();
- };
watch(
() => props.modelValue,
- (val) => {
+ (val, oldVal) => {
if (val) {
+ calculateZIndex()
props.lockScroll && (lockScrollCb = lockScroll());
+ props.escapable && window.addEventListener('keyup', onKeydown)
+ focusStack.push(layer)
} else {
- removeBodyAdditions();
+ oldVal !== undefined && removeAdditions()
}
},
{
immediate: true,
+ flush: 'post'
}
);
- onUnmounted(removeBodyAdditions);
-}
+ onUnmounted(removeAdditions);
+
+ return { execClose, overlayZIndex, modalZIndex };
+
+}
\ No newline at end of file
diff --git a/packages/devui-vue/devui/modal/src/const.ts b/packages/devui-vue/devui/modal/src/const.ts
new file mode 100644
index 0000000000..41698cb206
--- /dev/null
+++ b/packages/devui-vue/devui/modal/src/const.ts
@@ -0,0 +1 @@
+export const ZINDEX = 1;
diff --git a/packages/devui-vue/devui/modal/src/modal-types.ts b/packages/devui-vue/devui/modal/src/modal-types.ts
index c961d743be..b8437bdce1 100644
--- a/packages/devui-vue/devui/modal/src/modal-types.ts
+++ b/packages/devui-vue/devui/modal/src/modal-types.ts
@@ -1,4 +1,4 @@
-import type { PropType, ExtractPropTypes } from 'vue';
+import type { PropType, ExtractPropTypes, Ref } from 'vue';
export type ModalType = 'success' | 'failed' | 'warning' | 'info' | '';
@@ -62,6 +62,8 @@ export type EmitEventFn = (event: EmitName, result?: boolean) => void;
export interface UseModal {
execClose: () => void;
+ overlayZIndex: Ref;
+ modalZIndex: Ref;
}
export type ModalProps = ExtractPropTypes;
diff --git a/packages/devui-vue/devui/modal/src/modal.scss b/packages/devui-vue/devui/modal/src/modal.scss
index 50644ac573..35a7b31951 100644
--- a/packages/devui-vue/devui/modal/src/modal.scss
+++ b/packages/devui-vue/devui/modal/src/modal.scss
@@ -5,7 +5,7 @@
top: 50%;
left: 50%;
width: 300px;
- border-radius: $devui-border-radius;
+ border-radius: $devui-border-radius-card;
border: none;
opacity: 1;
transform: translate(-50%, -50%);
@@ -17,27 +17,10 @@
.btn-close {
position: absolute;
- right: 16px;
- top: 20px;
- width: 20px;
- height: 20px;
- line-height: 20px;
- text-align: center;
- cursor: pointer;
- background: transparent;
- border: 0;
+ right: 20px;
+ top: 18px;
-webkit-appearance: none;
-
- &:hover {
- color: $devui-icon-fill-active-hover;
- background-color: $devui-list-item-hover-bg;
- }
-
- & i {
- position: absolute;
- right: 0;
- top: 0;
- }
+ z-index: calc(var(--devui-z-index-model, 1050) + 1);
}
.type-content {
@@ -70,12 +53,17 @@
.#{$devui-prefix}-modal__header {
width: 100%;
- height: 56px;
+ height: 46px;
+ line-height: 26px;
padding: 20px 20px 0;
- font-size: $devui-font-size-card-title;
+ font-size: $devui-font-size-modal-title;
font-weight: bold;
+ letter-spacing: 0;
box-sizing: border-box;
- border: none;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ user-select: none;
.header-alert-icon {
display: inline-block;
@@ -97,10 +85,10 @@
width: 100%;
border-top: none;
text-align: center;
- padding: 0 32px 24px;
+ padding: 0 20px 20px 20px;
box-sizing: border-box;
- & > * {
+ &>* {
margin: 0 4px;
}
}
@@ -119,4 +107,4 @@
opacity: 0.2;
top: calc(50% - 24px);
}
-}
+}
\ No newline at end of file
diff --git a/packages/devui-vue/devui/modal/src/modal.tsx b/packages/devui-vue/devui/modal/src/modal.tsx
index f389a2f354..c1b27c72c0 100644
--- a/packages/devui-vue/devui/modal/src/modal.tsx
+++ b/packages/devui-vue/devui/modal/src/modal.tsx
@@ -1,12 +1,13 @@
import { computed, defineComponent, nextTick, ref, Teleport, toRefs, Transition, watch } from 'vue';
import { modalProps, ModalProps, ModalType } from './modal-types';
-import { Icon } from '../../icon';
+import { Icon } from '@devui/shared/components/icon';
import { FixedOverlay } from '../../overlay';
-import { useModal, useModalRender } from './composables/use-modal';
+import { useModal } from './composables/use-modal';
import { useDraggable } from './composables/use-draggable';
import DModalHeader from './components/header';
import DModalBody from './components/body';
-import { useNamespace } from '../../shared/hooks/use-namespace';
+import { CloseIcon } from './components/modal-icons';
+import { useNamespace } from '@devui/shared';
import './modal.scss';
interface TypeList {
@@ -24,8 +25,7 @@ export default defineComponent({
setup(props: ModalProps, { slots, attrs, emit }) {
const ns = useNamespace('modal');
const { modelValue, title, showClose, showOverlay, appendToBody, closeOnClickOverlay, keepLast } = toRefs(props);
- const { execClose } = useModal(props, emit);
- useModalRender(props);
+ const { execClose, overlayZIndex, modalZIndex } = useModal(props, emit);
const dialogRef = ref();
const headerRef = ref();
const draggable = computed(() => props.draggable);
@@ -90,11 +90,11 @@ export default defineComponent({
{showOverlay.value && (
)}
@@ -103,13 +103,8 @@ export default defineComponent({
ref={dialogRef}
class={ns.b()}
{...attrs}
- onClick={(e: Event) => e.stopPropagation()}
- style={{ transform: modalPosition.value }}>
- {showClose.value && (
-
-
-
- )}
+ style={{ transform: modalPosition.value, zIndex: modalZIndex.value }}>
+ {showClose.value && }
{props.type ? (
renderType()
) : (