vue二次封装ant-design-vue中的Modal弹窗组件,实现拖拽,全屏两种功能,原有参数属性不变

本文主要是介绍vue二次封装ant-design-vue中的Modal弹窗组件,实现拖拽,全屏两种功能,原有参数属性不变,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在我们的项目的有的地方需要用弹框的拖拽,以及弹窗自定义全屏显示的需求,所以再次将二次合一,同时弹框里面内容自适应屏幕高度

在ant-design-vue中,已经实现了拖拽,全屏的功能,下面是ant官网的示例

自定义渲染对话框

全屏

下面是对ant原有的功能进行二次封装,由于组件拆解了,子组件分为三部分:为如下代码:

一:子级的根目录modal下新建index.vue,弹框涉及到的参数可参考ant官网

<template>
<!--  v-bind处理a-table 传递过来的参数,你们开发的时候不用再子组件中接收这么ant原有的参数--><a-modal class="Kld-dfs-modal" :open="isOpen" :body-style="computeBodyStyle" :width="width" :centered="centered"@cancel="closeModal" :afterClose="handleAfterClose" :destroyOnClose="destroyOnClose" :keyboard="keyboardOpen":style="topStyle" :maskClosable="maskClosable" :mask="mask" :wrap-class-name="fullModal" v-bind="attrs"><template #title><div class="modalHeader">
<!-- 标题的ref用于控制是否可以拖拽--><div :ref="modalTitleRefS" class="draggableTitle">{{ title }}</div>
<!-- 弹框头部的标题,以及全屏的按钮--><ModalHeader :width="width" @fullScreen="handleFullScreen" @reduction="handleReduction" /></div></template><template #closeIcon>
<!-- 弹框头部的关闭按钮区域--><ModalClose /></template><div ref="bodySlot">
<!-- 弹框内容区域--><slot name="body"></slot></div><template #footer>
<!-- 弹框底部按钮区域--><div ref="footerSlot"><slot name="footer" v-if="haveFooter"></slot></div></template>
<!-- 实现是否拖拽弹框计算的核心代码--><template #modalRender="{ originVNode }"><div :style="transformStyle"><component :is="originVNode" /></div></template></a-modal>
</template><script  lang="ts">
import { defineComponent, ref, computed, watch, CSSProperties, watchEffect } from 'vue';
import { useDraggable } from '@vueuse/core';
import { FullscreenExitOutlined, FullscreenOutlined, CloseOutlined } from '@ant-design/icons-vue';import ModalClose from './components/ModalClose.vue';
import ModalHeader from './components/ModalHeader.vue';
import { Modal } from 'ant-design-vue';export default defineComponent({name: 'KldDfsModal',props: {centered: {type: Boolean,default: true},maskClosable: {type: Boolean,default: true,},mask: {type: Boolean,default: true},topStyle: {type: String,default: ''},closable: {type: Boolean,default: true,},destroyOnClose: {type: Boolean,default: false,},keyboardOpen: {type: Boolean,default: false,},title: {type: String,default: ''},open: {type: Boolean,default: false},haveFooter: {type: Boolean,default: true},width: {type: [String, Number],default: '60vw'},boxHeight: {type: [String, Number],default: 60},bodyStyle: {type: Object,default: {overflowX: 'hidden',overflowY: 'auto'}}},setup(props, { attrs, emit }) {/*****拖拽相关*****/const modalTitleRefS = ref('modalTitleRef')const modalTitleRef = ref<HTMLElement>();const { x, y, isDragging } = useDraggable(modalTitleRef);const startX = ref<number>(0);const startY = ref<number>(0);const startedDrag = ref(false);const transformX = ref(0);const transformY = ref(0);const preTransformX = ref(0);const preTransformY = ref(0);const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });const transformStyle = computed<CSSProperties>(() => {return {transform: `translate(${transformX.value}px, ${transformY.value}px)`,};});watch([x, y], () => {if (!startedDrag.value) {startX.value = x.value;startY.value = y.value;const bodyRect = document.body.getBoundingClientRect();const titleRect = modalTitleRef.value?.getBoundingClientRect() ?? { width: 0, height: 0 };dragRect.value.right = bodyRect.width - titleRect.width;dragRect.value.bottom = bodyRect.height - titleRect.height;preTransformX.value = transformX.value;preTransformY.value = transformY.value;}startedDrag.value = true;});watch(isDragging, () => {if (!isDragging) {startedDrag.value = false;}});watchEffect(() => {if (startedDrag.value) {transformX.value =preTransformX.value +Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -startX.value;transformY.value =preTransformY.value +Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -startY.value;}});/*****************/const bodySlot = ref(null);const footerSlot = ref(null);const isOpen = ref<boolean>(false);//const centered = ref<boolean>(true);const computeBodyStyle = ref<CSSProperties>();//动态计算内容高度,生成弹窗const computeWindowStyle = (bodyRealHeight: number = 0, headerRealHeight: number = 0, footerRealHeight: number = 0) => {let windowHeight = document.body.offsetHeight;let realHeight: number = 0;//后面增加数值的构成 model padding 上下20 + 20 header和body之间 25 footer和body之间 20if (bodyRealHeight + headerRealHeight + footerRealHeight + 85 >= windowHeight) {realHeight = windowHeight - headerRealHeight - footerRealHeight - 82;} else {realHeight = bodyRealHeight + 25;}computeBodyStyle.value = Object.assign({height: `${realHeight}px`}, props.bodyStyle);};const width = ref<string | number>(props.width);const fullModal = ref<string>();// 全屏const handleFullScreen = () => {width.value = '100%'modalTitleRefS.value = ''fullModal.value = 'kld-full-modal'transformX.value = 0;transformY.value = 0;}// 还原const handleReduction = () => {width.value = props.widthmodalTitleRefS.value = 'modalTitleRef'fullModal.value = ''}const closeModal = (e: Event) => {emit('cancel', e);};/*** @description: Modal 完全关闭后的回调*/const handleAfterClose = () => {console.log('Modal 完全关闭后的回调');fullModal.value = ''modalTitleRefS.value = 'modalTitleRef'width.value = props.widthemit('afterClose')};watch(() => props.open,(newVal) => {if (newVal) {isOpen.value = true;} else {isOpen.value = false;}},{ deep: true });watch(() => bodySlot.value, (newVal) => {if (newVal) {const bodyDom: any = newVal, footerDom: any = footerSlot.value, headerDom: any = modalTitleRef.value;x.value = startX.value;y.value = startY.value;computeWindowStyle(bodyDom.clientHeight, headerDom.clientHeight, footerDom.clientHeight);}}, { deep: true });return {modalTitleRefS,isOpen,modalTitleRef,computeBodyStyle,width,fullModal,transformStyle,handleFullScreen,handleReduction,closeModal,handleAfterClose,bodySlot,footerSlot,transformX,transformY,dragRect,startedDrag,isDragging,computeWindowStyle,startX,startY,preTransformX,preTransformY,x,y,attrs,listeners: emit,};},components: {CloseOutlined,FullscreenOutlined,FullscreenExitOutlined,AModal: Modal,ModalClose,ModalHeader}});</script><style scoped lang="less">
.draggableTitle {width: 100%;cursor: move
}
<!-- 用于弹框初始的位置-->
:global(.ant-modal-root .ant-modal-wrap) {overflow: hidden;
}:deep(.ant-modal-body)::-webkit-scrollbar {width: 0px;height: 0px;padding: 0px;
}:deep(.ant-modal-body)::-webkit-scrollbar-thumb {background: #c1c1c1;border-radius: 3px;
}:deep(.ant-modal-body)::-webkit-scrollbar-track {border-radius: 3px;background: #f1f1f1;
}
</style>
<style lang="less">
<!-- 用于弹框全屏的样式-->
@import url('./common.less');
</style>

子级的根目录modal下新建common.less,全屏的cs样式

// modal 全屏
.modalHeader {display: flex;justify-content: space-between;
}
.kld-full-modal{.ant-modal {max-width: 100% !important;top: 0 !important;padding-bottom: 0 !important;margin: 0 !important;}.ant-modal-content {display: flex;flex-direction: column;height: calc(100vh);}.ant-modal-body {flex: 1;max-height: calc(100vh);}
}

二:子级的根目录modal下新建components文件然后新建存放头部关闭按钮的组件以及全屏按钮的组件,

ModalClose组件

<template><a-tooltip ref="KldTooltip" color="#ffffff" placement="bottom"><template #title><span style="color: black;">关闭</span></template><CloseOutlined /></a-tooltip>
</template><script lang="ts">
import { ref } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { CloseOutlined } from '@ant-design/icons-vue';export default {name: "KldTooltip",setup(_, { attrs, emit }) {return {attrs,listeners: emit,KldTooltip: ref()};},components: {ATooltip: Tooltip,CloseOutlined}
};
</script>

ModalHeader组件

<template><div><a-tooltip color="#ffffff" v-if="width != '100%'" placement="bottom"><template #title><span style="color: black;">全屏</span></template><a-button type="text" class="ant-modal-close" style="margin-right: 30px;" @click="handleFullScreen"><FullscreenOutlined /></a-button></a-tooltip><a-tooltip color="#ffffff" v-else placement="bottom"><template #title><span style="color: black;">还原</span></template><a-button type="text" class="ant-modal-close" style="margin-right: 30px;" @click="handleReduction"><FullscreenExitOutlined /></a-button></a-tooltip></div>
</template><script lang="ts">
import { ref } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons-vue';export default {name: "KldTooltip",props: {width: {type: [String, Number],default: '40%'},},setup(props, { attrs, emit }) {const handleFullScreen = () => {emit('fullScreen');};const handleReduction = () => {emit('reduction');};return {props,attrs,listeners: emit,KldTooltip: ref(),handleFullScreen,handleReduction};},components: {ATooltip: Tooltip,FullscreenExitOutlined,FullscreenOutlined}
};
</script>

 三、然后在main.js里面引入,也可以直接在父组件引入,此处就不讲解引入了,直接父组件使用弹框

