mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-09 02:01:31 +08:00
add frontend/vben from vben-admin-thin
This commit is contained in:
8
frontend/vben/src/components/Modal/index.ts
Normal file
8
frontend/vben/src/components/Modal/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { withInstall } from '/@/utils'
|
||||
import './src/index.less'
|
||||
import basicModal from './src/BasicModal.vue'
|
||||
|
||||
export const BasicModal = withInstall(basicModal)
|
||||
export { useModalContext } from './src/hooks/useModalContext'
|
||||
export { useModal, useModalInner } from './src/hooks/useModal'
|
||||
export * from './src/typing'
|
||||
242
frontend/vben/src/components/Modal/src/BasicModal.vue
Normal file
242
frontend/vben/src/components/Modal/src/BasicModal.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<Modal v-bind="getBindValue" @cancel="handleCancel">
|
||||
<template #closeIcon v-if="!$slots.closeIcon">
|
||||
<ModalClose
|
||||
:canFullscreen="getProps.canFullscreen"
|
||||
:fullScreen="fullScreenRef"
|
||||
@cancel="handleCancel"
|
||||
@fullscreen="handleFullScreen"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #title v-if="!$slots.title">
|
||||
<ModalHeader
|
||||
:helpMessage="getProps.helpMessage"
|
||||
:title="getMergeProps.title"
|
||||
@dblclick="handleTitleDbClick"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer v-if="!$slots.footer">
|
||||
<ModalFooter v-bind="getBindValue" @ok="handleOk" @cancel="handleCancel">
|
||||
<template #[item]="data" v-for="item in Object.keys($slots)">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
</ModalFooter>
|
||||
</template>
|
||||
|
||||
<ModalWrapper
|
||||
:useWrapper="getProps.useWrapper"
|
||||
:footerOffset="wrapperFooterOffset"
|
||||
:fullScreen="fullScreenRef"
|
||||
ref="modalWrapperRef"
|
||||
:loading="getProps.loading"
|
||||
:loading-tip="getProps.loadingTip"
|
||||
:minHeight="getProps.minHeight"
|
||||
:height="getWrapperHeight"
|
||||
:visible="visibleRef"
|
||||
:modalFooterHeight="footer !== undefined && !footer ? 0 : undefined"
|
||||
v-bind="omit(getProps.wrapperProps, 'visible', 'height', 'modalFooterHeight')"
|
||||
@ext-height="handleExtHeight"
|
||||
@height-change="handleHeightChange"
|
||||
>
|
||||
<slot></slot>
|
||||
</ModalWrapper>
|
||||
|
||||
<template #[item]="data" v-for="item in Object.keys(omit($slots, 'default'))">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { ModalProps, ModalMethods } from './typing'
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
watch,
|
||||
unref,
|
||||
watchEffect,
|
||||
toRef,
|
||||
getCurrentInstance,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import Modal from './components/Modal'
|
||||
import ModalWrapper from './components/ModalWrapper.vue'
|
||||
import ModalClose from './components/ModalClose.vue'
|
||||
import ModalFooter from './components/ModalFooter.vue'
|
||||
import ModalHeader from './components/ModalHeader.vue'
|
||||
import { isFunction } from '/@/utils/is'
|
||||
import { deepMerge } from '/@/utils'
|
||||
import { basicProps } from './props'
|
||||
import { useFullScreen } from './hooks/useModalFullScreen'
|
||||
import { omit } from 'lodash-es'
|
||||
import { useDesign } from '/@/hooks/web/useDesign'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicModal',
|
||||
components: { Modal, ModalWrapper, ModalClose, ModalFooter, ModalHeader },
|
||||
inheritAttrs: false,
|
||||
props: basicProps,
|
||||
emits: ['visible-change', 'height-change', 'cancel', 'ok', 'register', 'update:visible'],
|
||||
setup(props, { emit, attrs }) {
|
||||
const visibleRef = ref(false)
|
||||
const propsRef = ref<Partial<ModalProps> | null>(null)
|
||||
const modalWrapperRef = ref<any>(null)
|
||||
const { prefixCls } = useDesign('basic-modal')
|
||||
|
||||
// modal Bottom and top height
|
||||
const extHeightRef = ref(0)
|
||||
const modalMethods: ModalMethods = {
|
||||
setModalProps,
|
||||
emitVisible: undefined,
|
||||
redoModalHeight: () => {
|
||||
nextTick(() => {
|
||||
if (unref(modalWrapperRef)) {
|
||||
;(unref(modalWrapperRef) as any).setModalHeight()
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
const instance = getCurrentInstance()
|
||||
if (instance) {
|
||||
emit('register', modalMethods, instance.uid)
|
||||
}
|
||||
|
||||
// Custom title component: get title
|
||||
const getMergeProps = computed((): Recordable => {
|
||||
return {
|
||||
...props,
|
||||
...(unref(propsRef) as any),
|
||||
}
|
||||
})
|
||||
|
||||
const { handleFullScreen, getWrapClassName, fullScreenRef } = useFullScreen({
|
||||
modalWrapperRef,
|
||||
extHeightRef,
|
||||
wrapClassName: toRef(getMergeProps.value, 'wrapClassName'),
|
||||
})
|
||||
|
||||
// modal component does not need title and origin buttons
|
||||
const getProps = computed((): Recordable => {
|
||||
const opt = {
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
okButtonProps: undefined,
|
||||
cancelButtonProps: undefined,
|
||||
title: undefined,
|
||||
}
|
||||
return {
|
||||
...opt,
|
||||
wrapClassName: unref(getWrapClassName),
|
||||
}
|
||||
})
|
||||
|
||||
const getBindValue = computed((): Recordable => {
|
||||
const attr = {
|
||||
...attrs,
|
||||
...unref(getMergeProps),
|
||||
visible: unref(visibleRef),
|
||||
wrapClassName: unref(getWrapClassName),
|
||||
}
|
||||
if (unref(fullScreenRef)) {
|
||||
return omit(attr, ['height', 'title'])
|
||||
}
|
||||
return omit(attr, 'title')
|
||||
})
|
||||
|
||||
const getWrapperHeight = computed(() => {
|
||||
if (unref(fullScreenRef)) return undefined
|
||||
return unref(getProps).height
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
visibleRef.value = !!props.visible
|
||||
fullScreenRef.value = !!props.defaultFullscreen
|
||||
})
|
||||
|
||||
watch(
|
||||
() => unref(visibleRef),
|
||||
(v) => {
|
||||
emit('visible-change', v)
|
||||
emit('update:visible', v)
|
||||
instance && modalMethods.emitVisible?.(v, instance.uid)
|
||||
nextTick(() => {
|
||||
if (props.scrollTop && v && unref(modalWrapperRef)) {
|
||||
;(unref(modalWrapperRef) as any).scrollTop()
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
immediate: false,
|
||||
},
|
||||
)
|
||||
|
||||
// 取消事件
|
||||
async function handleCancel(e: Event) {
|
||||
e?.stopPropagation()
|
||||
// 过滤自定义关闭按钮的空白区域
|
||||
if ((e.target as HTMLElement)?.classList?.contains(prefixCls + '-close--custom')) return
|
||||
if (props.closeFunc && isFunction(props.closeFunc)) {
|
||||
const isClose: boolean = await props.closeFunc()
|
||||
visibleRef.value = !isClose
|
||||
return
|
||||
}
|
||||
|
||||
visibleRef.value = false
|
||||
emit('cancel', e)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 设置modal参数
|
||||
*/
|
||||
function setModalProps(props: Partial<ModalProps>): void {
|
||||
// Keep the last setModalProps
|
||||
propsRef.value = deepMerge(unref(propsRef) || ({} as any), props)
|
||||
if (Reflect.has(props, 'visible')) {
|
||||
visibleRef.value = !!props.visible
|
||||
}
|
||||
if (Reflect.has(props, 'defaultFullscreen')) {
|
||||
fullScreenRef.value = !!props.defaultFullscreen
|
||||
}
|
||||
}
|
||||
|
||||
function handleOk(e: Event) {
|
||||
emit('ok', e)
|
||||
}
|
||||
|
||||
function handleHeightChange(height: string) {
|
||||
emit('height-change', height)
|
||||
}
|
||||
|
||||
function handleExtHeight(height: number) {
|
||||
extHeightRef.value = height
|
||||
}
|
||||
|
||||
function handleTitleDbClick(e) {
|
||||
if (!props.canFullscreen) return
|
||||
e.stopPropagation()
|
||||
handleFullScreen(e)
|
||||
}
|
||||
|
||||
return {
|
||||
handleCancel,
|
||||
getBindValue,
|
||||
getProps,
|
||||
handleFullScreen,
|
||||
fullScreenRef,
|
||||
getMergeProps,
|
||||
handleOk,
|
||||
visibleRef,
|
||||
omit,
|
||||
modalWrapperRef,
|
||||
handleExtHeight,
|
||||
handleHeightChange,
|
||||
handleTitleDbClick,
|
||||
getWrapperHeight,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
31
frontend/vben/src/components/Modal/src/components/Modal.tsx
Normal file
31
frontend/vben/src/components/Modal/src/components/Modal.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Modal } from 'ant-design-vue'
|
||||
import { defineComponent, toRefs, unref } from 'vue'
|
||||
import { basicProps } from '../props'
|
||||
import { useModalDragMove } from '../hooks/useModalDrag'
|
||||
import { useAttrs } from '/@/hooks/core/useAttrs'
|
||||
import { extendSlots } from '/@/utils/helper/tsxHelper'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Modal',
|
||||
inheritAttrs: false,
|
||||
props: basicProps,
|
||||
emits: ['cancel'],
|
||||
setup(props, { slots, emit }) {
|
||||
const { visible, draggable, destroyOnClose } = toRefs(props)
|
||||
const attrs = useAttrs()
|
||||
useModalDragMove({
|
||||
visible,
|
||||
destroyOnClose,
|
||||
draggable,
|
||||
})
|
||||
|
||||
const onCancel = (e: Event) => {
|
||||
emit('cancel', e)
|
||||
}
|
||||
|
||||
return () => {
|
||||
const propsData = { ...unref(attrs), ...props, onCancel } as Recordable
|
||||
return <Modal {...propsData}>{extendSlots(slots)}</Modal>
|
||||
}
|
||||
},
|
||||
})
|
||||
106
frontend/vben/src/components/Modal/src/components/ModalClose.vue
Normal file
106
frontend/vben/src/components/Modal/src/components/ModalClose.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div :class="getClass">
|
||||
<template v-if="canFullscreen">
|
||||
<Tooltip :title="t('component.modal.restore')" placement="bottom" v-if="fullScreen">
|
||||
<FullscreenExitOutlined role="full" @click="handleFullScreen" />
|
||||
</Tooltip>
|
||||
<Tooltip :title="t('component.modal.maximize')" placement="bottom" v-else>
|
||||
<FullscreenOutlined role="close" @click="handleFullScreen" />
|
||||
</Tooltip>
|
||||
</template>
|
||||
<Tooltip :title="t('component.modal.close')" placement="bottom">
|
||||
<CloseOutlined @click="handleCancel" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue'
|
||||
import { useDesign } from '/@/hooks/web/useDesign'
|
||||
import { Tooltip } from 'ant-design-vue'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModalClose',
|
||||
components: { Tooltip, FullscreenExitOutlined, FullscreenOutlined, CloseOutlined },
|
||||
props: {
|
||||
canFullscreen: { type: Boolean, default: true },
|
||||
fullScreen: { type: Boolean },
|
||||
},
|
||||
emits: ['cancel', 'fullscreen'],
|
||||
setup(props, { emit }) {
|
||||
const { prefixCls } = useDesign('basic-modal-close')
|
||||
const { t } = useI18n()
|
||||
|
||||
const getClass = computed(() => {
|
||||
return [
|
||||
prefixCls,
|
||||
`${prefixCls}--custom`,
|
||||
{
|
||||
[`${prefixCls}--can-full`]: props.canFullscreen,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
function handleCancel(e: Event) {
|
||||
emit('cancel', e)
|
||||
}
|
||||
|
||||
function handleFullScreen(e: Event) {
|
||||
e?.stopPropagation()
|
||||
e?.preventDefault()
|
||||
emit('fullscreen')
|
||||
}
|
||||
|
||||
return {
|
||||
t,
|
||||
getClass,
|
||||
prefixCls,
|
||||
handleCancel,
|
||||
handleFullScreen,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
@prefix-cls: ~'@{namespace}-basic-modal-close';
|
||||
.@{prefix-cls} {
|
||||
display: flex;
|
||||
height: 95%;
|
||||
align-items: center;
|
||||
|
||||
> span {
|
||||
margin-left: 48px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
&--can-full {
|
||||
> span {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(&--can-full) {
|
||||
> span:nth-child(1) {
|
||||
&:hover {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& span:nth-child(1) {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
& span:last-child {
|
||||
&:hover {
|
||||
color: @error-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<slot name="insertFooter"></slot>
|
||||
<a-button v-bind="cancelButtonProps" @click="handleCancel" v-if="showCancelBtn">
|
||||
{{ cancelText }}
|
||||
</a-button>
|
||||
<slot name="centerFooter"></slot>
|
||||
<a-button
|
||||
:type="okType"
|
||||
@click="handleOk"
|
||||
:loading="confirmLoading"
|
||||
v-bind="okButtonProps"
|
||||
v-if="showOkBtn"
|
||||
>
|
||||
{{ okText }}
|
||||
</a-button>
|
||||
<slot name="appendFooter"></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import { basicProps } from '../props'
|
||||
export default defineComponent({
|
||||
name: 'BasicModalFooter',
|
||||
props: basicProps,
|
||||
emits: ['ok', 'cancel'],
|
||||
setup(_, { emit }) {
|
||||
function handleOk(e: Event) {
|
||||
emit('ok', e)
|
||||
}
|
||||
|
||||
function handleCancel(e: Event) {
|
||||
emit('cancel', e)
|
||||
}
|
||||
|
||||
return { handleOk, handleCancel }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<BasicTitle :helpMessage="helpMessage">
|
||||
{{ title }}
|
||||
</BasicTitle>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { PropType } from 'vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { BasicTitle } from '/@/components/Basic'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'BasicModalHeader',
|
||||
components: { BasicTitle },
|
||||
props: {
|
||||
helpMessage: {
|
||||
type: [String, Array] as PropType<string | string[]>,
|
||||
},
|
||||
title: { type: String },
|
||||
},
|
||||
emits: ['dblclick'],
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<ScrollContainer ref="wrapperRef">
|
||||
<div ref="spinRef" :style="spinStyle" v-loading="loading" :loading-tip="loadingTip">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</ScrollContainer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import type { CSSProperties } from 'vue'
|
||||
import {
|
||||
defineComponent,
|
||||
computed,
|
||||
ref,
|
||||
watchEffect,
|
||||
unref,
|
||||
watch,
|
||||
onMounted,
|
||||
nextTick,
|
||||
onUnmounted,
|
||||
} from 'vue'
|
||||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn'
|
||||
import { ScrollContainer } from '/@/components/Container'
|
||||
import { createModalContext } from '../hooks/useModalContext'
|
||||
import { useMutationObserver } from '@vueuse/core'
|
||||
|
||||
const props = {
|
||||
loading: { type: Boolean },
|
||||
useWrapper: { type: Boolean, default: true },
|
||||
modalHeaderHeight: { type: Number, default: 57 },
|
||||
modalFooterHeight: { type: Number, default: 74 },
|
||||
minHeight: { type: Number, default: 200 },
|
||||
height: { type: Number },
|
||||
footerOffset: { type: Number, default: 0 },
|
||||
visible: { type: Boolean },
|
||||
fullScreen: { type: Boolean },
|
||||
loadingTip: { type: String },
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModalWrapper',
|
||||
components: { ScrollContainer },
|
||||
inheritAttrs: false,
|
||||
props,
|
||||
emits: ['height-change', 'ext-height'],
|
||||
setup(props, { emit }) {
|
||||
const wrapperRef = ref<ComponentRef>(null)
|
||||
const spinRef = ref<ElRef>(null)
|
||||
const realHeightRef = ref(0)
|
||||
const minRealHeightRef = ref(0)
|
||||
|
||||
let realHeight = 0
|
||||
|
||||
let stopElResizeFn: Fn = () => {}
|
||||
|
||||
useWindowSizeFn(setModalHeight.bind(null, false))
|
||||
|
||||
useMutationObserver(
|
||||
spinRef,
|
||||
() => {
|
||||
setModalHeight()
|
||||
},
|
||||
{
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
},
|
||||
)
|
||||
|
||||
createModalContext({
|
||||
redoModalHeight: setModalHeight,
|
||||
})
|
||||
|
||||
const spinStyle = computed((): CSSProperties => {
|
||||
return {
|
||||
minHeight: `${props.minHeight}px`,
|
||||
[props.fullScreen ? 'height' : 'maxHeight']: `${unref(realHeightRef)}px`,
|
||||
}
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
props.useWrapper && setModalHeight()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.fullScreen,
|
||||
(v) => {
|
||||
setModalHeight()
|
||||
if (!v) {
|
||||
realHeightRef.value = minRealHeightRef.value
|
||||
} else {
|
||||
minRealHeightRef.value = realHeightRef.value
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
const { modalHeaderHeight, modalFooterHeight } = props
|
||||
emit('ext-height', modalHeaderHeight + modalFooterHeight)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopElResizeFn && stopElResizeFn()
|
||||
})
|
||||
|
||||
async function scrollTop() {
|
||||
nextTick(() => {
|
||||
const wrapperRefDom = unref(wrapperRef)
|
||||
if (!wrapperRefDom) return
|
||||
;(wrapperRefDom as any)?.scrollTo?.(0)
|
||||
})
|
||||
}
|
||||
|
||||
async function setModalHeight() {
|
||||
// 解决在弹窗关闭的时候监听还存在,导致再次打开弹窗没有高度
|
||||
// 加上这个,就必须在使用的时候传递父级的visible
|
||||
if (!props.visible) return
|
||||
const wrapperRefDom = unref(wrapperRef)
|
||||
if (!wrapperRefDom) return
|
||||
|
||||
const bodyDom = wrapperRefDom.$el.parentElement
|
||||
if (!bodyDom) return
|
||||
bodyDom.style.padding = '0'
|
||||
await nextTick()
|
||||
|
||||
try {
|
||||
const modalDom = bodyDom.parentElement && bodyDom.parentElement.parentElement
|
||||
if (!modalDom) return
|
||||
|
||||
const modalRect = getComputedStyle(modalDom as Element).top
|
||||
const modalTop = Number.parseInt(modalRect)
|
||||
let maxHeight =
|
||||
window.innerHeight -
|
||||
modalTop * 2 +
|
||||
(props.footerOffset! || 0) -
|
||||
props.modalFooterHeight -
|
||||
props.modalHeaderHeight
|
||||
|
||||
// 距离顶部过进会出现滚动条
|
||||
if (modalTop < 40) {
|
||||
maxHeight -= 26
|
||||
}
|
||||
await nextTick()
|
||||
const spinEl = unref(spinRef)
|
||||
|
||||
if (!spinEl) return
|
||||
await nextTick()
|
||||
// if (!realHeight) {
|
||||
realHeight = spinEl.scrollHeight
|
||||
// }
|
||||
|
||||
if (props.fullScreen) {
|
||||
realHeightRef.value =
|
||||
window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 28
|
||||
} else {
|
||||
realHeightRef.value = props.height
|
||||
? props.height
|
||||
: realHeight > maxHeight
|
||||
? maxHeight
|
||||
: realHeight
|
||||
}
|
||||
emit('height-change', unref(realHeightRef))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
return { wrapperRef, spinRef, spinStyle, scrollTop, setModalHeight }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
163
frontend/vben/src/components/Modal/src/hooks/useModal.ts
Normal file
163
frontend/vben/src/components/Modal/src/hooks/useModal.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import type {
|
||||
UseModalReturnType,
|
||||
ModalMethods,
|
||||
ModalProps,
|
||||
ReturnMethods,
|
||||
UseModalInnerReturnType,
|
||||
} from '../typing'
|
||||
import {
|
||||
ref,
|
||||
onUnmounted,
|
||||
unref,
|
||||
getCurrentInstance,
|
||||
reactive,
|
||||
watchEffect,
|
||||
nextTick,
|
||||
toRaw,
|
||||
} from 'vue'
|
||||
import { isProdMode } from '/@/utils/env'
|
||||
import { isFunction } from '/@/utils/is'
|
||||
import { isEqual } from 'lodash-es'
|
||||
import { tryOnUnmounted } from '@vueuse/core'
|
||||
import { error } from '/@/utils/log'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const dataTransfer = reactive<any>({})
|
||||
|
||||
const visibleData = reactive<{ [key: number]: boolean }>({})
|
||||
|
||||
/**
|
||||
* @description: Applicable to independent modal and call outside
|
||||
*/
|
||||
export function useModal(): UseModalReturnType {
|
||||
const modal = ref<Nullable<ModalMethods>>(null)
|
||||
const loaded = ref<Nullable<boolean>>(false)
|
||||
const uid = ref<string>('')
|
||||
|
||||
function register(modalMethod: ModalMethods, uuid: string) {
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useModal() can only be used inside setup() or functional components!')
|
||||
}
|
||||
uid.value = uuid
|
||||
isProdMode() &&
|
||||
onUnmounted(() => {
|
||||
modal.value = null
|
||||
loaded.value = false
|
||||
dataTransfer[unref(uid)] = null
|
||||
})
|
||||
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return
|
||||
|
||||
modal.value = modalMethod
|
||||
loaded.value = true
|
||||
modalMethod.emitVisible = (visible: boolean, uid: number) => {
|
||||
visibleData[uid] = visible
|
||||
}
|
||||
}
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(modal)
|
||||
if (!instance) {
|
||||
error('useModal instance is undefined!')
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
const methods: ReturnMethods = {
|
||||
setModalProps: (props: Partial<ModalProps>): void => {
|
||||
getInstance()?.setModalProps(props)
|
||||
},
|
||||
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uid)]
|
||||
}),
|
||||
|
||||
redoModalHeight: () => {
|
||||
getInstance()?.redoModalHeight?.()
|
||||
},
|
||||
|
||||
openModal: <T = any>(visible = true, data?: T, openOnSet = true): void => {
|
||||
getInstance()?.setModalProps({
|
||||
visible: visible,
|
||||
})
|
||||
|
||||
if (!data) return
|
||||
const id = unref(uid)
|
||||
if (openOnSet) {
|
||||
dataTransfer[id] = null
|
||||
dataTransfer[id] = toRaw(data)
|
||||
return
|
||||
}
|
||||
const equal = isEqual(toRaw(dataTransfer[id]), toRaw(data))
|
||||
if (!equal) {
|
||||
dataTransfer[id] = toRaw(data)
|
||||
}
|
||||
},
|
||||
|
||||
closeModal: () => {
|
||||
getInstance()?.setModalProps({ visible: false })
|
||||
},
|
||||
}
|
||||
return [register, methods]
|
||||
}
|
||||
|
||||
export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => {
|
||||
const modalInstanceRef = ref<Nullable<ModalMethods>>(null)
|
||||
const currentInstance = getCurrentInstance()
|
||||
const uidRef = ref<string>('')
|
||||
|
||||
const getInstance = () => {
|
||||
const instance = unref(modalInstanceRef)
|
||||
if (!instance) {
|
||||
error('useModalInner instance is undefined!')
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
const register = (modalInstance: ModalMethods, uuid: string) => {
|
||||
isProdMode() &&
|
||||
tryOnUnmounted(() => {
|
||||
modalInstanceRef.value = null
|
||||
})
|
||||
uidRef.value = uuid
|
||||
modalInstanceRef.value = modalInstance
|
||||
currentInstance?.emit('register', modalInstance, uuid)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
const data = dataTransfer[unref(uidRef)]
|
||||
if (!data) return
|
||||
if (!callbackFn || !isFunction(callbackFn)) return
|
||||
nextTick(() => {
|
||||
callbackFn(data)
|
||||
})
|
||||
})
|
||||
|
||||
return [
|
||||
register,
|
||||
{
|
||||
changeLoading: (loading = true) => {
|
||||
getInstance()?.setModalProps({ loading })
|
||||
},
|
||||
getVisible: computed((): boolean => {
|
||||
return visibleData[~~unref(uidRef)]
|
||||
}),
|
||||
|
||||
changeOkLoading: (loading = true) => {
|
||||
getInstance()?.setModalProps({ confirmLoading: loading })
|
||||
},
|
||||
|
||||
closeModal: () => {
|
||||
getInstance()?.setModalProps({ visible: false })
|
||||
},
|
||||
|
||||
setModalProps: (props: Partial<ModalProps>) => {
|
||||
getInstance()?.setModalProps(props)
|
||||
},
|
||||
|
||||
redoModalHeight: () => {
|
||||
const callRedo = getInstance()?.redoModalHeight
|
||||
callRedo && callRedo()
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { InjectionKey } from 'vue'
|
||||
import { createContext, useContext } from '/@/hooks/core/useContext'
|
||||
|
||||
export interface ModalContextProps {
|
||||
redoModalHeight: () => void
|
||||
}
|
||||
|
||||
const key: InjectionKey<ModalContextProps> = Symbol()
|
||||
|
||||
export function createModalContext(context: ModalContextProps) {
|
||||
return createContext<ModalContextProps>(context, key)
|
||||
}
|
||||
|
||||
export function useModalContext() {
|
||||
return useContext<ModalContextProps>(key)
|
||||
}
|
||||
107
frontend/vben/src/components/Modal/src/hooks/useModalDrag.ts
Normal file
107
frontend/vben/src/components/Modal/src/hooks/useModalDrag.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Ref, unref, watchEffect } from 'vue'
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout'
|
||||
|
||||
export interface UseModalDragMoveContext {
|
||||
draggable: Ref<boolean>
|
||||
destroyOnClose: Ref<boolean | undefined> | undefined
|
||||
visible: Ref<boolean>
|
||||
}
|
||||
|
||||
export function useModalDragMove(context: UseModalDragMoveContext) {
|
||||
const getStyle = (dom: any, attr: any) => {
|
||||
return getComputedStyle(dom)[attr]
|
||||
}
|
||||
const drag = (wrap: any) => {
|
||||
if (!wrap) return
|
||||
wrap.setAttribute('data-drag', unref(context.draggable))
|
||||
const dialogHeaderEl = wrap.querySelector('.ant-modal-header')
|
||||
const dragDom = wrap.querySelector('.ant-modal')
|
||||
|
||||
if (!dialogHeaderEl || !dragDom || !unref(context.draggable)) return
|
||||
|
||||
dialogHeaderEl.style.cursor = 'move'
|
||||
|
||||
dialogHeaderEl.onmousedown = (e: any) => {
|
||||
if (!e) return
|
||||
// 鼠标按下,计算当前元素距离可视区的距离
|
||||
const disX = e.clientX
|
||||
const disY = e.clientY
|
||||
const screenWidth = document.body.clientWidth // body当前宽度
|
||||
const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
|
||||
|
||||
const dragDomWidth = dragDom.offsetWidth // 对话框宽度
|
||||
const dragDomheight = dragDom.offsetHeight // 对话框高度
|
||||
|
||||
const minDragDomLeft = dragDom.offsetLeft
|
||||
|
||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
|
||||
const minDragDomTop = dragDom.offsetTop
|
||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight
|
||||
// 获取到的值带px 正则匹配替换
|
||||
const domLeft = getStyle(dragDom, 'left')
|
||||
const domTop = getStyle(dragDom, 'top')
|
||||
let styL = +domLeft
|
||||
let styT = +domTop
|
||||
|
||||
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
|
||||
if (domLeft.includes('%')) {
|
||||
styL = +document.body.clientWidth * (+domLeft.replace(/%/g, '') / 100)
|
||||
styT = +document.body.clientHeight * (+domTop.replace(/%/g, '') / 100)
|
||||
} else {
|
||||
styL = +domLeft.replace(/px/g, '')
|
||||
styT = +domTop.replace(/px/g, '')
|
||||
}
|
||||
|
||||
document.onmousemove = function (e) {
|
||||
// 通过事件委托,计算移动的距离
|
||||
let left = e.clientX - disX
|
||||
let top = e.clientY - disY
|
||||
|
||||
// 边界处理
|
||||
if (-left > minDragDomLeft) {
|
||||
left = -minDragDomLeft
|
||||
} else if (left > maxDragDomLeft) {
|
||||
left = maxDragDomLeft
|
||||
}
|
||||
|
||||
if (-top > minDragDomTop) {
|
||||
top = -minDragDomTop
|
||||
} else if (top > maxDragDomTop) {
|
||||
top = maxDragDomTop
|
||||
}
|
||||
|
||||
// 移动当前元素
|
||||
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
|
||||
}
|
||||
|
||||
document.onmouseup = () => {
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDrag = () => {
|
||||
const dragWraps = document.querySelectorAll('.ant-modal-wrap')
|
||||
for (const wrap of Array.from(dragWraps)) {
|
||||
if (!wrap) continue
|
||||
const display = getStyle(wrap, 'display')
|
||||
const draggable = wrap.getAttribute('data-drag')
|
||||
if (display !== 'none') {
|
||||
// 拖拽位置
|
||||
if (draggable === null || unref(context.destroyOnClose)) {
|
||||
drag(wrap)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
if (!unref(context.visible) || !unref(context.draggable)) {
|
||||
return
|
||||
}
|
||||
useTimeoutFn(() => {
|
||||
handleDrag()
|
||||
}, 30)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { computed, Ref, ref, unref } from 'vue'
|
||||
|
||||
export interface UseFullScreenContext {
|
||||
wrapClassName: Ref<string | undefined>
|
||||
modalWrapperRef: Ref<ComponentRef>
|
||||
extHeightRef: Ref<number>
|
||||
}
|
||||
|
||||
export function useFullScreen(context: UseFullScreenContext) {
|
||||
// const formerHeightRef = ref(0);
|
||||
const fullScreenRef = ref(false)
|
||||
|
||||
const getWrapClassName = computed(() => {
|
||||
const clsName = unref(context.wrapClassName) || ''
|
||||
return unref(fullScreenRef) ? `fullscreen-modal ${clsName} ` : unref(clsName)
|
||||
})
|
||||
|
||||
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 }
|
||||
}
|
||||
127
frontend/vben/src/components/Modal/src/index.less
Normal file
127
frontend/vben/src/components/Modal/src/index.less
Normal file
@@ -0,0 +1,127 @@
|
||||
.fullscreen-modal {
|
||||
overflow: hidden;
|
||||
|
||||
.ant-modal {
|
||||
top: 0 !important;
|
||||
right: 0 !important;
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
|
||||
&-content {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal {
|
||||
width: 520px;
|
||||
padding-bottom: 0;
|
||||
|
||||
.ant-modal-body > .scrollbar {
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
&-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
|
||||
.base-title {
|
||||
cursor: move !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-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 {
|
||||
.ant-modal-confirm-content {
|
||||
// color: #fff;
|
||||
|
||||
> * {
|
||||
color: @text-color-help-dark;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-confirm-confirm.error .ant-modal-confirm-body > .anticon {
|
||||
color: @error-color;
|
||||
}
|
||||
|
||||
&-confirm-btns {
|
||||
.ant-btn:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&-confirm-info {
|
||||
.ant-modal-confirm-body > .anticon {
|
||||
color: @warning-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-confirm-confirm.success {
|
||||
.ant-modal-confirm-body > .anticon {
|
||||
color: @success-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-confirm .ant-modal-body {
|
||||
padding: 24px !important;
|
||||
}
|
||||
@media screen and (max-height: 600px) {
|
||||
.ant-modal {
|
||||
top: 60px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-height: 540px) {
|
||||
.ant-modal {
|
||||
top: 30px;
|
||||
}
|
||||
}
|
||||
@media screen and (max-height: 480px) {
|
||||
.ant-modal {
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
83
frontend/vben/src/components/Modal/src/props.ts
Normal file
83
frontend/vben/src/components/Modal/src/props.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import type { PropType, CSSProperties } from 'vue'
|
||||
import type { ModalWrapperProps } from './typing'
|
||||
import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
export const modalProps = {
|
||||
visible: { type: Boolean },
|
||||
scrollTop: { type: Boolean, default: true },
|
||||
height: { type: Number },
|
||||
minHeight: { type: Number },
|
||||
// open drag
|
||||
draggable: { type: Boolean, default: true },
|
||||
centered: { type: Boolean },
|
||||
cancelText: { type: String, default: t('common.cancelText') },
|
||||
okText: { type: String, default: t('common.okText') },
|
||||
|
||||
closeFunc: Function as PropType<() => Promise<boolean>>,
|
||||
}
|
||||
|
||||
export const basicProps = Object.assign({}, modalProps, {
|
||||
defaultFullscreen: { type: Boolean },
|
||||
// Can it be full screen
|
||||
canFullscreen: { type: Boolean, default: true },
|
||||
// After enabling the wrapper, the bottom can be increased in height
|
||||
wrapperFooterOffset: { type: Number, default: 0 },
|
||||
// Warm reminder message
|
||||
helpMessage: [String, Array] as PropType<string | string[]>,
|
||||
// Whether to setting wrapper
|
||||
useWrapper: { type: Boolean, default: true },
|
||||
loading: { type: Boolean },
|
||||
loadingTip: { type: String },
|
||||
/**
|
||||
* @description: Show close button
|
||||
*/
|
||||
showCancelBtn: { type: Boolean, default: true },
|
||||
/**
|
||||
* @description: Show confirmation button
|
||||
*/
|
||||
showOkBtn: { type: Boolean, default: true },
|
||||
|
||||
wrapperProps: Object as PropType<Partial<ModalWrapperProps>>,
|
||||
|
||||
afterClose: Function as PropType<() => Promise<VueNode>>,
|
||||
|
||||
bodyStyle: Object as PropType<CSSProperties>,
|
||||
|
||||
closable: { type: Boolean, default: true },
|
||||
|
||||
closeIcon: Object as PropType<VueNode>,
|
||||
|
||||
confirmLoading: { type: Boolean },
|
||||
|
||||
destroyOnClose: { type: Boolean },
|
||||
|
||||
footer: Object as PropType<VueNode>,
|
||||
|
||||
getContainer: Function as PropType<() => any>,
|
||||
|
||||
mask: { type: Boolean, default: true },
|
||||
|
||||
maskClosable: { type: Boolean, default: true },
|
||||
keyboard: { type: Boolean, default: true },
|
||||
|
||||
maskStyle: Object as PropType<CSSProperties>,
|
||||
|
||||
okType: { type: String, default: 'primary' },
|
||||
|
||||
okButtonProps: Object as PropType<ButtonProps>,
|
||||
|
||||
cancelButtonProps: Object as PropType<ButtonProps>,
|
||||
|
||||
title: { type: String },
|
||||
|
||||
visible: { type: Boolean },
|
||||
|
||||
width: [String, Number] as PropType<string | number>,
|
||||
|
||||
wrapClassName: { type: String },
|
||||
|
||||
zIndex: { type: Number },
|
||||
})
|
||||
209
frontend/vben/src/components/Modal/src/typing.ts
Normal file
209
frontend/vben/src/components/Modal/src/typing.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes'
|
||||
import type { CSSProperties, VNodeChild, ComputedRef } from 'vue'
|
||||
/**
|
||||
* @description: 弹窗对外暴露的方法
|
||||
*/
|
||||
export interface ModalMethods {
|
||||
setModalProps: (props: Partial<ModalProps>) => void
|
||||
emitVisible?: (visible: boolean, uid: number) => void
|
||||
redoModalHeight?: () => void
|
||||
}
|
||||
|
||||
export type RegisterFn = (modalMethods: ModalMethods, uuid?: string) => void
|
||||
|
||||
export interface ReturnMethods extends ModalMethods {
|
||||
openModal: <T = any>(props?: boolean, data?: T, openOnSet?: boolean) => void
|
||||
closeModal: () => void
|
||||
getVisible?: ComputedRef<boolean>
|
||||
}
|
||||
|
||||
export type UseModalReturnType = [RegisterFn, ReturnMethods]
|
||||
|
||||
export interface ReturnInnerMethods extends ModalMethods {
|
||||
closeModal: () => void
|
||||
changeLoading: (loading: boolean) => void
|
||||
changeOkLoading: (loading: boolean) => void
|
||||
getVisible?: ComputedRef<boolean>
|
||||
redoModalHeight: () => void
|
||||
}
|
||||
|
||||
export type UseModalInnerReturnType = [RegisterFn, ReturnInnerMethods]
|
||||
|
||||
export interface ModalProps {
|
||||
minHeight?: number
|
||||
height?: number
|
||||
// 启用wrapper后 底部可以适当增加高度
|
||||
wrapperFooterOffset?: number
|
||||
draggable?: boolean
|
||||
scrollTop?: boolean
|
||||
|
||||
// 是否可以进行全屏
|
||||
canFullscreen?: boolean
|
||||
defaultFullscreen?: boolean
|
||||
visible?: boolean
|
||||
// 温馨提醒信息
|
||||
helpMessage: string | string[]
|
||||
|
||||
// 是否使用modalWrapper
|
||||
useWrapper: boolean
|
||||
|
||||
loading: boolean
|
||||
loadingTip?: string
|
||||
|
||||
wrapperProps: Omit<ModalWrapperProps, 'loading'>
|
||||
|
||||
showOkBtn: boolean
|
||||
showCancelBtn: boolean
|
||||
closeFunc: () => Promise<any>
|
||||
|
||||
/**
|
||||
* Specify a function that will be called when modal is closed completely.
|
||||
* @type Function
|
||||
*/
|
||||
afterClose?: () => any
|
||||
|
||||
/**
|
||||
* Body style for modal body element. Such as height, padding etc.
|
||||
* @default {}
|
||||
* @type object
|
||||
*/
|
||||
bodyStyle?: CSSProperties
|
||||
|
||||
/**
|
||||
* Text of the Cancel button
|
||||
* @default 'cancel'
|
||||
* @type string
|
||||
*/
|
||||
cancelText?: string
|
||||
|
||||
/**
|
||||
* Centered Modal
|
||||
* @default false
|
||||
* @type boolean
|
||||
*/
|
||||
centered?: boolean
|
||||
|
||||
/**
|
||||
* Whether a close (x) button is visible on top right of the modal dialog or not
|
||||
* @default true
|
||||
* @type boolean
|
||||
*/
|
||||
closable?: boolean
|
||||
/**
|
||||
* Whether a close (x) button is visible on top right of the modal dialog or not
|
||||
*/
|
||||
closeIcon?: VNodeChild | JSX.Element
|
||||
|
||||
/**
|
||||
* Whether to apply loading visual effect for OK button or not
|
||||
* @default false
|
||||
* @type boolean
|
||||
*/
|
||||
confirmLoading?: boolean
|
||||
|
||||
/**
|
||||
* Whether to unmount child components on onClose
|
||||
* @default false
|
||||
* @type boolean
|
||||
*/
|
||||
destroyOnClose?: boolean
|
||||
|
||||
/**
|
||||
* Footer content, set as :footer="null" when you don't need default buttons
|
||||
* @default OK and Cancel buttons
|
||||
* @type any (string | slot)
|
||||
*/
|
||||
footer?: VNodeChild | JSX.Element
|
||||
|
||||
/**
|
||||
* Return the mount node for Modal
|
||||
* @default () => document.body
|
||||
* @type Function
|
||||
*/
|
||||
getContainer?: (instance: any) => HTMLElement
|
||||
|
||||
/**
|
||||
* Whether show mask or not.
|
||||
* @default true
|
||||
* @type boolean
|
||||
*/
|
||||
mask?: boolean
|
||||
|
||||
/**
|
||||
* Whether to close the modal dialog when the mask (area outside the modal) is clicked
|
||||
* @default true
|
||||
* @type boolean
|
||||
*/
|
||||
maskClosable?: boolean
|
||||
|
||||
/**
|
||||
* Style for modal's mask element.
|
||||
* @default {}
|
||||
* @type object
|
||||
*/
|
||||
maskStyle?: CSSProperties
|
||||
|
||||
/**
|
||||
* Text of the OK button
|
||||
* @default 'OK'
|
||||
* @type string
|
||||
*/
|
||||
okText?: string
|
||||
|
||||
/**
|
||||
* Button type of the OK button
|
||||
* @default 'primary'
|
||||
* @type string
|
||||
*/
|
||||
okType?: 'primary' | 'danger' | 'dashed' | 'ghost' | 'default'
|
||||
|
||||
/**
|
||||
* The ok button props, follow jsx rules
|
||||
* @type object
|
||||
*/
|
||||
okButtonProps?: ButtonProps
|
||||
|
||||
/**
|
||||
* The cancel button props, follow jsx rules
|
||||
* @type object
|
||||
*/
|
||||
cancelButtonProps?: ButtonProps
|
||||
|
||||
/**
|
||||
* The modal dialog's title
|
||||
* @type any (string | slot)
|
||||
*/
|
||||
title?: VNodeChild | JSX.Element
|
||||
|
||||
/**
|
||||
* Width of the modal dialog
|
||||
* @default 520
|
||||
* @type string | number
|
||||
*/
|
||||
width?: string | number
|
||||
|
||||
/**
|
||||
* The class name of the container of the modal dialog
|
||||
* @type string
|
||||
*/
|
||||
wrapClassName?: string
|
||||
|
||||
/**
|
||||
* The z-index of the Modal
|
||||
* @default 1000
|
||||
* @type number
|
||||
*/
|
||||
zIndex?: number
|
||||
}
|
||||
|
||||
export interface ModalWrapperProps {
|
||||
footerOffset?: number
|
||||
loading: boolean
|
||||
modalHeaderHeight: number
|
||||
modalFooterHeight: number
|
||||
minHeight: number
|
||||
height: number
|
||||
visible: boolean
|
||||
fullScreen: boolean
|
||||
useWrapper: boolean
|
||||
}
|
||||
Reference in New Issue
Block a user