ElementPlus el-message-box樣式錯(cuò)位問(wèn)題及解決
ElementPlus el-message-box樣式錯(cuò)位
不知道從哪個(gè)版本開(kāi)始發(fā)現(xiàn),element-plus的message-box在有圖標(biāo)的時(shí)候,錯(cuò)位比較嚴(yán)重,f12跟官網(wǎng)的樣式對(duì)比后發(fā)現(xiàn),好家伙!原來(lái)position: absolute被覆蓋了。
錯(cuò)位效果截圖
ElementPlus官網(wǎng)代碼截圖
本地項(xiàng)目代碼截圖
可以看出在本地中el-message-box__status樣式的position并未生效
解決方案
修改css樣式
.el-message-box__status { position: absolute !important; }
完成效果截圖
實(shí)現(xiàn)ElementPlus的MessageBox
ElementPlus 的 MessageBox 主要功能分析
- 1.提供一個(gè)函數(shù)用來(lái)展示消息框,這個(gè)函數(shù)提供如標(biāo)題、內(nèi)容等配置參數(shù)
- 2.消息框出現(xiàn)和消失時(shí)有動(dòng)畫(huà)
- 3.使用 Promise 獲取消息框的結(jié)果
基本思路
Vue 中動(dòng)態(tài)顯示一個(gè)組件,無(wú)非就是通過(guò) h 函數(shù)創(chuàng)建 VNode,并且把這個(gè) VNode 掛載在 DOM 樹(shù)上。這里有兩種掛載的方式:
createApp
在 main.js 中創(chuàng)建 Vue 實(shí)例用的就是這種方法,這也是 Vue3 中代替 Vue2 的 Vue.extend 的方法,簡(jiǎn)單使用案例如下:
const app = createApp(MessageBox, { ?? ?message: 'hello?' }) // 創(chuàng)建無(wú)父元素的文檔對(duì)象,掛載到 DOM 中后直接調(diào)用 app.unmount() 移除 MessageBox // 和掛載到 div 的區(qū)別是 MessageBox 不會(huì)作為 div 的子元素 const frg = document.createDocumentFragment() // app.mount 返回組件實(shí)例 // 組件實(shí)例內(nèi)包含 expose 出來(lái)的方法或者數(shù)據(jù) const vm = app.mount(frg) document.body.appendChild(frg)
h + render
和 createApp 方法大同小異
const vn = h(MessageBox, { ?? ?message: 'vnode' }) render(vn, container) document.body.appendChild(container)
可以看到無(wú)論是 createApp 方法還是 h 方法,都可以在第二個(gè)參數(shù)中傳入組件的 props,于是我們可以封裝一個(gè)動(dòng)態(tài)顯示組件的函數(shù),這個(gè)函數(shù)接受組件的 props。但是在封裝函數(shù)之前,讓我們先來(lái)實(shí)現(xiàn)
MessageBox 這個(gè)組件
MessageBox 組件實(shí)現(xiàn)
直接貼代碼,講下最關(guān)鍵的幾點(diǎn):
進(jìn)入退出動(dòng)態(tài)效果實(shí)現(xiàn)
設(shè)置一個(gè) transition flag,初始時(shí)為 false,組件 mounted 后 flag 為 true。
全局遮罩層
一個(gè) fixed 定位寬高為100%的 div。
剩下的主要看 script 部分
<template> ? ? <transition name="messagebox-fade" @after-leave="onDestroy"> ? ? ? ? <div @click="setVisiable(false)" v-show="visiable" ? ? ? ? ? ? class="z-50 flex items-center justify-center fixed top-0 left-0 w-full h-full bg-dark-50/50"> ? ? ? ? ? ? <div @click.stop class="transform -translate-y-1/2 p-2 rounded min-w-3/7 bg-white"> ? ? ? ? ? ? ? ? <p class="text-sm text-gray-600 font-light"> {{ title }}</p> ? ? ? ? ? ? ? ? <p class="my-4 text-lg"> ? ? ? ? ? ? ? ? ? ? <content-view :type="type"></content-view> ? ? ? ? ? ? ? ? </p> ? ? ? ? ? ? ? ? <div class="w-full flex justify-end items-center"> ? ? ? ? ? ? ? ? ? ? <button @click="okBtnClicked" class="btn btn-primary"> {{ ok }}</button> ? ? ? ? ? ? ? ? ? ? <button @click="cancelBtnClicked" v-if="cancel" class="ml-2 btn btn-danger"> {{ cancel }}</button> ? ? ? ? ? ? ? ? </div> ? ? ? ? ? ? </div> ? ? ? ? </div> ? ? </transition> </template>
<script setup> import { ref, onMounted, h } from 'vue' const { onOK, onCancel, message } = defineProps({ ? ? title: { ? ? ? ? type: String, ? ? ? ? default: '提示' ? ? }, ? ? message: { ? ? ? ? type: String, ? ? ? ? default: '' ? ? }, ? ? type: { ? ? ? ? type: String, ? ? ? ? validator: (value) => { ? ? ? ? ? ? return ['confirm', 'prompt'].includes(value) ? ? ? ? } ? ? }, ? ? ok: { ? ? ? ? type: String, ? ? ? ? default: 'OK' ? ? }, ? ? cancel: { ? ? ? ? type: String, ? ? ? ? default: '' ? ? }, ? ? onDestroy: Function, ? ? onOK: Function, ? ? onCancel: Function }) const promptContent = ref('') const ContentView = ({ type }) => { ? ? switch (type) { ? ? ? ? case (!type || 'confirm'): ? ? ? ? ? ? return h('p', null, message) ? ? ? ? case 'prompt': ? ? ? ? ? ? return h('input', { ? ? ? ? ? ? ? ? class: 'messagebox-input', ? ? ? ? ? ? ? ? onInput: (e) => promptContent.value = e.target.value ? ? ? ? ? ? }) ? ? } } const visiable = ref(false); const setVisiable = (vis) => { ? ? visiable.value = vis; } const okBtnClicked = () => { ? ? setVisiable(false); ? ? onOK(promptContent.value) } const cancelBtnClicked = () => { ? ? setVisiable(false) ? ? onCancel() } onMounted(() => { ? ? setVisiable(true); }) </script>
<style scoped> .btn { ? ? @apply outline-gray-100 border-none p-1 rounded bg-warm-gray-200 } .btn-primary { ? ? @apply bg-sky-300 } .messagebox-input { ? ? @apply border rounded border-light-900 outline-none w-full py-1 px-2 text-lg? } .messagebox-fade-enter-from, .messagebox-fade-leave-to { ? ? @apply opacity-0 } .messagebox-fade-enter-active, .messagebox-fade-leave-active { ? ? @apply transition-opacity } </style>
函數(shù)式組件
// 第一個(gè)參數(shù)是 props,第二個(gè)參數(shù)是 context,類(lèi)似 setup 的參數(shù) // 返回值為 VNode // 可以導(dǎo)出或者直接在組件內(nèi)部使用 const ContentView = ({ type }) => { ? ? switch (type) { ? ? ? ? case (!type || 'confirm'): ? ? ? ? ? ? return h('p', null, message) ? ? ? ? case 'prompt': ? ? ? ? ? ? return h('input', { ? ? ? ? ? ? ? ? class: 'messagebox-input', ? ? ? ? ? ? ? ? onInput: (e) => promptContent.value = e.target.value ? ? ? ? ? ? }) ? ? } }
封裝 MessageBox 顯示函數(shù)
import { createApp } from 'vue' import MessageBoxCpn from './MessageBox.vue' const fields = ['confirm', 'prompt'] export default function MessageBox(options) { ? ? return new Promise((resolve, reject) => { ? ? ? ? const app = createApp(MessageBoxCpn, { ? ? ? ? ? ? ...options, ? ? ? ? ? ? onDestroy: () => { ? ? ? ? ? ? ? ? app.unmount() ? ? ? ? ? ? }, ? ? ? ? ? ? onOK: (value) => { ? ? ? ? ? ? ? ? resolve(value) ? ? ? ? ? ? }, ? ? ? ? ? ? onCancel: () => { ? ? ? ? ? ? ? ? reject() ? ? ? ? ? ? } ? ? ? ? }) ? ? ? ? const frg = document.createDocumentFragment() ? ? ? ? app.mount(frg) ? ? ? ? document.body.appendChild(frg) ? ? }) } fields.forEach(field => { ? ? MessageBox[field] = (options) => { ? ? ? ? options.type = field ? ? ? ? return MessageBox(options) ? ? } })
通過(guò)組件的 props 傳入回調(diào),實(shí)現(xiàn)按鈕點(diǎn)擊事件的傳遞、MessageBox 關(guān)閉時(shí)取消掛載的操作。
另外可以通過(guò) MessageBox.prompt 等靜態(tài)方法直接調(diào)用對(duì)應(yīng) type 的 MessageBox,實(shí)現(xiàn)方式是在 MessageBox 上掛上對(duì)應(yīng)的靜態(tài)方法,并且覆蓋 options 的 type 選項(xiàng)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于Vue自定義指令實(shí)現(xiàn)按鈕級(jí)權(quán)限控制思路詳解
這篇文章主要介紹了基于vue自定義指令實(shí)現(xiàn)按鈕級(jí)權(quán)限控制,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05vue3組件化開(kāi)發(fā)常用API知識(shí)點(diǎn)總結(jié)
Vue是目前Web前端最流行的開(kāi)發(fā)框架技術(shù),?下面這篇文章主要給大家介紹了關(guān)于vue3組件化開(kāi)發(fā)常用API的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06vue單頁(yè)面打包文件大?首次加載慢?nginx帶你飛,從7.5M到1.3M蛻變過(guò)程(推薦)
這篇文章主要介紹了vue單頁(yè)面打包文件大?首次加載慢?nginx帶你飛,從7.5M到1.3M蛻變過(guò)程,需要的朋友可以參考下2018-01-01vue el-tree 默認(rèn)展開(kāi)第一個(gè)節(jié)點(diǎn)的實(shí)現(xiàn)代碼
這篇文章主要介紹了vue el-tree 默認(rèn)展開(kāi)第一個(gè)節(jié)點(diǎn)的實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05vue3界面使用router及使用watch監(jiān)聽(tīng)router的改變
vue2中使用router非常簡(jiǎn)單,但是vue3中略微有些改變,通過(guò)本文講解下他的改變,對(duì)vue3?watch監(jiān)聽(tīng)router相關(guān)知識(shí)感興趣的朋友一起看看吧2022-11-11vue項(xiàng)目中圖片懶加載時(shí)出現(xiàn)的問(wèn)題及解決
這篇文章主要介紹了vue項(xiàng)目中圖片懶加載時(shí)出現(xiàn)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04Vue之監(jiān)聽(tīng)數(shù)據(jù)的原理詳解
這篇文章主要為大家介紹了Vue之監(jiān)聽(tīng)數(shù)據(jù)的原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助<BR>2021-11-11