更多文章
一個彈窗組件通常包含兩個部分,分別是遮罩層和內容層。
遮罩層是背景層,一般是半透明或不透明的黑色。
內容層是放我們要展示的內容的容器。
<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>
現在彈窗組件的結構已經搭建出來了。
- 類
modal-bg
: 遮罩層 - 類
modal-container
: 內容層容器 - 類
modal-header
: 內容層頭部 - 類
modal-main
: 內容層主體部分(用來展示內容) - 類
modal-footer
: 內容層腳部 - 屬性
v-show
: 控制彈窗的展示與關閉 - 屬性
title
: 標題 - 方法
hideModal
: 點擊取消的回調函數 - 方法
submit
: 點擊確認的回調函數 - 插槽
slot
: 用來展示內容
定義完 HTML 結構,還得定義組件的 props
屬性,用來接收父組件的傳參,以方便在父組件通過屬性來控制彈窗。
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
(彈窗標題)。
另外還有兩個方法,分別是點擊取消和確認的回調函數,它們的作用是觸發對應的事件。
到這里,一個簡單的彈窗組件已經完成了(樣式后面再說)。
如何調用
一個組件寫完了,要怎么調用呢?
假設這個組件的文件名為 Modal.vue
,我們在父組件里這樣調用 (假設父組件和彈窗組件在同一文件夾下)。
<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,改造后變成這樣。
<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>