项目环境
@vue/cli 4.5.8
最终效果
需求分析
显示/隐藏
点击遮罩层能否关闭
宽度和zIndex自定义
标题栏 -显示标题和关闭按钮
主体
底部 -内置取消和确定功能
前置知识
teleport
通过其to属性可以把实例插入到对应的body
中
实现过程
搭建大体的html模版
<template> <teleport to="body"> <div class="modal"> <div class="modal-mask"></div> <div class="modal-content"> <div class="modal-header"> <slot name="header"></slot> </div> <div class="modal-body"> <slot></slot> </div> <div class="modal-footer" v-if="!footerHide"> <slot name="footer"> <button class="modal-button">取消</button> <button class="modal-button modal-button-primary">确定</button> </slot> </div> <div class="modal-close"></div> </div> </div> </teleport> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ name: "Modal", props: {}, emits: [], setup() { return { }; }, }); </script> <style lang="scss"> </style>
添加props和mehtods和css
<template> <teleport to="body"> <div class="modal" v-show="modelValue" :style="{ zIndex: zIndex }"> <div class="modal-mask" @click="maskClose"></div> <div class="modal-content" :style="{ width: width }"> <div class="modal-header"> <slot name="header">{{ title }}</slot> </div> <div class="modal-body"> <slot></slot> </div> <div class="modal-footer" v-if="!footerHide"> <slot name="footer"> <button class="modal-button" @click="closeModal('cancel')">取消</button> <button class="modal-button modal-button-primary" @click="sure">确定</button> </slot> </div> <div class="modal-close" @click="closeModal('close')"></div> </div> </div> </teleport> </template> <script lang="ts"> import { defineComponent } from "vue"; export default defineComponent({ name: "Modal", props: { modelValue: Boolean, title: String, footerHide: Boolean, width: { type: String, default: "500px", }, maskClosable: { type: Boolean, default: true, }, zIndex: { type: Number, default: 1000, }, }, emits: ["ok", "close", "update:modelValue"], // 方便TS推断 setup(props, { emit }) { const closeModal = (type: string) => { // 关闭Modal 并触发自定义事件‘close-有参数方便区分点击右上方的关闭按钮还是点击底部的取消’ }; const maskClose = () => { // 通过点击mask层关闭Modal }; const sure = () => { // 点击确定按钮关闭Modal并添加自定义事件‘ok’ }; return { closeModal, sure, maskClose }; }, }); </script> <style lang="scss"> .modal { position: fixed; top: 0; left: 0; z-index: 1000; &-mask { width: 100vw; height: 100vh; background-color: rgba($color: #000000, $alpha: 0.4); } &-content { width: 500px; position: absolute; top: 8vh; left: 50%; margin-left: -250px; background-color: #fff; border-radius: 8px; z-index: 1; font-size: 14px; } &-header { padding: 12px 16px; border-bottom: 1px solid #e4e7ed; } &-footer { padding: 12px 16px; border-top: 1px solid #e4e7ed; text-align: right; } &-body { padding: 16px; } &-close { position: absolute; top: 12px; right: 12px; width: 16px; height: 16px; cursor: pointer; &::before, &::after { content: ""; display: block; position: absolute; left: 8px; top: 0; width: 1px; height: 16px; background-color: #999; border-radius: 0.5px; transform: rotate(-45deg); z-index: -1; } &::before { transform: rotate(45deg); } &:hover::before, &:hover::after { background-color: #444; } } &-button { line-height: 1em; font-size: 14px; padding: 8px 20px; border: 1px solid #dcdfe6; outline: none; display: inline-block; border-radius: 4px; cursor: pointer; background-color: #fff; transition: 0.1s; &:hover { color: #409eff; border-color: #c6e2ff; background-color: #ecf5ff; } & + & { margin-left: 10px; } &-primary { background-color: #2d8cf0; border-color: #2d8cf0; color: white; &:hover { background: #66b1ff; border-color: #66b1ff; color: #fff; } } } } </style>
添加closeModal, sure, maskClose的具体实现--setup具体的实现
... setup(props, { emit }) { const closeModal = (type: string) => { emit("update:modelValue", false); emit("close", type); }; const maskClose = () => { if (props.maskClosable) closeModal("close"); }; const sure = () => { emit("update:modelValue", false); emit("ok"); }; return { closeModal, sure, maskClose }; }, ...
完整的代码在github