diff --git a/src/components/Modal/src/BasicModal.vue b/src/components/Modal/src/BasicModal.vue index 959e6db62..7558ebf01 100644 --- a/src/components/Modal/src/BasicModal.vue +++ b/src/components/Modal/src/BasicModal.vue @@ -145,6 +145,7 @@ if (unref(fullScreenRef)) { return omit(attr, ['height', 'title']); } + return omit(attr, 'title'); }); diff --git a/src/components/Modal/src/components/ModalWrapper.vue b/src/components/Modal/src/components/ModalWrapper.vue index 037538cda..605914a31 100644 --- a/src/components/Modal/src/components/ModalWrapper.vue +++ b/src/components/Modal/src/components/ModalWrapper.vue @@ -53,7 +53,7 @@ let stopElResizeFn: AnyFunction = () => {}; - useWindowSizeFn(setModalHeight.bind(null, false)); + useWindowSizeFn(setModalHeight.bind(null)); useMutationObserver( spinRef, diff --git a/src/components/Modal/src/hooks/useModal.ts b/src/components/Modal/src/hooks/useModal.ts index c4cade229..45f893868 100644 --- a/src/components/Modal/src/hooks/useModal.ts +++ b/src/components/Modal/src/hooks/useModal.ts @@ -4,6 +4,7 @@ import type { ModalProps, ReturnMethods, UseModalInnerReturnType, + RegisterFn, } from '../typing'; import { ref, @@ -97,7 +98,7 @@ export function useModal(): UseModalReturnType { getInstance()?.setModalProps({ visible: false }); }, }; - return [register, methods]; + return [register as RegisterFn, methods]; } export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { @@ -133,7 +134,7 @@ export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { }); return [ - register, + register as RegisterFn, { changeLoading: (loading = true) => { getInstance()?.setModalProps({ loading }); diff --git a/src/components/Modal/src/hooks/useModalDrag.ts b/src/components/Modal/src/hooks/useModalDrag.ts index 5ece37a6e..0d125562f 100644 --- a/src/components/Modal/src/hooks/useModalDrag.ts +++ b/src/components/Modal/src/hooks/useModalDrag.ts @@ -1,5 +1,6 @@ -import { Ref, unref, watchEffect } from 'vue'; +import { Ref, ref, unref, watchEffect } from 'vue'; import { useTimeoutFn } from '@vben/hooks'; +import { useRootSetting } from '/@/hooks/setting/useRootSetting'; export interface UseModalDragMoveContext { draggable: Ref; @@ -13,9 +14,18 @@ export function useModalDragMove(context: UseModalDragMoveContext) { }; const drag = (wrap: any) => { if (!wrap) return; + + const { getDarkMode } = useRootSetting(); + + const classPrefix = ref('.ant'); + + if (getDarkMode.value == 'dark') { + classPrefix.value = '.dark'; + } + wrap.setAttribute('data-drag', unref(context.draggable)); - const dialogHeaderEl = wrap.querySelector('.ant-modal-header'); - const dragDom = wrap.querySelector('.ant-modal'); + const dialogHeaderEl = wrap.querySelector(classPrefix.value + '-modal-header'); + const dragDom = wrap.querySelector(classPrefix.value + '-modal'); if (!dialogHeaderEl || !dragDom || !unref(context.draggable)) return; @@ -26,8 +36,9 @@ export function useModalDragMove(context: UseModalDragMoveContext) { // 鼠标按下,计算当前元素距离可视区的距离 const disX = e.clientX; const disY = e.clientY; + const screenWidth = document.body.clientWidth; // body当前宽度 - const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取) + const screenHeight = window.innerHeight; const dragDomWidth = dragDom.offsetWidth; // 对话框宽度 const dragDomheight = dragDom.offsetHeight; // 对话框高度 @@ -64,7 +75,7 @@ export function useModalDragMove(context: UseModalDragMoveContext) { left = maxDragDomLeft; } - if (-top > minDragDomTop) { + if (-top >= minDragDomTop) { top = -minDragDomTop; } else if (top > maxDragDomTop) { top = maxDragDomTop; @@ -82,7 +93,16 @@ export function useModalDragMove(context: UseModalDragMoveContext) { }; const handleDrag = () => { - const dragWraps = document.querySelectorAll('.ant-modal-wrap'); + const { getDarkMode } = useRootSetting(); + + const classPrefix = ref('.ant'); + + if (getDarkMode.value == 'dark') { + classPrefix.value = '.dark'; + } + + const dragWraps = document.querySelectorAll(classPrefix.value + '-modal-wrap'); + for (const wrap of Array.from(dragWraps)) { if (!wrap) continue; const display = getStyle(wrap, 'display'); diff --git a/src/components/Modal/src/hooks/useModalFullScreen.ts b/src/components/Modal/src/hooks/useModalFullScreen.ts index b53563a31..fd0619840 100644 --- a/src/components/Modal/src/hooks/useModalFullScreen.ts +++ b/src/components/Modal/src/hooks/useModalFullScreen.ts @@ -7,7 +7,6 @@ export interface UseFullScreenContext { } export function useFullScreen(context: UseFullScreenContext) { - // const formerHeightRef = ref(0); const fullScreenRef = ref(false); const getWrapClassName = computed(() => { @@ -18,26 +17,6 @@ export function useFullScreen(context: UseFullScreenContext) { function handleFullScreen(e: Event) { e && e.stopPropagation(); fullScreenRef.value = !unref(fullScreenRef); - - // const modalWrapper = unref(context.modalWrapperRef); - - // if (!modalWrapper) return; - - // const wrapperEl = modalWrapper.$el as HTMLElement; - // if (!wrapperEl) return; - // const modalWrapSpinEl = wrapperEl.querySelector('.ant-spin-nested-loading') as HTMLElement; - - // if (!modalWrapSpinEl) return; - - // if (!unref(formerHeightRef) && unref(fullScreenRef)) { - // formerHeightRef.value = modalWrapSpinEl.offsetHeight; - // } - - // if (unref(fullScreenRef)) { - // modalWrapSpinEl.style.height = `${window.innerHeight - unref(context.extHeightRef)}px`; - // } else { - // modalWrapSpinEl.style.height = `${unref(formerHeightRef)}px`; - // } } return { getWrapClassName, handleFullScreen, fullScreenRef }; } diff --git a/src/components/Modal/src/index.less b/src/components/Modal/src/index.less index a23be2920..0b631074f 100644 --- a/src/components/Modal/src/index.less +++ b/src/components/Modal/src/index.less @@ -13,6 +13,19 @@ height: 100%; } } + + .dark-modal { + top: 0 !important; + right: 0 !important; + bottom: 0 !important; + left: 0 !important; + width: 100% !important; + height: 100%; + + &-content { + height: 100%; + } + } } .ant-modal { @@ -125,3 +138,114 @@ top: 10px; } } + +.dark-modal { + width: 520px; + padding-bottom: 0; + + .dark-modal-body > .scrollbar { + padding: 14px; + } + + &-title { + font-size: 16px; + font-weight: bold; + + .base-title { + cursor: move !important; + } + } + + .dark-modal-body { + padding: 0; + + > .scrollbar > .scrollbar__bar.is-horizontal { + display: none; + } + } + + &-large { + top: 60px; + + &--mini { + top: 16px; + } + } + + &-header { + padding: 16px; + } + + &-content { + box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%); + } + + &-footer { + button + button { + margin-left: 10px; + } + } + + &-close { + font-weight: normal; + outline: none; + } + + &-close-x { + display: inline-block; + width: 96px; + height: 56px; + line-height: 56px; + } + + &-confirm-body { + .dark-modal-confirm-content { + // color: #fff; + + > * { + color: @text-color-help-dark; + } + } + } + + &-confirm-confirm.error .dark-modal-confirm-body > .darkicon { + color: @error-color; + } + + &-confirm-btns { + .dark-btn:last-child { + margin-right: 0; + } + } + + &-confirm-info { + .dark-modal-confirm-body > .darkicon { + color: @warning-color; + } + } + + &-confirm-confirm.success { + .dark-modal-confirm-body > .darkicon { + color: @success-color; + } + } +} + +.dark-modal-confirm .dark-modal-body { + padding: 24px !important; +} +@media screen and (max-height: 600px) { + .dark-modal { + top: 60px; + } +} +@media screen and (max-height: 540px) { + .dark-modal { + top: 30px; + } +} +@media screen and (max-height: 480px) { + .dark-modal { + top: 10px; + } +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 0e9b23fc4..a5f8a17a1 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,8 +2,8 @@ import type { RouteLocationNormalized, RouteRecordNormalized } from 'vue-router' import type { App, Component } from 'vue'; import { unref } from 'vue'; -import { isObject } from '/@/utils/is'; -import { cloneDeep } from 'lodash-es'; +import { intersectionWith, isEqual, mergeWith, unionWith } from 'lodash-es'; +import { isArray, isObject } from '/@/utils/is'; export const noop = () => {}; @@ -33,14 +33,51 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string { return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters; } -// 深度合并 -export function deepMerge(src: any = {}, target: any = {}): T { - let key: string; - const res: any = cloneDeep(src); - for (key in target) { - res[key] = isObject(res[key]) ? deepMerge(res[key], target[key]) : target[key]; +/** + * Recursively merge two objects. + * 递归合并两个对象。 + * + * @param source The source object to merge from. 要合并的源对象。 + * @param target The target object to merge into. 目标对象,合并后结果存放于此。 + * @param mergeArrays How to merge arrays. Default is "replace". + * 如何合并数组。默认为replace。 + * - "union": Union the arrays. 对数组执行并集操作。 + * - "intersection": Intersect the arrays. 对数组执行交集操作。 + * - "concat": Concatenate the arrays. 连接数组。 + * - "replace": Replace the source array with the target array. 用目标数组替换源数组。 + * @returns The merged object. 合并后的对象。 + */ +export function deepMerge( + source: T, + target: U, + mergeArrays: 'union' | 'intersection' | 'concat' | 'replace' = 'replace', +): T & U { + if (!target) { + return source as T & U; + } + if (!source) { + return target as T & U; } - return res; + return mergeWith({}, source, target, (sourceValue, targetValue) => { + if (isArray(targetValue) && isArray(sourceValue)) { + switch (mergeArrays) { + case 'union': + return unionWith(sourceValue, targetValue, isEqual); + case 'intersection': + return intersectionWith(sourceValue, targetValue, isEqual); + case 'concat': + return sourceValue.concat(targetValue); + case 'replace': + return targetValue; + default: + throw new Error(`Unknown merge array strategy: ${mergeArrays as string}`); + } + } + if (isObject(targetValue) && isObject(sourceValue)) { + return deepMerge(sourceValue, targetValue, mergeArrays); + } + return undefined; + }); } export function openWindow( diff --git a/src/views/mcms/emailProvider/index.vue b/src/views/mcms/emailProvider/index.vue index e0babf25b..bf505ebea 100644 --- a/src/views/mcms/emailProvider/index.vue +++ b/src/views/mcms/emailProvider/index.vue @@ -127,8 +127,8 @@ const [registerModal, { openModal }] = useModal(); const [registerForm, { validate }] = useForm({ - labelWidth: 140, - baseColProps: { span: 24 }, + labelWidth: 100, + baseColProps: { span: 18 }, schemas: formSchema, showActionButtonGroup: false, labelAlign: 'right', diff --git a/src/views/mcms/smsProvider/index.vue b/src/views/mcms/smsProvider/index.vue index be29b7a91..af7d155e6 100644 --- a/src/views/mcms/smsProvider/index.vue +++ b/src/views/mcms/smsProvider/index.vue @@ -128,9 +128,9 @@ const [registerModal, { openModal }] = useModal(); const [registerForm, { validate }] = useForm({ - labelWidth: 140, + labelWidth: 100, labelAlign: 'right', - baseColProps: { span: 24 }, + baseColProps: { span: 18 }, schemas: formSchema, showActionButtonGroup: false, });