Vue3抽屉(Drawer)

效果如下图:在线预览

在这里插入图片描述

APIs

参数说明类型默认值必传
width宽度,在 placementrightleft 时使用string | number378false
height高度,在 placementtopbottom 时使用string | number378false
title标题string | slotundefinedfalse
closable是否显示左上角的关闭按钮booleantruefalse
placement抽屉的方向‘top’ | ‘right’ | ‘bottom’ | ‘left’‘right’false
headerStyle设置 Drawer 头部的样式CSSProperties{}false
bodyStyle设置 Drawer 内容部分的样式CSSProperties{}false
extra抽屉右上角的操作区域string | slotundefinedfalse
footer抽屉的页脚string | slotundefinedfalse
footerStyle抽屉页脚的样式CSSProperties{}false
destroyOnClose关闭时是否销毁 Drawer 里的子元素booleanfalsefalse
zIndex设置 Drawerz-indexnumber1000false
open v-model抽屉是否可见booleanfalsefalse

Events

事件名称说明参数
close点击遮罩层或左上角叉或取消按钮的回调(e: Event) => void

创建抽屉组件Drawer.vue

<script setup lang="ts">
import { computed, useSlots, type CSSProperties } from 'vue'
interface Props {width?: string | number // 宽度,在 placement 为 right 或 left 时使用height?: string | number // 高度,在 placement 为 top 或 bottom 时使用title?: string // 标题 string | slotclosable?: boolean // 是否显示左上角的关闭按钮placement?: 'top' | 'right' | 'bottom' | 'left' // 抽屉的方向headerStyle?: CSSProperties // 设置 Drawer 头部的样式bodyStyle?: CSSProperties // 设置 Drawer 内容部分的样式extra?: string // 抽屉右上角的操作区域 string | slotfooter?: string // 抽屉的页脚 string | slotfooterStyle?: CSSProperties // 抽屉页脚的样式destroyOnClose?: boolean // 关闭时是否销毁 Drawer 里的子元素zIndex?: number // 设置 Drawer 的 z-indexopen?: boolean // (v-model) 抽屉是否可见
}
const props = withDefaults(defineProps<Props>(), {width: 378,height: 378,title: undefined,closable: true,placement: 'right',headerStyle: () => ({}),bodyStyle: () => ({}),extra: undefined,footer: undefined,footerStyle: () => ({}),destroyOnClose: false,zIndex: 1000,open: false
})
const drawerWidth = computed(() => {if (typeof props.width === 'number') {return props.width + 'px'}return props.width
})
const drawerHeight = computed(() => {if (typeof props.height === 'number') {return props.height + 'px'}return props.height
})
const slots = useSlots()
const showHeader = computed(() => {const titleSlots = slots.title?.()const extraSlots = slots.extra?.()let n = 0if (titleSlots && titleSlots.length) {n++}if (extraSlots && extraSlots.length) {n++}return Boolean(n) || props.title || props.extra || props.closable
})
const showFooter = computed(() => {const footerSlots = slots.footer?.()return (footerSlots && footerSlots.length) || props.footer
})
const emits = defineEmits(['update:open', 'close'])
function onBlur(e: Event) {emits('update:open', false)emits('close', e)
}
function onClose(e: Event) {emits('update:open', false)emits('close', e)
}
</script>
<template><div class="m-drawer" tabindex="-1"><Transition name="fade"><div v-show="open" class="m-drawer-mask" @click.self="onBlur"></div></Transition><Transition :name="`motion-${placement}`"><divv-show="open"class="m-drawer-wrapper":class="`drawer-${placement}`":style="`z-index: ${zIndex}; ${['top', 'bottom'].includes(placement) ? 'height:' + drawerHeight : 'width:' + drawerWidth};`"><div class="m-drawer-content"><div class="m-drawer-body-wrapper" v-if="!destroyOnClose"><div class="m-drawer-header" :style="headerStyle" v-show="showHeader"><div class="m-header-title"><svgv-if="closable"focusable="false"@click="onClose"class="u-close"data-icon="close"width="1em"height="1em"fill="currentColor"aria-hidden="true"viewBox="64 64 896 896"><pathd="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg><p class="u-title"><slot name="title">{{ title }}</slot></p></div><div class="m-drawer-extra"><slot name="extra">{{ extra }}</slot></div></div><div class="m-drawer-body" :style="bodyStyle"><slot></slot></div><div class="m-drawer-footer" :style="footerStyle" v-show="showFooter"><slot name="footer">{{ footer }}</slot></div></div><div class="m-drawer-body-wrapper" v-if="destroyOnClose && open"><div class="m-drawer-header" :style="headerStyle" v-show="showHeader"><div class="m-header-title"><svgfocusable="false"@click="onClose"class="u-close"data-icon="close"width="1em"height="1em"fill="currentColor"aria-hidden="true"viewBox="64 64 896 896"><pathd="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path></svg><p class="u-title"><slot name="title">{{ title }}</slot></p></div><div class="m-drawer-extra"><slot name="extra">{{ extra }}</slot></div></div><div class="m-drawer-body" :style="bodyStyle"><slot></slot></div><div class="m-drawer-footer" :style="footerStyle" v-show="showFooter"><slot name="footer">{{ footer }}</slot></div></div></div></div></Transition></div>
</template>
<style lang="less" scoped>
.fade-enter-active,
.fade-leave-active {transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {opacity: 0;
}
.motion-top-enter-active,
.motion-top-leave-active {transition: all 0.3s;
}
.motion-top-enter-from,
.motion-top-leave-to {transform: translateY(-100%);
}
.motion-right-enter-active,
.motion-right-leave-active {transition: all 0.3s;
}
.motion-right-enter-from,
.motion-right-leave-to {transform: translateX(100%);
}
.motion-bottom-enter-active,
.motion-bottom-leave-active {transition: all 0.3s;
}
.motion-bottom-enter-from,
.motion-bottom-leave-to {transform: translateY(100%);
}
.motion-left-enter-active,
.motion-left-leave-active {transition: all 0.3s;
}
.motion-left-enter-from,
.motion-left-leave-to {transform: translateX(-100%);
}
.m-drawer {position: fixed;inset: 0;z-index: 1000;pointer-events: none;.m-drawer-mask {position: absolute;inset: 0;z-index: 1000;background: rgba(0, 0, 0, 0.45);pointer-events: auto;}.m-drawer-wrapper {position: absolute;transition: all 0.3s;.m-drawer-content {width: 100%;height: 100%;overflow: auto;background: #ffffff;pointer-events: auto;.m-drawer-body-wrapper {display: flex;flex-direction: column;width: 100%;height: 100%;.m-drawer-header {display: flex;flex: 0;align-items: center;padding: 16px 24px;font-size: 16px;line-height: 1.5;border-bottom: 1px solid rgba(5, 5, 5, 0.06);.m-header-title {display: flex;flex: 1;align-items: center;min-width: 0;min-height: 0;.u-close {display: inline-block;margin-inline-end: 12px;width: 16px;height: 16px;fill: rgba(0, 0, 0, 0.45);cursor: pointer;transition: fill 0.2s;&:hover {fill: rgba(0, 0, 0, 0.88);}}.u-title {flex: 1;margin: 0;color: rgba(0, 0, 0, 0.88);font-weight: 600;font-size: 16px;line-height: 1.5;}}.m-drawer-extra {flex: none;color: rgba(0, 0, 0, 0.88);}}.m-drawer-body {flex: 1;min-width: 0;min-height: 0;padding: 24px;overflow: auto;}.m-drawer-footer {flex-shrink: 0;padding: 8px 16px;border-top: 1px solid rgba(5, 5, 5, 0.06);color: rgba(0, 0, 0, 0.88);}}}}.drawer-top {top: 0;inset-inline: 0;box-shadow:0 6px 16px 0 rgba(0, 0, 0, 0.08),0 3px 6px -4px rgba(0, 0, 0, 0.12),0 9px 28px 8px rgba(0, 0, 0, 0.05);}.drawer-right {top: 0;right: 0;bottom: 0;box-shadow:-6px 0 16px 0 rgba(0, 0, 0, 0.08),-3px 0 6px -4px rgba(0, 0, 0, 0.12),-9px 0 28px 8px rgba(0, 0, 0, 0.05);}.drawer-bottom {bottom: 0;inset-inline: 0;box-shadow:0 -6px 16px 0 rgba(0, 0, 0, 0.08),0 -3px 6px -4px rgba(0, 0, 0, 0.12),0 -9px 28px 8px rgba(0, 0, 0, 0.05);}.drawer-left {top: 0;bottom: 0;left: 0;box-shadow:6px 0 16px 0 rgba(0, 0, 0, 0.08),3px 0 6px -4px rgba(0, 0, 0, 0.12),9px 0 28px 8px rgba(0, 0, 0, 0.05);}
}
</style>

在要使用的页面引入

<script setup lang="ts">
import Drawer from './Drawer.vue'
import { ref } from 'vue'const open1 = ref<boolean>(false)
const open2 = ref<boolean>(false)
const open3 = ref<boolean>(false)
const open4 = ref<boolean>(false)
const open5 = ref<boolean>(false)
const options = ref([{label: 'top',value: 'top'},{label: 'right',value: 'right'},{label: 'bottom',value: 'bottom'},{label: 'left',value: 'left'}
])
const placement = ref('right')
const extraPlacement = ref('right')
const footerPlacement = ref('right')
function onClose() {// 点击遮罩层或左上角叉或取消按钮的回调open3.value = falseopen4.value = falseconsole.log('close')
}
</script>
<template><div><h1>{{ $route.name }} {{ $route.meta.title }}</h1><h2 class="mt30 mb10">基本使用</h2><Button type="primary" @click="open1 = true">Open</Button><Drawer v-model:open="open1" title="Basic Drawer" @close="onClose"><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Drawer><h2 class="mt30 mb10">自定义位置</h2><Radio v-model:value="placement" :options="options" style="margin-right: 8px" /><Button type="primary" @click="open2 = true">Open</Button><Drawerv-model:open="open2"title="Basic Drawer":closable="false"extra="extra"footer="footer":placement="placement"><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Drawer><h2 class="mt30 mb10">额外操作</h2><Radio v-model:value="extraPlacement" :options="options" style="margin-right: 8px" /><Button type="primary" @click="open3 = true">Open</Button><Drawer v-model:open="open3" title="Basic Drawer" :placement="extraPlacement"><template #extra><Button style="margin-right: 8px" @click="onClose">Cancel</Button><Button type="primary" @click="onClose">Submit</Button></template><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Drawer><h2 class="mt30 mb10">抽屉页脚</h2><Radio v-model:value="footerPlacement" :options="options" style="margin-right: 8px" /><Button type="primary" @click="open4 = true">Open</Button><Drawerv-model:open="open4"title="Basic Drawer":placement="footerPlacement":footer-style="{ textAlign: 'right' }"><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p><template #footer><Button style="margin-right: 8px" @click="onClose">Cancel</Button><Button type="primary" @click="onClose">Submit</Button></template></Drawer><h2 class="mt30 mb10">自定义 header & body 样式</h2><Button type="primary" @click="open5 = true">Open</Button><Drawerv-model:open="open5":closable="false"title="Basic Drawer":header-style="{ textAlign: 'center' }":body-style="{ textAlign: 'center' }"><p>Some contents...</p><p>Some contents...</p><p>Some contents...</p></Drawer></div>
</template>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/35512.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Nest系列 - 4. 听说人人都会CRUD,可是我还不会怎么办???-《4.2结合前端使用实现CRUD》

终于到了这一步&#xff0c;今天我们就将实现一个CRUD&#xff0c;主要是编写nest 部分&#xff0c;前端部分后面可以看git 代码 下面是效果演示&#xff08;大部分是参考满哥实现&#xff0c;&#x1f923;&#x1f923;&#x1f923;&#xff09; 前期准备 前端接口处理 im…

RT-Thread Studio实现动态线程

1创建项目 我的板子为STM32F03ZET6 点击RT-Thread项目 2选择板子&#xff08;根据自己的板子选择&#xff09; 3找到主函数 4编写代码 4-1创建函数入口 // 线程入口函数 static void thread_entry(void *parameter) {rt_uint32_t count 0;while (1){// 线程执行的代码rt_k…

互斥锁并不能保证任务不能被调度

互斥锁不能保证在临界区的时候&#xff0c;不发送任务调度&#xff0c;所以为了保护共享的资源不被调度访问&#xff0c;需要在两个线程都加互斥锁来保证任务不调度 #include <stdio.h> #include <pthread.h> #include <unistd.h> int shared_resource 0;p…

2025中国(宁波)出口跨境电商博览会

2025中国(宁波)出口跨境电商博览会 时间&#xff1a;2025年5月28-30日 地点&#xff1a;中国宁波国际会展中心 组织单位&#xff1a; 宁波欧德国际商务咨询服务有限公司 凤麟展览(宁波)有限公司 宁波市跨境电子商务协会 宁波市家居产业协会 详询主办方陆先生 I38&…

【openmpi】怎样使用openmpi并行运行python脚本?

创作日志&#xff1a; 装过一次openmpi&#xff0c;但是半年之后就忘记怎么用了&#xff0c;所以记录一下 1. 测试openmpi是否安装好 cd /home/xxxx/SnapHiC_Call_Loop/openmpi-4.1.6/examples make mpirun -np 4 hello_c得到如下输出就说明是装好的了 2. 没有导入路径的话导…

rtthread stm32h743的使用(十)i2c设备使用

我们要在rtthread studio 开发环境中建立stm32h743xih6芯片的工程。我们使用一块stm32h743及fpga的核心板完成相关实验&#xff0c;核心板如图&#xff1a; 1.建立新工程&#xff0c;选择相应的芯片型号及debug引脚及调试器 2.打开cubemux&#xff0c;设置外部时钟及串口外设…

制造业采购堡垒机的四大必要性看这里!

制造业包括的行业广泛&#xff0c;与大家的生活息息相关&#xff0c;例如食品制造业、汽车制造业、纺织业、服装制造业等等。但大家对于制造业不是很了解&#xff0c;不知道制造业也是需要采购堡垒机的&#xff0c;今天我们就来聊聊制造业采购堡垒机的必要性。 制造业采购堡垒机…

python selenium 下载

查看浏览器版本 下载地址&#xff1a; 新版本下载地址 https://googlechromelabs.github.io/chrome-for-testing/ 历史版本也可以用这个下载地址 http://chromedriver.storage.googleapis.com/index.html 找到对应的版本 126.0.xxx 下载

推荐给中小学生的暑假打字神器

暑假是孩子们放松身心、增长知识的好时机。在这个漫长的假期里&#xff0c;家长们不仅希望孩子能够快乐地度过每一天&#xff0c;还希望他们能在学习上有所进步。尤其是随着科技的发展&#xff0c;熟练的打字技巧已经成为现代学习和工作的基本技能之一。今天&#xff0c;我要向…

节流工具,避免操作太频繁

ThrottleUtil 用于保证某个操作在一定时间内只执行一次的工具。 package com.cashpro.kash.lending.loan.utils;/*** <pre>* Created by zhuguohui* Date: 2024/6/26* Time: 13:43* Desc:用于节流执行任务,限制任务执行的频次* </pre>*/import android.os.Handle…

基于requests模块爬取网易云歌曲评论并制作热词云图

本实践大作业要求 本次实践大作业主要要求主要包括&#xff1a; 1、选择一个热点或者你感兴趣的主题作为本次爬虫实践作业要完成的任务。 2、为了完成本次任务&#xff0c;需要确定从网上爬取的数据对象与范围。 3、利用python及网络爬虫相关技术实现从网上爬取相应内容数据。 …

nvm-desktop window安装,支持动态切换nodejs版本

一、安装 nvm-desktop 概述 1 、卸载干净笔记的nodejs 和nodejs的环境变量 2、安装 nvm-desktop 软件 3、配置环境变量 4、测试功能 # 此时已安装完成 其他&#xff1a;常见nodejs的问题解决参考&#xff1a;官网 mac 安装教程 https://github.com/1111mp/nvm-desktop/blob/…

【揭秘新潮流】实践教学新宠SmartEDA,让电子设计课“潮“起来!

在信息时代的浪潮下&#xff0c;电子设计课程早已不再是枯燥乏味的代名词。随着技术的飞速发展&#xff0c;一种名为SmartEDA的实践教学新选择正逐渐崭露头角&#xff0c;为电子设计课程注入了前所未有的活力与趣味性。今天&#xff0c;就让我们一起走进SmartEDA的世界&#xf…

二维数组广度优先遍历-腐烂的苹果

一、问题描述 二、解题思路 此问题通过广度优先遍历来解决&#xff0c;模拟苹果发霉变坏的过程 1.初始时遍历网格&#xff0c;借助队列来储存所有发霉的苹果&#xff0c;统计好苹果个数 2.每一分钟队列内发霉苹果都会对周围的苹果起作用&#xff08;向外部扩散&#xff09;&am…

照片变漫画怎么弄?这5个照片变漫画方法超简单

在艺术和社交融合的现在&#xff0c;将照片转换为漫画风格已经成为一种流行趋势。 无论是为了创造个性化的头像&#xff0c;还是制作有趣的社交媒体帖子&#xff0c;拥有一款能够将照片转换为漫画的软件将极大地丰富你的创意表达。 下面&#xff0c;本文将介绍几款能够实现这…

Day34:LeedCode 56. 合并区间 738.单调递增的数字 968.监控二叉树

56. 合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;int…

数学建模 —— MATLAB中的矩阵(上)

目录 矩阵的创建方法 (1)直接输入法 (2)函数创建法 (3)导入本地文件中的数据 矩阵元素的引用 矩阵元素的修改和删除 矩阵的创建方法 在MATLAB中&#xff0c;矩阵的创建方法主要有三种&#xff0c;分别是&#xff1a;直接输入法、函数创建法和导入本地文件中的数据。 (1)直…

Python 爬虫从入门到入狱之路一

实际上爬虫一共就四个主要步骤&#xff1a; 明确目标 (要知道你准备在哪个范围或者网站去搜索)爬 (将所有的网站的内容全部爬下来)取 (去掉对我们没用处的数据)处理数据&#xff08;按照我们想要的方式存储和使用&#xff09; 我们在之前写的爬虫程序中&#xff0c;都只是获取…

Ubuntu24.04下安装docker,并pull ubuntu22.04,然后编译安装vpp

一、docker安装说明 解决官方源无法下载的问题 二、使用步骤 1.更新软件包索引 sudo apt update2.安装必要的软件包&#xff0c;以允许apt通过HTTPS使用仓库 sudo apt install apt-transport-https ca-certificates curl software-properties-common3.添加Docker的官方GPG…

CNware快照技术采用双轨服务模式,显著改善虚拟机快照执行时执行后性能下降问题|附技术原理

在数字化时代&#xff0c;虚拟化技术已成为数据中心管理与云计算领域的基石。虚拟化技术允许在单一物理服务器上运行多个独立的虚拟环境&#xff0c;即虚拟机。每个虚拟机都能拥有专属的操作系统、应用程序和配置&#xff0c;彼此隔离&#xff0c;互不影响。然而&#xff0c;如…