feat(components): 新增 NovaDialog 通用弹框组件
- 基于 n-modal 封装的统一弹框组件 - 支持自定义宽度、高度、标题和按钮文字 - 提供 novaOpen/novaClose 方法控制显示状态 - 支持 #header 和 #content 插槽自定义内容 - 统一项目弹框交互体验和视觉风格
This commit is contained in:
parent
b8d6e5d781
commit
02778d7c4f
223
src/components/common/NovaDialog.vue
Normal file
223
src/components/common/NovaDialog.vue
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, toRefs } from 'vue'
|
||||||
|
import { NButton, NSpace } from 'naive-ui'
|
||||||
|
|
||||||
|
// 定义参数的类型
|
||||||
|
interface IDialogProps {
|
||||||
|
/** 弹框标题 */
|
||||||
|
title?: string
|
||||||
|
/** 弹框宽度 */
|
||||||
|
width?: string | number
|
||||||
|
/** 内容区域高度 */
|
||||||
|
height?: string | number
|
||||||
|
/** 确认按钮文本 */
|
||||||
|
confirmText?: string
|
||||||
|
/** 取消按钮文本 */
|
||||||
|
cancelText?: string
|
||||||
|
/** 全屏显示 */
|
||||||
|
fullscreen?: boolean
|
||||||
|
/** 确认按钮加载状态 */
|
||||||
|
loading?: boolean
|
||||||
|
/** 隐藏底部按钮 */
|
||||||
|
footerHidden?: boolean
|
||||||
|
/** 自动聚焦 */
|
||||||
|
autoFocus?: boolean
|
||||||
|
/** 居中显示 */
|
||||||
|
center?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置默认值
|
||||||
|
const props = withDefaults(defineProps<IDialogProps>(), {
|
||||||
|
title: '提示',
|
||||||
|
width: 500,
|
||||||
|
height: 'auto',
|
||||||
|
confirmText: '确定',
|
||||||
|
cancelText: '取消',
|
||||||
|
fullscreen: false,
|
||||||
|
loading: false,
|
||||||
|
footerHidden: false,
|
||||||
|
autoFocus: true,
|
||||||
|
center: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emits = defineEmits<{
|
||||||
|
novaConfirm: []
|
||||||
|
novaCancel: []
|
||||||
|
novaClose: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 弹框显示状态
|
||||||
|
const visible = ref(false)
|
||||||
|
|
||||||
|
// 确认按钮加载状态,使用toRefs保持响应式
|
||||||
|
const { loading } = toRefs(props)
|
||||||
|
const confirmLoading = ref(loading)
|
||||||
|
|
||||||
|
/** 打开弹框 */
|
||||||
|
function novaOpen() {
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭弹框 */
|
||||||
|
function novaClose() {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 快速关闭弹框 */
|
||||||
|
function novaQuickClose() {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认事件处理 */
|
||||||
|
function handleConfirm() {
|
||||||
|
emits('novaConfirm')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取消事件处理 */
|
||||||
|
function handleCancel() {
|
||||||
|
emits('novaCancel')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭事件处理 */
|
||||||
|
function handleClose() {
|
||||||
|
visible.value = false
|
||||||
|
emits('novaClose')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 暴露给父组件的方法 */
|
||||||
|
defineExpose({
|
||||||
|
novaOpen,
|
||||||
|
novaClose,
|
||||||
|
novaQuickClose,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-modal
|
||||||
|
v-model:show="visible"
|
||||||
|
:mask-closable="false"
|
||||||
|
:close-on-esc="false"
|
||||||
|
:auto-focus="autoFocus"
|
||||||
|
preset="card"
|
||||||
|
:loading="confirmLoading"
|
||||||
|
:show-icon="false"
|
||||||
|
:style="{
|
||||||
|
width: typeof width === 'number' ? `${width}px` : width,
|
||||||
|
}"
|
||||||
|
class="nova-dialog"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<!-- 头部插槽 -->
|
||||||
|
<template #header>
|
||||||
|
<div v-if="$slots.header" class="nova-dialog-custom-header">
|
||||||
|
<slot name="header" />
|
||||||
|
</div>
|
||||||
|
<div v-else class="nova-dialog-header">
|
||||||
|
<div class="nova-dialog-title">
|
||||||
|
{{ title }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<div
|
||||||
|
class="nova-dialog-content"
|
||||||
|
:style="{
|
||||||
|
height: fullscreen ? 'auto' : (typeof height === 'number' ? `${height}px` : height),
|
||||||
|
overflow: height === 'auto' ? 'visible' : 'auto',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<slot name="content" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部操作区域 -->
|
||||||
|
<template #footer>
|
||||||
|
<div v-if="!footerHidden" class="nova-dialog-footer">
|
||||||
|
<NSpace justify="center">
|
||||||
|
<NButton
|
||||||
|
size="medium"
|
||||||
|
@click="handleCancel"
|
||||||
|
>
|
||||||
|
{{ cancelText }}
|
||||||
|
</NButton>
|
||||||
|
<NButton
|
||||||
|
type="primary"
|
||||||
|
size="medium"
|
||||||
|
:loading="confirmLoading"
|
||||||
|
@click="handleConfirm"
|
||||||
|
>
|
||||||
|
{{ confirmText }}
|
||||||
|
</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</div>
|
||||||
|
<div v-else />
|
||||||
|
</template>
|
||||||
|
</n-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 弹框标题样式 */
|
||||||
|
.nova-dialog :deep(.n-card-header) {
|
||||||
|
padding: 0;
|
||||||
|
border-bottom: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nova-dialog-header {
|
||||||
|
padding: 12px 16px 8px;
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nova-dialog-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1a1a1a;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nova-dialog-custom-header {
|
||||||
|
padding: 12px 16px 8px;
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹框整体样式 */
|
||||||
|
.nova-dialog :deep(.n-card) {
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内容区域样式 */
|
||||||
|
.nova-dialog :deep(.n-card__content) {
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nova-dialog-content {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部样式 */
|
||||||
|
.nova-dialog :deep(.n-card__footer) {
|
||||||
|
padding: 8px 16px 12px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nova-dialog-footer {
|
||||||
|
/* 底部操作区域样式已由 n-space 处理 */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user