一個彈窗組件一般包含兩個部分,分別是遮罩層和內容層。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" @mouseup="cancelMove">
<div class="modal-container" :class="position">
<div class="modal-header" @mousedown="setStartingPoint" @mousemove="modalMove" @mouseup="cancelMove">
{{ 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" @mouseup="cancelMove">
<div class="modal-container">
<div class="modal-header" @mousedown="setStartingPoint" @mousemove="modalMove" @mouseup="cancelMove">
{{ 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>
複製代碼