小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
TIP 👉 东边日出西边雨,道是无晴却有晴。唐·刘禹锡《竹枝词》
前言
在我们日常项目开发中,我们在做移动端的时候会涉及到全屏窗口功能,所以封装了这个全屏窗口组件。一、全屏窗口组件(只移动端可用)
此组件建议使用js方式调用
import popupFullBox from '@/components/m/fullBox'
import UserInfo from './components/UserInfo.vue'
popupFullBox({
content: UserInfo,
contentProps: {
name: '张三',
phone: '18688886666'
}
})
popupFullBox 方法参数
1. title
- 窗口标题
- 值为字符串,默认值为:""
2. showClose
- 是否显示关闭按钮
- 值为布尔类型,默认值为:true
3. content
- 内容组件
- 值为Vue对象,必填
4. contentProps
- 弹窗内容组件的props
- 值为Object类型,属性名为组件的props的属性
5. contentEvents
- 弹窗内容组件的事件
- 值为Object类型,属性名为组件事件名,属性值为事件回调方法
6. contentWrapperStyle
- 内容包裹器样式
- 值为Object类型,属性名为驼峰格式的样式名,属性值为样式值字符串
7. className
- 弹框自定义样式名
- 值为字符串
8. scroll
- 内容区域是否需要滚动条
- 值为布尔类型
- 默认值为:true
9. scrollContentBgColor
- 滚动包裹器的背景色(当scroll参数为true是才有效)
- 值为颜色字符串,例:"#F5F5F5"
10. onBoxColse
- 点击“关闭”按钮时的回调函数
- 如果返回false,窗口不关闭
- 无返回值或者返回值为true,则窗口关闭
- 值为函数类型
- 默认值为:() => true
提示:
- 如果内容组件的部分区域需要滚动,请使用Scroll组件
二、全屏窗口示例
<template>
<div class="fullbox-demo">
<ul class="form-list">
<li class="form-item" @click="popupBox1">
<div>
<label>收款银行:</label>
<span>{{bankName}}</span>
</div>
<div>
<Icon name="right-arrow"></Icon>
</div>
</li>
<li class="form-item" @click="popupBox2">
<div>
<label>个人信息:</label>
<span>
<template v-if="userName && userPhone">{{userName}} / {{userPhone}}</template>
</span>
</div>
<div>
<Icon name="right-arrow"></Icon>
</div>
</li>
</ul>
</div>
</template>
<script>
import popupFullBox from '@/components/m/fullBox'
import BaseRadio from '@/components/base/radio/index.vue'
import UserInfo from './components/UserInfo.vue'
const bankMap = {
'01': '青莲使者',
'02': '青莲使者',
'03': '青莲使者',
'04': '青莲使者',
'05': '青莲使者',
'06': '青莲使者',
'07': '青莲使者',
'08': '青莲使者',
'09': '青莲使者',
'10': '青莲使者',
'11': '青莲使者',
'12': '青莲使者',
'13': '青莲使者',
'14': '青莲使者',
'15': '青莲使者',
'16': '青莲使者',
'17': '青莲使者',
'18': '青莲使者'
}
export default {
name: 'FullBoxDemo',
data () {
return {
bankCode: null,
bankName: null,
userName: null,
userPhone: null
}
},
methods: {
popupBox1 () {
const options = Object.keys(bankMap).map((key) => { return { value: key, text: bankMap[key] } })
const fullBox = popupFullBox({
title: '付款银行',
content: BaseRadio,
contentProps: {
title: `请选择银行:`,
value: this.bankCode,
options
},
contentEvents: {
change: (v) => {
this.bankCode = v
this.bankName = bankMap[v]
console.log('选择收款银行结果:', v)
// 300毫秒后关闭窗口
fullBox.close(300)
}
},
contentWrapperStyle: {
fontSize: '18px',
lineHeight: '45px'
}
})
},
popupBox2 () {
const fullBox = popupFullBox({
title: '个人信息',
scroll: false,
showClose: false,
content: UserInfo,
contentProps: {
name: this.userName,
phone: this.userPhone
},
contentEvents: {
change: (v) => {
this.userName = v.name
this.userPhone = v.phone
console.log('填写的用户信息:', v)
// 300毫秒后关闭窗口
fullBox.close(300)
}
},
contentWrapperStyle: {
fontSize: '18px',
lineHeight: '45px'
}
})
}
}
}
</script>
实现FullBox.vue
<!-- 全屏窗口组件 -->
<template>
<transition name="pop" :duration="300">
<div class="fullbox" v-show="visible" :class="className" @touchmove.prevent="" @mousewheel.prevent="">
<div class="back" v-show="showClose" @click="close"><Icon name="close" flip="horizontal" class="close-icon"></Icon></div>
<div class="fullbox-pane">
<div class="title" v-show="title">{{title}}</div>
<div class="content-wrap" :style="contentWrapperStyle">
<template v-if="!scroll">
<slot>
<component ref="fullBoxContent" v-if="content" :is="content" v-bind="contentProps" v-on="contentEvents"></component>
</slot>
</template>
<template v-if="scroll">
<Scroll :bounce="false" :eventId="scrollEventId" :scrollContentBgColor="scrollContentBgColor">
<slot>
<component ref="fullBoxContent" v-if="content" :is="content" v-bind="contentProps" v-on="contentEvents"></component>
</slot>
</Scroll>
</template>
</div>
</div>
</div>
</transition>
</template>
<script>
import Scroll from '@/components/base/scroll'
export default {
name: 'FullBox',
components: {
Scroll
},
props: {
// 是否显示窗口
value: {
type: Boolean,
default: false
},
// 标题
title: {
type: String,
default: '请选择'
},
// 是否显示关闭按钮
showClose: {
type: Boolean,
default: true
},
// 弹窗内容组件
content: Object,
// 弹窗内容组件的props
contentProps: Object,
// 弹窗内容组件的事件
contentEvents: Object,
// 内容包裹器样式
contentWrapperStyle: Object,
// 样式名
className: {
type: String,
default: ''
},
// 内容区域是否需要滚动条
scroll: {
type: Boolean,
default: true
},
// 滚动包裹器的背景色
scrollContentBgColor: String,
// 点击“关闭”按钮时的回调函数(如果返回false,窗口不关闭;无返回值或者返回值为true,则窗口关闭)
onBoxColse: {
type: Function,
default: () => true
}
},
data () {
return {
// 是否显示
visible: this.value,
// 滚动条初始化事件id
scrollEventId: 'fullbox' + new Date().getTime()
}
},
watch: {
value (val) {
this.visible = val
},
visible (val) {
if (val && this.scroll) {
this.$nextTick(() => {
this.$eventBus.$emit('init-scroll-' + this.scrollEventId)
})
}
}
},
methods: {
/**
* 关闭窗口
* @param delayTime 延时关闭时间(单位:毫秒)
*/
close (delayTime) {
if (this.onBoxColse(this.$refs.fullBoxContent) !== false) {
this.doClose(delayTime)
}
},
// 执行关闭窗口操作
doClose (delayTime) {
if (delayTime && typeof delayTime === 'number' && delayTime > 0) {
setTimeout(() => {
this.visible = false
this.$emit('input', this.visible)
this.$emit('close')
}, delayTime)
} else {
this.visible = false
this.$emit('input', this.visible)
this.$emit('close')
}
}
}
}
</script>
<style lang="scss" scoped>
.fullbox {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: #FFF;
box-shadow: 0 0 5px rgba(0, 0, 0, .3);
transition: all .3s ease;
z-index: 999;
.back {
height: $app-title-bar-height;
width: $app-title-bar-height;
position: absolute;
top: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
.close-icon {
font-size: 30px;
color: #666;
}
}
.fullbox-pane {
display: flex;
flex-direction: column;
height: 100%;
.title {
flex: none;
padding: 0 3em 0 1em;
height: $app-title-bar-height;
line-height: $app-title-bar-height;
background-color: #f5f5f5;
border-bottom: solid 1px #E5E5E5;
font-size: 32px;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #666;
}
.content-wrap {
position: relative;
display: flex;
flex-direction: column;
// align-items: center;
justify-content: center;
flex: 1;
overflow: hidden;
}
}
}
.pop-enter-active, .pop-leave {
transform: translateX(0);
// transform: scale(1);
// opacity: 1;
}
.pop-enter, .pop-leave-active {
transform: translateX(100%);
// transform: scale(.5);
// opacity: 0;
}
</style>
index.js
/**
* 全屏窗口组件
*/
import Vue from 'vue'
import FullBox from './FullBox.vue'
// FullBox构造函数
const FullBoxConstructor = Vue.extend({
extends: FullBox
})
function initInstance (instance, options) {
// 窗口标题
instance.title = options.title || ''
// 是否显示关闭按钮
instance.showClose = typeof options.showClose === 'boolean' ? options.showClose : true
// 窗口内容组件
instance.content = typeof options === 'object' && options._isVue ? options : options.content
// 窗口内容组件参数
instance.contentProps = typeof options.contentProps === 'object' ? options.contentProps : {}
// 窗口内容组件事件
instance.contentEvents = typeof options.contentEvents === 'object' ? options.contentEvents : {}
// 内容包裹器样式
instance.contentWrapperStyle = options.contentWrapperStyle
// 自定义样式名
instance.className = options.className || ''
// 是否显示滚动条
instance.scroll = options.scroll === false ? false : true
// scroll-content 背景颜色
instance.scrollContentBgColor = options.scrollContentBgColor
// “关闭”回调函数
if (options.onBoxColse && typeof options.onBoxColse === 'function') {
instance.onBoxColse = options.onBoxColse
}
// 父节点
let parentElement = options.parentElement || document.body
// 关闭时移除
instance.$on('input', visible => {
if (!visible) {
setTimeout(() => {
parentElement.removeChild(instance.$el)
instance.$destroy()
}, 2000)
/* // 获取popBox元素,如果Popop组件从refs中获取,如果Alert或Confirm组件,先获取Popop,在从Popop组件refs中获取
let popBox = instance.$refs.popBox || (instance.$refs.basePop && instance.$refs.basePop.$refs.popBox)
popBox.addEventListener('transitionend', event => {
// 动画完成后移除DOM节点
// parentElement.removeChild(instance.$el)
if (event.target.parentNode && event.target.parentNode.parentNode) {
event.target.parentNode.parentNode.removeChild(event.target.parentNode)
}
// 销毁组件
instance.$destroy()
}) */
}
})
// console.log('instance.$el=', instance.$el)
// 将节点添加到文档
parentElement.appendChild(instance.$el)
instance.visible = true
instance.closed = false
}
// 显示弹出窗口
export function popupFullBox (options = {}, vmExtends = {}) {
let instance = new FullBoxConstructor({
el: document.createElement('div'),
...vmExtends
})
initInstance(instance, options)
return instance
}
export default popupFullBox
「欢迎在评论区讨论」