Files
oci-portal-dash/src/components/FormModal.vue
T
2026-07-21 19:26:22 +08:00

92 lines
2.7 KiB
Vue

<script setup lang="ts">
import { NButton, NModal } from 'naive-ui'
import { computed } from 'vue'
import { useIsMobile } from '@/composables/useIsMobile'
const props = defineProps<{
show: boolean
title: string
submitting?: boolean
submitText?: string
submitDisabled?: boolean
danger?: boolean
width?: number
}>()
const emit = defineEmits<{ 'update:show': [boolean]; submit: [] }>()
const isMobile = useIsMobile()
/** 移动端全屏(100dvh 覆盖底栏与手势区),桌面维持定宽居中 */
const modalStyle = computed(() =>
isMobile.value
? { width: '100vw', height: '100dvh', maxWidth: '100vw', margin: '0', borderRadius: '0' }
: { width: `${props.width ?? 480}px`, maxWidth: 'calc(100vw - 24px)' },
)
</script>
<template>
<NModal
:show="show"
preset="card"
:title="title"
:style="modalStyle"
:class="isMobile ? 'form-modal-mobile' : ''"
:mask-closable="!submitting"
:close-on-esc="!submitting"
@update:show="emit('update:show', $event)"
>
<!-- 标题插槽:需要在标题旁放徽章等富内容时覆盖 -->
<template v-if="$slots.header" #header>
<slot name="header" />
</template>
<div class="form-modal-body">
<slot />
</div>
<template #footer>
<div class="flex justify-end gap-2.5">
<NButton
:size="isMobile ? 'medium' : 'small'"
:class="isMobile ? 'flex-1' : ''"
:disabled="submitting"
@click="emit('update:show', false)"
>
取消
</NButton>
<NButton
:size="isMobile ? 'medium' : 'small'"
:class="isMobile ? 'flex-1' : ''"
:type="danger ? 'error' : 'primary'"
:loading="submitting"
:disabled="submitDisabled"
@click="emit('submit')"
>
{{ submitText ?? '确定' }}
</NButton>
</div>
</template>
</NModal>
</template>
<style scoped>
/* 长表单不撑破视口:内容区限高滚动,页脚按钮常驻;
负 margin + 等量 padding 让滚动条贴弹窗边缘(变量继承自 .n-card) */
.form-modal-body {
overflow-y: auto;
margin-inline: calc(-1 * var(--n-padding-left, 24px));
padding-inline: var(--n-padding-left, 24px);
}
/* 限高只在桌面定宽形态生效:移动全屏由全局 .form-modal-mobile 的 flex:1
接管高度。不能靠全局规则 max-height:none 覆盖——main.css 先于组件
scoped 样式加载,同特异性下这里会反超,故直接用断点隔离 */
@media (min-width: 768px) {
.form-modal-body {
max-height: min(62vh, calc(100vh - 220px));
}
}
</style>
<!-- 移动端全屏形态 .form-modal-mobile 的规则在 main.css(全局):
NModal teleport body,scoped 够不到;且该类被各弹窗跨 chunk 复用 -->