一個彈窗組件一般包含兩個部分,分別是遮罩層和內容層。css
遮罩層是背景層,通常是半透明或不透明的黑色。html
內容層是放咱們要展現的內容的容器。vue
<template> <div class="modal-bg" v-show="show"> <div class="modal-container"> <div class="modal-header"> {{ title }} </div> <div class="modal-main"> <slot></slot> </div> <div class="modal-footer"> <button @click="hideModal">取消</button> <button @click="submit">確認</button> </div> </div> </div> </template>
如今彈窗組件的結構已經搭建出來了。node
modal-bg
: 遮罩層modal-container
: 內容層容器modal-header
: 內容層頭部modal-main
: 內容層主體部分(用來展現內容)modal-footer
: 內容層腳部v-show
: 控制彈窗的展現與關閉title
: 標題hideModal
: 點擊取消的回調函數submit
: 點擊確認的回調函數slot
: 用來展現內容定義完 HTML 結構,還得定義組件的 props
屬性,用來接收父組件的傳參,以方便在父組件經過屬性來控制彈窗。git
export default { name: 'modal', props: { show: { type: Boolean, default: false }, title: { type: String, default: '' }, }, methods: { hideModal() { this.$emit('hideModal') }, submit() { this.$emit('submit') }, } }
從上述代碼可知,組件只有兩個 prop
屬性,分別是 show
(控制彈窗展現與關閉)和 title
(彈窗標題)。
另外還有兩個方法,分別是點擊取消和確認的回調函數,它們的做用是觸發對應的事件。
到這裏,一個簡單的彈窗組件已經完成了(樣式後面再說)。github
一個組件寫完了,要怎麼調用呢?element-ui
假設這個組件的文件名爲 Modal.vue
,咱們在父組件裏這樣調用 (假設父組件和彈窗組件在同一文件夾下)。ide
<Modal :show="show" :title="title" @hideModal="hideModal" @submit="submit"> <p>這裏放彈窗的內容</p> </Modal>
import Modal from './Modal.vue' export default { data() { return { title: '彈窗標題', show: true, } }, components: { Modal }, methods: { hideModal() { // 取消彈窗回調 this.show = false }, submit() { // 確認彈窗回調 this.show = false } } }
把子組件要求的兩個屬性和兩個方法都寫上,如今來看看這個彈窗的效果。
一個簡單的彈窗組件就這樣完成了。函數
如今市面上的 UI 庫特別多,因此一些通用的組件樣式不建議本身寫,直接用現成的就好。在這個組件上,咱們能夠使用 element-ui,改造後變成這樣。flex
<template> <div class="modal-bg" v-show="show"> <div class="modal-container"> <div class="modal-header"> {{ title }} </div> <div class="modal-main"> <slot></slot> </div> <div class="modal-footer"> <el-button round @click="hideModal">取消</el-button> <el-button type="primary" round @click="submit">確認</el-button> </div> </div> </div> </template>
嗯... 看起來只有兩個按鈕變化了,不過不要緊,後面的內容部分確定還有用得上的時候。
看起來這個簡單的彈窗組件真的是很是簡單,咱們能夠在此基礎上適當的增長一些功能,例如:拖拽。
一個彈窗組件的拖拽通常經過三個事件來控制,分別是 mousedown
、mousemove
、mouseup
。
mousedown
用來獲取鼠標點擊時彈窗的座標mousemove
用來計算鼠標移動時彈窗的座標mouseup
取消彈窗的移動先來看代碼。
<template> <div class="modal-bg" v-show="show" @mousemove="modalMove" @mouseup="cancelMove"> <div class="modal-container" :class="position"> <div class="modal-header" @mousedown="setStartingPoint"> {{ title }} </div> <div class="modal-main"> <slot></slot> </div> <div class="modal-footer"> <el-button round @click="hideModal">取消</el-button> <el-button type="primary" round @click="submit">確認</el-button> </div> </div> </div> </template>
在彈窗上增長了三個事件 mousedown
、mousemove
、mouseup
,用來控制彈窗移動(點擊彈窗頭部進行拖拽)。
data() { return { x: 0, // 彈窗 X 座標 y: 0, // 彈窗 Y 座標 node: null, // 彈窗元素 isCanMove: false // 是否能拖動彈窗 } }, mounted() { // 將彈窗元素賦值給 node this.node = document.querySelector('.modal-container') }, setStartingPoint(e) { this.x = e.clientX - this.node.offsetLeft this.y = e.clientY - this.node.offsetTop this.isCanMove = true }, modalMove(e) { if (this.isCanMove) { this.node.style.left = e.clientX - this.x + 'px' this.node.style.top = e.clientY - this.y + 'px' } }, cancelMove() { this.isCanMove = false }
經過這些新增的代碼,這個彈窗就具備了拖拽的功能。
最後附上這個彈窗組件的完整代碼
<template> <div class="modal-bg" v-show="show" @mousemove="modalMove" @mouseup="cancelMove"> <div class="modal-container"> <div class="modal-header" @mousedown="setStartingPoint"> {{ title }} </div> <div class="modal-main"> <slot></slot> </div> <div class="modal-footer"> <el-button round @click="hideModal">取消</el-button> <el-button type="primary" round @click="submit">確認</el-button> </div> </div> </div> </template>
<script> export default { name: 'modal', props: { show: { type: Boolean, default: false }, title: { type: String, default: '' }, }, data() { return { x: 0, y: 0, node: null, isCanMove: false } }, mounted() { this.node = document.querySelector('.modal-container') }, methods: { hideModal() { this.$emit('hideModal') }, submit() { this.$emit('submit') }, setStartingPoint(e) { this.x = e.clientX - this.node.offsetLeft this.y = e.clientY - this.node.offsetTop this.isCanMove = true }, modalMove(e) { if (this.isCanMove) { this.node.style.left = e.clientX - this.x + 'px' this.node.style.top = e.clientY - this.y + 'px' } }, cancelMove() { this.isCanMove = false } } } </script>
<style scoped> .modal-bg { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,.5); z-index: 10; } .modal-container { background: #fff; border-radius: 10px; overflow: hidden; position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); } .modal-header { height: 56px; background: #409EFF; color: #fff; display: flex; align-items: center; justify-content: center; cursor: move; } .modal-footer { display: flex; align-items: center; justify-content: center; height: 57px; border-top: 1px solid #ddd; } .modal-footer button { width: 100px; } .modal-main { padding: 15px 40px; } </style>