開發一個簡單的 Vue 彈窗組件

更多文章

一個彈窗組件一般包含兩個部分,分別是遮罩層和內容層。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>

嗯... 看起來只有兩個按鈕變化了,不過不要緊,後面的內容部分確定還有用得上的時候。

功能

看起來這個簡單的彈窗組件真的是很是簡單,咱們能夠在此基礎上適當的增長一些功能,例如:拖拽。

一個彈窗組件的拖拽通常經過三個事件來控制,分別是 mousedownmousemovemouseup

  • 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>

在彈窗上增長了三個事件 mousedownmousemovemouseup,用來控制彈窗移動(點擊彈窗頭部進行拖拽)。

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>
相關文章
相關標籤/搜索