1、现状
在开发两个管理系统,现在的页面20+,其中包含不少的弹窗。在项目开发过程中没人提过弹窗要全屏的事情,且在数据量较大的位置已经增加了可全屏的入口。但老板两次说为什么不给所有的弹窗增加全屏的功能,从中感觉到了侮辱,做页面也有些年头,但从没见过有网站所有窗口支持全屏功能的。
2、思考
- 这个需求是不是合理?
个人认为有20%合理性,这主要是因为在做需求时习惯性忘记要加全屏功能。
80%不合理,因为至少80%的页面没有全屏的必要性,确认弹窗难道也有全屏的必要?
-
这个需求要不要做?
这是个伪命题,老板说要还能不做?虽然也不是一定要做。
其次,这是从技术角度还有些意思的探索。
所以在没有答应的情况下准备做一些技术实践。
-
要怎么做?
a、不可能为每个弹窗单独增加全屏功能
b、侵入性应该尽量少
c、接受少量场景缺陷(80%不会触发)
3、实现
使用的是antd + vue3
,思路是一样的,其他场景改改可用。
antd中有实现弹窗全屏的实现,我们要做的就是如何简单的实现同一界面多窗口控制。
全屏实现原理:
- 全屏时通过
wrap-class-name
设置class
- 通过
class
实现全屏
4、干货
- 全屏事件监听
const isFullScreen = ref<boolean>(false)
// 将多个控制弹窗显示的变量加入监听
watch([blockChainVisible, ()=>userUploadConfig.visible, ()=>modalConfig.visible, kvEditModel, tagEditModel], () => {
// 设置 1s 延时,避免弹窗还未显示便执行
setTimeout(() => {
const html = document.getElementsByClassName('ant-modal-header') || []
const len = html.length
if (len > 0) {
for(let i=0; i < len; i++) {
// 清理元素已存在监听,并重新增加监听
html[i].removeEventListener('dblclick', () => {})
html[i].addEventListener("dblclick", () => {
isFullScreen.value = !isFullScreen.value
});
}
}
},1000)
})
-
为弹窗增加
class
绑定:wrapClassName="isFullScreen ? 'full-modal':''"
-
设置样式
.full-modal { .ant-modal { width: 100%; top: 0; padding-bottom: 0; margin: 0; } .ant-modal-content { display: flex; flex-direction: column; height: calc(100vh); width: calc(100vw); } .ant-modal-body { flex: 1; } }
因为很多页面都需要这段样式,所以将这段样式写在
app.vue
中
如此,在弹窗显示时,双击标题区可以实现全屏与非全屏切换。
5、待优化项
- 为弹窗增加显示的切换入口(双击入口不易发现)
- 配置更加简化
6、更新
既然全屏是通过class
实现的,那为何不能通过原始的方式添加class
呢?这样就不需要isFullScreen
变量及对应的绑定。
于是:可以删除所有isFullScreen
相关的代码,只需要如下的监听和full-modal
样式就行
watch([blockChainVisible, ()=>userUploadConfig.visible, ()=>modalConfig.visible], () => {
setTimeout(() => {
const html = document.getElementsByClassName('ant-modal-wrap') || []
const len = html.length
if (len > 0) {
for(let i=0; i < len; i++) {
html[i].addEventListener("dblclick", () => {
if (html[i].classList.value.includes('full-modal')) {
html[i].classList.remove("full-modal")
} else {
html[i].classList.add("full-modal")
}
});
}
}
},1000)
})
7、终极版
解决了一下问题:
- 简化代码
- 移除双击触发的隐藏入口,增加icon型显示入口
完整使用方式:
- 在
app.vue
中配置全局样式
// app.vue
// 弹窗全局样式
.full-modal {
.ant-modal {
width: 100%;
top: 0;
padding-bottom: 0;
margin: 0;
}
.ant-modal-content {
display: flex;
flex-direction: column;
height: calc(100vh);
width: calc(100vw);
}
.ant-modal-body {
flex: 1;
}
}
// 弹窗全局切换icon样式
.screen-change {
width: 16px;
height: 16px;
background-size: 16px 16px;
margin-right: 40px;
margin-top: 5px;
background-repeat: no-repeat;
float: right;
cursor: pointer;
}
2、全局切换公共函数
// utils.ts
import { watch } from 'vue'
// 入口
export function switchFullscreen (visiableTag: any){
watch(visiableTag, () => {
setTimeout(() => {
setIcon(true)
},100)
})
}
const setModalWrapClass = (isAdd: boolean) => {
const html = document.getElementsByClassName('ant-modal-wrap') || []
const len = html.length
if (len > 0) {
for(let i=0; i < len; i++) {
if (isAdd) {
html[i].classList.add("full-modal")
} else {
html[i].classList.remove("full-modal")
}
}
}
}
const setIcon = (isAdd: boolean) => {
// 隐藏已存在icon,在弹窗消失时会同步消失,所以隐藏不会造成太大问题,当然清除更好
const screenTag = document.getElementsByClassName('screen-change') || []
const screenTagLen = screenTag.length
if (screenTagLen > 0) {
for(let i = 0; i < screenTagLen; i++) {
screenTag[i].setAttribute('style', 'display: none')
}
}
const html = document.getElementsByClassName('ant-modal-title') || []
const len = html.length
if (len > 0) {
for(let i=0; i < len; i++) {
let newImage = document.createElement('img')
newImage.setAttribute('class', 'screen-change')
if (isAdd) {
newImage.setAttribute('src', './src/assets/fullscreen.png')
newImage.setAttribute('id', 'fullscreen')
html[i].appendChild(newImage)
newImage.onclick = () => {
setModalWrapClass(true)
setIcon(false)
}
} else {
newImage.setAttribute('src', './src/assets/collapse.png')
newImage.setAttribute('id', 'cancelfullscreen')
html[i].appendChild(newImage)
newImage.onclick = () => {
setModalWrapClass(false)
setIcon(true)
}
}
}
}
}
3、页面中按需引入
<script lang="ts" setup>
import { switchFullscreen } from '@/utils'
import { onMounted } from 'vue'
onMounted(() => {
// 传入需要控制的弹窗visiable参数
switchFullscreen([blockChainVisible, ()=>modalConfig.visible])
});
</script>
正常效果:
放大效果: