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