父组件

<template><kld-dfs-modal :open="createVisible" :title="createTitle" :width="'40%'" :destroyOnClose="true" :haveFooter="true":boxHeight="85" @cancel="createVisible = false" :maskClosable="false"><template #body><ApplicationCreate @close="handleClose" :editFormRef="editFormRef" :editId="editId":createTitle="createTitle" /><p class="h-20" v-for="index in 20" :key="index">根据屏幕高度自适应</p></template><template #footer><a-button>测试底部按钮插槽</a-button></template></kld-dfs-modal>
</template><script lang="ts" setup>
const createVisible = ref<boolean>(false); //创建
const createTitle = ref<string>(""); //创建弹窗标题const showModalA = () => {createVisible.value = true;createTitle.value = "创建应用";editFormRef.value = {}editId.value = ''
};
// 关闭创建弹窗
const handleClose = (type: string) => {if (type === "提交") {}createVisible.value = false;
};
</script>

效果图如下:内容可自适应屏幕高度,如果不需要可通过弹框标题的ref控制

 
如果在使用中有什么问题,还请多多指点,也可以私信或者留言,觉得可用麻烦点点赞以及收藏

这篇关于vue二次封装ant-design-vue中的Modal弹窗组件,实现拖拽,全屏两种功能,原有参数属性不变的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/619294

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi